diff --git a/core/api/v1/config_api.go b/core/api/v1/config_api.go new file mode 100644 index 0000000..c256485 --- /dev/null +++ b/core/api/v1/config_api.go @@ -0,0 +1,152 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + cache2 "cutego/core/cache" + "cutego/core/entity" + "cutego/core/service" + "cutego/pkg/cache" + "cutego/pkg/excels" + "cutego/pkg/file" + "cutego/pkg/page" + "cutego/pkg/resp" + "cutego/pkg/util" + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + "net/http" + "strconv" + "strings" +) + +type ConfigApi struct { + configService service.ConfigService +} + +// GetConfigValue 根据参数键名查询参数值 +func (a ConfigApi) GetConfigValue(c *gin.Context) { + param := c.Query("key") + if !gotool.StrUtils.HasEmpty(param) { + key := a.configService.GetConfigKey(param) + c.JSON(http.StatusOK, resp.Success(key.ConfigValue, "")) + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("参数不合法")) + } +} + +// List 查询设置列表 +func (a ConfigApi) List(c *gin.Context) { + query := request.ConfigQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + find, i := a.configService.FindPage(query) + resp.OK(c, page.Page{ + List: find, + Total: i, + Size: query.PageSize, + }) +} + +// Add 添加数据 +func (a ConfigApi) Add(c *gin.Context) { + config := entity.SysConfig{} + if c.Bind(&config) != nil { + resp.ParamError(c) + return + } + // 检验key是否存在 + if a.configService.CheckConfigKeyUnique(config) { + resp.Error(c, "新增参数'"+config.ConfigName+"'失败, 参数键名已存在") + } + config.CreateBy = util.GetUserInfo(c).UserName + if a.configService.Save(config) > 0 { + // 进行缓存数据添加 + cache2.SetRedisConfig(config) + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Get 查询数据 +func (a ConfigApi) Get(c *gin.Context) { + param := c.Param("configId") + configId, _ := strconv.ParseInt(param, 10, 64) + resp.OK(c, a.configService.GetInfo(configId)) +} + +// Edit 修改数据 +func (a ConfigApi) Edit(c *gin.Context) { + config := entity.SysConfig{} + if c.Bind(&config) != nil { + resp.ParamError(c) + return + } + // 检验key是否存在 + if a.configService.CheckConfigKeyUnique(config) { + resp.Error(c, "修改参数'"+config.ConfigName+"'失败, 参数键名已存在") + } + config.UpdateBy = util.GetUserInfo(c).UserName + if a.configService.Edit(config) > 0 { + cache2.SetRedisConfig(config) + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Delete 删除数据 +func (a ConfigApi) Delete(c *gin.Context) { + ids := strings.Split(c.Param("ids"), ",") + list := make([]int64, 0) + for _, id := range ids { + parseInt, _ := strconv.ParseInt(id, 10, 64) + list = append(list, parseInt) + } + // 进行校验, 查看是否可以删除 + byIds := a.configService.CheckConfigByIds(list) + for _, config := range *byIds { + if config.ConfigType == "Y" { + resp.Error(c, "内置参数"+config.ConfigName+"不能删除") + return + } + } + // 进行删除 + if a.configService.Remove(list) { + // 刷新缓存 + strs := make([]string, 0) + for _, config := range *byIds { + strs = append(strs, config.ConfigKey) + } + cache.RemoveList(strs) + resp.OK(c) + } else { + resp.Error(c) + } +} + +// RefreshCache 刷新缓存 +func (a ConfigApi) RefreshCache(c *gin.Context) { + all := a.configService.FindAll() + for _, sysConfig := range *all { + cache2.RemoveRedisConfig(sysConfig.ConfigKey) + cache2.SetRedisConfig(sysConfig) + } +} + +// Export 导出数据 +func (a ConfigApi) Export(c *gin.Context) { + query := request.ConfigQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + find, _ := a.configService.FindPage(query) + items := make([]interface{}, 0) + for _, config := range *find { + items = append(items, config) + } + _, files := excels.ExportExcel(items, "配置表") + file.DownloadExcel(c, files) +} diff --git a/core/api/v1/cron_job_api.go b/core/api/v1/cron_job_api.go new file mode 100644 index 0000000..daadc05 --- /dev/null +++ b/core/api/v1/cron_job_api.go @@ -0,0 +1,106 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/core/service" + "cutego/pkg/cache" + "cutego/pkg/page" + "cutego/pkg/resp" + "github.com/gin-gonic/gin" + "strconv" + "strings" +) + +type CronJobApi struct { + cronJobService service.CronJobService +} + +// List 获取定时任务数据 +func (a CronJobApi) List(c *gin.Context) { + query := request.CronJobQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + find, i := a.cronJobService.FindPage(query) + resp.OK(c, page.Page{ + List: find, + Total: i, + Size: query.PageSize, + }) +} + +// Edit 修改定时任务 +func (a CronJobApi) Edit(c *gin.Context) { + dictType := entity.SysDictType{} + if c.Bind(&dictType) != nil { + resp.ParamError(c) + return + } + //检验定时任务是否存在 + if a.dictTypeService.CheckDictTypeUnique(dictType) { + resp.Error(c, "修改字典'"+dictType.DictName+"'失败, 定时任务已存在") + return + } + //修改数据 + if a.dictTypeService.Edit(dictType) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Add 新增定时任务 +func (a CronJobApi) Add(c *gin.Context) { + dictType := entity.SysDictType{} + if c.Bind(&dictType) != nil { + resp.ParamError(c) + return + } + //检验定时任务是否存在 + if a.dictTypeService.CheckDictTypeUnique(dictType) { + resp.Error(c, "新增字典'"+dictType.DictName+"'失败, 定时任务已存在") + return + } + //新增定时任务 + if a.dictTypeService.Save(dictType) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Remove 批量删除定时任务 +func (a CronJobApi) Remove(c *gin.Context) { + param := c.Param("dictId") + split := strings.Split(param, ",") + ids := make([]int64, 0) + types := make([]string, 0) + for _, s := range split { + parseInt, _ := strconv.ParseInt(s, 10, 64) + ids = append(ids, parseInt) + } + //校验定时任务是否使用 + for _, id := range ids { + dictType := a.dictTypeService.GetById(id) + if len(a.dictDataService.FindByDictType(dictType.DictType)) > 0 { + resp.Error(c, dictType.DictName+"已分配,不能删除") + return + } + types = append(types, dictType.DictType) + } + //批量删除 + if a.dictTypeService.Remove(ids) { + //从缓存中删除数据 + cache.RemoveList(types) + resp.OK(c) + } else { + resp.Error(c) + } +} + +// 改变任务状态 +func (a CronJobApi) ChangeStatus(context *gin.Context) { + +} diff --git a/core/api/v1/dept_api.go b/core/api/v1/dept_api.go new file mode 100644 index 0000000..22aec25 --- /dev/null +++ b/core/api/v1/dept_api.go @@ -0,0 +1,149 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/core/service" + "cutego/pkg/resp" + "cutego/pkg/tree/tree_dept" + "cutego/pkg/util" + "github.com/gin-gonic/gin" + "net/http" + "strconv" + "strings" + "time" +) + +type DeptApi struct { + deptService service.DeptService +} + +// TreeSelect 查询部门菜单树 +func (a DeptApi) DeptTreeSelect(c *gin.Context) { + query := request.DeptQuery{} + if c.BindQuery(&query) == nil { + treeSelect := a.deptService.FindTreeSelect(query) + list := tree_dept.DeptList{} + c.JSON(http.StatusOK, resp.Success(list.GetTree(treeSelect))) + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("参数绑定错误")) + } +} + +// RoleDeptTreeSelect 加载对应角色部门列表树 +func (a DeptApi) RoleDeptTreeSelect(c *gin.Context) { + m := make(map[string]interface{}) + param := c.Param("roleId") + roleId, _ := strconv.ParseInt(param, 10, 64) + checkedKeys := a.deptService.FindDeptListByRoleId(roleId) + m["checkedKeys"] = checkedKeys + treeSelect := a.deptService.FindTreeSelect(request.DeptQuery{}) + list := tree_dept.DeptList{} + tree := list.GetTree(treeSelect) + m["depts"] = tree + resp.OK(c, m) +} + +// Find 查询部门列表 +func (a DeptApi) Find(c *gin.Context) { + query := request.DeptQuery{} + if c.BindQuery(&query) != nil { + resp.ParamError(c) + return + } + resp.OK(c, a.deptService.FindDeptList(query)) +} + +// ExcludeChild 查询部门列表(排除节点) +func (a DeptApi) ExcludeChild(c *gin.Context) { + param := c.Param("deptId") + deptId, err := strconv.Atoi(param) + if err != nil { + resp.ParamError(c) + return + } + list := a.deptService.FindDeptList(request.DeptQuery{}) + var depts = *list + deptList := make([]entity.SysDept, 0) + for _, dept := range depts { + if dept.DeptId == deptId || strings.Contains(dept.Ancestors, strconv.Itoa(deptId)) { + continue + } + deptList = append(deptList, dept) + } + resp.OK(c, deptList) +} + +// GetInfo 根据部门编号获取详细信息 +func (a DeptApi) GetInfo(c *gin.Context) { + param := c.Param("deptId") + deptId, err := strconv.Atoi(param) + if err != nil { + resp.ParamError(c) + return + } + resp.OK(c, a.deptService.GetDeptById(deptId)) +} + +// Add 添加部门 +func (a DeptApi) Add(c *gin.Context) { + dept := entity.SysDept{} + if c.Bind(&dept) != nil { + resp.ParamError(c) + return + } + // 校验部门名称是否唯一 + unique := a.deptService.CheckDeptNameUnique(dept) + if unique { + resp.Error(c, "新增部门'"+dept.DeptName+"'失败, 部门名称已存在") + return + } + info := a.deptService.GetDeptById(dept.ParentId) + if info.Status == "1" { + resp.Error(c, "部门停用, 不允许新增") + return + } + dept.Ancestors = info.Ancestors + "," + strconv.Itoa(dept.ParentId) + dept.CreateBy = util.GetUserInfo(c).UserName + if a.deptService.Save(dept) > 0 { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Delete 删除部门 +func (a DeptApi) Delete(c *gin.Context) { + param := c.Param("deptId") + deptId, _ := strconv.Atoi(param) + // 是否存在部门子节点 + if a.deptService.HasChildByDeptId(deptId) > 0 { + resp.Error(c, "存在下级部门,不允许删除") + return + } + if a.deptService.CheckDeptExistUser(deptId) > 0 { + resp.Error(c, "部门存在用户,不允许删除") + return + } + if a.deptService.Remove(deptId) > 0 { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Edit 修改部门数据接口 +func (a DeptApi) Edit(c *gin.Context) { + dept := entity.SysDept{} + if c.Bind(&dept) != nil { + resp.ParamError(c) + return + } + dept.UpdateTime = time.Now() + dept.UpdateBy = util.GetUserInfo(c).UserName + if a.deptService.Edit(dept) { + resp.OK(c) + } else { + resp.Error(c) + } +} diff --git a/core/api/v1/dict_data_api.go b/core/api/v1/dict_data_api.go new file mode 100644 index 0000000..e67b7cb --- /dev/null +++ b/core/api/v1/dict_data_api.go @@ -0,0 +1,128 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/api/v1/response" + "cutego/core/entity" + "cutego/core/service" + "cutego/pkg/excels" + "cutego/pkg/file" + "cutego/pkg/page" + "cutego/pkg/resp" + "cutego/pkg/util" + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + "net/http" + "strconv" + "strings" + "time" +) + +type DictDataApi struct { + dictDataService service.DictDataService +} + +// GetByType 根据字典类型查询字典数据d +func (a DictDataApi) GetByType(c *gin.Context) { + param := c.Param("dictType") + if !gotool.StrUtils.HasEmpty(param) { + list := a.dictDataService.FindByDictType(param) + var result []response.DictDataResponse + for _, data := range list { + result = append(result, response.DictDataResponse{ + DictCode: data.DictCode, + DictLabel: data.DictLabel, + DictSort: data.DictSort, + DictValue: data.DictValue, + DictType: data.DictType, + IsDefault: data.IsDefault, + }) + } + c.JSON(http.StatusOK, resp.Success(result)) + } +} + +// List 查询字典数据集合 +func (a DictDataApi) List(c *gin.Context) { + query := request.DiceDataQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + list, i := a.dictDataService.FindPage(query) + resp.OK(c, page.Page{ + List: list, + Total: i, + Size: query.PageSize, + }) +} + +// Get 根据id查询字典数据 +func (a DictDataApi) Get(c *gin.Context) { + param := c.Param("dictCode") + dictCode, _ := strconv.ParseInt(param, 10, 64) + resp.OK(c, a.dictDataService.GetByCode(dictCode)) +} + +// Add 添加字典数据 +func (a DictDataApi) Add(c *gin.Context) { + data := entity.SysDictData{} + if c.Bind(&data) != nil { + resp.ParamError(c) + return + } + data.CreateBy = util.GetUserInfo(c).UserName + if a.dictDataService.Save(data) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Edit 编辑字典数据 +func (a DictDataApi) Edit(c *gin.Context) { + data := entity.SysDictData{} + if c.Bind(&data) != nil { + resp.ParamError(c) + return + } + data.UpdateBy = util.GetUserInfo(c).UserName + data.UpdateTime = time.Now() + if a.dictDataService.Edit(data) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Delete 删除数据 +func (a DictDataApi) Delete(c *gin.Context) { + param := c.Param("dictCode") + split := strings.Split(param, ",") + dictCodeList := make([]int64, 0) + for _, s := range split { + diceCode, _ := strconv.ParseInt(s, 10, 64) + dictCodeList = append(dictCodeList, diceCode) + } + if a.dictDataService.Remove(dictCodeList) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Export 导出excel +func (a DictDataApi) Export(c *gin.Context) { + query := request.DiceDataQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + items := make([]interface{}, 0) + list, _ := a.dictDataService.FindPage(query) + for _, data := range *list { + items = append(items, data) + } + _, files := excels.ExportExcel(items, "字典数据表") + file.DownloadExcel(c, files) +} diff --git a/core/api/v1/dict_type_api.go b/core/api/v1/dict_type_api.go new file mode 100644 index 0000000..33ee8bb --- /dev/null +++ b/core/api/v1/dict_type_api.go @@ -0,0 +1,133 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + service2 "cutego/core/service" + "cutego/pkg/cache" + "cutego/pkg/excels" + "cutego/pkg/file" + "cutego/pkg/page" + "cutego/pkg/resp" + "github.com/gin-gonic/gin" + "strconv" + "strings" +) + +type DictTypeApi struct { + dictTypeService service2.DictTypeService + dictDataService service2.DictDataService +} + +// List 获取字典类型数据 +func (a DictTypeApi) List(c *gin.Context) { + query := request.DictTypeQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + find, i := a.dictTypeService.FindPage(query) + resp.OK(c, page.Page{ + List: find, + Total: i, + Size: query.PageSize, + }) +} + +// Get 根据id查询字典类型数据 +func (a DictTypeApi) Get(c *gin.Context) { + param := c.Param("dictTypeId") + dictTypeId, _ := strconv.ParseInt(param, 0, 64) + resp.OK(c, a.dictTypeService.GetById(dictTypeId)) +} + +// Edit 修改字典类型 +func (a DictTypeApi) Edit(c *gin.Context) { + dictType := entity.SysDictType{} + if c.Bind(&dictType) != nil { + resp.ParamError(c) + return + } + //检验字典类型是否存在 + if a.dictTypeService.CheckDictTypeUnique(dictType) { + resp.Error(c, "修改字典'"+dictType.DictName+"'失败, 字典类型已存在") + return + } + //修改数据 + if a.dictTypeService.Edit(dictType) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Add 新增字典类型 +func (a DictTypeApi) Add(c *gin.Context) { + dictType := entity.SysDictType{} + if c.Bind(&dictType) != nil { + resp.ParamError(c) + return + } + //检验字典类型是否存在 + if a.dictTypeService.CheckDictTypeUnique(dictType) { + resp.Error(c, "新增字典'"+dictType.DictName+"'失败, 字典类型已存在") + return + } + //新增字典类型 + if a.dictTypeService.Save(dictType) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Remove 批量删除字典类型 +func (a DictTypeApi) Remove(c *gin.Context) { + param := c.Param("dictId") + split := strings.Split(param, ",") + ids := make([]int64, 0) + types := make([]string, 0) + for _, s := range split { + parseInt, _ := strconv.ParseInt(s, 10, 64) + ids = append(ids, parseInt) + } + //校验字典类型是否使用 + for _, id := range ids { + dictType := a.dictTypeService.GetById(id) + if len(a.dictDataService.FindByDictType(dictType.DictType)) > 0 { + resp.Error(c, dictType.DictName+"已分配,不能删除") + return + } + types = append(types, dictType.DictType) + } + //批量删除 + if a.dictTypeService.Remove(ids) { + //从缓存中删除数据 + cache.RemoveList(types) + resp.OK(c) + } else { + resp.Error(c) + } +} + +// RefreshCache 刷新缓存 +func (a DictTypeApi) RefreshCache(c *gin.Context) { + a.dictTypeService.RefreshCache() + resp.OK(c) +} + +// Export 导出Excel +func (a DictTypeApi) Export(c *gin.Context) { + query := request.DictTypeQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + find, _ := a.dictTypeService.FindPage(query) + list := make([]interface{}, 0) + for _, dictType := range *find { + list = append(list, dictType) + } + _, files := excels.ExportExcel(list, "字典类型表") + file.DownloadExcel(c, files) +} diff --git a/core/api/v1/login_api.go b/core/api/v1/login_api.go new file mode 100644 index 0000000..557dc3e --- /dev/null +++ b/core/api/v1/login_api.go @@ -0,0 +1,119 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + cache2 "cutego/core/cache" + "cutego/core/entity" + service2 "cutego/core/service" + "cutego/pkg/common" + "cutego/pkg/page" + "cutego/pkg/resp" + "cutego/pkg/tree/tree_menu" + "cutego/pkg/util" + "github.com/gin-gonic/gin" + "net/http" + "strings" + "time" +) + +type LoginApi struct { + loginService service2.LoginService + roleService service2.RoleService + permissionService service2.PermissionService + menuService service2.MenuService + loginInfoService service2.LoginInfoService +} + +// Login 登录 +func (a LoginApi) Login(c *gin.Context) { + loginBody := request.LoginBody{} + if c.BindJSON(&loginBody) == nil { + m := make(map[string]string) + loginStatus, token := a.loginService.Login(loginBody.UserName, loginBody.Password) + if loginStatus { + a.handleLoginInfo(c, loginBody, loginStatus) + // 将token存入到redis中 + cache2.SetRedisToken(loginBody.UserName, token) + m["token"] = token + c.JSON(http.StatusOK, resp.Success(m)) + } else { + a.handleLoginInfo(c, loginBody, loginStatus) + c.JSON(http.StatusOK, resp.ErrorResp(token)) + } + } else { + c.JSON(http.StatusOK, resp.ErrorResp(500, "参数绑定错误")) + } +} + +func (a LoginApi) handleLoginInfo(c *gin.Context, body request.LoginBody, loginStatus bool) { + status := common.If(loginStatus, "1", "0") + + clientIp := a.loginInfoService.GetRequestClientIp(c) + var location string + if "127.0.0.1" == clientIp { + location = "本机" + } else { + location = a.loginInfoService.GetLocationByIp(clientIp).Country + } + + a.loginInfoService.Save(entity.SysLoginInfo{ + UserName: body.UserName, + IpAddr: clientIp, + LoginLocation: location, + Browser: c.GetHeader("User-Agent"), + OS: strings.ReplaceAll(c.GetHeader("sec-ch-ua-platform"), "\"", ""), + Status: status.(string), + LoginTime: time.Now(), + }) +} + +// GetUserInfo 获取用户信息 +func (a LoginApi) GetUserInfo(c *gin.Context) { + m := make(map[string]interface{}) + user := a.loginService.GetCurrentUser(c) + // 查询用户角色集合 + roleKeys := a.permissionService.GetRolePermissionByUserId(user) + // 权限集合 + perms := a.permissionService.GetMenuPermission(user) + m["roles"] = roleKeys + m["permissions"] = perms + m["user"] = user + c.JSON(http.StatusOK, resp.Success(m)) +} + +// GetRouters 根据用户ID查询菜单 +func (a LoginApi) GetRouters(c *gin.Context) { + // 获取当前登录用户 + user := a.loginService.GetCurrentUser(c) + menus := a.menuService.GetMenuTreeByUserId(user) + systemMenus := tree_menu.SystemMenus{} + systemMenus = *menus + array := systemMenus.ConvertToINodeArray(menus) + generateTree := tree_menu.GenerateTree(array, nil) + c.JSON(http.StatusOK, resp.Success(generateTree)) +} + +// GetLoginHistory 根据用户名称查询登录记录 +func (a LoginApi) GetLoginHistory(c *gin.Context) { + // 获取当前登录用户 + user := a.loginService.GetCurrentUser(c) + // 配置参数 + query := request.LoginInfoQuery{} + query.UserName = user.UserName + // 查询 + list, i := a.loginInfoService.FindPage(query) + success := resp.Success(page.Page{ + Size: query.PageSize, + Total: i, + List: list, + }, "查询成功") + c.JSON(http.StatusOK, success) +} + +// Logout 退出登录 +func (a LoginApi) Logout(c *gin.Context) { + // 删除Redis缓存 + name := util.GetUserInfo(c).UserName + cache2.RemoveRedisToken(name) + resp.OK(c) +} diff --git a/core/api/v1/menu_api.go b/core/api/v1/menu_api.go new file mode 100644 index 0000000..ce3c05f --- /dev/null +++ b/core/api/v1/menu_api.go @@ -0,0 +1,112 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/core/service" + "cutego/pkg/resp" + "cutego/pkg/tree/tree_menu" + "cutego/pkg/util" + "github.com/gin-gonic/gin" + "net/http" + "strconv" + "time" +) + +type MenuApi struct { + menuService service.MenuService +} + +// List 查询菜单数据 +func (a MenuApi) List(c *gin.Context) { + // 获取当前登录用户 + info := util.GetUserInfo(c) + // 获取参数 + query := request.MenuQuery{} + if c.Bind(&query) != nil { + resp.Error(c) + return + } + resp.OK(c, a.menuService.FindMenuList(query, info)) +} + +// GetInfo 根据id查询菜单详情 +func (a MenuApi) GetInfo(c *gin.Context) { + param := c.Param("menuId") + menuId, err := strconv.Atoi(param) + if err != nil { + resp.ParamError(c, "参数绑定错误") + return + } + resp.OK(c, a.menuService.GetMenuByMenuId(menuId)) +} + +// RoleMenuTreeSelect 加载对应角色菜单列表树 +func (a MenuApi) RoleMenuTreeSelect(c *gin.Context) { + m := make(map[string]interface{}) + param := c.Param("roleId") + roleId, _ := strconv.ParseInt(param, 10, 64) + // 获取当前登录用户 + info := util.GetUserInfo(c) + menuList := a.menuService.GetMenuTreeByUserId(info) + menus := tree_menu.SystemMenus{} + tree := menus.GetTree(menuList) + ids := a.menuService.FindMenuListByRoleId(roleId) + m["checkedKeys"] = ids + m["menus"] = tree + c.JSON(http.StatusOK, resp.Success(m)) +} + +// TreeSelect 获取菜单下拉树列表 +func (a MenuApi) TreeSelect(c *gin.Context) { + info := util.GetUserInfo(c) + menus := a.menuService.GetMenuTreeByUserId(info) + systemMenus := tree_menu.SystemMenus{} + tree := systemMenus.GetTree(menus) + c.JSON(http.StatusOK, resp.Success(tree)) +} + +// Add 添加菜单数据 +func (a MenuApi) Add(c *gin.Context) { + menu := entity.SysMenu{} + if c.Bind(&menu) != nil { + resp.ParamError(c, "参数绑定异常") + return + } + if a.menuService.Save(menu) > 0 { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Edit 修改菜单数据 +func (a MenuApi) Edit(c *gin.Context) { + menu := entity.SysMenu{} + if c.Bind(&menu) != nil { + resp.ParamError(c) + return + } + menu.UpdateBy = util.GetUserInfo(c).UserName + menu.UpdateTime = time.Now() + if a.menuService.Edit(menu) > 0 { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Delete 删除菜单 +func (a MenuApi) Delete(c *gin.Context) { + param := c.Param("menuId") + menuId, err := strconv.Atoi(param) + if err != nil { + resp.ParamError(c) + return + } + if a.menuService.Remove(menuId) > 0 { + resp.OK(c) + } else { + resp.Error(c) + } +} diff --git a/core/api/v1/notice_api.go b/core/api/v1/notice_api.go new file mode 100644 index 0000000..f0109b8 --- /dev/null +++ b/core/api/v1/notice_api.go @@ -0,0 +1,86 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/core/service" + "cutego/pkg/page" + "cutego/pkg/resp" + "cutego/pkg/util" + "github.com/gin-gonic/gin" + "strconv" + "strings" + "time" +) + +type NoticeApi struct { + noticeService service.NoticeService +} + +// List 查询集合 +func (a NoticeApi) List(c *gin.Context) { + query := request.NoticeQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + find, i := a.noticeService.FindPage(query) + resp.OK(c, page.Page{ + List: find, + Total: i, + Size: query.PageSize, + }) +} + +// Add 添加公告 +func (a NoticeApi) Add(c *gin.Context) { + notice := entity.SysNotice{} + if c.Bind(¬ice) != nil { + resp.ParamError(c) + return + } + notice.CreateBy = util.GetUserInfo(c).UserName + if a.noticeService.Save(notice) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Delete 删除 +func (a NoticeApi) Delete(c *gin.Context) { + ids := strings.Split(c.Param("ids"), ",") + idList := make([]int64, 0) + for _, s := range ids { + id, _ := strconv.ParseInt(s, 10, 64) + idList = append(idList, id) + } + if a.noticeService.Remove(idList) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Get 查询 +func (a NoticeApi) Get(c *gin.Context) { + param := c.Param("id") + id, _ := strconv.ParseInt(param, 10, 64) + resp.OK(c, a.noticeService.GetById(id)) +} + +// Edit 修改 +func (a NoticeApi) Edit(c *gin.Context) { + notice := entity.SysNotice{} + if c.Bind(¬ice) != nil { + resp.ParamError(c) + return + } + notice.UpdateTime = time.Now() + notice.UpdateBy = util.GetUserInfo(c).UserName + if a.noticeService.Edit(notice) { + resp.OK(c) + } else { + resp.Error(c) + } +} diff --git a/core/api/v1/post_api.go b/core/api/v1/post_api.go new file mode 100644 index 0000000..bc598d9 --- /dev/null +++ b/core/api/v1/post_api.go @@ -0,0 +1,137 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + service2 "cutego/core/service" + "cutego/pkg/excels" + "cutego/pkg/file" + "cutego/pkg/page" + "cutego/pkg/resp" + "cutego/pkg/util" + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + "strconv" + "strings" + "time" +) + +type PostApi struct { + postService service2.PostService + userPostService service2.UserPostService +} + +// List 查询刚问分页数据 +func (a PostApi) List(c *gin.Context) { + query := request.PostQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + list, i := a.postService.FindPage(query) + resp.OK(c, page.Page{ + List: list, + Total: i, + Size: query.PageSize, + }) +} + +// Add 新增岗位 +func (a PostApi) Add(c *gin.Context) { + post := entity.SysPost{} + if c.Bind(&post) != nil { + resp.ParamError(c) + return + } + // 校验岗位名称是否存在 + if a.postService.CheckPostNameUnique(post) { + resp.Error(c, "新增岗位'"+post.PostName+"'失败, 岗位名称已存在") + return + } + // 检验岗位编码是否存在 + if a.postService.CheckPostCodeUnique(post) { + resp.Error(c, "新增岗位'"+post.PostCode+"'失败, 岗位编码已存在") + return + } + post.CreateBy = util.GetUserInfo(c).UserName + if a.postService.Save(post) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Get 根据岗位编号获取详细信息 +func (a PostApi) Get(c *gin.Context) { + param := c.Param("postId") + postId, _ := strconv.ParseInt(param, 10, 64) + resp.OK(c, a.postService.GetPostById(postId)) +} + +// Delete 删除岗位数据 +func (a PostApi) Delete(c *gin.Context) { + // 获取postId + param := c.Param("postId") + list := make([]int64, 0) + if gotool.StrUtils.HasNotEmpty(param) { + strs := strings.Split(param, ",") + for _, str := range strs { + postId, _ := strconv.ParseInt(str, 10, 64) + list = append(list, postId) + } + } + // 判断是否可以删除 + postId := a.userPostService.CountUserPostById(list) + if postId > 0 { + post := a.postService.GetPostById(postId) + resp.Error(c, post.PostName+"岗位已分配, 不允许删除") + return + } + if a.postService.Remove(list) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Edit 修改岗位数据接口 +func (a PostApi) Edit(c *gin.Context) { + post := entity.SysPost{} + if c.Bind(&post) != nil { + resp.ParamError(c) + return + } + // 校验岗位名称是否存在 + if a.postService.CheckPostNameUnique(post) { + resp.Error(c, "修改岗位'"+post.PostName+"'失败, 岗位名称已存在") + return + } + // 检验岗位编码是否存在 + if a.postService.CheckPostCodeUnique(post) { + resp.Error(c, "修改岗位'"+post.PostCode+"'失败, 岗位编码已存在") + return + } + post.UpdateBy = util.GetUserInfo(c).UserName + post.UpdateTime = time.Now() + if a.postService.Edit(post) { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Export 导出excel +func (a PostApi) Export(c *gin.Context) { + query := request.PostQuery{} + if c.Bind(&query) != nil { + resp.ParamError(c) + return + } + list, _ := a.postService.FindPage(query) + excelList := make([]interface{}, 0) + for _, post := range *list { + excelList = append(excelList, post) + } + _, files := excels.ExportExcel(excelList, "岗位数据表") + file.DownloadExcel(c, files) +} diff --git a/core/api/v1/request/config_request.go b/core/api/v1/request/config_request.go new file mode 100644 index 0000000..97a8439 --- /dev/null +++ b/core/api/v1/request/config_request.go @@ -0,0 +1,10 @@ +package request + +import "cutego/pkg/base" + +type ConfigQuery struct { + base.GlobalQuery + ConfigName string `form:"configName"` + ConfigType string `form:"configType"` + ConfigKey string `form:"configKey"` +} diff --git a/core/api/v1/request/cron_job_request.go b/core/api/v1/request/cron_job_request.go new file mode 100644 index 0000000..a507b07 --- /dev/null +++ b/core/api/v1/request/cron_job_request.go @@ -0,0 +1,8 @@ +package request + +import "cutego/pkg/base" + +type CronJobQuery struct { + base.GlobalQuery + JobName string `form:"jobName"` +} diff --git a/core/api/v1/request/dept_request.go b/core/api/v1/request/dept_request.go new file mode 100644 index 0000000..7a04a96 --- /dev/null +++ b/core/api/v1/request/dept_request.go @@ -0,0 +1,8 @@ +package request + +// DeptQuery 部门查询参数结构体 GET请求 +type DeptQuery struct { + ParentId int `form:"parentId"` + DeptName string `form:"deptName"` + Status string `form:"status"` +} diff --git a/core/api/v1/request/dict_data_reqest.go b/core/api/v1/request/dict_data_reqest.go new file mode 100644 index 0000000..cd17793 --- /dev/null +++ b/core/api/v1/request/dict_data_reqest.go @@ -0,0 +1,10 @@ +package request + +import "cutego/pkg/base" + +type DiceDataQuery struct { + base.GlobalQuery + DictType string `form:"dictType"` + DictLabel string `form:"dictLabel"` + Status string `form:"status"` +} diff --git a/core/api/v1/request/dict_type_request.go b/core/api/v1/request/dict_type_request.go new file mode 100644 index 0000000..66e252e --- /dev/null +++ b/core/api/v1/request/dict_type_request.go @@ -0,0 +1,10 @@ +package request + +import "cutego/pkg/base" + +type DictTypeQuery struct { + base.GlobalQuery + DictName string `form:"dictName"` + Status string `form:"status"` + DictType string `form:"dictType"` +} diff --git a/core/api/v1/request/login_info_request.go b/core/api/v1/request/login_info_request.go new file mode 100644 index 0000000..2e8c10b --- /dev/null +++ b/core/api/v1/request/login_info_request.go @@ -0,0 +1,9 @@ +package request + +import "cutego/pkg/base" + +// LoginInfoQuery 用户get请求数据参数 +type LoginInfoQuery struct { + base.GlobalQuery + UserName string // 筛选用户名称 +} diff --git a/core/api/v1/request/login_request.go b/core/api/v1/request/login_request.go new file mode 100644 index 0000000..51e0e49 --- /dev/null +++ b/core/api/v1/request/login_request.go @@ -0,0 +1,7 @@ +package request + +// LoginBody 登录参数 +type LoginBody struct { + UserName string `json:"username"` // 用户名 + Password string `json:"password"` // 密码 +} diff --git a/core/api/v1/request/menu_request.go b/core/api/v1/request/menu_request.go new file mode 100644 index 0000000..4c905a3 --- /dev/null +++ b/core/api/v1/request/menu_request.go @@ -0,0 +1,12 @@ +package request + +import "cutego/pkg/base" + +// MenuQuery 菜单查询条件封装 +type MenuQuery struct { + base.GlobalQuery + MenuName string `json:"menuName" form:"menuName"` + Visible string `json:"visible" form:"visible"` + Status string `json:"status" form:"status"` + UserId int64 `json:"userId" form:"userId"` +} diff --git a/core/api/v1/request/notice_reqest.go b/core/api/v1/request/notice_reqest.go new file mode 100644 index 0000000..47a04cd --- /dev/null +++ b/core/api/v1/request/notice_reqest.go @@ -0,0 +1,10 @@ +package request + +import "cutego/pkg/base" + +type NoticeQuery struct { + base.GlobalQuery + NoticeTitle string `form:"noticeTitle"` + NoticeType string `form:"noticeType"` + CreateBy string `form:"createBy"` +} diff --git a/core/api/v1/request/post_request.go b/core/api/v1/request/post_request.go new file mode 100644 index 0000000..5d68f69 --- /dev/null +++ b/core/api/v1/request/post_request.go @@ -0,0 +1,10 @@ +package request + +import "cutego/pkg/base" + +type PostQuery struct { + base.GlobalQuery + PostCode string `form:"postCode"` + Status string `form:"status"` + PostName string `form:"postName"` +} diff --git a/core/api/v1/request/role_request.go b/core/api/v1/request/role_request.go new file mode 100644 index 0000000..0977e6c --- /dev/null +++ b/core/api/v1/request/role_request.go @@ -0,0 +1,17 @@ +package request + +import "cutego/pkg/base" + +// RoleQuery 角色Get请求参数 +type RoleQuery struct { + base.GlobalQuery + RoleName string `form:"roleName"` // 角色名称 + Status string `form:"status"` // 角色状态 + RoleKey string `form:"roleKey"` // 角色Key +} + +// RoleBody 角色Post和Put参数 +type RoleBody struct { + RoleId int64 `json:"roleId"` + Status string `json:"status"` +} diff --git a/core/api/v1/request/user_request.go b/core/api/v1/request/user_request.go new file mode 100644 index 0000000..85a2945 --- /dev/null +++ b/core/api/v1/request/user_request.go @@ -0,0 +1,39 @@ +package request + +import ( + "cutego/pkg/base" + "time" +) + +// UserQuery 用户get请求数据参数 +type UserQuery struct { + base.GlobalQuery + RoleId int64 `form:"roleId"` // 角色id + UserName string `form:"userName"` // 用户名 + Status string `form:"status"` // 状态 + PhoneNumber string `form:"phoneNumber"` // 手机号 + DeptId int64 `form:"deptId"` // 部门id +} + +// UserBody 用户接收POST 或者 PUT请求参数 +type UserBody struct { + UserId int64 `xorm:"pk autoincr" json:"userId"` // 用户ID + DeptId int64 `json:"deptId"` // 部门ID + UserName string `json:"userName"` // 登录用户名 + NickName string `json:"nickName"` // 用户昵称 + Email string `json:"email"` // 邮箱 + PhoneNumber string `json:"phoneNumber"` // 手机号 + Sex string `json:"sex"` // 性别0男1女 + Avatar string `json:"avatar"` // 头像路径 + Password string `json:"password"` // 密码 + Status string `json:"status"` // 状态 0正常1停用 + DelFlag string `json:"delFlag"` // 0正常1删除 + LoginIp string `json:"loginIp"` // 登录ip + LoginDate time.Time `json:"loginDate"` // 登录时间 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 + RoleIds []int64 `xorm:"-" json:"roleIds"` // 角色id组 + PostIds []int64 `xorm:"-" json:"postIds"` // 岗位id组 +} diff --git a/core/api/v1/request/user_role_request.go b/core/api/v1/request/user_role_request.go new file mode 100644 index 0000000..5f6bcb6 --- /dev/null +++ b/core/api/v1/request/user_role_request.go @@ -0,0 +1,7 @@ +package request + +// UserRoleBody 用户角色Post和Put参数接收结构体 +type UserRoleBody struct { + RoleId int64 `form:"roleId" json:"roleId"` + UserIds []int64 `form:"userIds" json:"userIds"` +} diff --git a/core/api/v1/response/dict_data_response.go b/core/api/v1/response/dict_data_response.go new file mode 100644 index 0000000..9c3ec38 --- /dev/null +++ b/core/api/v1/response/dict_data_response.go @@ -0,0 +1,11 @@ +package response + +// DictDataResponse 字典数据实体返回结构体 +type DictDataResponse struct { + DictCode int64 `excel:"name=字典编码" xorm:"pk autoincr" json:"dictCode"` // 字典ID + DictSort int `excel:"name=字典排序" xorm:"int" json:"dictSort"` // 字典排序 + DictLabel string `excel:"name=字典标签" xorm:"varchar(128)" json:"dictLabel"` // 字典标签 + DictValue string `excel:"name=字典键值" xorm:"varchar(128)" json:"dictValue"` // 字典键值 + DictType string `excel:"name=字典类型" xorm:"varchar(128)" json:"dictType"` // 字典类型 + IsDefault string `excel:"name=是否默认,format=Y=是,N=否" json:"isDefault"` // 是否默认 +} diff --git a/core/api/v1/response/user_response.go b/core/api/v1/response/user_response.go new file mode 100644 index 0000000..a1a9563 --- /dev/null +++ b/core/api/v1/response/user_response.go @@ -0,0 +1,41 @@ +package response + +import ( + models2 "cutego/core/entity" + "time" +) + +// UserResponse 用户实体返回结构体 +type UserResponse struct { + UserId int64 `json:"userId"` // 用户ID + DeptId int64 `excel:"name=部门" json:"deptId"` // 部门ID + UserName string `excel:"name=用户登录名" json:"userName"` // 登录用户名 + NickName string `excel:"name=用户昵称" json:"nickName"` // 用户昵称 + Email string `excel:"name=用户邮箱" json:"email"` // 邮箱 + PhoneNumber string `excel:"name=手机号" json:"phoneNumber"` // 手机号 + Sex string `excel:"name=性别,format=0=男,1=女,2=未知" json:"sex"` // 性别0男1女 + Avatar string `json:"avatar"` // 头像路径 + Status string `json:"status"` // 状态 0正常1停用 + DelFlag string `json:"delFlag"` // 0正常1删除 + LoginIp string `json:"loginIp"` // 登录ip + LoginDate time.Time `json:"loginDate"` // 登录时间 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 + SysDept models2.SysDept `xorm:"extends" json:"dept"` // 部门实体 +} + +// UserInfo 用户整体数据 +type UserInfo struct { + User *UserResponse `json:"user,omitempty"` // 用户数据 + Roles []*models2.SysRole `json:"roles,omitempty"` // 角色集合 + Posts []*models2.SysPost `json:"posts,omitempty"` // 部门集合 + PostIds *[]int64 `json:"postIds,omitempty"` // 岗位id集合 + RoleIds *[]int64 `json:"roleIds,omitempty"` // 觉得id集合 +} + +// IsAdmin 判断当前用户是否是管理员 +func (r UserResponse) IsAdmin() bool { + return r.UserId == 1 +} diff --git a/core/api/v1/role_api.go b/core/api/v1/role_api.go new file mode 100644 index 0000000..3f54b5c --- /dev/null +++ b/core/api/v1/role_api.go @@ -0,0 +1,202 @@ +package v1 + +import ( + req2 "cutego/core/api/v1/request" + models2 "cutego/core/entity" + service2 "cutego/core/service" + "cutego/pkg/excels" + "cutego/pkg/page" + "cutego/pkg/resp" + "cutego/pkg/util" + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + "net/http" + "strconv" + "time" +) + +type RoleApi struct { + roleService service2.RoleService + userService service2.UserService +} + +// Find 分页查询角色数据 +func (a RoleApi) Find(c *gin.Context) { + query := req2.RoleQuery{} + if c.BindQuery(&query) == nil { + list, i := a.roleService.FindPage(query) + p := page.Page{ + List: list, + Total: i, + Size: query.PageSize, + } + c.JSON(http.StatusOK, resp.Success(p)) + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("参数绑定异常")) + } +} + +// GetRoleId 根据人roleId查询角色数据 +func (a RoleApi) GetRoleId(c *gin.Context) { + param := c.Param("roleId") + roleId, err := strconv.ParseInt(param, 10, 64) + if err == nil { + role := a.roleService.GetRoleByRoleId(roleId) + c.JSON(200, resp.Success(role)) + } else { + c.JSON(500, resp.ErrorResp("参数绑定异常")) + } +} + +// Add 添加角色业务操作 +func (a RoleApi) Add(c *gin.Context) { + role := models2.SysRole{} + if c.BindJSON(&role) == nil { + if a.roleService.CheckRoleNameUnique(role) > 0 { + c.JSON(500, resp.ErrorResp(500, "新增角色'"+role.RoleName+"'失败, 角色名称已存在")) + } else if a.roleService.CheckRoleKeyUnique(role) > 0 { + c.JSON(500, resp.ErrorResp(500, "新增角色'"+role.RoleName+"'失败, 角色权限已存在")) + } + role.CreateBy = util.GetUserInfo(c).UserName + if a.roleService.Save(role) > 0 { + c.JSON(200, resp.Success("保存成功")) + } else { + c.JSON(500, resp.Success("保存失败")) + } + } else { + c.JSON(500, resp.ErrorResp("参数绑定异常")) + } +} + +// Edit 修改角色 +func (a RoleApi) Edit(c *gin.Context) { + role := models2.SysRole{} + if c.BindJSON(&role) == nil { + if a.roleService.CheckRoleNameUnique(role) > 0 { + c.JSON(500, resp.ErrorResp(500, "修改角色'"+role.RoleName+"'失败, 角色名称已存在")) + } else if a.roleService.CheckRoleKeyUnique(role) > 0 { + c.JSON(500, resp.ErrorResp(500, "修改角色'"+role.RoleName+"'失败, 角色权限已存在")) + } + role.CreateBy = util.GetUserInfo(c).UserName + role.CreateTime = time.Now() + if a.roleService.Edit(role) > 0 { + c.JSON(200, resp.Success("修改成功")) + } else { + c.JSON(500, resp.Success("修改失败")) + } + } else { + c.JSON(500, resp.ErrorResp("参数绑定异常")) + } +} + +// Delete 删除角色 +func (a RoleApi) Delete(c *gin.Context) { + param := c.Param("roleId") + roleId, err := strconv.ParseInt(param, 10, 64) + if err != nil { + c.JSON(500, resp.ErrorResp("参数绑定异常")) + return + } + if a.roleService.Remove(roleId) > 0 { + c.JSON(200, resp.Success("删除成功")) + } else { + c.JSON(500, resp.Success("删除失败")) + } +} + +// ChangeStatus 状态修改 +func (a RoleApi) ChangeStatus(c *gin.Context) { + body := models2.SysRole{} + if c.BindJSON(&body) != nil { + c.JSON(500, resp.ErrorResp("参数绑定异常")) + return + } + allowed, s := a.roleService.CheckRoleAllowed(body.RoleId) + if !allowed { + c.JSON(500, resp.ErrorResp(s)) + return + } + body.UpdateTime = time.Now() + body.UpdateBy = util.GetUserInfo(c).UserName + if a.roleService.EditRoleStatus(&body) > 0 { + resp.OK(c, "修改成功") + } else { + resp.Error(c) + } +} + +// AllocatedList 查询已分配用户角色列表 +func (a RoleApi) AllocatedList(c *gin.Context) { + query := req2.UserQuery{} + if c.BindQuery(&query) != nil { + resp.Error(c) + return + } + list, i := a.userService.GetAllocatedList(query) + resp.OK(c, page.Page{ + List: list, + Total: i, + }) +} + +// UnallocatedList 查询未分配用户角色列表 +func (a RoleApi) UnallocatedList(c *gin.Context) { + query := req2.UserQuery{} + if c.BindQuery(&query) != nil { + resp.Error(c) + return + } + list, i := a.userService.GetUnallocatedList(query) + resp.OK(c, page.Page{ + List: list, + Total: i, + }) +} + +// CancelAuthUser 取消授权用户 +func (a RoleApi) CancelAuthUser(c *gin.Context) { + roleUser := models2.SysUserRole{} + if c.BindJSON(&roleUser) != nil { + resp.Error(c) + return + } + if a.roleService.DeleteAuthUser(roleUser) > 0 { + resp.OK(c, "操作成功") + } else { + resp.Error(c, "操作失败") + } +} + +// UpdateAuthUserAll 批量选择用户授权 +func (a RoleApi) UpdateAuthUserAll(c *gin.Context) { + body := req2.UserRoleBody{} + if c.Bind(&body) != nil { + resp.Error(c) + return + } + if a.roleService.InsertAuthUsers(body) > 0 { + resp.OK(c, "操作成功") + } else { + resp.Error(c, "操作失败") + } +} + +// Export 导出Excel +func (a RoleApi) Export(c *gin.Context) { + query := req2.RoleQuery{} + items := make([]interface{}, 0) + if c.BindQuery(&query) == nil { + list, _ := a.roleService.FindPage(query) + for _, role := range list { + items = append(items, *role) + } + _, file := excels.ExportExcel(items, "角色表") + c.Header("Content-Type", "application/octet-stream") + c.Header("Content-Disposition", "attachment; filename="+gotool.IdUtils.IdUUIDToRan(false)+".xlsx") + c.Header("Content-Transfer-Encoding", "binary") + c.Header("FileName", gotool.IdUtils.IdUUIDToRan(false)+".xlsx") + file.Write(c.Writer) + } else { + c.JSON(200, resp.ErrorResp(500, "参数错误")) + } +} diff --git a/core/api/v1/user_api.go b/core/api/v1/user_api.go new file mode 100644 index 0000000..ab9fb43 --- /dev/null +++ b/core/api/v1/user_api.go @@ -0,0 +1,334 @@ +package v1 + +import ( + "cutego/core/api/v1/request" + "cutego/core/api/v1/response" + models2 "cutego/core/entity" + service2 "cutego/core/service" + "cutego/pkg/common" + "cutego/pkg/excels" + "cutego/pkg/page" + "cutego/pkg/resp" + "cutego/pkg/util" + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + "io" + "net/http" + "strconv" +) + +// UserApi 用户操作api +type UserApi struct { + userService service2.UserService + roleService service2.RoleService + postService service2.PostService +} + +// Find 查询用户列表 +func (a UserApi) Find(c *gin.Context) { + query := request.UserQuery{} + if c.BindQuery(&query) == nil { + list, i := a.userService.FindList(query) + success := resp.Success(page.Page{ + Size: query.PageSize, + Total: i, + List: list, + }, "查询成功") + c.JSON(http.StatusOK, success) + } else { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "参数错误")) + } +} + +// GetInfo 查询用户信息 +func (a UserApi) GetInfo(c *gin.Context) { + param := c.Param("userId") + r := new(response.UserInfo) + // 查询角色 + roleAll, _ := a.roleService.FindAll(nil) + // 岗位所有数据 + postAll := a.postService.FindAll() + // 判断id传入的是否为空 + if !gotool.StrUtils.HasEmpty(param) { + parseInt, err := strconv.ParseInt(param, 10, 64) + if err == nil { + // 判断当前登录用户是否是admin + m := new(models2.SysUser) + if m.IsAdmin(parseInt) { + r.Roles = roleAll + } else { + roles := make([]*models2.SysRole, 0) + for _, role := range roleAll { + if role.RoleId != 1 { + roles = append(roles, role) + } + } + r.Roles = roles + } + // 根据id获取用户数据 + r.User = a.userService.GetUserById(parseInt) + // 根据用户ID查询岗位id集合 + r.PostIds = a.postService.FindPostListByUserId(parseInt) + // 根据用户ID查询角色id集合 + r.RoleIds = a.roleService.FindRoleListByUserId(parseInt) + } + } else { + //id为空不取管理员角色 + roles := make([]*models2.SysRole, 0) + for _, role := range roleAll { + if role.RoleId != 1 { + roles = append(roles, role) + } + } + r.Roles = roles + } + r.Posts = postAll + c.JSON(http.StatusOK, resp.Success(r, "操作成功")) +} + +// AuthRole 根据用户编号获取授权角色 +func (a UserApi) AuthRole(c *gin.Context) { + m := make(map[string]interface{}) + userId := c.Param("userId") + parseInt, err := strconv.ParseInt(userId, 10, 64) + if err != nil { + common.ErrorLog(err) + c.JSON(http.StatusInternalServerError, resp.ErrorResp(err)) + } + user := a.userService.GetUserById(parseInt) + // 查询角色 + roles := a.roleService.GetRoleListByUserId(parseInt) + flag := models2.SysUser{}.IsAdmin(parseInt) + if flag { + m["roles"] = roles + } else { + roleList := make([]models2.SysRole, 0) + for _, role := range *roles { + if role.RoleId != 1 { + roleList = append(roleList, role) + } + } + m["roles"] = roleList + } + m["user"] = user + c.JSON(http.StatusOK, resp.Success(m)) +} + +// Add 新增用户 +func (a UserApi) Add(c *gin.Context) { + userBody := request.UserBody{} + if c.BindJSON(&userBody) == nil { + // 根据用户名查询用户 + user := a.userService.GetUserByUserName(userBody.UserName) + if user != nil { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "失败, 登录账号已存在")) + return + } else if a.userService.CheckPhoneNumUnique(userBody) != nil { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "失败, 手机号码已存在")) + return + } else if a.userService.CheckEmailUnique(userBody) != nil { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "失败, 邮箱已存在")) + return + } + // 进行密码加密 + userBody.Password = gotool.BcryptUtils.Generate(userBody.Password) + // 添加用户 + if a.userService.Save(userBody) { + c.JSON(http.StatusOK, resp.Success(nil)) + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("保存失败")) + } + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("参数错误")) + } +} + +// Edit 修改用户 +func (a UserApi) Edit(c *gin.Context) { + userBody := request.UserBody{} + if c.BindJSON(&userBody) == nil { + if a.userService.CheckPhoneNumUnique(userBody) != nil { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "失败, 手机号码已存在")) + return + } else if a.userService.CheckEmailUnique(userBody) != nil { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "失败, 邮箱已存在")) + return + } + // 进行用户修改操作 + if a.userService.Edit(userBody) > 0 { + resp.OK(c) + return + } else { + resp.Error(c) + return + } + } else { + resp.ParamError(c) + return + } +} + +// Remove 删除用户 +func (a UserApi) Remove(c *gin.Context) { + param := c.Param("userId") + userId, err := strconv.ParseInt(param, 10, 64) + if err != nil { + common.ErrorLog(err) + c.JSON(http.StatusInternalServerError, resp.ErrorResp("参数错误")) + return + } + if a.userService.Remove(userId) > 0 { + c.JSON(http.StatusOK, resp.Success(nil)) + return + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("删除失败")) + return + } +} + +// ResetPwd 修改重置密码 +func (a UserApi) ResetPwd(c *gin.Context) { + userBody := request.UserBody{} + if c.BindJSON(&userBody) == nil { + if a.userService.CheckUserAllowed(userBody) { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("不允许操作超级管理员用户")) + return + } + userBody.Password = gotool.BcryptUtils.Generate(userBody.Password) + if a.userService.ResetPwd(userBody) > 0 { + c.JSON(http.StatusOK, resp.Success(nil)) + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("重置失败")) + } + } else { + c.JSON(http.StatusInternalServerError, resp.ErrorResp("参数错误")) + } +} + +// Export 导出excel +func (a UserApi) Export(c *gin.Context) { + query := request.UserQuery{} + if c.BindQuery(&query) == nil { + items := make([]interface{}, 0) + list, _ := a.userService.FindList(query) + for _, userResponse := range list { + items = append(items, *userResponse) + } + _, file := excels.ExportExcel(items, "用户表") + c.Header("Content-Type", "application/octet-stream") + c.Header("Content-Disposition", "attachment; filename="+gotool.IdUtils.IdUUIDToRan(false)+".xlsx") + c.Header("Content-Transfer-Encoding", "binary") + c.Header("FileName", gotool.IdUtils.IdUUIDToRan(false)+".xlsx") + file.Write(c.Writer) + } else { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "参数错误")) + } +} + +// Profile 查询个人信息 +func (a UserApi) Profile(c *gin.Context) { + m := make(map[string]interface{}) + info := util.GetUserInfo(c) + u := a.userService.GetUserById(info.UserId) + m["user"] = u + // 查询所属角色组 + m["roleGroup"] = a.roleService.GetRolesByUserName(info.UserName) + m["postGroup"] = a.postService.FindPostByUserName(info.UserName) + resp.OK(c, m) +} + +// UpdateProfile 修改个人数据 +func (a UserApi) UpdateProfile(c *gin.Context) { + user := request.UserBody{} + if c.Bind(&user) != nil { + resp.ParamError(c) + return + } + if a.userService.CheckEmailUnique(user) != nil { + resp.Error(c, "修改用户'"+user.UserName+"'失败, 邮箱账号已存在") + return + } + if a.userService.CheckPhoneNumUnique(user) != nil { + resp.Error(c, "修改用户'"+user.UserName+"'失败, 手机号已存在") + return + } + if a.userService.EditProfile(user) > 0 { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// UpdatePwd 修改个人密码 +func (a UserApi) UpdatePwd(c *gin.Context) { + oldPassword := c.Query("oldPassword") + newPassword := c.Query("newPassword") + info := util.GetUserInfo(c) + name := a.userService.GetUserByUserName(info.UserName) + hash := gotool.BcryptUtils.CompareHash(name.Password, oldPassword) + if !hash { + resp.Error(c, "修改密码失败, 旧密码错误") + return + } + generate := gotool.BcryptUtils.Generate(oldPassword) + compareHash := gotool.BcryptUtils.CompareHash(generate, newPassword) + if compareHash { + resp.Error(c, "新密码不能与旧密码相同") + return + } + pwd := a.userService.EditPwd(info.UserId, gotool.BcryptUtils.Generate(newPassword)) + if pwd { + resp.OK(c) + } else { + resp.Error(c) + } +} + +// Avatar 修改头像 +func (a UserApi) Avatar(c *gin.Context) { + dirPath := common.GetDirPath("avatar") + file, _, err := c.Request.FormFile("avatarFile") + fileName := gotool.IdUtils.IdUUIDToRan(true) + ".jpg" + filePath := dirPath + fileName + fileAppend, err := gotool.FileUtils.OpenFileAppend(filePath) + defer fileAppend.Close() + if err != nil { + common.ErrorLog(err) + resp.Error(c) + return + } + _, err = io.Copy(fileAppend, file) + if err != nil { + common.ErrorLog(err) + resp.Error(c) + return + } + info := util.GetUserInfo(c) + info.Avatar = filePath + avatar := a.userService.EditAvatar(info) + if avatar { + m := make(map[string]interface{}) + m["imgUrl"] = filePath + resp.OK(c, m) + } else { + resp.Error(c) + } +} + +// 修改可用状态 +func (a UserApi) ChangeStatus(c *gin.Context) { + user := request.UserBody{} + if c.Bind(&user) != nil { + resp.ParamError(c) + return + } + if user.UserId == 1 && user.Status == "1" { + c.JSON(http.StatusOK, resp.ErrorResp(http.StatusInternalServerError, "不可禁用admin账号")) + return + } + if a.userService.EditStatus(user) { + resp.OK(c) + } else { + resp.Error(c) + } +} diff --git a/core/cache/config_cache.go b/core/cache/config_cache.go new file mode 100644 index 0000000..c58f64e --- /dev/null +++ b/core/cache/config_cache.go @@ -0,0 +1,30 @@ +package cache + +import ( + models2 "cutego/core/entity" + "cutego/pkg/cache" + "cutego/pkg/common" + "cutego/pkg/constant" +) + +// GetRedisConfig 根据key从缓存中获取配置数据 +// @Param key 键 +// @Return *models2.SysConfig +func GetRedisConfig(key string) *models2.SysConfig { + val := cache.GetCache(constant.RedisConst{}.GetRedisConfigKey() + key) + s := new(models2.SysConfig) + return common.JsonToStruct(val, s).(*models2.SysConfig) +} + +// SetRedisConfig 将配置存入缓存 +// @Param config models2.SysConfig +func SetRedisConfig(config models2.SysConfig) { + cache.SetCache(config.ConfigKey, common.StructToJson(config)) +} + +// RemoveRedisConfig 从缓存中删除配置 +// @Param configKey string 配置键 +// @Return 影响的行数 +func RemoveRedisConfig(configKey string) int { + return cache.RemoveCache(constant.RedisConst{}.GetRedisConfigKey() + configKey) +} diff --git a/core/cache/dict_cache.go b/core/cache/dict_cache.go new file mode 100644 index 0000000..2fe0665 --- /dev/null +++ b/core/cache/dict_cache.go @@ -0,0 +1,35 @@ +package cache + +import ( + models2 "cutego/core/entity" + "cutego/pkg/cache" + "cutego/pkg/common" + "cutego/pkg/constant" +) + +// GetRedisDict 根据key获取缓存中的字典数据 +// @Param key string 键 +// @Return interface {} +func GetRedisDict(key string) interface{} { + val := cache.GetCache(key) + s := make([]interface{}, 0) + return common.JsonToStruct(val, s) +} + +// SetRedisDict 保存字典数据 +// @Param dictType string 字典类型 +// @Param list []models2.SysDictData +func SetRedisDict(dictType string, list []models2.SysDictData) { + cache.SetCache(constant.RedisConst{}.GetRedisDictKey()+dictType, list) +} + +// RemoveRedisDictList 批量删除字典数据 +// @Param dictType []string 字典类型集合 +func RemoveRedisDictList(dictType []string) { + includeKey := make([]string, 0) + header := constant.RedisConst{}.GetRedisDictKey() + for _, e := range dictType { + includeKey = append(includeKey, header+e) + } + cache.RemoveList(includeKey) +} diff --git a/core/cache/online_cache.go b/core/cache/online_cache.go new file mode 100644 index 0000000..1b924c7 --- /dev/null +++ b/core/cache/online_cache.go @@ -0,0 +1,24 @@ +package cache + +import ( + "cutego/pkg/cache" + "cutego/pkg/config" + "cutego/pkg/constant" +) + +// SetRedisToken 将token存入到redis +// @Param username string 用户名 +// @Param token string token令牌 +func SetRedisToken(username string, token string) { + if config.AppEnvConfig.Login.Single { + cache.SetCacheTTL(constant.RedisOnlineUserKey+username, token, 3600) + } +} + +// RemoveRedisToken 将token从redis中删除 +// @Param username string 用户名 +// @Return 删除的行数 +func RemoveRedisToken(username string) int { + // 不管是不是单点登录, 直接踢 + return cache.RemoveCache(constant.RedisOnlineUserKey + username) +} diff --git a/core/dao/config_dao.go b/core/dao/config_dao.go new file mode 100644 index 0000000..1972b65 --- /dev/null +++ b/core/dao/config_dao.go @@ -0,0 +1,147 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" + "github.com/go-xorm/xorm" +) + +type ConfigDao struct { +} + +func (d ConfigDao) sql(session *xorm.Session) *xorm.Session { + return session.Table("sys_config") +} + +// SelectByConfigKey 根据键名查询参数配置信息 +func (d ConfigDao) SelectByConfigKey(configKey string) *entity.SysConfig { + config := entity.SysConfig{} + _, err := d.sql(SqlDB.NewSession()).Where("config_key = ?", configKey).Get(&config) + if err != nil { + common.ErrorLog(err) + return nil + } + return &config +} + +// SelectPage 分页查询数据 +func (d ConfigDao) SelectPage(query request.ConfigQuery) (*[]entity.SysConfig, int64) { + configs := make([]entity.SysConfig, 0) + session := d.sql(SqlDB.NewSession()) + if gotool.StrUtils.HasNotEmpty(query.ConfigName) { + session.And("config_name like concat('%', ?, '%')", query.ConfigName) + } + if gotool.StrUtils.HasNotEmpty(query.ConfigType) { + session.And("config_type = ?", query.ConfigType) + } + if gotool.StrUtils.HasNotEmpty(query.ConfigKey) { + session.And("config_key like concat('%', ?, '%')", query.ConfigKey) + } + if gotool.StrUtils.HasNotEmpty(query.BeginTime) { + session.And("date_format(create_time,'%y%m%d') >= date_format(?,'%y%m%d')", query.BeginTime) + } + if gotool.StrUtils.HasNotEmpty(query.EndTime) { + session.And("date_format(create_time,'%y%m%d') <= date_format(?,'%y%m%d')", query.EndTime) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&configs) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return &configs, total +} + +// CheckConfigKeyUnique 校验是否存在 +func (d ConfigDao) CheckConfigKeyUnique(config entity.SysConfig) int64 { + session := d.sql(SqlDB.NewSession()) + if config.ConfigId > 0 { + session.Where("config_id != ?", config.ConfigId) + } + count, err := session.And("config_key = ?", config.ConfigKey).Cols("config_id").Count() + if err != nil { + common.ErrorLog(err) + return 0 + } + return count +} + +// Insert 添加数据 +func (d ConfigDao) Insert(config entity.SysConfig) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&config) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return insert +} + +// SelectById 查询数据 +func (d ConfigDao) SelectById(id int64) *entity.SysConfig { + config := entity.SysConfig{} + session := d.sql(SqlDB.NewSession()) + _, err := session.Where("config_id = ?", id).Get(&config) + if err != nil { + common.ErrorLog(err) + return nil + } + return &config +} + +// Update 修改数据 +func (d ConfigDao) Update(config entity.SysConfig) int64 { + session := SqlDB.NewSession() + session.Begin() + update, err := session.Where("config_id = ?", config.ConfigId).Update(&config) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return update +} + +// CheckConfigByIds 根据id集合查询 +func (d ConfigDao) CheckConfigByIds(list []int64) *[]entity.SysConfig { + configs := make([]entity.SysConfig, 0) + err := d.sql(SqlDB.NewSession()).In("config_id", list).Find(&configs) + if err != nil { + common.ErrorLog(err) + return nil + } + return &configs +} + +// Remove 删除数据 +func (d ConfigDao) Delete(list []int64) bool { + session := SqlDB.NewSession() + session.Begin() + _, err := session.In("config_id", list).Delete(&entity.SysConfig{}) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return false + } + session.Commit() + return true +} + +// SelectAll 查询所有数据 +func (d ConfigDao) SelectAll() *[]entity.SysConfig { + configs := make([]entity.SysConfig, 0) + session := SqlDB.NewSession() + err := session.Find(&configs) + if err != nil { + common.ErrorLog(err) + return nil + } + return &configs +} diff --git a/core/dao/cron_job_dao.go b/core/dao/cron_job_dao.go new file mode 100644 index 0000000..07d4d6b --- /dev/null +++ b/core/dao/cron_job_dao.go @@ -0,0 +1,99 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" + "github.com/go-xorm/xorm" +) + +type CronJobDao struct { +} + +func (d CronJobDao) sql(session *xorm.Session) *xorm.Session { + return session.Table("sys_cron_job") +} + +// SelectPage 分页查询数据 +func (d CronJobDao) SelectPage(query request.CronJobQuery) ([]entity.SysCronJob, int64) { + configs := make([]entity.SysCronJob, 0) + session := d.sql(SqlDB.NewSession()) + if gotool.StrUtils.HasNotEmpty(query.JobName) { + session.And("job_name like concat('%', ?, '%')", query.JobName) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&configs) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return configs, total +} + +// Insert 添加数据 +func (d CronJobDao) Insert(config entity.SysCronJob) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&config) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return insert +} + +// SelectById 查询数据 +func (d CronJobDao) SelectById(id int64) *entity.SysCronJob { + config := entity.SysCronJob{} + session := d.sql(SqlDB.NewSession()) + _, err := session.Where("job_id = ?", id).Get(&config) + if err != nil { + common.ErrorLog(err) + return nil + } + return &config +} + +// Update 修改数据 +func (d CronJobDao) Update(config entity.SysCronJob) int64 { + //session := SqlDB.NewSession() + //session.Begin() + //update, err := session.Where("job_id = ?", config.JobId).Update(&config) + //if err != nil { + // common.ErrorLog(err) + // session.Rollback() + // return 0 + //} + //session.Commit() + return CustomUpdateById("job_id", config.JobId, config) +} + +// Remove 删除数据 +func (d CronJobDao) Delete(list []int64) bool { + session := SqlDB.NewSession() + session.Begin() + _, err := session.In("config_id", list).Delete(&entity.SysCronJob{}) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return false + } + session.Commit() + return true +} + +// 通过方法别名获取任务详情 +func (d CronJobDao) SelectByFuncAlias(funcAlias string) *entity.SysCronJob { + config := entity.SysCronJob{} + session := d.sql(SqlDB.NewSession()) + _, err := session.Where("func_alias = ?", funcAlias).Get(&config) + if err != nil { + common.ErrorLog(err) + return nil + } + return &config +} diff --git a/core/dao/dept_dao.go b/core/dao/dept_dao.go new file mode 100644 index 0000000..367b23e --- /dev/null +++ b/core/dao/dept_dao.go @@ -0,0 +1,153 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "github.com/druidcaesa/gotool" +) + +type DeptDao struct { +} + +// SelectTree 根据条件查询部门集合 +func (d DeptDao) SelectTree(query request.DeptQuery) *[]entity.SysDept { + depts := make([]entity.SysDept, 0) + session := SqlDB.NewSession().Where("del_flag = '0'") + if query.ParentId > 0 { + session.And("parent_id = ?", query.ParentId) + } + if !gotool.StrUtils.HasEmpty(query.DeptName) { + session.And("dept_name like concat('%', ?, '%')", query.DeptName) + } + if !gotool.StrUtils.HasEmpty(query.Status) { + session.And("status = ?", query.Status) + } + err := session.OrderBy("parent_id").OrderBy("order_num").Find(&depts) + if err != nil { + common.ErrorLog(err) + return nil + } + return &depts +} + +// SelectDeptListByRoleId 根据角色ID查询部门树信息 +func (d DeptDao) SelectDeptListByRoleId(id int64, strictly bool) *[]int64 { + list := make([]int64, 0) + session := SqlDB.NewSession().Table([]string{"sys_dept", "d"}).Cols("d.dept_id") + session.Join("LEFT", []string{"sys_role_dept", "rd"}, "d.dept_id = rd.dept_id"). + Where("rd.role_id = ?", id) + if strictly { + session.And("d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = ?)", id) + } + err := session.OrderBy("d.parent_id").OrderBy("d.order_num").Find(&list) + if err != nil { + common.ErrorLog(err) + return nil + } + return &list +} + +// GetList 查询部门列表 +func (d DeptDao) GetList(query request.DeptQuery) *[]entity.SysDept { + list := make([]entity.SysDept, 0) + session := SqlDB.NewSession().OrderBy("parent_id").OrderBy("order_num") + session.Where("del_flag = '0'") + if query.ParentId > 0 { + session.And("parent_id = ?", query.ParentId) + } + if gotool.StrUtils.HasNotEmpty(query.DeptName) { + session.And("dept_name like concat('%', ?, '%')", query.DeptName) + } + if gotool.StrUtils.HasNotEmpty(query.Status) { + session.And("status = ?", query.Status) + } + err := session.Find(&list) + if err != nil { + common.ErrorLog(err) + return nil + } + return &list +} + +// SelectDeptById 根据部门编号获取详细信息 +func (d DeptDao) SelectDeptById(id int) *entity.SysDept { + dept := entity.SysDept{} + _, err := SqlDB.NewSession().Where("dept_id = ?", id).Get(&dept) + if err != nil { + common.ErrorLog(err) + return nil + } + return &dept +} + +// Insert 添加部门数据 +func (d DeptDao) Insert(dept entity.SysDept) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&dept) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return insert +} + +// CheckDeptNameUnique 校验部门名称是否唯一 +func (d DeptDao) CheckDeptNameUnique(dept entity.SysDept) int64 { + session := SqlDB.NewSession() + count, err := session.Table("sys_dept").Cols("dept_id").Where("dept_name=?", dept.DeptName).And("parent_id = ?", dept.ParentId).Limit(1).Count() + if err != nil { + common.ErrorLog(err) + return 1 + } + return count + +} + +// HasChildByDeptId 是否存在部门子节点 +func (d DeptDao) HasChildByDeptId(id int) int64 { + count, _ := SqlDB.NewSession().Table("sys_dept").Cols("dept_id").Where("parent_id = ?", id). + And("del_flag = '0'").Limit(1).Count() + return count +} + +// CheckDeptExistUser 查询部门是否存在用户 +func (d DeptDao) CheckDeptExistUser(id int) int64 { + count, _ := SqlDB.NewSession().Table("sys_user").Cols("user_id").Where("dept_id = ?", id). + And("del_flag = '0'").Count() + return count +} + +// Delete 删除部门 +func (d DeptDao) Delete(id int) int64 { + dept := entity.SysDept{ + DeptId: id, + } + session := SqlDB.NewSession() + session.Begin() + i, err := session.Where("dept_id = ?", id).Delete(&dept) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return i +} + +// Update 更新部门 +func (d DeptDao) Update(dept entity.SysDept) int64 { + session := SqlDB.NewSession() + session.Begin() + update, err := session.Where("dept_id = ?", dept.DeptId).Update(&dept) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return update +} diff --git a/core/dao/dict_data_dao.go b/core/dao/dict_data_dao.go new file mode 100644 index 0000000..ae1a568 --- /dev/null +++ b/core/dao/dict_data_dao.go @@ -0,0 +1,129 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" + "github.com/go-xorm/xorm" +) + +type DictDataDao struct { +} + +func (d *DictDataDao) sql(session *xorm.Session) *xorm.Session { + return session.Table("sys_dict_data") +} + +// SelectByDictType 根据字典类型查询字典数据 +// @Param dictType string 字典类型 +// @Return []entity.SysDictData +func (d *DictDataDao) SelectByDictType(dictType string) []entity.SysDictData { + data := make([]entity.SysDictData, 0) + session := d.sql(SqlDB.NewSession()) + err := session.Where("status = '0' ").And("dict_type = ?", dictType).OrderBy("dict_sort").Asc("dict_sort"). + Find(&data) + if err != nil { + common.ErrorLog(err) + return nil + } + return data +} + +// GetDiceDataAll 查询所有字典数据 +// @Return *[]entity.SysDictData +func (d DictDataDao) GetDiceDataAll() *[]entity.SysDictData { + session := d.sql(SqlDB.NewSession()) + data := make([]entity.SysDictData, 0) + err := session.Where("status = '0' ").OrderBy("dict_sort").Asc("dict_sort"). + Find(&data) + if err != nil { + common.ErrorLog(err) + return nil + } + return &data +} + +// SelectPage 查询集合数据 +// @Param query request.DiceDataQuery +// @Return *[]entity.SysDictData +// @Return 总行数 +func (d *DictDataDao) SelectPage(query request.DiceDataQuery) (*[]entity.SysDictData, int64) { + list := make([]entity.SysDictData, 0) + session := SqlDB.NewSession().Table("sys_dict_data").OrderBy("dict_sort").Asc("dict_sort") + if gotool.StrUtils.HasNotEmpty(query.DictType) { + session.And("dict_type = ?", query.DictType) + } + if gotool.StrUtils.HasNotEmpty(query.DictLabel) { + session.And("dict_label like concat('%', ?, '%')", query.DictLabel) + } + if gotool.StrUtils.HasNotEmpty(query.Status) { + session.And("status = ?", query.Status) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&list) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return &list, total +} + +// SelectByDictCode 根据dictCode查询字典数据 +// @Param dictCode int64 +// @Return *entity.SysDictData +func (d *DictDataDao) SelectByDictCode(dictCode int64) *entity.SysDictData { + data := entity.SysDictData{} + session := SqlDB.NewSession() + _, err := session.Where("dict_code = ?", dictCode).Get(&data) + if err != nil { + common.ErrorLog(err) + return nil + } + return &data +} + +// Insert 添加字典数据 +// @Param data entity.SysDictData +// @Return 新增的行数 +func (d *DictDataDao) Insert(data entity.SysDictData) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&data) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return insert +} + +// Delete 删除字典数据 +func (d *DictDataDao) Delete(codes []int64) bool { + session := SqlDB.NewSession() + session.Begin() + _, err := session.In("dict_code", codes).Delete(&entity.SysDictData{}) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return false + } + session.Commit() + return true +} + +// 修改字典数据 +func (d *DictDataDao) Update(data entity.SysDictData) bool { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Where("dict_code = ?", data.DictCode).Update(&data) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return false + } + session.Commit() + return true +} diff --git a/core/dao/dict_type_dao.go b/core/dao/dict_type_dao.go new file mode 100644 index 0000000..96c713b --- /dev/null +++ b/core/dao/dict_type_dao.go @@ -0,0 +1,123 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" + "github.com/go-xorm/xorm" +) + +type DictTypeDao struct { +} + +func (d DictTypeDao) sql(session *xorm.Session) *xorm.Session { + return session.Table("sys_dict_type") +} + +// SelectAll 查询所有字典类型数据 +func (d DictTypeDao) SelectAll() []*entity.SysDictType { + types := make([]*entity.SysDictType, 0) + err := d.sql(SqlDB.NewSession()).Where("status = '0'").Find(&types) + if err != nil { + common.ErrorLog(err) + return nil + } + return types +} + +// SelectPage 分页查询字典类型数据 +func (d DictTypeDao) SelectPage(query request.DictTypeQuery) (*[]entity.SysDictType, int64) { + list := make([]entity.SysDictType, 0) + session := SqlDB.NewSession().Table("sys_dict_type") + if gotool.StrUtils.HasNotEmpty(query.DictName) { + session.And("dict_name like concat('%', ?, '%')", query.DictName) + } + if gotool.StrUtils.HasNotEmpty(query.Status) { + session.And("status = ?", query.Status) + } + if gotool.StrUtils.HasNotEmpty(query.DictType) { + session.And("AND dict_type like concat('%', ?, '%')", query.DictType) + } + if gotool.StrUtils.HasNotEmpty(query.BeginTime) { + session.And("date_format(create_time,'%y%m%d') >= date_format(?,'%y%m%d')", query.BeginTime) + } + if gotool.StrUtils.HasNotEmpty(query.EndTime) { + session.And("date_format(create_time,'%y%m%d') <= date_format(?,'%y%m%d')", query.EndTime) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&list) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return &list, total +} + +// SelectById 根据id查询字典类型数据 +func (d DictTypeDao) SelectById(id int64) *entity.SysDictType { + dictType := entity.SysDictType{} + _, err := SqlDB.NewSession().Where("dict_id = ?", id).Get(&dictType) + if err != nil { + common.ErrorLog(err) + return nil + } + return &dictType +} + +// CheckDictTypeUnique 检验字典类型是否存在 +func (d DictTypeDao) CheckDictTypeUnique(dictType entity.SysDictType) int64 { + session := SqlDB.NewSession().Table("sys_dict_type") + if dictType.DictId > 0 { + session.And("dict_id != ?", dictType.DictId) + } + count, err := session.Where("dict_type = ?", dictType.DictType).Cols("dict_id").Count() + if err != nil { + common.ErrorLog(err) + return 0 + } + return count +} + +// Update 修改字典 +func (d DictTypeDao) Update(dictType entity.SysDictType) bool { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Where("dict_id = ?", dictType.DictId).Update(&dictType) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return false + } + session.Commit() + return true +} + +// Insert 新增字典类型 +func (d DictTypeDao) Insert(dictType entity.SysDictType) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&dictType) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return insert +} + +// Delete 批量删除 +func (d DictTypeDao) Delete(ids []int64) bool { + session := SqlDB.NewSession() + session.Begin() + _, err := session.In("dict_id", ids).Delete(entity.SysDictType{}) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return false + } + session.Commit() + return true +} diff --git a/core/dao/index.go b/core/dao/index.go new file mode 100644 index 0000000..2017da6 --- /dev/null +++ b/core/dao/index.go @@ -0,0 +1,133 @@ +package dao + +import ( + models2 "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/config" + "cutego/pkg/constant" + redisTool "cutego/pkg/redispool" + "fmt" + _ "github.com/go-sql-driver/mysql" + "github.com/go-xorm/xorm" + "time" +) + +// X 全局DB +var ( + SqlDB *xorm.Engine + RedisDB *redisTool.RedisClient +) + +func initDatabase() { + var err error + // 配置mysql数据库 + ds := config.AppEnvConfig.DataSource + jdbc := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local", + ds.Username, + ds.Password, + ds.Host, + ds.Port, + ds.Database, + ds.Charset, + ) + SqlDB, _ = xorm.NewEngine(ds.DbType, jdbc) + if err != nil { + common.FatalfLog("db error: %#v\n", err.Error()) + } + err = SqlDB.Ping() + if err != nil { + common.FatalfLog("db connect error: %#v\n", err.Error()) + } + SqlDB.SetMaxIdleConns(ds.MaxIdleSize) + SqlDB.SetMaxOpenConns(ds.MaxOpenSize) + timer := time.NewTicker(time.Minute * 30) + go func(x *xorm.Engine) { + for _ = range timer.C { + err = x.Ping() + if err != nil { + common.FatalfLog("db connect error: %#v\n", err.Error()) + } + } + }(SqlDB) + SqlDB.ShowSQL(true) +} +func initRedis() { + // 配置redis数据库 + RedisDB = redisTool.NewRedis() +} +func init() { + initDatabase() + initRedis() + cacheInitDataToRedis() +} + +// 初始化缓存数据 +func cacheInitDataToRedis() { + initDict() + initConfig() +} + +func initDict() { + // 查询字典类型数据 + dictTypeDao := new(DictTypeDao) + typeAll := dictTypeDao.SelectAll() + // 所有字典数据 + d := new(DictDataDao) + listData := d.GetDiceDataAll() + for _, dictType := range typeAll { + dictData := make([]map[string]interface{}, 0) + for _, data := range *listData { + if dictType.DictType == data.DictType { + dictData = append(dictData, map[string]interface{}{ + "dictCode": data.DictCode, + "dictSort": data.DictSort, + "dictLabel": data.DictLabel, + "dictValue": data.DictValue, + "isDefault": data.IsDefault, + "remark": data.Remark, + }) + } + } + RedisDB.SET(constant.RedisConst{}.GetRedisDictKey()+dictType.DictType, common.StructToJson(dictData)) + } +} + +func initConfig() { + // 查询配置数据存入到缓存中 + configDao := new(ConfigDao) + configSession := configDao.sql(SqlDB.NewSession()) + configs := make([]*models2.SysConfig, 0) + err := configSession.Find(&configs) + if err != nil { + common.ErrorLog(err) + return + } + for _, sysConfig := range configs { + RedisDB.SET(constant.RedisConst{}.GetRedisConfigKey()+sysConfig.ConfigKey, common.StructToJson(map[string]interface{}{ + "configId": sysConfig.ConfigId, + "configName": sysConfig.ConfigName, + "configKey": sysConfig.ConfigKey, + "configValue": sysConfig.ConfigValue, + "configType": sysConfig.ConfigType, + "remark": sysConfig.Remark, + })) + } +} + +// 通用: 根据ID更新 +// @Param idColumnName 字段名称 +// @Param idColumnValue 字段值 +// @Param bean 更新的bean内容 +// @Return +func CustomUpdateById(idColumnName string, idColumnValue interface{}, bean interface{}) int64 { + session := SqlDB.NewSession() + session.Begin() + update, err := session.Where(idColumnName+" = ?", idColumnValue).Update(&bean) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return update +} diff --git a/core/dao/login_info_dao.go b/core/dao/login_info_dao.go new file mode 100644 index 0000000..c63e1cd --- /dev/null +++ b/core/dao/login_info_dao.go @@ -0,0 +1,44 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/go-xorm/xorm" +) + +type LoginInfoDao struct { +} + +// 查询公共sql +func (d LoginInfoDao) sql(session *xorm.Session) *xorm.Session { + return session.Table("sys_login_info") +} + +// SelectPage 分页查询数据 +func (d LoginInfoDao) SelectPage(query request.LoginInfoQuery) (*[]entity.SysLoginInfo, int64) { + loginInfos := make([]entity.SysLoginInfo, 0) + session := d.sql(SqlDB.NewSession()) + session.And("user_name = ?", query.UserName) + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&loginInfos) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return &loginInfos, total +} + +// Insert 添加登录记录 +func (d LoginInfoDao) Insert(body entity.SysLoginInfo) *entity.SysLoginInfo { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Table("sys_login_info").Insert(&body) + if err != nil { + common.ErrorLog(err) + session.Rollback() + } + session.Commit() + return &body +} diff --git a/core/dao/menu_dao.go b/core/dao/menu_dao.go new file mode 100644 index 0000000..01a2b9f --- /dev/null +++ b/core/dao/menu_dao.go @@ -0,0 +1,179 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "github.com/druidcaesa/gotool" +) + +type MenuDao struct { +} + +// GetMenuPermission 根据用户ID查询权限 +func (d MenuDao) GetMenuPermission(id int64) *[]string { + var perms []string + session := SqlDB.Table([]string{"sys_menu", "m"}) + err := session.Distinct("m.perms"). + Join("LEFT", []string{"sys_role_menu", "rm"}, "m.menu_id = rm.menu_id"). + Join("LEFT", []string{"sys_user_role", "ur"}, "rm.role_id = ur.role_id"). + Join("LEFT", []string{"sys_role", "r"}, "r.role_id = ur.role_id"). + Where("m.status = '0'").And("r.status = '0'").And("ur.user_id = ?", id).Find(&perms) + if err != nil { + common.ErrorLog(err) + return nil + } + return &perms +} + +// GetMenuAll 查询所有菜单数据 +func (d MenuDao) GetMenuAll() *[]entity.SysMenu { + menus := make([]entity.SysMenu, 0) + session := SqlDB.Table([]string{entity.SysMenu{}.TableName(), "m"}) + err := session.Distinct("m.menu_id").Cols("m.parent_id", "m.menu_name", "m.path", "m.component", "m.visible", "m.status", "m.perms", "m.is_frame", "m.is_cache", "m.menu_type", "m.icon", "m.order_num", "m.create_time"). + Where("m.menu_type in ('M', 'C')").And("m.status = 0").OrderBy("m.parent_id").OrderBy("m.order_num").Find(&menus) + if err != nil { + common.ErrorLog(err) + return nil + } + return &menus +} + +// GetMenuByUserId 根据用户ID查询菜单 +func (d MenuDao) GetMenuByUserId(id int64) *[]entity.SysMenu { + menus := make([]entity.SysMenu, 0) + session := SqlDB.Table([]string{entity.SysMenu{}.TableName(), "m"}) + err := session.Distinct("m.menu_id").Cols("m.parent_id", "m.menu_name", "m.path", "m.component", "m.visible", "m.status", "m.perms", "m.is_frame", "m.is_cache", "m.menu_type", "m.icon", "m.order_num", "m.create_time"). + Join("LEFT", []string{"sys_role_menu", "rm"}, "m.menu_id = rm.menu_id"). + Join("LEFT", []string{"sys_user_role", "ur"}, "rm.role_id = ur.role_id"). + Join("LEFT", []string{"sys_role", "ro"}, "ur.role_id = ro.role_id"). + Join("LEFT", []string{"sys_user", "u"}, "ur.user_id = u.user_id").Where("u.user_id = ?", id). + And("m.menu_type in ('M', 'C')").And("m.status = 0").OrderBy("m.parent_id").OrderBy("m.order_num").Find(&menus) + if err != nil { + common.ErrorLog(err) + return nil + } + return &menus +} + +// SelectMenuByRoleId 根据角色ID查询菜单树信息 +func (d MenuDao) SelectMenuByRoleId(id int64, strictly bool) *[]int64 { + list := make([]int64, 0) + session := SqlDB.NewSession().Table([]string{"sys_menu", "m"}) + session.Join("LEFT", []string{"sys_role_menu", "rm"}, "m.menu_id = rm.menu_id") + session.Where("rm.role_id = ?", id) + if strictly { + session.And("m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = ?)", id) + } + err := session.OrderBy("m.parent_id").OrderBy("m.order_num").Cols("m.menu_id").Find(&list) + if err != nil { + common.ErrorLog(err) + return nil + } + return &list +} + +// SelectMenuList 查询系统菜单列表 +func (d MenuDao) SelectMenuList(query request.MenuQuery) *[]entity.SysMenu { + list := make([]entity.SysMenu, 0) + session := SqlDB.NewSession().OrderBy("parent_id").OrderBy("order_num") + if gotool.StrUtils.HasNotEmpty(query.MenuName) { + session.And("menu_name like concat('%', ?, '%')", query.MenuName) + } + if gotool.StrUtils.HasNotEmpty(query.Visible) { + session.And("visible = ?", query.Visible) + } + if gotool.StrUtils.HasNotEmpty(query.Status) { + session.And("status = ?", query.Status) + } + err := session.Find(&list) + if err != nil { + common.ErrorLog(err) + return nil + } + return &list +} + +// SelectMenuListByUserId 根据用户查询系统菜单列表 +func (d MenuDao) SelectMenuListByUserId(query request.MenuQuery) *[]entity.SysMenu { + session := SqlDB.NewSession().OrderBy("parent_id").OrderBy("order_num") + list := make([]entity.SysMenu, 0) + session.Distinct("m.menu_id", "m.parent_id", "m.menu_name", "m.path", "m.component", "m.visible", "m.status", "ifnull(m.perms,'') as perms", "m.is_frame", "m.is_cache", "m.menu_type", "m.icon", "m.order_num", "m.create_time") + session.Join("LEFT", []string{"sys_role_menu", "rm"}, "m.menu_id = rm.menu_id"). + Join("LEFT", []string{"sys_user_role", "ur"}, "rm.role_id = ur.role_id"). + Join("LEFT", []string{"sys_role", "ro"}, "ur.role_id = ro.role_id"). + Where("ur.user_id = ?", query.UserId) + if gotool.StrUtils.HasNotEmpty(query.MenuName) { + session.And("menu_name like concat('%', ?, '%')", query.MenuName) + } + if gotool.StrUtils.HasNotEmpty(query.Visible) { + session.And("visible = ?", query.Visible) + } + if gotool.StrUtils.HasNotEmpty(query.Status) { + session.And("status = ?", query.Status) + } + err := session.Find(&list) + if err != nil { + common.ErrorLog(err) + return nil + } + return &list +} + +// SelectMenuByMenuId 根据菜单ID查询信息 +func (d MenuDao) SelectMenuByMenuId(id int) *entity.SysMenu { + menu := entity.SysMenu{ + MenuId: id, + } + _, err := SqlDB.NewSession().Where("menu_id = ?", menu.MenuId).Get(&menu) + if err != nil { + common.ErrorLog(err) + return nil + } + return &menu +} + +// Insert 添加菜单数据 +func (d MenuDao) Insert(menu entity.SysMenu) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&menu) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return insert +} + +// Update 修改菜单数据 +func (d MenuDao) Update(menu entity.SysMenu) int64 { + session := SqlDB.NewSession() + session.Begin() + update, err := session.Where("menu_id = ?", menu.MenuId).Update(&menu) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return update +} + +// Delete 删除菜单操作 +func (d MenuDao) Delete(id int) int64 { + menu := entity.SysMenu{ + MenuId: id, + } + session := SqlDB.NewSession() + session.Begin() + i, err := session.Delete(&menu) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return i +} diff --git a/core/dao/notice_dao.go b/core/dao/notice_dao.go new file mode 100644 index 0000000..3aef1ac --- /dev/null +++ b/core/dao/notice_dao.go @@ -0,0 +1,87 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" +) + +type NoticeDao struct { +} + +// SelectPage 查询集合 +func (d NoticeDao) SelectPage(query request.NoticeQuery) (*[]entity.SysNotice, int64) { + notices := make([]entity.SysNotice, 0) + session := SqlDB.NewSession().Table(entity.SysNotice{}.TableName()) + if gotool.StrUtils.HasNotEmpty(query.NoticeTitle) { + session.And("notice_title like concat('%', ?, '%')", query.NoticeTitle) + } + if gotool.StrUtils.HasNotEmpty(query.NoticeType) { + session.And("notice_type = ?", query.NoticeType) + } + if gotool.StrUtils.HasNotEmpty(query.CreateBy) { + session.And("create_by like concat('%', ?, '%')", query.CreateBy) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(¬ices) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return ¬ices, total +} + +// Insert 添加数据 +func (d NoticeDao) Insert(notice entity.SysNotice) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(¬ice) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return insert +} + +// Delete 批量删除 +func (d NoticeDao) Delete(list []int64) int64 { + session := SqlDB.NewSession() + session.Begin() + i, err := session.In("notice_id", list).Delete(&entity.SysNotice{}) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return i +} + +// SelectById 查询 +func (d NoticeDao) SelectById(id int64) *entity.SysNotice { + notice := entity.SysNotice{} + _, err := SqlDB.NewSession().Where("notice_id = ?", id).Get(¬ice) + if err != nil { + common.ErrorLog(err) + return nil + } + return ¬ice +} + +// Update 修改数据 +func (d NoticeDao) Update(notice entity.SysNotice) int64 { + session := SqlDB.NewSession() + session.Begin() + update, err := session.Where("notice_id = ?", notice.NoticeId).Update(¬ice) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return update +} diff --git a/core/dao/post_dao.go b/core/dao/post_dao.go new file mode 100644 index 0000000..af5ed36 --- /dev/null +++ b/core/dao/post_dao.go @@ -0,0 +1,153 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" + "github.com/go-xorm/xorm" +) + +type PostDao struct { +} + +func (d PostDao) sqlSelectJoin(session *xorm.Session) *xorm.Session { + return session.Table([]string{"sys_post", "p"}). + Join("LEFT", []string{"sys_user_post", "up"}, "up.post_id = p.post_id"). + Join("LEFT", []string{"sys_user", "u"}, "u.user_id = up.user_id") +} + +// SelectAll 查询所有岗位数据, 数据库操作 +func (d PostDao) SelectAll() []*entity.SysPost { + session := SqlDB.NewSession() + posts := make([]*entity.SysPost, 0) + err := session.Find(&posts) + if err != nil { + common.ErrorLog(err) + return nil + } + return posts +} + +// SelectPostListByUserId 根据用户id查询岗位id集合 +func (d PostDao) SelectPostListByUserId(userId int64) *[]int64 { + var ids []int64 + selectSql := d.sqlSelectJoin(SqlDB.NewSession()) + err := selectSql.Where("u.user_id = ?", userId).Cols("p.post_id").Find(&ids) + if err != nil { + common.ErrorLog(err) + return nil + } + return &ids +} + +// SelectPage 查询岗位分页数据 +func (d PostDao) SelectPage(query request.PostQuery) (*[]entity.SysPost, int64) { + posts := make([]entity.SysPost, 0) + session := SqlDB.NewSession().Table(entity.SysPost{}.TableName()) + if gotool.StrUtils.HasNotEmpty(query.PostCode) { + session.And("post_code like concat('%', ?, '%')", query.PostCode) + } + if gotool.StrUtils.HasNotEmpty(query.Status) { + session.And("status = ?", query.Status) + } + if gotool.StrUtils.HasNotEmpty(query.PostName) { + session.And("post_name like concat('%', ?, '%')", query.PostName) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&posts) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return &posts, total +} + +// CheckPostNameUnique 校验岗位名称是否存在 +func (d PostDao) CheckPostNameUnique(post entity.SysPost) int64 { + session := SqlDB.NewSession().Table("sys_post").Cols("post_id"). + Where("post_name = ?", post.PostName) + if post.PostId > 0 { + session.And("post_id != ?", post.PostId) + } + count, _ := session.Count() + return count +} + +// CheckPostCodeUnique 校验岗位编码是否存在 +func (d PostDao) CheckPostCodeUnique(post entity.SysPost) int64 { + session := SqlDB.NewSession().Table("sys_post").Cols("post_id"). + Where("post_code = ?", post.PostCode) + if post.PostId > 0 { + session.And("post_id != ?", post.PostId) + } + count, _ := session.Count() + return count +} + +// Insert 添加岗位数据 +func (d PostDao) Insert(post entity.SysPost) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&post) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return insert +} + +// GetPostById 根据id查询岗位数据 +func (d PostDao) GetPostById(post entity.SysPost) *entity.SysPost { + _, err := SqlDB.NewSession().Where("post_id = ?", post.PostId).Get(&post) + if err != nil { + common.ErrorLog(err) + return nil + } + return &post +} + +// Delete 批量删除岗位 +func (d PostDao) Delete(posts []int64) int64 { + + session := SqlDB.NewSession() + session.Begin() + i, err := session.In("post_id", posts).Delete(&entity.SysPost{}) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return i +} + +// Update 修改岗位数据 +func (d PostDao) Update(post entity.SysPost) bool { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Where("post_id = ?", post.PostId).Update(&post) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return false + } + session.Commit() + return true +} + +func (d PostDao) SelectPostByUserName(name string) *[]entity.SysPost { + posts := make([]entity.SysPost, 0) + session := SqlDB.NewSession().Table([]string{entity.SysPost{}.TableName(), "p"}) + err := session.Cols("p.post_id", "p.post_name", "p.post_code"). + Join("LEFT", []string{"sys_user_post", "up"}, "up.post_id = p.post_id"). + Join("LEFT", []string{"sys_user", "u"}, "u.user_id = up.user_id").Where("u.user_name = ?", name).Find(&posts) + if err != nil { + common.ErrorLog(err) + return nil + } + return &posts +} diff --git a/core/dao/role_dao.go b/core/dao/role_dao.go new file mode 100644 index 0000000..94fdc99 --- /dev/null +++ b/core/dao/role_dao.go @@ -0,0 +1,205 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" + "github.com/go-xorm/xorm" +) + +type RoleDao struct { +} + +// 角色公用sql +func (d RoleDao) sqlSelectJoin() *xorm.Session { + return SqlDB.Table([]string{entity.SysRole{}.TableName(), "r"}). + Join("LEFT", []string{"sys_user_role", "ur"}, "ur.role_id = r.role_id"). + Join("LEFT", []string{"sys_user", "u"}, "u.user_id = ur.user_id"). + Join("LEFT", []string{"sys_dept", "d"}, "u.dept_id = d.dept_id") +} + +// 用户角色关系查询sql +func (d RoleDao) sqlSelectRoleAndUser() *xorm.Session { + return SqlDB.Table([]string{entity.SysRole{}.TableName(), "r"}). + Join("LEFT", []string{"sys_user_role", "ur"}, "ur.role_id = r.role_id"). + Join("LEFT", []string{"sys_user", "u"}, "u.user_id = ur.user_id") +} + +// SelectPage 根据条件查询角色数据 +func (d RoleDao) SelectPage(q *request.RoleQuery) ([]*entity.SysRole, int64) { + roles := make([]*entity.SysRole, 0) + session := d.sqlSelectJoin() + if !gotool.StrUtils.HasEmpty(q.RoleName) { + session.And("r.role_name like concat('%', ?, '%')", q.RoleName) + } + if !gotool.StrUtils.HasEmpty(q.Status) { + session.And("r.status = ?", q.Status) + } + if !gotool.StrUtils.HasEmpty(q.RoleKey) { + session.And("r.role_key like concat('%', ?, '%')", q.RoleKey) + } + if !gotool.StrUtils.HasEmpty(q.BeginTime) { + session.And("date_format(r.create_time,'%y%m%d') >= date_format(?,'%y%m%d')", q.BeginTime) + } + if !gotool.StrUtils.HasEmpty(q.EndTime) { + session.And("date_format(r.create_time,'%y%m%d') <= date_format(?,'%y%m%d')", q.EndTime) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(q.PageSize, page.StartSize(q.PageNum, q.PageSize)).OrderBy("r.role_sort").Find(&roles) + if err != nil { + return nil, 0 + } + return roles, total +} + +// SelectAll 查询所有角色 +func (d RoleDao) SelectAll() []*entity.SysRole { + sql := d.sqlSelectJoin() + roles := make([]*entity.SysRole, 0) + err := sql.Find(&roles) + if err != nil { + common.ErrorLog(err) + return nil + } + return roles +} + +// SelectRoleListByUserId 根据用户id查询用户角色id集合 +func (d RoleDao) SelectRoleListByUserId(userId int64) *[]int64 { + sqlSelectRoleAndUser := d.sqlSelectRoleAndUser() + var roleIds []int64 + err := sqlSelectRoleAndUser.Cols("r.role_id").Where("u.user_id = ?", userId).Find(&roleIds) + if err != nil { + common.ErrorLog(err) + return nil + } + return &roleIds +} + +// SelectRolePermissionByUserId 查询用户角色集合 +func (d RoleDao) SelectRolePermissionByUserId(id int64) *[]string { + var roleKeys []string + err := d.sqlSelectJoin().Cols("r.role_key").Where("r.del_flag = '0'").And("ur.user_id = ?", id).Find(&roleKeys) + if err != nil { + common.ErrorLog(err) + return nil + } + return &roleKeys +} + +// GetRoleListByUserId 根据用户ID查询角色 +func (d RoleDao) GetRoleListByUserId(id int64) *[]entity.SysRole { + roles := make([]entity.SysRole, 0) + err := d.sqlSelectJoin().Where("r.del_flag = '0'").And("ur.user_id = ?", id).Find(&roles) + if err != nil { + common.ErrorLog(err) + return nil + } + return &roles +} + +// SelectRoleByRoleId 根据角色id查询角色数据 +func (d RoleDao) SelectRoleByRoleId(id int64) *entity.SysRole { + role := entity.SysRole{} + _, err := d.sqlSelectJoin().Where("r.role_id = ?", id).Get(&role) + if err != nil { + common.ErrorLog(err) + return nil + } + return &role +} + +// CheckRoleNameUnique 校验角色名称是否唯一 +func (d RoleDao) CheckRoleNameUnique(role entity.SysRole) int64 { + session := SqlDB.Table(role.TableName()).Where("role_name = ?", role.RoleName) + if role.RoleId > 0 { + session.And("role_id != ?", role.RoleId) + } + count, err := session.Count(&role) + if err != nil { + common.ErrorLog(err) + } + return count +} + +// CheckRoleKeyUnique 校验角色权限是否唯一 +func (d RoleDao) CheckRoleKeyUnique(role entity.SysRole) int64 { + session := SqlDB.Table(role.TableName()).Where("role_key = ?", role.RoleKey) + if role.RoleId > 0 { + session.And("role_id != ?", role.RoleId) + } + count, err := session.Count(&role) + if err != nil { + common.ErrorLog(err) + } + return count +} + +// Add 添加角色进入数据库操作 +func (d RoleDao) Insert(role entity.SysRole) entity.SysRole { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Insert(&role) + if err != nil { + common.ErrorLog(err) + session.Rollback() + } + session.Commit() + return role +} + +// Update 修改数据 +func (d RoleDao) Update(role entity.SysRole) int64 { + session := SqlDB.NewSession() + session.Begin() + update, err := session.Where("role_id = ?", role.RoleId).Update(&role) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return update +} + +// Delete 删除角色 +func (d RoleDao) Delete(role entity.SysRole) int64 { + session := SqlDB.NewSession() + session.Begin() + i, err := session.Delete(&role) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return i +} + +// UpdateRoleStatus 修改角色状态 +func (d RoleDao) UpdateRoleStatus(role *entity.SysRole) int64 { + session := SqlDB.NewSession() + session.Begin() + update, err := session.Where("role_id = ?", role.RoleId).Cols("status", "update_by", "update_time").Update(role) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return update +} + +// SelectRolesByUserName 查询角色组 +func (d RoleDao) SelectRolesByUserName(name string) *[]entity.SysRole { + roles := make([]entity.SysRole, 0) + session := d.sqlSelectJoin() + err := session.Where("r.del_flag = '0'").And("u.user_name = ?", name).Find(&roles) + if err != nil { + common.ErrorLog(err) + return nil + } + return &roles +} diff --git a/core/dao/role_menu_dao.go b/core/dao/role_menu_dao.go new file mode 100644 index 0000000..fe799b5 --- /dev/null +++ b/core/dao/role_menu_dao.go @@ -0,0 +1,37 @@ +package dao + +import ( + models2 "cutego/core/entity" + "cutego/pkg/common" +) + +type RoleMenuDao struct { +} + +// Insert 添加角色菜单关系 +func (d RoleMenuDao) Insert(list []models2.SysRoleMenu) int64 { + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&list) + if err != nil { + common.ErrorLog(err) + session.Rollback() + } + session.Commit() + return insert +} + +// Delete 删除角色和菜单关系 +func (d RoleMenuDao) Delete(role models2.SysRole) { + menu := models2.SysRoleMenu{ + RoleId: role.RoleId, + } + session := SqlDB.NewSession() + session.Begin() + _, err := session.Delete(&menu) + if err != nil { + common.ErrorLog(err) + session.Rollback() + } + session.Commit() +} diff --git a/core/dao/user_dao.go b/core/dao/user_dao.go new file mode 100644 index 0000000..0ddae74 --- /dev/null +++ b/core/dao/user_dao.go @@ -0,0 +1,276 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/api/v1/response" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/page" + "github.com/druidcaesa/gotool" + "github.com/go-xorm/xorm" + "time" +) + +type UserDao struct { +} + +// 查询公共sql +func (d UserDao) sqlSelectJoin() *xorm.Session { + return SqlDB.NewSession().Table([]string{"sys_user", "u"}). + Join("LEFT", []string{"sys_dept", "d"}, "u.dept_id = d.dept_id"). + Join("LEFT", []string{"sys_user_role", "ur"}, "u.user_id = ur.user_id"). + Join("LEFT", []string{"sys_role", "r"}, "r.role_id = ur.role_id") +} + +// SelectPage 查询用户集合 +func (d UserDao) SelectPage(query request.UserQuery) ([]*response.UserResponse, int64) { + resp := make([]*response.UserResponse, 0) + sql := d.sqlSelectJoin() + if !gotool.StrUtils.HasEmpty(query.UserName) { + sql.And("u.user_name like concat('%',?,'%')", query.UserName) + } + if !gotool.StrUtils.HasEmpty(query.Status) { + sql.And("i.status = ?", query.Status) + } + if !gotool.StrUtils.HasEmpty(query.PhoneNumber) { + sql.And("u.phone_number like concat('%',?,'%')", query.PhoneNumber) + } + if !gotool.StrUtils.HasEmpty(query.BeginTime) { + sql.And("date_format(u.create_time,'%y%m%d') >= date_format(?,'%y%m%d')", query.BeginTime) + } + if !gotool.StrUtils.HasEmpty(query.EndTime) { + sql.And("date_format(u.create_time,'%y%m%d') <= date_format(?,'%y%m%d')", query.EndTime) + } + if query.DeptId > 0 { + sql.And("u.dept_id = ? OR u.dept_id in ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(?, ancestors))", query.DeptId, query.DeptId) + } + total, _ := page.GetTotal(sql.Clone()) + err := sql.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&resp) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return resp, total +} + +// GetUserById 根据id查询用户数据 +func (d UserDao) GetUserById(userId int64) *response.UserResponse { + var resp response.UserResponse + get, err := d.sqlSelectJoin().Where("u.user_id = ?", userId).Get(&resp) + if err != nil { + common.ErrorLog(err) + } + if !get { + return nil + } + return &resp +} + +// GetUserByUserName 根据用户名查询用户数据 +func (d UserDao) GetUserByUserName(user entity.SysUser) *entity.SysUser { + i, err := SqlDB.Get(&user) + if err != nil { + common.ErrorLog(err) + return nil + } + if i { + return &user + } + return nil +} + +// CheckEmailUnique 校验邮箱是否存在 +func (d UserDao) CheckEmailUnique(user request.UserBody) *entity.SysUser { + sysUser := entity.SysUser{} + session := SqlDB.NewSession().Table("sys_user") + session.Cols("user_id", "email") + session.Where("email = ?", user.Email) + if user.UserId > 0 { + session.And("user_id != ?", user.UserId) + } + get, _ := session.Limit(1).Get(&sysUser) + if !get { + return nil + } + return &sysUser +} + +// CheckPhoneNumUnique 校验手机号是否存在 +func (d UserDao) CheckPhoneNumUnique(body request.UserBody) *entity.SysUser { + sysUser := entity.SysUser{} + session := SqlDB.NewSession().Table("sys_user") + session.Cols("user_id", "phone_num") + session.Where("phone_num = ?", body.PhoneNumber) + if body.UserId > 0 { + session.And("user_id != ?", body.UserId) + } + get, _ := session.Limit(1).Get(&sysUser) + if !get { + return nil + } + return &sysUser +} + +// Insert 添加用户 +func (d UserDao) Insert(body request.UserBody) *request.UserBody { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Table("sys_user").Insert(&body) + if err != nil { + common.ErrorLog(err) + session.Rollback() + } + session.Commit() + return &body +} + +// Update 修改用户数据 +func (d UserDao) Update(body request.UserBody) int64 { + session := SqlDB.NewSession().Table("sys_user") + session.Begin() + _, err := session.Where("user_id = ?", body.UserId).Update(&body) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return 1 +} + +// Delete 根据id删除用户数据 +func (d UserDao) Delete(id int64) int64 { + user := entity.SysUser{ + UserId: id, + } + session := SqlDB.NewSession().Table("sys_user") + session.Begin() + i, err := session.Delete(&user) + if err != nil { + common.ErrorLog(err) + session.Rollback() + } + session.Commit() + return i +} + +// ResetPwd 修改用户密码数据库操作 +func (d UserDao) ResetPwd(body request.UserBody) int64 { + user := entity.SysUser{ + UserId: body.UserId, + Password: body.Password, + } + session := SqlDB.NewSession() + session.Begin() + _, err := session.Where("user_id = ?", user.UserId).Cols("password").Update(&user) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return 1 +} + +// GetAllocatedList 查询未分配用户角色列表 +func (d UserDao) GetAllocatedList(query request.UserQuery) ([]*response.UserResponse, int64) { + resp := make([]*response.UserResponse, 0) + session := SqlDB.NewSession() + session.Table([]string{"sys_user", "u"}).Distinct("u.user_id", "u.dept_id", "u.user_name", "u.nick_name", "u.email", "u.phone_number", "u.status", "u.create_time"). + Join("LEFT", []string{"sys_dept", "d"}, "u.dept_id = d.dept_id"). + Join("LEFT", []string{"sys_user_role", "ur"}, "u.user_id = ur.user_id"). + Join("LEFT", []string{"sys_role", "r"}, "r.role_id = ur.role_id").Where("u.del_flag = '0'").And("r.role_id = ?", query.RoleId) + if gotool.StrUtils.HasNotEmpty(query.UserName) { + session.And("u.user_name like concat('%', ?, '%')", query.UserName) + } + if gotool.StrUtils.HasNotEmpty(query.PhoneNumber) { + session.And("u.phone_number like concat('%', ?, '%')", query.PhoneNumber) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&resp) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return resp, total +} + +// GetUnallocatedList 查询未分配用户角色列表 +func (d UserDao) GetUnallocatedList(query request.UserQuery) ([]*response.UserResponse, int64) { + resp := make([]*response.UserResponse, 0) + session := SqlDB.NewSession() + session.Table([]string{"sys_user", "u"}).Distinct("u.user_id", "u.dept_id", "u.user_name", "u.nick_name", "u.email", "u.phone_number", "u.status", "u.create_time"). + Join("LEFT", []string{"sys_dept", "d"}, "u.dept_id = d.dept_id"). + Join("LEFT", []string{"sys_user_role", "ur"}, "u.user_id = ur.user_id"). + Join("LEFT", []string{"sys_role", "r"}, "r.role_id = ur.role_id").Where("u.del_flag = '0'").And("r.role_id = ? or r.role_id IS NULL", query.RoleId). + And("u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = ?)", query.RoleId) + if gotool.StrUtils.HasNotEmpty(query.UserName) { + session.And("u.user_name like concat('%', ?, '%')", query.UserName) + } + if gotool.StrUtils.HasNotEmpty(query.PhoneNumber) { + session.And("u.phone_number like concat('%', ?, '%')", query.PhoneNumber) + } + total, _ := page.GetTotal(session.Clone()) + err := session.Limit(query.PageSize, page.StartSize(query.PageNum, query.PageSize)).Find(&resp) + if err != nil { + common.ErrorLog(err) + return nil, 0 + } + return resp, total +} + +// UpdatePwd 修改密码 +func (d UserDao) UpdatePwd(id int64, hash string) int64 { + user := entity.SysUser{} + user.UserId = id + user.Password = hash + session := SqlDB.NewSession() + session.Begin() + update, err := session.Cols("password").Where("user_id = ?", id).Update(&user) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return update +} + +// UpdateAvatar 修改头像 +func (d UserDao) UpdateAvatar(info *response.UserResponse) int64 { + user := entity.SysUser{ + Avatar: info.Avatar, + UserId: info.UserId, + UpdateBy: info.UserName, + UpdateTime: time.Now(), + } + session := SqlDB.NewSession() + session.Begin() + update, err := session.Cols("avatar", "update_by", "update_time").Where("user_id = ?", user.UserId).Update(&user) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return update +} + +func (d UserDao) UpdateStatus(info request.UserBody) int64 { + user := entity.SysUser{ + UserId: info.UserId, + Status: info.Status, + UpdateBy: info.UserName, + UpdateTime: time.Now(), + } + session := SqlDB.NewSession() + session.Begin() + update, err := session.Cols("status", "update_by", "update_time").Where("user_id = ?", user.UserId).Update(&user) + if err != nil { + session.Rollback() + common.ErrorLog(err) + return 0 + } + session.Commit() + return update +} diff --git a/core/dao/user_post_dao.go b/core/dao/user_post_dao.go new file mode 100644 index 0000000..570935d --- /dev/null +++ b/core/dao/user_post_dao.go @@ -0,0 +1,47 @@ +package dao + +import ( + "cutego/core/entity" + "cutego/pkg/common" +) + +type UserPostDao struct { +} + +// BatchInsert 批量新增用户岗位信息 +func (d UserPostDao) BatchInsert(posts []entity.SysUserPost) { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Table(entity.SysUserPost{}.TableName()).Insert(&posts) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return + } + session.Commit() +} + +// Delete 删除用户和岗位关系 +func (d UserPostDao) Delete(id int64) { + post := entity.SysUserPost{ + UserId: id, + } + session := SqlDB.NewSession() + session.Begin() + _, err := session.Delete(&post) + if err != nil { + common.ErrorLog(err) + session.Rollback() + } + session.Commit() +} + +// CountById 通过岗位ID查询岗位使用数量 +func (d UserPostDao) CountById(id int64) int64 { + count, err := SqlDB.NewSession().Table("sys_user_post").Cols("post_id").Where("post_id = ?", id).Count() + if err != nil { + common.ErrorLog(err) + return 0 + } + return count +} diff --git a/core/dao/user_role_dao.go b/core/dao/user_role_dao.go new file mode 100644 index 0000000..dcd016f --- /dev/null +++ b/core/dao/user_role_dao.go @@ -0,0 +1,76 @@ +package dao + +import ( + "cutego/core/api/v1/request" + "cutego/core/entity" + "cutego/pkg/common" +) + +type UserRoleDao struct { +} + +// BatchInsert 批量新增用户角色信息 +func (d UserRoleDao) BatchInsert(roles []entity.SysUserRole) { + session := SqlDB.NewSession() + session.Begin() + _, err := session.Table(entity.SysUserRole{}.TableName()).Insert(&roles) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return + } + session.Commit() +} + +// Delete 删除用户和角色关系 +func (d UserRoleDao) Delete(id int64) { + role := entity.SysUserRole{ + UserId: id, + } + session := SqlDB.NewSession() + session.Begin() + _, err := session.Delete(&role) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return + } + session.Commit() +} + +// DeleteAuthUser 取消用户授权 +func (d UserRoleDao) DeleteAuthUser(role entity.SysUserRole) int64 { + session := SqlDB.NewSession() + session.Begin() + i, err := session.Delete(&role) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return i +} + +// InsertAuthUsers 批量选择用户授权 +func (d UserRoleDao) InsertAuthUsers(body request.UserRoleBody) int64 { + ids := body.UserIds + roles := make([]entity.SysUserRole, 0) + for i := 0; i < len(ids); i++ { + role := entity.SysUserRole{ + RoleId: body.RoleId, + UserId: ids[i], + } + roles = append(roles, role) + } + session := SqlDB.NewSession() + session.Begin() + insert, err := session.Insert(&roles) + if err != nil { + common.ErrorLog(err) + session.Rollback() + return 0 + } + session.Commit() + return insert +} diff --git a/core/entity/sys_config.go b/core/entity/sys_config.go new file mode 100644 index 0000000..c070da8 --- /dev/null +++ b/core/entity/sys_config.go @@ -0,0 +1,22 @@ +package entity + +import ( + "time" +) + +type SysConfig struct { + ConfigId int `excel:"name=参数主键" xorm:"pk autoincr" json:"configId"` // 主键id + ConfigName string `excel:"name=参数名称" xorm:"varchar(100)" json:"configName"` // 参数名称 + ConfigKey string `excel:"name=参数键名" xorm:"varchar(100)" json:"configKey"` // 参数建名 + ConfigValue string `excel:"name=参数键值" xorm:"varchar(1)" json:"configValue"` // 参数键值 + ConfigType string `excel:"name=系统内置,format=Y=是,N=否" xorm:"char(1)" json:"configType"` // 系统内置(Y是 N否) + CreateBy string `xorm:"varchar(64)" json:"createBy"` // 创建人 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + UpdateBy string `xorm:"varchar(64)" json:"updateBy"` // 更新人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + Remark string `xorm:"varchar(500)" json:"remark"` // 备注 +} + +func (c SysConfig) TableName() string { + return "sys_config" +} diff --git a/core/entity/sys_cron_job.go b/core/entity/sys_cron_job.go new file mode 100644 index 0000000..20b825e --- /dev/null +++ b/core/entity/sys_cron_job.go @@ -0,0 +1,24 @@ +package entity + +import ( + "time" +) + +type SysCronJob struct { + JobId int `xorm:"pk autoincr" json:"jobId"` // 任务主键 + JobName string `xorm:"varchar(100)" json:"jobName"` // 任务名称 + JobCron string `xorm:"varchar(255)" json:"job_cron"` // cron表达式 + FuncAlias string `xorm:"varchar(100)" json:"funcAlias"` // 方法别名(程序内注册的别名) + FuncParam string `xorm:"varchar(1)" json:"funcParam"` // 方法参数 + Status string `xorm:"char(1)" json:"status"` // 状态(1、Running 0、Stop) + Level int `xorm:"int(1)" json:"level"` // 任务级别(0、普通 1、一般 2、重要 3、强保) + CreateBy string `xorm:"varchar(64)" json:"createBy"` // 创建人 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + UpdateBy string `xorm:"varchar(64)" json:"updateBy"` // 更新人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + Remark string `xorm:"varchar(500)" json:"remark"` // 备注 +} + +func (c SysCronJob) TableName() string { + return "sys_cron_job" +} diff --git a/core/entity/sys_dept.go b/core/entity/sys_dept.go new file mode 100644 index 0000000..8e9d497 --- /dev/null +++ b/core/entity/sys_dept.go @@ -0,0 +1,45 @@ +package entity + +import "time" + +// SysDept 部门结构体 +type SysDept struct { + DeptId int `xorm:"pk autoincr" json:"deptId"` + Ancestors string `xorm:"varchar(50)" json:"ancestors"` + DeptName string `xorm:"varchar(128)" json:"deptName"` + OrderNum int `json:"orderNum"` + Leader string `xorm:"varchar(20)" json:"leader"` + ParentId int `json:"parentId"` + Phone string `xorm:"varchar(11)" json:"phone"` + Status string `xorm:"char(1)" json:"status"` + Email string `json:"email"` + DelFlag string `xorm:"char(1) default('0')" json:"delFlag"` + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 +} + +func (d SysDept) GetLabel() string { + return d.DeptName +} + +func (d SysDept) GetId() int { + return d.DeptId +} + +func (d SysDept) GetParentId() int { + return d.ParentId +} + +func (d SysDept) GetData() interface{} { + return d +} + +func (d SysDept) IsRoot() bool { + return d.ParentId == 0 || d.ParentId == d.DeptId +} + +func (SysDept) TableName() string { + return "sys_dept" +} diff --git a/core/entity/sys_dict_data.go b/core/entity/sys_dict_data.go new file mode 100644 index 0000000..2c5934c --- /dev/null +++ b/core/entity/sys_dict_data.go @@ -0,0 +1,25 @@ +package entity + +import ( + "time" +) + +// SysDictData 字典数据 +type SysDictData struct { + DictCode int64 `excel:"name=字典编码" xorm:"pk autoincr" json:"dictCode"` // 字典ID + DictSort int `excel:"name=字典排序" xorm:"int" json:"dictSort"` // 字典排序 + DictLabel string `excel:"name=字典标签" xorm:"varchar(128)" json:"dictLabel"` // 字典标签 + DictValue string `excel:"name=字典键值" xorm:"varchar(128)" json:"dictValue"` // 字典键值 + DictType string `excel:"name=字典类型" xorm:"varchar(128)" json:"dictType"` // 字典类型 + IsDefault string `excel:"name=是否默认,format=Y=是,N=否" json:"isDefault"` // 是否默认 + Status string `excel:"name=状态,format=0=正常,1=停用" xorm:"char(1)" json:"status"` // 状态 0正常1停用 + Remark string `xorm:"varchar(512)" json:"remark"` // 备注 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 +} + +func (SysDictData) TableName() string { + return "sys_dict_data" +} diff --git a/core/entity/sys_dict_type.go b/core/entity/sys_dict_type.go new file mode 100644 index 0000000..3879c63 --- /dev/null +++ b/core/entity/sys_dict_type.go @@ -0,0 +1,22 @@ +package entity + +import ( + "time" +) + +// SysDictType 字典类型表 +type SysDictType struct { + DictId int64 `excel:"name=字典主键" xorm:"pk autoincr" json:"dictId"` // 字典ID + DictName string `excel:"name=字典名称" xorm:"varchar(128)" json:"dictName"` // 字典名称 + DictType string `excel:"name=字典类型" xorm:"varchar(128)" json:"dictType"` // 字典类型 + Status string `excel:"name=字典状态,format=0=正常,1=停用" xorm:"char(1)" json:"status"` // 状态 0正常1停用 + Remark string `xorm:"varchar(512)" json:"remark"` // 备注 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 +} + +func (SysDictType) TableName() string { + return "sys_dict_type" +} diff --git a/core/entity/sys_login_info.go b/core/entity/sys_login_info.go new file mode 100644 index 0000000..890bab1 --- /dev/null +++ b/core/entity/sys_login_info.go @@ -0,0 +1,21 @@ +package entity + +import ( + "time" +) + +type SysLoginInfo struct { + Id int `excel:"name=主键id" xorm:"pk autoincr" json:"id"` // 主键id + UserName string `excel:"name=用户账号" xorm:"varchar(50)" json:"userName"` // 用户账号 + IpAddr string `excel:"name=登录IP地址" xorm:"varchar(128)" json:"ipAddr"` // 登录IP地址 + LoginLocation string `excel:"name=登录地点" xorm:"varchar(255)" json:"loginLocation"` // 登录地点 + Browser string `excel:"name=浏览器类型" xorm:"varchar(255)" json:"browser"` // 浏览器类型 + OS string `excel:"name=操作系统" xorm:"os varchar(50)" json:"os"` // 操作系统 + Status string `excel:"name=登录状态,format=1=成功,0=失败" xorm:"char(1)" json:"status"` // 登录状态(1成功 0失败) + Msg string `excel:"name=提示消息" xorm:"varchar(255)" json:"msg"` // 提示消息 + LoginTime time.Time `excel:"name=登录时间" xorm:"login_time" json:"loginTime"` // 登录时间 +} + +func (c SysLoginInfo) TableName() string { + return "sys_login_info" +} diff --git a/core/entity/sys_menu.go b/core/entity/sys_menu.go new file mode 100644 index 0000000..a4656f5 --- /dev/null +++ b/core/entity/sys_menu.go @@ -0,0 +1,59 @@ +package entity + +import ( + "strings" + "time" +) + +// SysMenu 菜单结构体 +type SysMenu struct { + MenuId int `xorm:"pk autoincr" json:"menuId"` // 主键Id + ParentId int `json:"parentId"` // 父Id + MenuName string `xorm:"varchar(128)" json:"menuName"` // 菜单名称 + OrderNum int `xorm:"int" json:"orderNum"` // 显示顺序 + Path string `xorm:"varchar(200)" json:"path"` // 请求路径 + MenuType string `xorm:"char(1)" json:"menuType"` // 菜单类型 (M目录 C菜单 F按钮) + Visible string `xorm:"char(1)" json:"visible"` // 菜单状态 (0显示 1隐藏) + IsFrame int `json:"isFrame"` // 是否为外链(0是 1否) + IsCache int `json:"isCache"` // 是否缓存(0缓存 1不缓存) + Perms string `xorm:"varchar(100)" json:"perms"` // 权限标识 + Icon string `xorm:"varchar(100)" json:"icon"` // 图标 + Remark string `xorm:"varchar(512)" json:"remark"` // 备注 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 + Status string `xorm:"char(1)" json:"status"` // 菜单状态(0正常 1停用) + Component string `xorm:"varchar(255)" json:"component"` // 组件路径 +} + +func (SysMenu) TableName() string { + return "sys_menu" +} +func (s SysMenu) GetPath() string { + return s.Path +} +func (s SysMenu) GetName() string { + return strings.Title(s.Path) +} +func (s SysMenu) GetMenuId() int { + return s.MenuId +} +func (s SysMenu) GetParentId() int { + return s.ParentId +} +func (s SysMenu) GetData() interface{} { + return s +} +func (s SysMenu) IsRoot() bool { + // 这里通过FatherId等于0 或者 FatherId等于自身Id表示顶层根节点 + return s.ParentId == 0 || s.ParentId == s.MenuId +} + +func (s SysMenu) GetId() int { + return s.MenuId +} + +func (s SysMenu) GetLabel() string { + return s.MenuName +} diff --git a/core/entity/sys_notice.go b/core/entity/sys_notice.go new file mode 100644 index 0000000..2005fe6 --- /dev/null +++ b/core/entity/sys_notice.go @@ -0,0 +1,20 @@ +package entity + +import "time" + +type SysNotice struct { + NoticeId int64 `xorm:"pk autoincr" json:"noticeId"` // 公告id + NoticeTitle string `xorm:"notice_title" json:"noticeTitle"` // 公告标题 + NoticeType string `xorm:"notice_type" json:"noticeType"` // 公告类型(1通知 2公告) + NoticeContent string `json:"noticeContent"` // 公告内容 + Status string `json:"status"` // 公告状态(0正常 1关闭) + CreateBy string `xorm:"varchar(64)" json:"createBy"` // 创建人 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + UpdateBy string `xorm:"varchar(64)" json:"updateBy"` // 更新人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + Remark string `xorm:"varchar(500)" json:"remark"` // 备注 +} + +func (SysNotice) TableName() string { + return "sys_notice" +} diff --git a/core/entity/sys_post.go b/core/entity/sys_post.go new file mode 100644 index 0000000..e823c1d --- /dev/null +++ b/core/entity/sys_post.go @@ -0,0 +1,21 @@ +package entity + +import "time" + +// SysPost 岗位表对应结构体 +type SysPost struct { + PostId int64 `excel:"name=岗位编号" xorm:"pk autoincr" json:"postId"` // 岗位ID + PostCode string `excel:"name=岗位编码" xorm:"varchar(64)" json:"postCode"` // 岗位编码 + PostName string `excel:"name=岗位名称" xorm:"varchar(64)" json:"postName"` // 岗位名称 + PostSort int `excel:"name=岗位排序" json:"postSort"` // 显示顺序 + Status string `excel:"name=状态,format=0=正常,1=停用" xorm:"char(1)" json:"status"` // 状态 0正常 1停用 + Remark string `xorm:"varchar(512)" json:"remark"` // 备注 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 +} + +func (SysPost) TableName() string { + return "sys_post" +} diff --git a/core/entity/sys_role.go b/core/entity/sys_role.go new file mode 100644 index 0000000..7a767aa --- /dev/null +++ b/core/entity/sys_role.go @@ -0,0 +1,26 @@ +package entity + +import "time" + +type SysRole struct { + RoleId int64 `excel:"name=角色序号" xorm:"pk autoincr" json:"roleId"` // 角色id + RoleName string `excel:"name=角色名称" xorm:"varchar(64)" json:"roleName"` // 角色名称 + RoleKey string `excel:"name=角色权限" xorm:"varchar(64)" json:"roleKey"` // 角色权限标识 + RoleSort int `excel:"name=角色排序" xorm:"int" json:"roleSort"` // 角色顺序 + DataScope string `excel:"-" json:"dataScope"` // 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限) + MenuCheckStrictly bool `excel:"-" json:"menuCheckStrictly"` // 菜单树选择项是否关联显示 + DeptCheckStrictly bool `excel:"-" json:"deptCheckStrictly"` // 部门树选择项是否关联显示 + Status string `excel:"name=角色状态,format=0=正常,1=停用" xorm:"char(1)" json:"status"` // 角色状态 0正常1停用 + DelFlag string `excel:"" xorm:"char(1)" json:"delFlag"` // 删除标记0正常1删除 + CreateTime time.Time `excel:"" xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `excel:"" json:"createBy"` // 创建人 + UpdateTime time.Time `excel:"" json:"updateTime"` // 更新时间 + UpdateBy string `excel:"" json:"updateBy"` // 更新人 + Remark string `excel:"" json:"remark"` // 备注 + MenuIds []int64 `xorm:"-" json:"menuIds"` // 菜单组 + DeptIds []int64 `xorm:"-" json:"deptIds"` // 部门组 +} + +func (r SysRole) TableName() string { + return "sys_role" +} diff --git a/core/entity/sys_role_menu.go b/core/entity/sys_role_menu.go new file mode 100644 index 0000000..814cf25 --- /dev/null +++ b/core/entity/sys_role_menu.go @@ -0,0 +1,11 @@ +package entity + +// SysRoleMenu 角色菜单数据结构 +type SysRoleMenu struct { + RoleId int64 `xorm:"pk" json:"roleId"` // 角色id + MenuId int64 `xorm:"pk" json:"menuId"` // 菜单id +} + +func (SysRoleMenu) TableName() string { + return "sys_role_menu" +} diff --git a/core/entity/sys_user.go b/core/entity/sys_user.go new file mode 100644 index 0000000..c1500a3 --- /dev/null +++ b/core/entity/sys_user.go @@ -0,0 +1,40 @@ +package entity + +import ( + "reflect" + "time" +) + +// SysUser 用户表数据结构体 +type SysUser struct { + UserId int64 `xorm:"pk autoincr" json:"userId"` // 用户ID + DeptId int64 `json:"deptId"` // 部门ID + UserName string `xorm:"varchar(128)" json:"userName"` // 登录用户名 + NickName string `xorm:"varchar(128)" json:"nickName"` // 用户昵称 + Email string `xorm:"varchar(1024)" json:"email"` // 邮箱 + PhoneNumber string `xorm:"varchar(11)" json:"phoneNumber"` // 手机号 + Sex string `xorm:"char(1)" json:"sex"` // 性别0男1女 + Avatar string `xorm:"varchar(128)" json:"avatar"` // 头像路径 + Password string `xorm:"varchar(128)" json:"-"` // 密码 + Status string `xorm:"char(1)" json:"status"` // 状态 0正常1停用 + DelFlag string `xorm:"char(1)" json:"delFlag"` // 0正常1删除 + LoginIp string `xorm:"varchar(128)" json:"loginIp"` // 登录ip + LoginDate time.Time `json:"loginDate"` // 登录时间 + CreateTime time.Time `xorm:"created" json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 +} + +func (receiver SysUser) TableName() string { + return "sys_user" +} + +// IsAdmin 判断用户是不是管理员 +func (u SysUser) IsAdmin(userId int64) bool { + return userId > 0 && 1 == userId +} + +func (a SysUser) IsEmpty() bool { + return reflect.DeepEqual(a, SysUser{}) +} diff --git a/core/entity/sys_user_post.go b/core/entity/sys_user_post.go new file mode 100644 index 0000000..99e5dd8 --- /dev/null +++ b/core/entity/sys_user_post.go @@ -0,0 +1,10 @@ +package entity + +type SysUserPost struct { + UserId int64 `json:"userId"` // 用户id + PostId int64 `json:"postId"` // 部门id +} + +func (SysUserPost) TableName() string { + return "sys_user_post" +} diff --git a/core/entity/sys_user_role.go b/core/entity/sys_user_role.go new file mode 100644 index 0000000..7fbdbdf --- /dev/null +++ b/core/entity/sys_user_role.go @@ -0,0 +1,10 @@ +package entity + +type SysUserRole struct { + UserId int64 `xorm:"pk" json:"userId"` // 用户id + RoleId int64 `xorm:"pk" json:"roleId"` // 角色id +} + +func (SysUserRole) TableName() string { + return "sys_user_role" +} diff --git a/core/job/index.go b/core/job/index.go new file mode 100644 index 0000000..36ca716 --- /dev/null +++ b/core/job/index.go @@ -0,0 +1,21 @@ +package job + +import ( + "cutego/core/service" + "cutego/pkg/common" +) + +// 定时任务: 别名与方法的映射 +var AliasFuncMap = make(map[string]func()) + +// 注册任务 +func RegisterFunc(aliasName string, f func()) { + currentJob := service.CronJobService{}.GetInfoByAlias(aliasName) + AliasFuncMap[aliasName] = f + common.InfoLogf("注册定时任务 --- %s ---> Success", currentJob.JobName) +} + +// 注册方法 +func init() { + //RegisterFunc("test1", TestJob) +} diff --git a/core/job/test_job.go b/core/job/test_job.go new file mode 100644 index 0000000..ac297c0 --- /dev/null +++ b/core/job/test_job.go @@ -0,0 +1,7 @@ +package job + +import "fmt" + +func TestJob() { + fmt.Println("哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈") +} diff --git a/core/router/config_router.go b/core/router/config_router.go new file mode 100644 index 0000000..0c953bb --- /dev/null +++ b/core/router/config_router.go @@ -0,0 +1,29 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +func initConfigRouter(router *gin.RouterGroup) { + v := new(v1.ConfigApi) + group := router.Group("/config") + { + // 根据参数键名查询参数值 + group.GET("", v.GetConfigValue) + // 查询设置列表 + group.GET("/list", v.List) + // 添加 + group.POST("/create", v.Add) + // 查询 + group.GET("/:configId", v.Get) + // 修改 + group.PUT("/modify", v.Edit) + // 批量删除 + group.DELETE("/:ids", v.Delete) + // 刷新缓存 + group.DELETE("/refreshCache", v.RefreshCache) + // 导出数据 + group.GET("/export", v.Export) + } +} diff --git a/core/router/cron_job_router.go b/core/router/cron_job_router.go new file mode 100644 index 0000000..5d75a45 --- /dev/null +++ b/core/router/cron_job_router.go @@ -0,0 +1,24 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +// 初始化定时任务路由 +func initCronJobRouter(router *gin.RouterGroup) { + v := new(v1.CronJobApi) + group := router.Group("/cronJob") + { + // 查询定时任务分页数据 + group.GET("/list", v.List) + // 修改定时任务 + group.PUT("/modify", v.Edit) + // 新增定时任务 + group.POST("/create", v.Add) + // 删除定时任务 + group.DELETE("/:jobId", v.Remove) + // 改变定时任务状态 + group.DELETE("/changeStatus", v.ChangeStatus) + } +} diff --git a/core/router/dept_router.go b/core/router/dept_router.go new file mode 100644 index 0000000..e80eee4 --- /dev/null +++ b/core/router/dept_router.go @@ -0,0 +1,30 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +// 部门接口操作 +func initDeptRouter(router *gin.RouterGroup) { + v := new(v1.DeptApi) + group := router.Group("/dept") + { + // 获取部门下拉树列表 + group.GET("/treeSelect", v.DeptTreeSelect) + // 加载对应角色部门列表树 + group.GET("/roleDeptTreeSelect/:roleId", v.RoleDeptTreeSelect) + // 查询部门列表 + group.GET("/list", v.Find) + // 查询部门列表(排除节点) + group.GET("/list/exclude/:deptId", v.ExcludeChild) + // 根据部门编号获取详细信息 + group.GET("/:deptId", v.GetInfo) + // 添加部门 + group.POST("/create", v.Add) + // 删除部门 + group.DELETE("/:deptId", v.Delete) + // 修改部门 + group.PUT("/modify", v.Edit) + } +} diff --git a/core/router/dict_data_router.go b/core/router/dict_data_router.go new file mode 100644 index 0000000..d15af3d --- /dev/null +++ b/core/router/dict_data_router.go @@ -0,0 +1,28 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +// 初始化字典数据路由 +func initDictDataRouter(router *gin.RouterGroup) { + v := new(v1.DictDataApi) + group := router.Group("/dict/data") + { + // 根据字典类型查询字典数据信息 + group.GET("/type/:dictType", v.GetByType) + // 查询字典数据集合 + group.GET("/list", v.List) + // 根据id查询字典数据 + group.GET("/:dictCode", v.Get) + // 添加字点数据 + group.POST("/create", v.Add) + // 删除字典数据 + group.DELETE("/:dictCode", v.Delete) + // 导出字典数据 + group.GET("/export", v.Export) + // 编辑字典数据 + group.PUT("/modify", v.Edit) + } +} diff --git a/core/router/dict_type_router.go b/core/router/dict_type_router.go new file mode 100644 index 0000000..925a85e --- /dev/null +++ b/core/router/dict_type_router.go @@ -0,0 +1,28 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +// 初始化字典类型路由 +func initDictTypeRouter(router *gin.RouterGroup) { + v := new(v1.DictTypeApi) + group := router.Group("/dict/type") + { + // 查询字典类型分页数据 + group.GET("/list", v.List) + // 根据id查询字典类型数据 + group.GET("/:dictTypeId", v.Get) + // 修改字典类型 + group.PUT("/modify", v.Edit) + // 新增字典类型 + group.POST("/create", v.Add) + // 删除字典类型 + group.DELETE("/:dictId", v.Remove) + // 刷新缓存 + group.DELETE("/refreshCache", v.RefreshCache) + // 导出excel + group.GET("/export", v.Export) + } +} diff --git a/core/router/login_router.go b/core/router/login_router.go new file mode 100644 index 0000000..526fff8 --- /dev/null +++ b/core/router/login_router.go @@ -0,0 +1,21 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +// 登录调用路由 +func initLoginRouter(router *gin.RouterGroup) { + loginApi := new(v1.LoginApi) + loginRouter := router.Group("/") + { + // 登录 + loginRouter.POST("/login", loginApi.Login) + loginRouter.GET("/getInfo", loginApi.GetUserInfo) + loginRouter.GET("/getRouters", loginApi.GetRouters) + loginRouter.GET("/getLoginHistory", loginApi.GetLoginHistory) + // 退出登录 + loginRouter.POST("/logout", loginApi.Logout) + } +} diff --git a/core/router/menu_router.go b/core/router/menu_router.go new file mode 100644 index 0000000..c3d1e2b --- /dev/null +++ b/core/router/menu_router.go @@ -0,0 +1,27 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +func initMenuRouter(router *gin.RouterGroup) { + v := new(v1.MenuApi) + vg := router.Group("/menu") + { + // 查询菜单数据 + vg.GET("/:menuId", v.GetInfo) + // 查询菜单列表 + vg.GET("/list", v.List) + // 加载对应角色菜单列表树 + vg.GET("/roleMenuTreeSelect/:roleId", v.RoleMenuTreeSelect) + // 获取菜单下拉树列表 + vg.GET("/treeSelect", v.TreeSelect) + // 添加菜单数据 + vg.POST("/create", v.Add) + // 修改菜单数据 + vg.PUT("/modify", v.Edit) + // 删除菜单数据 + vg.DELETE("/:menuId", v.Delete) + } +} diff --git a/core/router/notice_router.go b/core/router/notice_router.go new file mode 100644 index 0000000..9aeeb9d --- /dev/null +++ b/core/router/notice_router.go @@ -0,0 +1,22 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +func initNoticeRouter(router *gin.RouterGroup) { + v := new(v1.NoticeApi) + group := router.Group("/notice") + { + group.GET("/list", v.List) + // 添加公告 + group.POST("/create", v.Add) + // 删除 + group.DELETE("/:ids", v.Delete) + // 查询 + group.GET("/:id", v.Get) + // 修改 + group.PUT("/modify", v.Edit) + } +} diff --git a/core/router/post_router.go b/core/router/post_router.go new file mode 100644 index 0000000..937856b --- /dev/null +++ b/core/router/post_router.go @@ -0,0 +1,26 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +// 初始化岗位路由 +func initPostRouter(router *gin.RouterGroup) { + v := new(v1.PostApi) + group := router.Group("/post") + { + // 查询岗位数据 + group.GET("/list", v.List) + // 添加岗位 + group.POST("/create", v.Add) + // 查询岗位详情 + group.GET("/:postId", v.Get) + // 删除岗位数据 + group.DELETE("/:postId", v.Delete) + // 修改岗位数据接口 + group.PUT("/modify", v.Edit) + // 导出excel + group.GET("/export", v.Export) + } +} diff --git a/core/router/role_router.go b/core/router/role_router.go new file mode 100644 index 0000000..09cee43 --- /dev/null +++ b/core/router/role_router.go @@ -0,0 +1,35 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +func initRoleRouter(router *gin.RouterGroup) { + roleApi := new(v1.RoleApi) + group := router.Group("/role") + { + // 查询角色数据 + group.GET("/findList", roleApi.Find) + // 根据id查询角色数据 + group.GET("/:roleId", roleApi.GetRoleId) + // 添加角色 + group.POST("/create", roleApi.Add) + // 修改角色 + group.PUT("/modify", roleApi.Edit) + // 删除角色 + group.DELETE("/:roleId", roleApi.Delete) + // 修改角色状态 + group.PUT("/changeStatus", roleApi.ChangeStatus) + // 查看分配角色列表 + group.GET("/authUser/allocatedList", roleApi.AllocatedList) + // 查询未分配用户角色列表 + group.GET("/authUser/unallocatedList", roleApi.UnallocatedList) + // 取消授权 + group.PUT("/authUser/cancel", roleApi.CancelAuthUser) + // 批量选择用户授权 + group.PUT("/authUser/selectAll", roleApi.UpdateAuthUserAll) + // 导出excel + group.GET("/export", roleApi.Export) + } +} diff --git a/core/router/router.go b/core/router/router.go new file mode 100644 index 0000000..ba80e22 --- /dev/null +++ b/core/router/router.go @@ -0,0 +1,49 @@ +package router + +import ( + "cutego/pkg/filter" + "cutego/pkg/jwt" + "cutego/pkg/middleware" + "cutego/pkg/middleware/logger" + "cutego/pkg/websocket" + "github.com/gin-gonic/gin" +) + +func Init() *gin.Engine { + router := gin.New() + router.Use(gin.Logger()) + router.Use(gin.Recovery()) + router.Use(logger.LoggerToFile()) + router.Use(middleware.Recover) + router.Use(jwt.JWTAuth()) + router.Use(filter.DemoHandler()) + // websocket + router.GET("/websocket", websocket.HandleWebSocketMessage) + // v1版本api + v1Router := router.Group("/api/v1") + { + // 登录接口 + initLoginRouter(v1Router) + // 用户路由接口 + initUserRouter(v1Router) + // 部门路由注册 + initDeptRouter(v1Router) + // 初始化字典数据路由 + initDictDataRouter(v1Router) + // 注册配置路由 + initConfigRouter(v1Router) + // 注册角色路由 + initRoleRouter(v1Router) + // 注册菜单路由 + initMenuRouter(v1Router) + // 注册岗位路由 + initPostRouter(v1Router) + // 注册字典类型路由 + initDictTypeRouter(v1Router) + // 注册公告路由 + initNoticeRouter(v1Router) + // 注册定时任务路由 + initCronJobRouter(v1Router) + } + return router +} diff --git a/core/router/user_router.go b/core/router/user_router.go new file mode 100644 index 0000000..9e587b2 --- /dev/null +++ b/core/router/user_router.go @@ -0,0 +1,36 @@ +package router + +import ( + "cutego/core/api/v1" + "github.com/gin-gonic/gin" +) + +// 用户路由 +func initUserRouter(router *gin.RouterGroup) { + userApi := new(v1.UserApi) + userRouter := router.Group("/user") + { + userRouter.GET("/list", userApi.Find) + userRouter.GET("/getInfo/:userId", userApi.GetInfo) + userRouter.GET("/getInfo", userApi.GetInfo) + userRouter.GET("/authRole/:userId", userApi.AuthRole) + // 新增用户 + userRouter.POST("/create", userApi.Add) + // 修改用户 + userRouter.PUT("/modify", userApi.Edit) + // 删除用户 + userRouter.DELETE("/remove/:userId", userApi.Remove) + // 重置密码 + userRouter.PUT("/resetPwd", userApi.ResetPwd) + userRouter.GET("/export", userApi.Export) + userRouter.GET("/profile", userApi.Profile) + // 修改个人数据 + userRouter.PUT("/profile", userApi.UpdateProfile) + // 修改个人密码 + userRouter.PUT("/profile/updatePwd", userApi.UpdatePwd) + // 修改头像 + userRouter.POST("/profile/avatar", userApi.Avatar) + // 修改可用状态 + userRouter.PUT("/changeStatus", userApi.ChangeStatus) + } +} diff --git a/core/service/config_service.go b/core/service/config_service.go new file mode 100644 index 0000000..cbc2092 --- /dev/null +++ b/core/service/config_service.go @@ -0,0 +1,64 @@ +package service + +import ( + "cutego/core/api/v1/request" + cache2 "cutego/core/cache" + "cutego/core/dao" + "cutego/core/entity" +) + +type ConfigService struct { + configDao dao.ConfigDao +} + +// GetConfigKey 根据键名查询参数配置信息 +func (s ConfigService) GetConfigKey(param string) *entity.SysConfig { + // 从缓存中取出数据判断是否存在, 存在直接使用, 不存在就从数据库查询 + val := cache2.GetRedisConfig(param) + if val != nil { + return val + } + configKey := s.configDao.SelectByConfigKey(param) + cache2.SetRedisConfig(*configKey) + return configKey +} + +// FindPage 分页查询数据 +func (s ConfigService) FindPage(query request.ConfigQuery) (*[]entity.SysConfig, int64) { + return s.configDao.SelectPage(query) +} + +// CheckConfigKeyUnique 校验是否存在 +func (s ConfigService) CheckConfigKeyUnique(config entity.SysConfig) bool { + return s.configDao.CheckConfigKeyUnique(config) > 0 +} + +// Save 添加数据 +func (s ConfigService) Save(config entity.SysConfig) int64 { + return s.configDao.Insert(config) +} + +// GetInfo 查询数据 +func (s ConfigService) GetInfo(id int64) *entity.SysConfig { + return s.configDao.SelectById(id) +} + +// Edit 修改数据 +func (s ConfigService) Edit(config entity.SysConfig) int64 { + return s.configDao.Update(config) +} + +// Remove 批量删除 +func (s ConfigService) Remove(list []int64) bool { + return s.configDao.Delete(list) +} + +// CheckConfigByIds 根据id集合查询 +func (s ConfigService) CheckConfigByIds(list []int64) *[]entity.SysConfig { + return s.configDao.CheckConfigByIds(list) +} + +// FindAll 查询所有数据 +func (s ConfigService) FindAll() *[]entity.SysConfig { + return s.configDao.SelectAll() +} diff --git a/core/service/cron_job_service.go b/core/service/cron_job_service.go new file mode 100644 index 0000000..76943b8 --- /dev/null +++ b/core/service/cron_job_service.go @@ -0,0 +1,41 @@ +package service + +import ( + "cutego/core/api/v1/request" + "cutego/core/dao" + "cutego/core/entity" +) + +type CronJobService struct { + cronJobService dao.CronJobDao +} + +// FindPage 分页查询数据 +func (s CronJobService) FindPage(query request.CronJobQuery) ([]entity.SysCronJob, int64) { + return s.cronJobService.SelectPage(query) +} + +// Save 添加数据 +func (s CronJobService) Save(config entity.SysCronJob) int64 { + return s.cronJobService.Insert(config) +} + +// GetInfo 查询数据 +func (s CronJobService) GetInfo(id int64) *entity.SysCronJob { + return s.cronJobService.SelectById(id) +} + +// GetInfo 查询数据 +func (s CronJobService) GetInfoByAlias(funcAlias string) *entity.SysCronJob { + return s.cronJobService.SelectByFuncAlias(funcAlias) +} + +// Edit 修改数据 +func (s CronJobService) Edit(config entity.SysCronJob) int64 { + return s.cronJobService.Update(config) +} + +// Remove 批量删除 +func (s CronJobService) Remove(list []int64) bool { + return s.cronJobService.Delete(list) +} diff --git a/core/service/dept_service.go b/core/service/dept_service.go new file mode 100644 index 0000000..2c004c8 --- /dev/null +++ b/core/service/dept_service.go @@ -0,0 +1,67 @@ +package service + +import ( + "cutego/core/api/v1/request" + dao2 "cutego/core/dao" + "cutego/core/entity" +) + +type DeptService struct { + deptDao dao2.DeptDao + roleDao dao2.RoleDao +} + +// TreeSelect 根据条件查询部门树 +func (s DeptService) FindTreeSelect(query request.DeptQuery) *[]entity.SysDept { + treeSelect := s.deptDao.SelectTree(query) + return treeSelect +} + +// FindDeptListByRoleId 根据角色ID查询部门树信息 +func (s DeptService) FindDeptListByRoleId(id int64) *[]int64 { + role := s.roleDao.SelectRoleByRoleId(id) + return s.deptDao.SelectDeptListByRoleId(id, role.DeptCheckStrictly) +} + +// FindDeptList 查询部门列表 +func (s DeptService) FindDeptList(query request.DeptQuery) *[]entity.SysDept { + return s.deptDao.GetList(query) +} + +// GetDeptById 根据部门编号获取详细信息 +func (s DeptService) GetDeptById(id int) *entity.SysDept { + return s.deptDao.SelectDeptById(id) +} + +// Save 添加部门数据 +func (s DeptService) Save(dept entity.SysDept) int64 { + return s.deptDao.Insert(dept) +} + +// CheckDeptNameUnique 校验部门名称是否唯一 +func (s DeptService) CheckDeptNameUnique(dept entity.SysDept) bool { + if s.deptDao.CheckDeptNameUnique(dept) > 0 { + return true + } + return false +} + +// Remove 删除部门 +func (s DeptService) Remove(id int) int64 { + return s.deptDao.Delete(id) +} + +// HasChildByDeptId 是否存在部门子节点 +func (s DeptService) HasChildByDeptId(id int) int64 { + return s.deptDao.HasChildByDeptId(id) +} + +// CheckDeptExistUser 查询部门是否存在用户 +func (s DeptService) CheckDeptExistUser(id int) int64 { + return s.deptDao.CheckDeptExistUser(id) +} + +// 修改部门 +func (s DeptService) Edit(dept entity.SysDept) bool { + return s.deptDao.Update(dept) > 0 +} diff --git a/core/service/dict_data_service.go b/core/service/dict_data_service.go new file mode 100644 index 0000000..6384759 --- /dev/null +++ b/core/service/dict_data_service.go @@ -0,0 +1,76 @@ +package service + +import ( + "cutego/core/api/v1/request" + cache2 "cutego/core/cache" + "cutego/core/dao" + "cutego/core/entity" + "cutego/pkg/constant" +) + +type DictDataService struct { + dictDataDao dao.DictDataDao +} + +// FindByDictType 根据字典类型查询字典数据 +func (s DictDataService) FindByDictType(dictType string) []entity.SysDictData { + // 先从缓存中拉数据 + key := cache2.GetRedisDict(dictType) + if key != nil { + return key.([]entity.SysDictData) + } else { + // 缓存中为空, 从数据库中取数据 + return s.dictDataDao.SelectByDictType(dictType) + } +} + +// FindPage 查询字段数据集合 +func (s DictDataService) FindPage(query request.DiceDataQuery) (*[]entity.SysDictData, int64) { + return s.dictDataDao.SelectPage(query) +} + +// GetByCode 根据code查询字典数据 +// @Param code int64 +// @Return *entity.SysDictData +func (s DictDataService) GetByCode(code int64) *entity.SysDictData { + return s.dictDataDao.SelectByDictCode(code) +} + +// Save 新增字典数据 +// @Param data entity.SysDictData +// @Return bool +func (s DictDataService) Save(data entity.SysDictData) bool { + insert := s.dictDataDao.Insert(data) + if insert > 0 { + // 刷新缓存数据 + byType := s.GetNoCacheByType(data.DictType) + cache2.SetRedisDict(data.DictType, byType) + } + return insert > 0 +} + +// Remove 删除数据 +// @Param codes 字典code集合 +// @Return bool +func (s DictDataService) Remove(codes []int64) bool { + dictType := s.GetByCode(codes[0]).DictType + remove := s.dictDataDao.Delete(codes) + if remove { + // 刷新缓存 + code := s.GetNoCacheByType(dictType) + cache2.SetRedisDict(dictType, code) + } + return remove +} + +// GetNoCacheByType 根据字典类型查询字典数据 +// @Param dictType 字典类型 +// @Return []entity.SysDictData +func (s DictDataService) GetNoCacheByType(dictType string) []entity.SysDictData { + return s.dictDataDao.SelectByDictType(constant.RedisConst{}.GetRedisDictKey() + dictType) +} + +// 修改字典数据 +func (s DictDataService) Edit(data entity.SysDictData) bool { + return s.dictDataDao.Update(data) +} diff --git a/core/service/dict_type_service.go b/core/service/dict_type_service.go new file mode 100644 index 0000000..4765944 --- /dev/null +++ b/core/service/dict_type_service.go @@ -0,0 +1,98 @@ +package service + +import ( + "cutego/core/api/v1/request" + cache2 "cutego/core/cache" + dao2 "cutego/core/dao" + models2 "cutego/core/entity" +) + +type DictTypeService struct { + dictTypeDao dao2.DictTypeDao + dictDataDao dao2.DictDataDao +} + +// FindPage 分页查询字典类型数据 +func (s DictTypeService) FindPage(query request.DictTypeQuery) (*[]models2.SysDictType, int64) { + return s.dictTypeDao.SelectPage(query) +} + +// GetById 根据id查询字典类型数据 +func (s DictTypeService) GetById(id int64) *models2.SysDictType { + return s.dictTypeDao.SelectById(id) +} + +// CheckDictTypeUnique 检验字典类型是否存在 +func (s DictTypeService) CheckDictTypeUnique(dictType models2.SysDictType) bool { + return s.dictTypeDao.CheckDictTypeUnique(dictType) > 0 +} + +// Edit 修改字典数据 +func (s DictTypeService) Edit(dictType models2.SysDictType) bool { + return s.dictTypeDao.Update(dictType) +} + +// Save 新增字典类型 +func (s DictTypeService) Save(dictType models2.SysDictType) bool { + insert := s.dictTypeDao.Insert(dictType) + if insert > 0 { + cache2.SetRedisDict(dictType.DictType, nil) + } + return insert > 0 +} + +// Remove 批量删除 +func (s DictTypeService) Remove(ids []int64) bool { + return s.dictTypeDao.Delete(ids) +} + +// FindAll 查询所有字典类型数据 +func (s DictTypeService) FindAll() []*models2.SysDictType { + return s.dictTypeDao.SelectAll() +} + +// RemoveAllCache 删除所有字典缓存 +func (s DictTypeService) RemoveAllCache() []string { + typeList := make([]string, 0) + allType := s.FindAll() + for _, dictType := range allType { + typeList = append(typeList, dictType.DictType) + } + // 删除缓存 + cache2.RemoveRedisDictList(typeList) + return typeList +} + +// LoadDictCache 将字典数据存入缓存 +func (s DictTypeService) LoadDictCache() { + typeList := make([]string, 0) + allType := s.FindAll() + for _, dictType := range allType { + typeList = append(typeList, dictType.DictType) + } + allData := s.dictDataDao.GetDiceDataAll() + for _, key := range typeList { + list := make([]models2.SysDictData, 0) + for _, data := range *allData { + if key == data.DictType { + list = append(list, data) + } + } + cache2.SetRedisDict(key, list) + } +} + +// RefreshCache 刷新缓存数据 +func (s DictTypeService) RefreshCache() { + typeList := s.RemoveAllCache() + allData := s.dictDataDao.GetDiceDataAll() + for _, key := range typeList { + list := make([]models2.SysDictData, 0) + for _, data := range *allData { + if key == data.DictType { + list = append(list, data) + } + } + cache2.SetRedisDict(key, list) + } +} diff --git a/core/service/login_info_service.go b/core/service/login_info_service.go new file mode 100644 index 0000000..c34b817 --- /dev/null +++ b/core/service/login_info_service.go @@ -0,0 +1,57 @@ +package service + +import ( + "cutego/core/api/v1/request" + "cutego/core/dao" + "cutego/core/entity" + "cutego/pkg/config" + "github.com/gin-gonic/gin" + "github.com/yinheli/qqwry" + "net" + "os" +) + +type LoginInfoService struct { + loginInfoDao dao.LoginInfoDao +} + +// FindPage 分页查询数据 +func (s LoginInfoService) FindPage(query request.LoginInfoQuery) (*[]entity.SysLoginInfo, int64) { + return s.loginInfoDao.SelectPage(query) +} + +// Save 添加登录记录业务逻辑 +func (s LoginInfoService) Save(body entity.SysLoginInfo) bool { + // 添加登录记录数据库操作 + user := s.loginInfoDao.Insert(body) + if user != nil { + return true + } + return false +} + +// GetRequestClientIp 获取请求客户端的ip +func (s LoginInfoService) GetRequestClientIp(c *gin.Context) string { + reqIP := c.ClientIP() + if reqIP == "::1" { + reqIP = "127.0.0.1" + } + return reqIP +} + +// 纯真数据库获取ip地址 +// @return {"Ip": "180.89.94.90","Country": "北京市","City": "鹏博士宽带"} +func (s LoginInfoService) GetLocationByIp(ipAddr string) *qqwry.QQwry { + address := net.ParseIP(ipAddr) + if ipAddr == "" || address == nil { + panic("参数ipAddr是空的") + } else { + dir, err := os.Getwd() + if err != nil { + panic("无法获取当前路径, " + err.Error()) + } + q := qqwry.NewQQwry(dir + "/" + config.BaseConfigDirPath + "/scanip/qqwry.dat") + q.Find(ipAddr) + return q + } +} diff --git a/core/service/login_service.go b/core/service/login_service.go new file mode 100644 index 0000000..5be2c21 --- /dev/null +++ b/core/service/login_service.go @@ -0,0 +1,46 @@ +package service + +import ( + "cutego/core/api/v1/response" + "cutego/pkg/common" + "cutego/pkg/jwt" + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + "strings" +) + +type LoginService struct { + userService UserService +} + +// Login 用户登录业务处理 +func (s LoginService) Login(name string, password string) (bool, string) { + user := s.userService.GetUserByUserName(name) + if user == nil { + return false, "用户不存在" + } + if !gotool.BcryptUtils.CompareHash(user.Password, password) { + return false, "密码错误" + } + // 生成token + token, err := jwt.CreateUserToken(s.userService.GetUserById(user.UserId)) + if err != nil { + common.ErrorLog(err) + return false, "" + } + // 数据存储到redis中 + return true, token +} + +// GetCurrentUser 获取当前登录用户 +func (s LoginService) GetCurrentUser(c *gin.Context) *response.UserResponse { + token := c.Request.Header.Get("Authorization") + str := strings.Split(token, " ") + // parseToken 解析token包含的信息 + claims, err := jwt.ParseToken(str[1]) + if err != nil { + common.ErrorLog(err) + } + info := claims.UserInfo + return &info +} diff --git a/core/service/menu_service.go b/core/service/menu_service.go new file mode 100644 index 0000000..3be5224 --- /dev/null +++ b/core/service/menu_service.go @@ -0,0 +1,62 @@ +package service + +import ( + "cutego/core/api/v1/request" + "cutego/core/api/v1/response" + dao2 "cutego/core/dao" + models2 "cutego/core/entity" +) + +type MenuService struct { + menuDao dao2.MenuDao + roleDao dao2.RoleDao +} + +// GetMenuTreeByUserId 根据用户ID查询菜单 +func (s MenuService) GetMenuTreeByUserId(user *response.UserResponse) *[]models2.SysMenu { + var menuList *[]models2.SysMenu + // 判断是否是管理员 + flag := models2.SysUser{}.IsAdmin(user.UserId) + if flag { + menuList = s.menuDao.GetMenuAll() + } else { + menuList = s.menuDao.GetMenuByUserId(user.UserId) + } + return menuList +} + +// FindMenuListByRoleId 根据角色ID查询菜单树信息 +func (s MenuService) FindMenuListByRoleId(id int64) *[]int64 { + role := s.roleDao.SelectRoleByRoleId(id) + return s.menuDao.SelectMenuByRoleId(id, role.MenuCheckStrictly) +} + +// GetMenuList 获取菜单列表 +func (s MenuService) FindMenuList(query request.MenuQuery, info *response.UserResponse) *[]models2.SysMenu { + if info.IsAdmin() { + return s.menuDao.SelectMenuList(query) + } else { + query.UserId = info.UserId + return s.menuDao.SelectMenuListByUserId(query) + } +} + +// GetMenuByMenuId 根据菜单ID查询信息 +func (s MenuService) GetMenuByMenuId(id int) *models2.SysMenu { + return s.menuDao.SelectMenuByMenuId(id) +} + +// Save 添加菜单数据 +func (s MenuService) Save(menu models2.SysMenu) int64 { + return s.menuDao.Insert(menu) +} + +// Edit 修改菜单数据 +func (s MenuService) Edit(menu models2.SysMenu) int64 { + return s.menuDao.Update(menu) +} + +// Remove 删除菜单操作 +func (s MenuService) Remove(id int) int64 { + return s.menuDao.Delete(id) +} diff --git a/core/service/notice_service.go b/core/service/notice_service.go new file mode 100644 index 0000000..0296dc9 --- /dev/null +++ b/core/service/notice_service.go @@ -0,0 +1,36 @@ +package service + +import ( + "cutego/core/api/v1/request" + "cutego/core/dao" + "cutego/core/entity" +) + +type NoticeService struct { + noticeDao dao.NoticeDao +} + +// FindPage 查询集合数据 +func (s NoticeService) FindPage(query request.NoticeQuery) (*[]entity.SysNotice, int64) { + return s.noticeDao.SelectPage(query) +} + +// Save 添加公告 +func (s NoticeService) Save(notice entity.SysNotice) bool { + return s.noticeDao.Insert(notice) > 0 +} + +// Remove 批量删除 +func (s NoticeService) Remove(list []int64) bool { + return s.noticeDao.Delete(list) > 0 +} + +// GetById 查询 +func (s NoticeService) GetById(id int64) *entity.SysNotice { + return s.noticeDao.SelectById(id) +} + +// Edit 修改 +func (s NoticeService) Edit(notice entity.SysNotice) bool { + return s.noticeDao.Update(notice) > 0 +} diff --git a/core/service/permission_service.go b/core/service/permission_service.go new file mode 100644 index 0000000..afd4196 --- /dev/null +++ b/core/service/permission_service.go @@ -0,0 +1,43 @@ +package service + +import ( + "cutego/core/api/v1/response" + dao2 "cutego/core/dao" + "cutego/core/entity" + "github.com/druidcaesa/gotool" +) + +type PermissionService struct { + roleDao dao2.RoleDao + menuDao dao2.MenuDao +} + +// GetRolePermissionByUserId 查询用户角色集合 +func (s PermissionService) GetRolePermissionByUserId(user *response.UserResponse) *[]string { + admin := entity.SysUser{}.IsAdmin(user.UserId) + roleKeys := s.roleDao.SelectRolePermissionByUserId(user.UserId) + if admin && roleKeys != nil { + *roleKeys = append(*roleKeys, "admin") + } + duplication := gotool.StrArrayUtils.ArrayDuplication(*roleKeys) + return &duplication +} + +// GetMenuPermission 获取菜单数据权限 +func (s PermissionService) GetMenuPermission(user *response.UserResponse) *[]string { + flag := entity.SysUser{}.IsAdmin(user.UserId) + // 查询菜单数据权限 + permission := s.menuDao.GetMenuPermission(user.UserId) + if flag && permission != nil { + *permission = append(*permission, "*:*:*") + } + var ret []string + duplication := gotool.StrArrayUtils.ArrayDuplication(*permission) + for i := 0; i < len(duplication); i++ { + if (i > 0 && duplication[i-1] == duplication[i]) || len(duplication[i]) == 0 { + continue + } + ret = append(ret, duplication[i]) + } + return &ret +} diff --git a/core/service/post_service.go b/core/service/post_service.go new file mode 100644 index 0000000..7be1560 --- /dev/null +++ b/core/service/post_service.go @@ -0,0 +1,77 @@ +package service + +import ( + "bytes" + "cutego/core/api/v1/request" + "cutego/core/dao" + "cutego/core/entity" + "github.com/druidcaesa/gotool" +) + +type PostService struct { + postDao dao.PostDao +} + +// FindAll 查询所有岗位业务方法 +func (s PostService) FindAll() []*entity.SysPost { + return s.postDao.SelectAll() +} + +// FindPostListByUserId 根据用户id查询岗位id集合 +func (s PostService) FindPostListByUserId(userId int64) *[]int64 { + return s.postDao.SelectPostListByUserId(userId) +} + +// FindList 查询岗位分页列表 +func (s PostService) FindPage(query request.PostQuery) (*[]entity.SysPost, int64) { + return s.postDao.SelectPage(query) +} + +// CheckPostNameUnique 校验岗位名称是否存在 +func (s PostService) CheckPostNameUnique(post entity.SysPost) bool { + return s.postDao.CheckPostNameUnique(post) > 0 +} + +// CheckPostCodeUnique 校验岗位编码是否存在 +func (s PostService) CheckPostCodeUnique(post entity.SysPost) bool { + return s.postDao.CheckPostCodeUnique(post) > 0 +} + +// Save 添加岗位数据 +func (s PostService) Save(post entity.SysPost) bool { + return s.postDao.Insert(post) > 0 +} + +// GetPostById 根据id查询岗位数据 +func (s PostService) GetPostById(id int64) *entity.SysPost { + post := entity.SysPost{ + PostId: id, + } + return s.postDao.GetPostById(post) +} + +// Remove 批量删除岗位信息 +func (s PostService) Remove(ids []int64) bool { + return s.postDao.Delete(ids) > 0 +} + +// Edit 修改岗位数据 +func (s PostService) Edit(post entity.SysPost) bool { + return s.postDao.Update(post) +} + +// FindPostByUserName 获取岗位数据 +func (s PostService) FindPostByUserName(name string) string { + list := s.postDao.SelectPostByUserName(name) + var buffer bytes.Buffer + var postName string + for _, post := range *list { + buffer.WriteString(post.PostName) + buffer.WriteString(",") + } + s2 := buffer.String() + if gotool.StrUtils.HasNotEmpty(s2) { + postName = s2[0:(len(s2) - 1)] + } + return postName +} diff --git a/core/service/role_service.go b/core/service/role_service.go new file mode 100644 index 0000000..ecbb090 --- /dev/null +++ b/core/service/role_service.go @@ -0,0 +1,133 @@ +package service + +import ( + "bytes" + req2 "cutego/core/api/v1/request" + dao2 "cutego/core/dao" + models2 "cutego/core/entity" + "github.com/druidcaesa/gotool" +) + +type RoleService struct { + roleDao dao2.RoleDao + roleMenuDao dao2.RoleMenuDao + userRoleDao dao2.UserRoleDao +} + +// FindAll 查询所有角色 +func (s RoleService) FindAll(query *req2.RoleQuery) ([]*models2.SysRole, int64) { + if query == nil { + all := s.roleDao.SelectAll() + return all, 0 + } + return s.roleDao.SelectPage(query) +} + +// FindRoleListByUserId 根据用户id查询角色id集合 +func (s RoleService) FindRoleListByUserId(parseInt int64) *[]int64 { + return s.roleDao.SelectRoleListByUserId(parseInt) +} + +// GetRoleListByUserId 根据用户ID查询角色 +func (s RoleService) GetRoleListByUserId(id int64) *[]models2.SysRole { + return s.roleDao.GetRoleListByUserId(id) +} + +// FindPage 分页查询角色数据 +func (s RoleService) FindPage(query req2.RoleQuery) ([]*models2.SysRole, int64) { + return s.roleDao.SelectPage(&query) +} + +// GetRoleByRoleId 根据角色id查询角色数据 +func (s RoleService) GetRoleByRoleId(id int64) *models2.SysRole { + return s.roleDao.SelectRoleByRoleId(id) +} + +// CheckRoleNameUnique 判断角色名城是否存在 +func (s RoleService) CheckRoleNameUnique(role models2.SysRole) int64 { + return s.roleDao.CheckRoleNameUnique(role) +} + +// CheckRoleKeyUnique 校验角色权限是否唯一 +func (s RoleService) CheckRoleKeyUnique(role models2.SysRole) int64 { + return s.roleDao.CheckRoleKeyUnique(role) + +} + +// Save 添加角色数据 +func (s RoleService) Save(role models2.SysRole) int64 { + role = s.roleDao.Insert(role) + return s.BindRoleMenu(role) +} + +// 添加角色菜单关系 +func (s RoleService) BindRoleMenu(role models2.SysRole) int64 { + list := make([]models2.SysRoleMenu, 0) + for _, id := range role.MenuIds { + menu := models2.SysRoleMenu{ + RoleId: role.RoleId, + MenuId: id, + } + list = append(list, menu) + } + return s.roleMenuDao.Insert(list) +} + +// Edit 修改角色数据 +func (s RoleService) Edit(role models2.SysRole) int64 { + // 删除菜单关联关系 + s.roleMenuDao.Delete(role) + s.BindRoleMenu(role) + // 修改数据 + return s.roleDao.Update(role) +} + +// Remove 删除角色 +func (s RoleService) Remove(id int64) int64 { + role := models2.SysRole{ + RoleId: id, + } + // 删除菜单角色关系 + s.roleMenuDao.Delete(role) + // 删除角色 + return s.roleDao.Delete(role) +} + +// CheckRoleAllowed 校验是否可以操作 +func (s RoleService) CheckRoleAllowed(id int64) (bool, string) { + if id == 1 { + return false, "超级管理员不允许操作" + } + return true, "" +} + +// EditRoleStatus 角色状态修改 +func (s RoleService) EditRoleStatus(role *models2.SysRole) int64 { + return s.roleDao.UpdateRoleStatus(role) +} + +// DeleteAuthUser 取消授权用户 +func (s RoleService) DeleteAuthUser(userRole models2.SysUserRole) int64 { + return s.userRoleDao.DeleteAuthUser(userRole) +} + +// InsertAuthUsers 批量选择用户授权 +func (s RoleService) InsertAuthUsers(body req2.UserRoleBody) int64 { + return s.userRoleDao.InsertAuthUsers(body) +} + +// GetRolesByUserName 查询所属角色组 +func (s RoleService) GetRolesByUserName(name string) string { + list := s.roleDao.SelectRolesByUserName(name) + var buffer bytes.Buffer + var roleName string + for _, role := range *list { + buffer.WriteString(role.RoleName) + buffer.WriteString(",") + } + s2 := buffer.String() + if gotool.StrUtils.HasNotEmpty(s2) { + roleName = s2[0:(len(s2) - 1)] + } + return roleName +} diff --git a/core/service/user_post_service.go b/core/service/user_post_service.go new file mode 100644 index 0000000..f108343 --- /dev/null +++ b/core/service/user_post_service.go @@ -0,0 +1,19 @@ +package service + +import ( + "cutego/core/dao" +) + +type UserPostService struct { + userPostDao dao.UserPostDao +} + +// CountUserPostById 统计岗位数据数量 +func (s UserPostService) CountUserPostById(ids []int64) int64 { + for _, id := range ids { + if s.userPostDao.CountById(id) > 0 { + return id + } + } + return 0 +} diff --git a/core/service/user_service.go b/core/service/user_service.go new file mode 100644 index 0000000..ec25c17 --- /dev/null +++ b/core/service/user_service.go @@ -0,0 +1,151 @@ +package service + +import ( + "cutego/core/api/v1/request" + "cutego/core/api/v1/response" + dao2 "cutego/core/dao" + models2 "cutego/core/entity" +) + +// UserService 用户操作业务逻辑 +type UserService struct { + userDao dao2.UserDao + userPostDao dao2.UserPostDao + userRoleDao dao2.UserRoleDao +} + +// FindList 查询用户集合业务方法 +func (s UserService) FindList(query request.UserQuery) ([]*response.UserResponse, int64) { + return s.userDao.SelectPage(query) +} + +// GetUserById 根据id查询用户数据 +func (s UserService) GetUserById(parseInt int64) *response.UserResponse { + return s.userDao.GetUserById(parseInt) +} + +// GetUserByUserName 根据用户名查询用户 +func (s UserService) GetUserByUserName(name string) *models2.SysUser { + user := models2.SysUser{} + user.UserName = name + return s.userDao.GetUserByUserName(user) +} + +// CheckEmailUnique 校验邮箱是否存在 +func (s UserService) CheckEmailUnique(user request.UserBody) *models2.SysUser { + return s.userDao.CheckEmailUnique(user) +} + +// CheckPhoneNumUnique 校验手机号是否存在 +func (s UserService) CheckPhoneNumUnique(body request.UserBody) *models2.SysUser { + return s.userDao.CheckPhoneNumUnique(body) +} + +// Insert 添加用户业务逻辑 +func (s UserService) Save(body request.UserBody) bool { + // 添加用户数据库操作 + user := s.userDao.Insert(body) + if user != nil { + s.BindUserPost(user) + s.BindUserRole(user) + return true + } + return false +} + +// 新增用户岗位信息 +func (s UserService) BindUserPost(user *request.UserBody) { + postIds := user.PostIds + if len(postIds) > 0 { + sysUserPosts := make([]models2.SysUserPost, 0) + for i := 0; i < len(postIds); i++ { + m := models2.SysUserPost{ + UserId: user.UserId, + PostId: postIds[i], + } + sysUserPosts = append(sysUserPosts, m) + } + s.userPostDao.BatchInsert(sysUserPosts) + } +} + +// 新增用户角色信息 +func (s UserService) BindUserRole(user *request.UserBody) { + roleIds := user.RoleIds + if len(roleIds) > 0 { + roles := make([]models2.SysUserRole, 0) + for i := 0; i < len(roleIds); i++ { + role := models2.SysUserRole{ + RoleId: roleIds[i], + UserId: user.UserId, + } + roles = append(roles, role) + } + s.userRoleDao.BatchInsert(roles) + } +} + +// Edit 修改用户数据 +func (s UserService) Edit(body request.UserBody) int64 { + // 删除原有用户和角色关系 + s.userRoleDao.Delete(body.UserId) + // 重新添加用具角色关系 + s.BindUserRole(&body) + // 删除原有用户岗位关系 + s.userPostDao.Delete(body.UserId) + // 添加新的用户岗位关系 + s.BindUserPost(&body) + // 修改用户数据 + return s.userDao.Update(body) +} + +// Remove 根据用户id删除用户相关数据 +func (s UserService) Remove(id int64) int64 { + // 删除原有用户和角色关系 + s.userRoleDao.Delete(id) + // 删除原有用户岗位关系 + s.userPostDao.Delete(id) + // 删除用户数据 + return s.userDao.Delete(id) +} + +// CheckUserAllowed 校验是否可以修改用户密码 +func (s UserService) CheckUserAllowed(body request.UserBody) bool { + user := models2.SysUser{} + return user.IsAdmin(body.UserId) +} + +// ResetPwd 修改用户密码 +func (s UserService) ResetPwd(body request.UserBody) int64 { + return s.userDao.ResetPwd(body) +} + +// GetAllocatedList 查询未分配用户角色列表 +func (s UserService) GetAllocatedList(query request.UserQuery) ([]*response.UserResponse, int64) { + return s.userDao.GetAllocatedList(query) +} + +// GetUnallocatedList 查询未分配用户角色列表 +func (s UserService) GetUnallocatedList(query request.UserQuery) ([]*response.UserResponse, int64) { + return s.userDao.GetUnallocatedList(query) +} + +// EditProfile 修改数据 +func (s UserService) EditProfile(user request.UserBody) int64 { + return s.userDao.Update(user) +} + +// EditPwd 修改密码 +func (s UserService) EditPwd(id int64, hash string) bool { + return s.userDao.UpdatePwd(id, hash) > 0 +} + +// EditAvatar 修改头像 +func (s UserService) EditAvatar(info *response.UserResponse) bool { + return s.userDao.UpdateAvatar(info) > 0 +} + +// EditStatus 修改可用状态 +func (s UserService) EditStatus(info request.UserBody) bool { + return s.userDao.UpdateStatus(info) > 0 +} diff --git a/pkg/base/model.go b/pkg/base/model.go new file mode 100644 index 0000000..afacf78 --- /dev/null +++ b/pkg/base/model.go @@ -0,0 +1,19 @@ +package base + +import "time" + +// GlobalModel 全局映射实体 +type GlobalModel struct { + CreateTime time.Time `json:"createTime"` // 创建时间 + CreateBy string `json:"createBy"` // 创建人 + UpdateTime time.Time `json:"updateTime"` // 更新时间 + UpdateBy string `json:"updateBy"` // 更新人 +} + +// GlobalQuery 全局Query通用条件 +type GlobalQuery struct { + BeginTime string `form:"beginTime"` // 开始时间 + EndTime string `form:"endTime"` // 结束时间 + PageNum int `form:"pageNum"` // 当前页码 + PageSize int `form:"pageSize"` // 显示条数 +} diff --git a/pkg/cache/index.go b/pkg/cache/index.go new file mode 100644 index 0000000..0b5bdb0 --- /dev/null +++ b/pkg/cache/index.go @@ -0,0 +1,58 @@ +package cache + +import ( + "cutego/core/dao" + "cutego/pkg/common" + "cutego/pkg/constant" +) + +// RemoveList 批量根据Key删除数据 +// @Param list []string 键合集 +func RemoveList(list []string) { + dao.RedisDB.DELALL(list) +} + +// RemoveKey 根据key删除 +// @Param key 键 +// @Return int 删除的数量 +func RemoveCache(key string) int { + del, err := dao.RedisDB.DEL(key) + if err != nil { + common.ErrorLog(err) + } + return del +} + +// GetCache 获取缓存数据 +// @Param key 键 +// @Return string 值 +func GetCache(key string) string { + val, err := dao.RedisDB.GET(key) + if err != nil { + common.ErrorLog(constant.RedisConst{}.GetRedisError(), err.Error()) + return "" + } + return val +} + +// SetCache 设置缓存数据 +// @Param key 键 +// @Param value 值 +// @Return 新增的行数 +func SetCache(key string, value interface{}) int { + n, err := dao.RedisDB.SET(key, common.StructToJson(value)) + if err != nil { + common.ErrorLog(constant.RedisConst{}.GetRedisError(), err.Error()) + return 0 + } + return int(n) +} + +// SetCache 设置缓存数据, 并指定过期时间 +// @Param key 键 +// @Param value 值 +// @Param sec 过期时间(单位: 秒) +// @Return 新增的行数 +func SetCacheTTL(key string, value interface{}, sec int) { + dao.RedisDB.SETEX(key, sec, common.StructToJson(value)) +} diff --git a/pkg/common/interface.go b/pkg/common/interface.go new file mode 100644 index 0000000..aeb7415 --- /dev/null +++ b/pkg/common/interface.go @@ -0,0 +1,51 @@ +package common + +// RedisData 存储数据结构 +type RedisData struct { + Key string + Field string + Value string + Expire int64 +} + +// RedisDataArray RedisData of array +type RedisDataArray []*RedisData + +// IRedis redis client interface +type IRedis interface { + // KEYS get patten key array + KEYS(patten string) ([]string, error) + + // SCAN get patten key array + SCAN(patten string) ([]string, error) + + // DEL delete k-v + DEL(key string) (int, error) + + // DELALL delete key array + DELALL(key []string) (int, error) + + // GET get k-v + GET(key string) (string, error) + + // SET set k-v + //SET(key string, value string) (int64, error) + + // SETEX set k-v expire seconds + SETEX(key string, sec int, value string) (int64, error) + + // EXPIRE set key expire seconds + EXPIRE(key string, sec int64) (int64, error) + + // HGETALL get map of key + HGETALL(key string) (map[string]string, error) + + // HGET get value of key-field + HGET(key string, field string) (string, error) + + // HSET set value of key-field + //HSET(key string, field string, value string) (int64, error) + + // Write 向redis中写入多组数据 + Write(data RedisDataArray) +} diff --git a/pkg/common/log.go b/pkg/common/log.go new file mode 100644 index 0000000..29cc4f0 --- /dev/null +++ b/pkg/common/log.go @@ -0,0 +1,23 @@ +package common + +import "github.com/druidcaesa/gotool" + +func ErrorLog(v ...interface{}) { + gotool.Logs.ErrorLog().Println(v) +} + +func FatalfLog(format string, v ...interface{}) { + gotool.Logs.ErrorLog().Fatalf(format, v) +} + +func InfoLog(v ...interface{}) { + gotool.Logs.InfoLog().Println(v) +} + +func InfoLogf(format string, v ...interface{}) { + gotool.Logs.InfoLog().Printf(format+"\n", v) +} + +func DebugLogf(format string, v ...interface{}) { + gotool.Logs.DebugLog().Printf(format+"\n", v) +} diff --git a/pkg/common/util.go b/pkg/common/util.go new file mode 100644 index 0000000..3a62508 --- /dev/null +++ b/pkg/common/util.go @@ -0,0 +1,142 @@ +package common + +import ( + "cutego/pkg/config" + "encoding/json" + "fmt" + "os" + "runtime" + "strconv" +) + +// IntToString int转string +func IntToString(n int) string { + return strconv.Itoa(n) +} +func mapToBytes(data map[string]interface{}) []byte { + bytes, _ := json.Marshal(data) + return bytes +} + +// MapToStruct map转struct +func MapToStruct(data map[string]interface{}, v interface{}) { + _ = json.Unmarshal(mapToBytes(data), v) +} + +// GetDirPath 获取目录路径 +func GetDirPath(resType string) string { + sysType := runtime.GOOS + switch sysType { + case "linux": + if resType == "log" { + return config.AppCoreConfig.CuteGoConfig.File.Linux.Logs + } else if resType == "avatar" { + return config.AppCoreConfig.CuteGoConfig.File.Linux.Avatar + } else if resType == "file" { + return config.AppCoreConfig.CuteGoConfig.File.Linux.Path + } + break + case "windows": + if resType == "log" { + return config.AppCoreConfig.CuteGoConfig.File.Windows.Logs + } else if resType == "avatar" { + return config.AppCoreConfig.CuteGoConfig.File.Windows.Avatar + } else if resType == "file" { + return config.AppCoreConfig.CuteGoConfig.File.Windows.Path + } + break + case "mac": + if resType == "log" { + return config.AppCoreConfig.CuteGoConfig.File.Mac.Logs + } else if resType == "avatar" { + return config.AppCoreConfig.CuteGoConfig.File.Mac.Avatar + } else if resType == "file" { + return config.AppCoreConfig.CuteGoConfig.File.Mac.Path + } + break + case "darwin": + if resType == "log" { + return config.AppCoreConfig.CuteGoConfig.File.Mac.Logs + } else if resType == "avatar" { + return config.AppCoreConfig.CuteGoConfig.File.Mac.Avatar + } else if resType == "file" { + return config.AppCoreConfig.CuteGoConfig.File.Mac.Path + } + } + return config.AppCoreConfig.CuteGoConfig.File.Linux.Logs +} + +// CreateAllDir 递归创建文件夹 +func CreateAllDir(filePath string) error { + if !IsFileOrDirExist(filePath) { + err := os.MkdirAll(filePath, os.ModePerm) + if err != nil { + fmt.Println("创建文件夹失败, error info:", err) + return err + } + return err + } + return nil +} + +// IsFileOrDirExist 判断所给路径文件/文件夹是否存在(返回true是存在) +func IsFileOrDirExist(path string) bool { + // os.Stat获取文件信息 + _, err := os.Stat(path) + if err != nil { + if os.IsExist(err) { + return true + } + return false + } + return true +} + +// 类三元表达式 +// condition 成立条件 +// trueVal 当条件为true时返回 +// false 当条件为false时返回 +func If(condition bool, trueVal, falseVal interface{}) interface{} { + if condition { + return trueVal + } + return falseVal +} + +// 结构体、Map等转Json字符串 +// @Param v interface{} +// @Return Json字符串 +func StructToJson(v interface{}) string { + jsonBytes, err := json.Marshal(&v) + if err != nil { + ErrorLog(err) + return "" + } + s := string(jsonBytes) + DebugLogf("StructToJson, json=%s", s) + return s +} + +// Json字符串转结构体、Map等 +// +// 单个对象 +// s := new(models2.SysConfig) +// return common.JsonToStruct(get, s).(*models2.SysConfig) +// +// 切片(interface{}.(期望类型)) +// s := make([]interface {}, 0) +// target := common.JsonToStruct(get, s) +// target.([]entity.SysDictData) +// +// @Param data Json字符串 +// @Param s 容器(结构体、Map等) +// @Return interface{} +func JsonToStruct(data string, s interface{}) interface{} { + err := json.Unmarshal([]byte(data), &s) + if err != nil { + ErrorLog(err) + return nil + } + DebugLogf("JsonToStruct, obj=%v", s) + return s +} diff --git a/pkg/config/index.go b/pkg/config/index.go new file mode 100644 index 0000000..f6c106e --- /dev/null +++ b/pkg/config/index.go @@ -0,0 +1,96 @@ +package config + +import ( + config "cutego/pkg/config/models" + "fmt" + "gopkg.in/yaml.v2" + "io/ioutil" + "os" +) + +var ( + // AppCoreConfig 核心配置 + AppCoreConfig *config.ApplicationCoreStruct + // AppEnvConfig 环境配置 + AppEnvConfig *config.ApplicationEnvStruct +) + +// GetRootPath 获取项目根路径 +func GetRootPath() string { + rootPath, _ := os.Getwd() + return rootPath +} + +// GetPathSeparator 获取路径分隔符 +func GetPathSeparator() string { + return string(os.PathSeparator) +} + +// PathExists 判断文件或文件夹是否存在 +// 如果返回的错误为nil,说明文件或文件夹存在 +// 如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在 +// 如果返回的错误为其它类型,则不确定是否在存在 +func PathExists(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +// LoadYamlFile 加载yaml文件 +func LoadYamlFile(filename string, v interface{}) { + data, err := ioutil.ReadFile(filename) + if err != nil { + panic(err.Error()) + } + err = yaml.Unmarshal(data, v) + if err != nil { + panic(err.Error()) + } +} + +// 配置文件所在路径 +const BaseConfigDirPath = "configs" + +func readAppYmlFile(resourcePath string) { + + // 读取主配置文件 + applicationCoreFileName := BaseConfigDirPath + "/application.yml" + applicationCoreFilePath := resourcePath + GetPathSeparator() + applicationCoreFileName + exists, _ := PathExists(applicationCoreFilePath) + if !exists { + panic(applicationCoreFileName + "配置文件不存在!") + } + AppCoreConfig = &config.ApplicationCoreStruct{} + // 由于要改变appConfig内部的值, 所以这里要取址 + LoadYamlFile(applicationCoreFilePath, AppCoreConfig) + + // 读取环境文件 + applicationEnvFileName := fmt.Sprintf(BaseConfigDirPath+"/application-%s.yml", AppCoreConfig.CuteGoConfig.Active) + applicationEnvFilePath := resourcePath + GetPathSeparator() + applicationEnvFileName + exists, _ = PathExists(applicationEnvFilePath) + if !exists { + panic(applicationEnvFileName + "配置文件不存在!") + } + AppEnvConfig = &config.ApplicationEnvStruct{} + // 由于要改变appConfig内部的值, 所以这里要取址 + LoadYamlFile(applicationEnvFilePath, AppEnvConfig) +} + +// ExecuteTip 执行日志 +func ExecuteTip(message string) { + fmt.Println(fmt.Sprintf("-------------------- %s --------------------", message)) +} + +func init() { + // 资源文件所在的路径 + resourcePath := GetRootPath() + ExecuteTip("初始化应用配置 start") + readAppYmlFile(resourcePath) + ExecuteTip("初始化应用配置 end") + return +} diff --git a/pkg/config/models/application_core.go b/pkg/config/models/application_core.go new file mode 100644 index 0000000..9e64e9d --- /dev/null +++ b/pkg/config/models/application_core.go @@ -0,0 +1,67 @@ +package config + +type ApplicationCoreStruct struct { + CuteGoConfig CuteGoConfig `yaml:"cutego"` +} + +// CuteGoConfig 总配置 +type CuteGoConfig struct { + // 默认激活dev配置 + Active string `yaml:"active" default:"dev"` + // 开启演示模式 + DemoMode bool `yaml:"demo-mode"` + Mail MailConfig `yaml:"mail"` + TaskPool TaskPoolConfig `yaml:"task-pool"` + Captcha CaptchaConfig `yaml:"captcha"` + File FileConfig `yaml:"file"` +} + +// MailConfig 邮件 +type MailConfig struct { + // 服务地址 + Host string `yaml:"host"` + // 服务端口 + Port int `yaml:"port"` + // 用户名 + Username string `yaml:"username"` + // 密码 + Password string `yaml:"password"` + // 默认编码 + DefaultEncoding string `yaml:"default-encoding"` +} + +// TaskPoolConfig 线程池 +type TaskPoolConfig struct { + // 核心线程池大小 + CorePoolSize int `yaml:"core-pool-size"` + // 最大线程数(尽可能的大) + MaxPoolSize int `yaml:"max-pool-size"` + // 活跃时间(单位: s) + KeepAliveSeconds int `yaml:"keep-alive-seconds"` + // 队列容量 + QueueCapacity int `yaml:"queue-capacity"` +} + +// CaptchaConfig 验证码有效时间(单位: s) +type CaptchaConfig struct { + // 邮箱 + Email int `yaml:"email"` + // 手机短信 + Sms int `yaml:"sms"` +} + +// FileConfig 文件上传 +type FileConfig struct { + // 文件大小(单位: mb) + FileMaxSize int `yaml:"file-max-size"` + // 头像大小(单位: mb) + AvatarMaxSize int `yaml:"avatar-max-size"` + Mac FilePath `yaml:"mac"` + Linux FilePath `yaml:"linux"` + Windows FilePath `yaml:"windows"` +} +type FilePath struct { + Path string `yaml:"path"` + Avatar string `yaml:"avatar"` + Logs string `yaml:"logs"` +} diff --git a/pkg/config/models/application_env.go b/pkg/config/models/application_env.go new file mode 100644 index 0000000..bba2961 --- /dev/null +++ b/pkg/config/models/application_env.go @@ -0,0 +1,111 @@ +package config + +type ApplicationEnvStruct struct { + Server ServerConfig `yaml:"server"` + DataSource DataSourceConfig `yaml:"datasource"` + Redis RedisConfig `yaml:"redis"` + MongoDb MongoDbConfig `yaml:"mongodb"` + Login LoginConfig `yaml:"login"` + Jwt JwtConfig `yaml:"jwt"` + Logger LoggerConfig `yaml:"logger"` +} + +// ServerConfig web服务 +type ServerConfig struct { + // Running in "debug" mode. Switch to "release" mode in production + RunMode string `yaml:"run-mode"` + // web服务监听端口(生产的端口有可能不一样,所以根据环境隔离开) + Port int `yaml:"port"` +} + +// DataSourceConfig 数据源 +type DataSourceConfig struct { + // 数据库类型 + DbType string `yaml:"db-type" default:"mysql"` + // 服务地址 + Host string `yaml:"host"` + // 服务端口 + Port int `yaml:"port"` + // 用户名称 + Username string `yaml:"username"` + // 用户密码 + Password string `yaml:"password"` + // 数据库名称 + Database string `yaml:"database"` + // 编码 + Charset string `yaml:"charset"` + // 空闲时的最大连接数 + MaxIdleSize int `yaml:"max-idle-size"` + // 数据库的最大打开连接数 + MaxOpenSize int `yaml:"max-open-size"` +} + +// RedisConfig 缓存 +type RedisConfig struct { + // 数据库索引 + Database int `yaml:"database"` + // 服务地址 + Host string `yaml:"host"` + // 服务端口 + Port int `yaml:"port"` + // 服务密码 + Password string `yaml:"password"` + // 连接超时时间 + Timeout int `yaml:"timeout"` + Pool RedisPoolConfig `yaml:"pool"` +} + +// RedisPoolConfig redis连接池配置 +type RedisPoolConfig struct { + // 连接池最大连接数(使用负值表示没有限制, 最佳配置为cpu核数+1) + MaxActive int `yaml:"max-active"` + // 连接池中的最大空闲连接 + MaxIdle int `yaml:"max-idle"` + // 连接池最大阻塞等待时间(使用负值表示没有限制) + MaxWait int `yaml:"max-wait"` +} + +// MongoDbConfig MongoDB +type MongoDbConfig struct { + Url string `yaml:"url"` + Port string `yaml:"port"` + DB string `yaml:"db"` + Username string `yaml:"username"` + Password string `yaml:"password"` +} + +// LoginConfig 登录相关 +type LoginConfig struct { + // 是否限制单用户登录 + Single bool `yaml:"single"` +} + +// JwtConfig jwt参数 +type JwtConfig struct { + // 请求头前缀 + Header string `yaml:"header" json:"header,omitempty"` + // 令牌前缀 + TokenStartWith string `yaml:"token-start-with" json:"token_start_with,omitempty"` + // 加密密钥 + TokenSecret string `yaml:"token-secret" json:"token_secret,omitempty"` + // 令牌过期时间 此处单位: h , 默认1小时 + TokenExpired int `yaml:"token-expired" json:"token_expired,omitempty"` + // token在cookie中的别称 + CookieKey string `yaml:"cookie-key"` + // 在线用户key + OnlineKey string `yaml:"online-key" json:"online_key,omitempty"` + // token 续期检查时间范围(默认30分钟, 单位毫秒), 在token即将过期的一段时间内用户操作了, 则给用户的token续期 + Detect int `yaml:"detect" json:"detect,omitempty"` + // 续期时间范围, 默认1小时, 单位毫秒 + Renew int `yaml:"renew" json:"renew,omitempty"` +} + +// LoggerConfig 日志文件 +type LoggerConfig struct { + // 最大保存时间(单位: d) + MaxSaveAge int `yaml:"max-save-age"` + // 日志切割时间间隔(单位: d) + RotationTime int `yaml:"rotation-time"` + // 日志级别 + Level string `yaml:"level"` +} diff --git a/pkg/constant/index.go b/pkg/constant/index.go new file mode 100644 index 0000000..84ad0b3 --- /dev/null +++ b/pkg/constant/index.go @@ -0,0 +1,5 @@ +package constant + +const ( + RedisOnlineUserKey = "online:" +) diff --git a/pkg/constant/mysql_const.go b/pkg/constant/mysql_const.go new file mode 100644 index 0000000..63c7d57 --- /dev/null +++ b/pkg/constant/mysql_const.go @@ -0,0 +1,12 @@ +package constant + +const ( + mysqlErrorMsg = "调用MySQL发生异常, %s" +) + +type MysqlConst struct{} + +// GetMysqlError Mysql异常拼接常量 +func (c MysqlConst) GetMysqlError() string { + return mysqlErrorMsg +} diff --git a/pkg/constant/redis_const.go b/pkg/constant/redis_const.go new file mode 100644 index 0000000..3e71262 --- /dev/null +++ b/pkg/constant/redis_const.go @@ -0,0 +1,25 @@ +package constant + +const ( + redisErrorMsg = "调用Redis发生异常, %s" + redisDictKey = "dict:" + redisConfigKey = "config:" +) + +// RedisConst Redis相关操作常量 +type RedisConst struct{} + +// GetRedisError Redis异常拼接常量 +func (c RedisConst) GetRedisError() string { + return redisErrorMsg +} + +// GetRedisDictKey 获取Redis的Dict的key +func (c RedisConst) GetRedisDictKey() string { + return redisDictKey +} + +// GetRedisConfigKey 获取redis的config的key +func (c RedisConst) GetRedisConfigKey() string { + return redisConfigKey +} diff --git a/pkg/cronjob/index.go b/pkg/cronjob/index.go new file mode 100644 index 0000000..2ef0db2 --- /dev/null +++ b/pkg/cronjob/index.go @@ -0,0 +1,70 @@ +package cronjob + +import ( + "cutego/core/api/v1/request" + "cutego/core/job" + "cutego/core/service" + "cutego/pkg/common" + "github.com/robfig/cron" + "time" +) + +// Cron表达式参考 +// 每隔5秒执行一次:*/5 * * * * ? +// 每隔1分钟执行一次:0 */1 * * * ? +// 每天23点执行一次:0 0 23 * * ? +// 每天凌晨1点执行一次:0 0 1 * * ? +// 每月1号凌晨1点执行一次:0 0 1 1 * ? +// 每月最后一天23点执行一次:0 0 23 L * ? +// 每周星期天凌晨1点实行一次:0 0 1 ? * L +// 在26分、29分、33分执行一次:0 26,29,33 * * * ? +// 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ? + +// 定时任务: 别名与调度器的映射 +var AliasCronMap = make(map[string]*cron.Cron) + +// 停止任务, 不会停止已开始的任务 +func StopCronFunc(aliasName string) { + common.InfoLogf("停止任务 %s ---> Start", aliasName) + AliasCronMap[aliasName].Stop() + common.InfoLogf("停止任务 %s ---> Finish", aliasName) +} + +// 开始任务 +func StartCronFunc(aliasName string) { + common.InfoLogf("唤起任务 %s ---> Start", aliasName) + AliasCronMap[aliasName].Start() + common.InfoLogf("唤起任务 %s ---> Finish", aliasName) +} + +func init() { + if len(job.AliasFuncMap) > 0 { + //go test() + index := 1 + for true { + q := request.CronJobQuery{} + q.PageNum = index + data, _ := service.CronJobService{}.FindPage(q) + if len(data) == 0 { + break + } + for _, datum := range data { + c := cron.New() + c.AddFunc(datum.JobCron, job.AliasFuncMap[datum.FuncAlias]) + c.Start() + + AliasCronMap[datum.FuncAlias] = c + common.InfoLogf("调度定时任务 --- %s ---> Success", datum.JobName) + } + index += 1 + } + } +} + +// 测试通过 +func test() { + time.Sleep(time.Second * 10) + StopCronFunc("test1") + time.Sleep(time.Second * 10) + StartCronFunc("test1") +} diff --git a/pkg/excels/export.go b/pkg/excels/export.go new file mode 100644 index 0000000..5c3bd0a --- /dev/null +++ b/pkg/excels/export.go @@ -0,0 +1,143 @@ +package excels + +import ( + "github.com/druidcaesa/gotool" + "github.com/xuri/excelize/v2" + "reflect" + "strconv" +) + +// 获取title头list +func getTitle(list []map[string]string) []string { + titleList := make([]string, 0) + for _, item := range list { + for _, s := range item { + titleList = append(titleList, s) + } + } + return titleList +} + +// ExportExcel 导出excel +func ExportExcel(list []interface{}, title string) (error, *excelize.File) { + // 获取标题 + headerList, expList := ExcelCreate(list[0]) + headers := getTitle(headerList) + // 默认存在第一个工作簿是 Sheet1 首字母要大写, 否则会报错。 + // 如果想额外的创建工作簿, 可以使用, sheet2 := file.NewSheet("Sheet2"), 工作簿的名称不区分大小写。 + // 如果有多个工作簿, 可以使用 file.SetActiveSheet(index) 来指定打开文件时focus到哪个工作簿 + sheet1 := "Sheet1" + files := excelize.NewFile() + character := string(65 + len(headers) - 1) + /* -------------------- 第一行大标题 -------------------- */ + // 设置行高 + err := files.SetRowHeight(sheet1, 1, 25) + if err != nil { + return err, nil + } + // 合并单元格 + err = files.MergeCell(sheet1, "A1", character+"1") + if err != nil { + return err, nil + } + // 设置单元格样式:对齐;字体, 大小;单元格边框 + styleTitle, _ := files.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"},"font":{"bold":true,"italic":false,"family":"Calibri","size":16,"color":"#000000"},"border":[{"type":"left","color":"#3FAD08","style":0},{"type":"top","color":"#3FAD08","style":0},{"type":"bottom","color":"#3FAD08","style":2},{"type":"right","color":"#3FAD08","style":0}]}`) + err = files.SetCellStyle(sheet1, "A1", character+"1", styleTitle) + if err != nil { + return err, nil + } + + err = files.SetCellValue(sheet1, "A1", title) + if err != nil { + return err, nil + } + + /* -------------------- 字段标题 -------------------- */ + styleHeader, _ := files.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"},"font":{"bold":false,"italic":false,"family":"Calibri","size":10,"color":"#000000"}}`) + err = files.SetCellStyle(sheet1, "A2", character+"2", styleHeader) + if err != nil { + return err, nil + } + for k, v := range headers { + err = files.SetCellValue(sheet1, string(65+k)+"2", v) + if err != nil { + return err, nil + } + } + // 设置最后一列宽度 + err = files.SetColWidth(sheet1, "C", character, 20) + if err != nil { + return err, nil + } + // 冻结窗口:冻结第一行和第二行 + err = files.SetPanes(sheet1, `{"freeze":true,"split":false,"x_split":0,"y_split":2}`) + if err != nil { + return err, nil + } + ///* -------------------- 填充行数据 -------------------- */ + line := 3 + for _, v := range list { + var num = 0 + lineChr := strconv.Itoa(line) + // 设置样式 + err = files.SetCellStyle(sheet1, "A"+lineChr, character+lineChr, styleHeader) + if err != nil { + return err, nil + } + // 反射获取数据和类型 + getValue := reflect.ValueOf(v) + getType := reflect.TypeOf(v) + n := getValue.NumField() + for i := 0; i < n; i++ { + val := getValue.Field(i) + name := getType.Field(i).Name + if !getIsTitle(name, headerList) { + continue + } + err = files.SetCellValue(sheet1, string(65+num)+lineChr, getExp(name, expList, val.Interface())) + if err != nil { + return err, nil + } + num++ + } + line++ + } + return nil, files +} + +func getIsTitle(name string, headerList []map[string]string) bool { + flag := false + for _, m := range headerList { + if gotool.StrUtils.HasNotEmpty(m[name]) { + flag = true + } + } + return flag +} + +func getExp(name string, expList []map[string][]map[string]string, value interface{}) interface{} { + for _, m := range expList { + if len(m[name]) > 0 { + maps := m[name] + for _, m2 := range maps { + if gotool.StrUtils.HasNotEmpty(m2[interfaceToString(value)]) { + value = m2[interfaceToString(value)] + } + } + } + } + return value +} + +func interfaceToString(inter interface{}) string { + switch inter.(type) { + case string: + return inter.(string) + case int: + return strconv.Itoa(inter.(int)) + case int64: + return strconv.FormatInt(inter.(int64), 10) + default: + return inter.(string) + } +} diff --git a/pkg/excels/tag.go b/pkg/excels/tag.go new file mode 100644 index 0000000..d9d6e7d --- /dev/null +++ b/pkg/excels/tag.go @@ -0,0 +1,84 @@ +package excels + +import ( + "reflect" + "strings" +) + +// 定义tagName名称 +const tagName = "excel" + +// Excels 定义Excel接口 +type Excels interface { + Excels(interface{}) (bool, string) +} + +// DefaultExcels 默认Excel配置 +type DefaultExcels struct { +} + +// Excels 实现接口 +func (e DefaultExcels) Excels(val interface{}) (bool, string) { + return true, "" +} + +// `excel:"name='',readConverterExp=''"` +// 获取标题头 +func getExcelFromTagTitle(proName string, tag string) map[string]string { + args := strings.SplitN(tag, ",", 2) + m := make(map[string]string) + for i := 0; i < len(args); i++ { + contains := strings.Contains(args[i], "name") + if contains { + m[proName] = strings.Split(args[i], "=")[1] + break + } + } + return m +} + +// `excel:"name='',readConverterExp='Y=1,N=2'"` +func getReadConverterExp(proName string, tag string) map[string][]map[string]string { + list := make(map[string][]map[string]string, 0) + args := strings.SplitN(tag, ",", 2) + for i := 0; i < len(args); i++ { + if strings.Contains(args[i], "format") { + maps := make([]map[string]string, 0) + converter := strings.SplitN(args[i], "=", 2)[1] + values := strings.Split(converter, ",") + for j := 0; j < len(values); j++ { + split := strings.Split(values[j], "=") + m := make(map[string]string) + m[split[0]] = split[1] + maps = append(maps, m) + } + list[proName] = maps + break + } + } + return list +} + +// ExcelCreate 获取name名称和readConverterExp集合 +func ExcelCreate(s interface{}) ([]map[string]string, []map[string][]map[string]string) { + titleList := make([]map[string]string, 0) + expList := make([]map[string][]map[string]string, 0) + // 获取值域 + v := reflect.ValueOf(s) + t := reflect.TypeOf(s) + var title map[string]string + var exp map[string][]map[string]string + // 遍历反射树形 + for i := 0; i < t.NumField(); i++ { + tag := v.Type().Field(i).Tag.Get(tagName) + if tag == "" || tag == "-" { + continue + } + name := t.Field(i).Name + title = getExcelFromTagTitle(name, tag) + exp = getReadConverterExp(name, tag) + titleList = append(titleList, title) + expList = append(expList, exp) + } + return titleList, expList +} diff --git a/pkg/file/export.go b/pkg/file/export.go new file mode 100644 index 0000000..cecf9fc --- /dev/null +++ b/pkg/file/export.go @@ -0,0 +1,16 @@ +package file + +import ( + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + "github.com/xuri/excelize/v2" +) + +// DownloadExcel 公共下载execl方法 +func DownloadExcel(c *gin.Context, file *excelize.File) { + c.Header("Content-Type", "application/octet-stream") + c.Header("Content-Disposition", "attachment; filename="+gotool.IdUtils.IdUUIDToRan(false)+".xlsx") + c.Header("Content-Transfer-Encoding", "binary") + c.Header("FileName", gotool.IdUtils.IdUUIDToRan(false)+".xlsx") + _ = file.Write(c.Writer) +} diff --git a/pkg/filter/demo_handler.go b/pkg/filter/demo_handler.go new file mode 100644 index 0000000..dcaf32c --- /dev/null +++ b/pkg/filter/demo_handler.go @@ -0,0 +1,43 @@ +package filter + +import ( + "cutego/pkg/config" + "github.com/gin-gonic/gin" + "net/http" + "strings" +) + +func DemoHandler() gin.HandlerFunc { + return func(c *gin.Context) { + if config.AppCoreConfig.CuteGoConfig.DemoMode { + request := inDisRequest() + for i := 0; i < len(request); i++ { + if c.Request.Method == http.MethodDelete || c.Request.Method == http.MethodPut || strings.Contains(c.Request.RequestURI, request[i]) { + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusInternalServerError, + "msg": "演示模式, 不允许操作", + }) + c.Abort() + return + } + } + + } + + } +} + +// 禁用请求 +func inDisRequest() []string { + return []string{ + "/remove", + "/profile/avatar", + "/resetPwd", + "/edit", + "/insert", + "/add", + "/delete", + "/export", + "/import", + } +} diff --git a/pkg/jwt/jwt_filter.go b/pkg/jwt/jwt_filter.go new file mode 100644 index 0000000..605a80c --- /dev/null +++ b/pkg/jwt/jwt_filter.go @@ -0,0 +1,28 @@ +package jwt + +import ( + "github.com/gin-gonic/gin" + "strings" +) + +// 是否在放行范围内 +func doSquare(c *gin.Context) bool { + request := inSquareRequest() + for i := 0; i < len(request); i++ { + replace := strings.Contains(c.Request.RequestURI, request[i]) + if replace { + return true + } + } + return false +} + +// 放行的请求 +func inSquareRequest() []string { + var req []string + // 放行登录请求 + req = append(req, "/api/v1/login") + // 放行websocket请求 + req = append(req, "/websocket") + return req +} diff --git a/pkg/jwt/jwt_handler.go b/pkg/jwt/jwt_handler.go new file mode 100644 index 0000000..3fa6a81 --- /dev/null +++ b/pkg/jwt/jwt_handler.go @@ -0,0 +1,173 @@ +package jwt + +import ( + "cutego/core/api/v1/response" + "cutego/core/dao" + "cutego/pkg/cache" + "cutego/pkg/config" + "errors" + "github.com/dgrijalva/jwt-go" + "github.com/gin-gonic/gin" + "net/http" + "strings" + "time" +) + +// JWTAuth 中间件, 检查token +func JWTAuth() gin.HandlerFunc { + return func(c *gin.Context) { + // 放行的请求先放行 + if doSquare(c) { + return + } + authHeader := c.Request.Header.Get(config.AppEnvConfig.Jwt.Header) + if authHeader == "" { + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusUnauthorized, + "msg": "请求未携带token, 无权限访问", + }) + c.Abort() + return + } + // 按空格分割 + authHeaderSplit := strings.SplitN(authHeader, " ", 2) + if !(len(authHeaderSplit) == 2 && authHeaderSplit[0] == config.AppEnvConfig.Jwt.TokenStartWith) { + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusUnauthorized, + "msg": "请求头中Token格式有误", + }) + c.Abort() + return + } + // authHeaderSplit[1]是获取到的tokenString, 我们使用之前定义好的解析JWT的函数来解析它 + currentTokenStr := authHeaderSplit[1] + claims, err := ParseToken(currentTokenStr) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusUnauthorized, + "msg": err.Error(), + }) + c.Abort() + return + } + singleTag := config.AppEnvConfig.Login.Single + if singleTag { + token, err := dao.RedisDB.GET(claims.UserInfo.UserName) + if err == nil { + if !(token == currentTokenStr) { + c.JSON(http.StatusOK, gin.H{ + "status": http.StatusUnauthorized, + "msg": "您的账号已在其他终端登录, 请重新登录", + }) + c.Abort() + return + } + } + } + // 继续交由下一个路由处理,并将解析出的信息传递下去 + c.Set("claims", claims) + } +} + +// 一些常量 +var ( + TokenExpired error = errors.New("授权已过期") + TokenNotValidYet error = errors.New("Token not active yet") + TokenMalformed error = errors.New("令牌非法") + TokenInvalid error = errors.New("Couldn't handle this token:") +) + +// CuteClaims 自定义声明结构体并内嵌jwt.StandardClaims +// jwt包自带的jwt.StandardClaims只包含了官方字段 +// 我们这里需要额外记录一些字段,所以要自定义结构体 +// 如果想要保存更多信息,都可以添加到这个结构体中 +type CuteClaims struct { + UserInfo response.UserResponse `json:"userInfo"` + jwt.StandardClaims +} + +// CreateToken 生成一个token +func CreateToken(claims CuteClaims) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + // 定义Secret + var tokenSecret = []byte(config.AppEnvConfig.Jwt.TokenSecret) + return token.SignedString(tokenSecret) +} + +// CreateUserToken 生成含有用户信息的token +func CreateUserToken(u *response.UserResponse) (string, error) { + if config.AppEnvConfig.Jwt.TokenExpired == 0 { + config.AppEnvConfig.Jwt.TokenExpired = 1 + } + // 定义JWT的过期时间 + tokenExpired := time.Hour * time.Duration(config.AppEnvConfig.Jwt.TokenExpired) + // 定义Secret + var tokenSecret = []byte(config.AppEnvConfig.Jwt.TokenSecret) + + // 创建我们自己的声明 + c := CuteClaims{ + UserInfo: *u, // 自定义字段 + StandardClaims: jwt.StandardClaims{ + ExpiresAt: time.Now().Add(tokenExpired).Unix(), // 过期时间 + Issuer: "tianjun@odboy.cn", // 签发人 + }, + } + // 使用指定的签名方法创建签名对象 + token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) + // 使用指定的secret签名并获得完整的编码后的字符串token + return token.SignedString(tokenSecret) +} + +// ParseToken 解析Token +func ParseToken(tokenString string) (*CuteClaims, error) { + // 定义Secret + var tokenSecret = []byte(config.AppEnvConfig.Jwt.TokenSecret) + token, err := jwt.ParseWithClaims(tokenString, &CuteClaims{}, func(token *jwt.Token) (interface{}, error) { + return tokenSecret, nil + }) + if err != nil { + if ve, ok := err.(*jwt.ValidationError); ok { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + return nil, TokenMalformed + } else if ve.Errors&jwt.ValidationErrorExpired != 0 { + // Token 过期(授权已过期) + return nil, TokenExpired + } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { + return nil, TokenNotValidYet + } else { + return nil, TokenInvalid + } + } + } + if claims, ok := token.Claims.(*CuteClaims); ok && token.Valid { + if config.AppEnvConfig.Login.Single { + tokenData := cache.GetCache(claims.UserInfo.UserName) + if tokenData == "" { + return nil, TokenExpired + } + } + return claims, nil + } + return nil, TokenInvalid +} + +// RefreshToken 更新token +func RefreshToken(tokenString string) (string, error) { + // 定义Secret + var tokenSecret = []byte(config.AppEnvConfig.Jwt.TokenSecret) + jwt.TimeFunc = func() time.Time { + return time.Unix(0, 0) + } + token, err := jwt.ParseWithClaims(tokenString, &CuteClaims{}, func(token *jwt.Token) (interface{}, error) { + return tokenSecret, nil + }) + if err != nil { + return "", err + } + if claims, ok := token.Claims.(*CuteClaims); ok && token.Valid { + jwt.TimeFunc = time.Now + claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix() + return CreateToken(*claims) + } + return "", TokenInvalid +} diff --git a/pkg/middleware/logger/logger.go b/pkg/middleware/logger/logger.go new file mode 100644 index 0000000..de0c31c --- /dev/null +++ b/pkg/middleware/logger/logger.go @@ -0,0 +1,141 @@ +package logger + +import ( + "cutego/pkg/common" + "cutego/pkg/config" + "fmt" + "github.com/druidcaesa/gotool" + "github.com/gin-gonic/gin" + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/rifflock/lfshook" + "github.com/sirupsen/logrus" + "os" + "path" + "time" +) + +// LoggerToFile 日志记录到文件 +func LoggerToFile() gin.HandlerFunc { + dirPath := common.GetDirPath("log") + fileName := path.Join(dirPath, "application.log") + if !common.IsFileOrDirExist(dirPath) { + err := common.CreateAllDir(dirPath) + if err != nil { + common.ErrorLog(err) + } + } + if !gotool.FileUtils.Exists(fileName) { + create, err := os.Create(fileName) + if err != nil { + common.ErrorLog(err) + } + defer create.Close() + } + // 写入文件 + src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend) + if err != nil { + fmt.Println("err", err) + } + logger := logrus.New() + // 输出源 + logger.Out = src + switch config.AppEnvConfig.Logger.Level { + case "debug": + logger.SetLevel(logrus.DebugLevel) + break + case "warn": + logger.SetLevel(logrus.WarnLevel) + break + case "info": + logger.SetLevel(logrus.InfoLevel) + break + case "error": + logger.SetLevel(logrus.ErrorLevel) + break + case "fatal": + logger.SetLevel(logrus.FatalLevel) + case "panic": + logger.SetLevel(logrus.PanicLevel) + break + case "trace": + logger.SetLevel(logrus.TraceLevel) + default: + logger.SetLevel(logrus.DebugLevel) + } + logger.SetLevel(logrus.DebugLevel) + // 设置 rotatelogs + if config.AppEnvConfig.Logger.MaxSaveAge == 0 { + config.AppEnvConfig.Logger.MaxSaveAge = 7 + } + if config.AppEnvConfig.Logger.RotationTime == 0 { + config.AppEnvConfig.Logger.RotationTime = 1 + } + logWriter, err := rotatelogs.New( + // 分割后的文件名称 + fileName+".%Y%m%d.logs", + // 生成软链, 指向最新日志文件 + rotatelogs.WithLinkName(fileName), + rotatelogs.WithMaxAge(time.Duration(config.AppEnvConfig.Logger.MaxSaveAge)*24*time.Hour), + rotatelogs.WithRotationTime(time.Duration(config.AppEnvConfig.Logger.RotationTime)*24*time.Hour), + ) + writeMap := lfshook.WriterMap{ + logrus.InfoLevel: logWriter, + logrus.FatalLevel: logWriter, + logrus.DebugLevel: logWriter, + logrus.WarnLevel: logWriter, + logrus.ErrorLevel: logWriter, + logrus.PanicLevel: logWriter, + } + lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{ + TimestampFormat: "2006-01-02 15:04:05", + }) + // 新增 Hook + logger.AddHook(lfHook) + return func(c *gin.Context) { + // 开始时间 + startTime := time.Now() + // 处理请求 + c.Next() + // 结束时间 + endTime := time.Now() + // 执行时间 + latencyTime := endTime.Sub(startTime) + // 请求方式 + reqMethod := c.Request.Method + // 请求路由 + reqUri := c.Request.RequestURI + // 状态码 + statusCode := c.Writer.Status() + // 请求IP + clientIP := c.ClientIP() + // 日志格式 + logger.WithFields(logrus.Fields{ + "status_code": statusCode, + "latency_time": latencyTime, + "client_ip": clientIP, + "req_method": reqMethod, + "req_uri": reqUri, + }).Info() + } +} + +// 日志记录到 MongoDB +func LoggerToMongo() gin.HandlerFunc { + return func(c *gin.Context) { + + } +} + +// 日志记录到 ES +func LoggerToES() gin.HandlerFunc { + return func(c *gin.Context) { + + } +} + +// 日志记录到 MQ +func LoggerToMQ() gin.HandlerFunc { + return func(c *gin.Context) { + + } +} diff --git a/pkg/middleware/recover.go b/pkg/middleware/recover.go new file mode 100644 index 0000000..38ce0a1 --- /dev/null +++ b/pkg/middleware/recover.go @@ -0,0 +1,36 @@ +package middleware + +import ( + "cutego/pkg/common" + "github.com/gin-gonic/gin" + "net/http" + "runtime/debug" +) + +func Recover(c *gin.Context) { + defer func() { + if r := recover(); r != nil { + common.ErrorLog("panic: %v\n", r) + debug.PrintStack() + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusInternalServerError, + "msg": errorToString(r), + "data": nil, + }) + // 终止后续接口调用, 不加的话recover捕捉到异常后, 还会继续执行接口后续的代码 + c.Abort() + } + }() + // 加载完 defer recover, 继续后续接口调用 + c.Next() +} + +// recover错误, 转string +func errorToString(r interface{}) string { + switch v := r.(type) { + case error: + return v.Error() + default: + return r.(string) + } +} diff --git a/pkg/page/page.go b/pkg/page/page.go new file mode 100644 index 0000000..88f6667 --- /dev/null +++ b/pkg/page/page.go @@ -0,0 +1,41 @@ +package page + +import ( + "cutego/pkg/common" + "github.com/go-xorm/xorm" +) + +// Page 分页结构体 +type Page struct { + Size int `json:"size"` // 显示条数 + Total int64 `json:"total"` // 总条数 + List interface{} `json:"list"` // 数据 +} + +type Start struct { +} + +// StartSize 获取分页偏移量 +func StartSize(pageNum int, size int) int { + if pageNum == 0 { + pageNum = 1 + } + if size == 0 { + size = 10 + } + num := (pageNum - 1) * size + return num +} + +// GetTotal 获取总条数 +func GetTotal(engine *xorm.Session, args ...interface{}) (int64, error) { + if args != nil { + engine.Table(args) + } + count, err := engine.Count() + if err != nil { + common.ErrorLog(err.Error()) + return 0, err + } + return count, nil +} diff --git a/pkg/redispool/index.go b/pkg/redispool/index.go new file mode 100644 index 0000000..5904e21 --- /dev/null +++ b/pkg/redispool/index.go @@ -0,0 +1,220 @@ +package redispool + +import ( + "cutego/pkg/common" + "cutego/pkg/config" + "fmt" + "github.com/druidcaesa/gotool" + "github.com/gomodule/redigo/redis" + "time" +) + +// https://godoc.org/github.com/gomodule/redigo/redis#pkg-examples +// https://github.com/gomodule/redigo + +// RedisClient redis client instance +type RedisClient struct { + pool *redis.Pool + // 数据接收 + chanRx chan common.RedisDataArray + // 是否退出 + isExit bool +} + +// NewRedis new redis client +func NewRedis() *RedisClient { + return &RedisClient{ + pool: newPool(), + chanRx: make(chan common.RedisDataArray, 100), + } +} + +// newPool 线程池 +func newPool() *redis.Pool { + if config.AppEnvConfig.Redis.Pool.MaxIdle == 0 { + config.AppEnvConfig.Redis.Pool.MaxIdle = 3 + } + return &redis.Pool{ + MaxIdle: config.AppEnvConfig.Redis.Pool.MaxIdle, + IdleTimeout: time.Duration(config.AppEnvConfig.Redis.Pool.MaxWait) * time.Second, + MaxActive: config.AppEnvConfig.Redis.Pool.MaxActive, + Dial: func() (redis.Conn, error) { + c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", config.AppEnvConfig.Redis.Host, config.AppEnvConfig.Redis.Port)) + if err != nil { + common.FatalfLog("Redis.Dial: %v", err) + return nil, err + } + if gotool.StrUtils.HasNotEmpty(config.AppEnvConfig.Redis.Password) { + if _, err := c.Do("AUTH", config.AppEnvConfig.Redis.Password); err != nil { + c.Close() + common.FatalfLog("Redis.AUTH: %v", err) + return nil, err + } + } + if _, err := c.Do("SELECT", config.AppEnvConfig.Redis.Database); err != nil { + c.Close() + common.FatalfLog("Redis.SELECT: %v", err) + return nil, err + } + return c, nil + }, + } +} + +// Start 启动接收任务协程 +func (r *RedisClient) Start() { + r.isExit = false + // 开启协程用于循环接收数据 + go r.loopRead() +} + +// Stop 停止接收任务 +func (r *RedisClient) Stop() { + r.isExit = true + // 关闭数据接收通道 + close(r.chanRx) + // 关闭redis线程池 + r.pool.Close() +} + +// Write 向redis中写入多组数据 +func (r *RedisClient) Write(data common.RedisDataArray) { + r.chanRx <- data +} + +// loopRead 循环接收数据 +func (r *RedisClient) loopRead() { + for !r.isExit { + select { + case rx := <-r.chanRx: + for _, it := range rx { + if len(it.Key) > 0 { + if len(it.Field) > 0 { + if _, err := r.HSET(it.Key, it.Field, it.Value); err != nil { + common.DebugLogf("[%s, %s, %s]: %s\n", it.Key, it.Field, it.Value, err.Error()) + } + } else { + if _, err := r.SET(it.Key, it.Value); err != nil { + common.DebugLogf("[%s, %s, %s]: %s\n", it.Key, it.Field, it.Value, err.Error()) + } + } + if it.Expire > 0 { + r.EXPIRE(it.Key, it.Expire) + } + } + } + } + } + +} + +// Error get redis connect error +func (r *RedisClient) Error() error { + conn := r.pool.Get() + defer conn.Close() + return conn.Err() +} + +// 常用Redis操作命令的封装 +// http://redis.io/commands + +// KEYS get patten key array +func (r *RedisClient) KEYS(patten string) ([]string, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.Strings(conn.Do("KEYS", patten)) +} + +// SCAN 获取大量key +func (r *RedisClient) SCAN(patten string) ([]string, error) { + conn := r.pool.Get() + defer conn.Close() + var out []string + var cursor uint64 = 0xffffff + isFirst := true + for cursor != 0 { + if isFirst { + cursor = 0 + isFirst = false + } + arr, err := conn.Do("SCAN", cursor, "MATCH", patten, "COUNT", 100) + if err != nil { + return out, err + } + switch arr := arr.(type) { + case []interface{}: + cursor, _ = redis.Uint64(arr[0], nil) + it, _ := redis.Strings(arr[1], nil) + out = append(out, it...) + } + } + out = gotool.StrArrayUtils.ArrayDuplication(out) + return out, nil +} + +// DEL delete k-v +func (r *RedisClient) DEL(key string) (int, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.Int(conn.Do("DEL", key)) +} + +// DELALL delete key array +func (r *RedisClient) DELALL(key []string) (int, error) { + conn := r.pool.Get() + defer conn.Close() + arr := make([]interface{}, len(key)) + for i, v := range key { + arr[i] = v + } + return redis.Int(conn.Do("DEL", arr...)) +} + +// GET get k-v +func (r *RedisClient) GET(key string) (string, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.String(conn.Do("GET", key)) +} + +// SET set k-v +func (r *RedisClient) SET(key string, value string) (int64, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.Int64(conn.Do("SET", key, value)) +} + +// SETEX set k-v expire seconds +func (r *RedisClient) SETEX(key string, sec int, value string) (int64, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.Int64(conn.Do("SETEX", key, sec, value)) +} + +// EXPIRE set key expire seconds +func (r *RedisClient) EXPIRE(key string, sec int64) (int64, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.Int64(conn.Do("EXPIRE", key, sec)) +} + +// HGETALL get map of key +func (r *RedisClient) HGETALL(key string) (map[string]string, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.StringMap(conn.Do("HGETALL", key)) +} + +// HGET get value of key-field +func (r *RedisClient) HGET(key string, field string) (string, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.String(conn.Do("HGET", key, field)) +} + +// HSET set value of key-field +func (r *RedisClient) HSET(key string, field string, value string) (int64, error) { + conn := r.pool.Get() + defer conn.Close() + return redis.Int64(conn.Do("HSET", key, field, value)) +} diff --git a/pkg/resp/index.go b/pkg/resp/index.go new file mode 100644 index 0000000..83ad2ea --- /dev/null +++ b/pkg/resp/index.go @@ -0,0 +1,102 @@ +package resp + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +// Response 数据返回结构体 +type Response struct { + Status int `json:"status"` // 返回状态值 + Msg string `json:"msg"` // 返回的提示语 + Data interface{} `json:"data"` // 返回数据 +} + +// Success 正确返回 +func Success(data interface{}, msg ...string) *Response { + response := Response{ + Status: http.StatusOK, + Data: data, + Msg: "操作成功", + } + if len(msg) > 0 { + response.Msg = msg[0] + } + return &response +} + +// ErrorResp 错误返回 +func ErrorResp(data ...interface{}) *Response { + response := Response{ + Status: http.StatusInternalServerError, + Msg: "操作失败", + Data: nil, + } + for _, value := range data { + switch value.(type) { + case string: + response.Msg = value.(string) + case int: + response.Status = value.(int) + case interface{}: + response.Data = value.(interface{}) + } + } + return &response +} + +func Error(c *gin.Context, data ...interface{}) { + response := Response{ + Status: http.StatusInternalServerError, + Msg: "操作失败", + Data: nil, + } + for _, value := range data { + switch value.(type) { + case string: + response.Msg = value.(string) + case int: + response.Status = value.(int) + case interface{}: + response.Data = value.(interface{}) + } + } + c.JSON(http.StatusOK, response) + return +} +func ParamError(c *gin.Context, data ...interface{}) { + response := Response{ + Status: http.StatusInternalServerError, + Msg: "参数绑定异常", + Data: nil, + } + for _, value := range data { + switch value.(type) { + case string: + response.Msg = value.(string) + case int: + response.Status = value.(int) + case interface{}: + response.Data = value.(interface{}) + } + } + c.JSON(http.StatusBadRequest, response) + return +} +func OK(c *gin.Context, data ...interface{}) { + response := Response{ + Status: http.StatusOK, + Msg: "操作成功", + Data: nil, + } + for _, datum := range data { + switch datum.(type) { + case string: + response.Msg = datum.(string) + case interface{}: + response.Data = datum.(interface{}) + } + } + c.JSON(http.StatusOK, response) + return +} diff --git a/pkg/tree/tree_dept/tree_dept.go b/pkg/tree/tree_dept/tree_dept.go new file mode 100644 index 0000000..5779904 --- /dev/null +++ b/pkg/tree/tree_dept/tree_dept.go @@ -0,0 +1,219 @@ +package tree_dept + +import ( + "cutego/core/entity" + "sort" +) + +// Tree 统一定义菜单树的数据结构, 也可以自定义添加其他字段 +type Tree struct { + Id int `json:"id"` + Data interface{} `json:"-"` // 自定义对象 + Label string `json:"label"` + Leaf bool `json:"-"` // 叶子节点 + Selected bool `json:"-"` // 选中 + PartialSelected bool `json:"-"` // 部分选中 + Children []Tree `json:"children"` // 子节点 +} + +// INode 其他的结构体想要生成菜单树, 直接实现这个接口 +type INode interface { + GetId() int + GetLabel() string + GetParentId() int + GetData() interface{} + // IsRoot 判断当前节点是否是顶层根节点 + IsRoot() bool +} +type INodes []INode + +func (nodes INodes) Len() int { + return len(nodes) +} +func (nodes INodes) Swap(i, j int) { + nodes[i], nodes[j] = nodes[j], nodes[i] +} +func (nodes INodes) Less(i, j int) bool { + return nodes[i].GetId() < nodes[j].GetId() +} + +// GenerateTree 自定义的结构体实现 INode 接口后调用此方法生成树结构 +// nodes 需要生成树的节点 +// selectedNode 生成树后选中的节点 +// menuTrees 生成成功后的树结构对象 +func GenerateTree(nodes, selectedNodes []INode) (trees []Tree) { + trees = []Tree{} + // 定义顶层根和子节点 + var roots, childList []INode + for _, v := range nodes { + if v.IsRoot() { + // 判断顶层根节点 + roots = append(roots, v) + } + childList = append(childList, v) + } + + for _, v := range roots { + childTree := &Tree{ + Id: v.GetId(), + Label: v.GetLabel(), + Data: v.GetData(), + } + // 递归之前, 根据父节点确认 childTree 的选中状态 + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) + // 递归 + recursiveTree(childTree, childList, selectedNodes) + // 递归之后, 根据子节点确认 childTree 的选中状态 + if !childTree.Selected { + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) + } + // 递归之后, 根据子节点确认 childTree 的半选中状态 + childTree.PartialSelected = nodePartialSelected(childTree.Children) + // 递归之后, 根据子节确认是否是叶子节点 + childTree.Leaf = len(childTree.Children) == 0 + trees = append(trees, *childTree) + } + return +} + +// recursiveTree 递归生成树结构 +// tree 递归的树对象 +// nodes 递归的节点 +// selectedNodes 选中的节点 +func recursiveTree(tree *Tree, nodes, selectedNodes []INode) { + data := tree.Data.(INode) + + for _, v := range nodes { + if v.IsRoot() { + // 如果当前节点是顶层根节点就跳过 + continue + } + if data.GetId() == v.GetParentId() { + childTree := &Tree{ + Id: v.GetId(), + Label: v.GetLabel(), + Data: v.GetData(), + } + // 递归之前, 根据子节点和父节点确认 childTree 的选中状态 + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) || tree.Selected + recursiveTree(childTree, nodes, selectedNodes) + + if !childTree.Selected { + // 递归之后, 根据子节点确认 childTree 的选中状态 + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) + } + // 递归之后, 根据子节点确认 childTree 的半选中状态 + childTree.PartialSelected = nodePartialSelected(childTree.Children) + // 递归之后, 根据子节确认是否是叶子节点 + childTree.Leaf = len(childTree.Children) == 0 + tree.Children = append(tree.Children, *childTree) + } + } +} + +// FindRelationNode 在 allTree 中查询 nodes 中节点的所有父节点 +// nodes 要查询父节点的子节点数组 +// allTree 所有节点数组 +func FindRelationNode(nodes, allNodes []INode) (respNodes []INode) { + nodeMap := make(map[int]INode) + for _, v := range nodes { + recursiveFindRelationNode(nodeMap, allNodes, v, 0) + } + + for _, v := range nodeMap { + respNodes = append(respNodes, v) + } + sort.Sort(INodes(respNodes)) + return +} + +// recursiveFindRelationNode 递归查询关联父子节点 +// nodeMap 查询结果搜集到map中 +// allNodes 所有节点 +// node 递归节点 +// t 递归查找类型:0 查找父、子节点;1 只查找父节点;2 只查找子节点 +func recursiveFindRelationNode(nodeMap map[int]INode, allNodes []INode, node INode, t int) { + nodeMap[node.GetId()] = node + for _, v := range allNodes { + if _, ok := nodeMap[v.GetId()]; ok { + continue + } + // 查找父节点 + if t == 0 || t == 1 { + if node.GetParentId() == v.GetId() { + nodeMap[v.GetId()] = v + if v.IsRoot() { + // 是顶层根节点时, 不再进行递归 + continue + } + recursiveFindRelationNode(nodeMap, allNodes, v, 1) + } + } + // 查找子节点 + if t == 0 || t == 2 { + if node.GetId() == v.GetParentId() { + nodeMap[v.GetId()] = v + recursiveFindRelationNode(nodeMap, allNodes, v, 2) + } + } + } +} + +// nodeSelected 判断节点的选中状态 +// node 进行判断节点 +func nodeSelected(node INode, selectedNodes []INode, children []Tree) bool { + for _, v := range selectedNodes { + if node.GetId() == v.GetId() { + // 1. 如果选择节点数组中存在当前节点 + return true + } + } + + if len(children) == 0 { + // 2. 不满足前置条件1, 且没有子节点 + return false + } + selectedNum := 0 + for _, v := range children { + if v.Selected { + selectedNum++ + } + } + if selectedNum == len(children) { + // 不满足前置条件1, 2 , 且子节点全部是选中状态 + return true + } + return false +} + +// nodePartialSelected 判断节点的半选中状态 +func nodePartialSelected(trees []Tree) bool { + selectedNum := 0 + for _, v := range trees { + if v.Selected { + selectedNum++ + } + } + if selectedNum == len(trees) || selectedNum == 0 { + // 子节点全选中, 或一个也没有选中 + return false + } + return true +} + +type DeptList []entity.SysDept + +// ConvertToINodeArray 将当前数组转换成父类 INode 接口 数组 +func (s DeptList) ConvertToINodeArray(*[]entity.SysDept) (nodes []INode) { + for _, v := range s { + nodes = append(nodes, v) + } + return +} + +// GetTree 获取树结构 +func (s DeptList) GetTree(treeSelect *[]entity.SysDept) []Tree { + s = *treeSelect + array := s.ConvertToINodeArray(treeSelect) + return GenerateTree(array, nil) +} diff --git a/pkg/tree/tree_menu/tree_menu.go b/pkg/tree/tree_menu/tree_menu.go new file mode 100644 index 0000000..68c12c8 --- /dev/null +++ b/pkg/tree/tree_menu/tree_menu.go @@ -0,0 +1,283 @@ +package tree_menu + +import ( + "cutego/core/entity" + "github.com/druidcaesa/gotool" + "sort" +) + +// Tree 统一定义菜单树的数据结构, 也可以自定义添加其他字段 +type Tree struct { + Name string `json:"name,omitempty"` // 节点名字 + Path string `json:"path,omitempty"` + Hidden bool `json:"hidden"` + Redirect string `json:"redirect,omitempty"` + Component string `json:"component,omitempty"` + AlwaysShow bool `json:"alwaysShow"` + Data interface{} `json:"-"` // 自定义对象 + Meta interface{} `json:"meta"` + Leaf bool `json:"-"` // 叶子节点 + Selected bool `json:"-"` // 选中 + PartialSelected bool `json:"-"` // 部分选中 + Children []Tree `json:"children"` // 子节点 + Id int `json:"id"` + Label string `json:"label"` +} + +// INode 其他的结构体想要生成菜单树, 直接实现这个接口 +type INode interface { + // GetName 获取显示名字 + GetName() string + // GetMenuId 获取id + GetMenuId() int + // GetParentId 获取父id + GetParentId() int + // GetData 获取附加数据 + GetData() interface{} + // IsRoot 判断当前节点是否是顶层根节点 + IsRoot() bool + // GetPath 路径 + GetPath() string + GetId() int + GetLabel() string +} +type INodes []INode + +func (nodes INodes) Len() int { + return len(nodes) +} +func (nodes INodes) Swap(i, j int) { + nodes[i], nodes[j] = nodes[j], nodes[i] +} +func (nodes INodes) Less(i, j int) bool { + return nodes[i].GetMenuId() < nodes[j].GetMenuId() +} + +// GenerateTree 自定义的结构体实现 INode 接口后调用此方法生成树结构 +// nodes 需要生成树的节点 +// selectedNode 生成树后选中的节点 +// menuTrees 生成成功后的树结构对象 +func GenerateTree(nodes, selectedNodes []INode) (trees []Tree) { + trees = []Tree{} + // 定义顶层根和子节点 + var roots, childs []INode + for _, v := range nodes { + if v.IsRoot() { + // 判断顶层根节点 + roots = append(roots, v) + } + childs = append(childs, v) + } + + for _, v := range roots { + var flag = false + var component = "Layout" + m := make(map[string]interface{}) + if v.GetData() != nil { + menu := v.GetData().(entity.SysMenu) + m["title"] = menu.MenuName + m["icon"] = menu.Icon + m["noCache"] = menu.IsCache == 1 + if menu.IsFrame == 0 { + m["link"] = menu.Path + } else { + m["link"] = nil + } + flag = menu.Visible == "1" + if !gotool.StrUtils.HasEmpty(menu.Component) { + component = menu.Component + } + } + childTree := &Tree{ + Name: v.GetName(), + Data: v.GetData(), + Path: "/" + v.GetPath(), + Hidden: flag, + AlwaysShow: true, + Redirect: "noRedirect", + Meta: m, + Component: component, + Id: v.GetId(), + Label: v.GetLabel(), + } + // 递归之前, 根据父节点确认 childTree 的选中状态 + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) + // 递归 + recursiveTree(childTree, childs, selectedNodes) + // 递归之后, 根据子节点确认 childTree 的选中状态 + if !childTree.Selected { + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) + } + // 递归之后, 根据子节点确认 childTree 的半选中状态 + childTree.PartialSelected = nodePartialSelected(childTree.Children) + // 递归之后, 根据子节确认是否是叶子节点 + childTree.Leaf = len(childTree.Children) == 0 + trees = append(trees, *childTree) + } + return +} + +// recursiveTree 递归生成树结构 +// tree 递归的树对象 +// nodes 递归的节点 +// selectedNodes 选中的节点 +func recursiveTree(tree *Tree, nodes, selectedNodes []INode) { + data := tree.Data.(INode) + + for _, v := range nodes { + if v.IsRoot() { + // 如果当前节点是顶层根节点就跳过 + continue + } + var flag = false + var component = "Layout" + m := make(map[string]interface{}) + if v.GetData() != nil { + menu := v.GetData().(entity.SysMenu) + flag = menu.Visible == "1" + m["title"] = menu.MenuName + m["icon"] = menu.Icon + m["noCache"] = menu.IsCache == 1 + if menu.IsFrame == 0 { + m["link"] = menu.Path + } else { + m["link"] = nil + } + if !gotool.StrUtils.HasEmpty(menu.Component) { + component = menu.Component + } + } + if data.GetMenuId() == v.GetParentId() { + childTree := &Tree{ + Name: v.GetName(), + Data: v.GetData(), + Path: v.GetPath(), + Hidden: flag, + Meta: m, + Component: component, + Id: v.GetId(), + Label: v.GetLabel(), + } + // 递归之前, 根据子节点和父节点确认 childTree 的选中状态 + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) || tree.Selected + recursiveTree(childTree, nodes, selectedNodes) + + if !childTree.Selected { + // 递归之后, 根据子节点确认 childTree 的选中状态 + childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) + } + // 递归之后, 根据子节点确认 childTree 的半选中状态 + childTree.PartialSelected = nodePartialSelected(childTree.Children) + // 递归之后, 根据子节确认是否是叶子节点 + childTree.Leaf = len(childTree.Children) == 0 + tree.Children = append(tree.Children, *childTree) + } + } +} + +// FindRelationNode 在 allTree 中查询 nodes 中节点的所有父节点 +// nodes 要查询父节点的子节点数组 +// allTree 所有节点数组 +func FindRelationNode(nodes, allNodes []INode) (respNodes []INode) { + nodeMap := make(map[int]INode) + for _, v := range nodes { + recursiveFindRelationNode(nodeMap, allNodes, v, 0) + } + + for _, v := range nodeMap { + respNodes = append(respNodes, v) + } + sort.Sort(INodes(respNodes)) + return +} + +// recursiveFindRelationNode 递归查询关联父子节点 +// nodeMap 查询结果搜集到map中 +// allNodes 所有节点 +// node 递归节点 +// t 递归查找类型:0 查找父、子节点;1 只查找父节点;2 只查找子节点 +func recursiveFindRelationNode(nodeMap map[int]INode, allNodes []INode, node INode, t int) { + nodeMap[node.GetMenuId()] = node + for _, v := range allNodes { + if _, ok := nodeMap[v.GetMenuId()]; ok { + continue + } + // 查找父节点 + if t == 0 || t == 1 { + if node.GetParentId() == v.GetMenuId() { + nodeMap[v.GetMenuId()] = v + if v.IsRoot() { + // 是顶层根节点时, 不再进行递归 + continue + } + recursiveFindRelationNode(nodeMap, allNodes, v, 1) + } + } + // 查找子节点 + if t == 0 || t == 2 { + if node.GetMenuId() == v.GetParentId() { + nodeMap[v.GetMenuId()] = v + recursiveFindRelationNode(nodeMap, allNodes, v, 2) + } + } + } +} + +// nodeSelected 判断节点的选中状态 +// node 进行判断节点 +func nodeSelected(node INode, selectedNodes []INode, children []Tree) bool { + for _, v := range selectedNodes { + if node.GetMenuId() == v.GetMenuId() { + // 1. 如果选择节点数组中存在当前节点 + return true + } + } + + if len(children) == 0 { + // 2. 不满足前置条件1, 且没有子节点 + return false + } + selectedNum := 0 + for _, v := range children { + if v.Selected { + selectedNum++ + } + } + if selectedNum == len(children) { + // 不满足前置条件1, 2 , 且子节点全部是选中状态 + return true + } + return false +} + +// nodePartialSelected 判断节点的半选中状态 +func nodePartialSelected(trees []Tree) bool { + selectedNum := 0 + for _, v := range trees { + if v.Selected { + selectedNum++ + } + } + if selectedNum == len(trees) || selectedNum == 0 { + // 子节点全选中, 或一个也没有选中 + return false + } + return true +} + +type SystemMenus []entity.SysMenu + +// ConvertToINodeArray 将当前数组转换成父类 INode 接口 数组 +func (s SystemMenus) ConvertToINodeArray(*[]entity.SysMenu) (nodes []INode) { + for _, v := range s { + nodes = append(nodes, v) + } + return +} + +// GetTree 获取树结构数据 +func (s SystemMenus) GetTree(m *[]entity.SysMenu) []Tree { + s = *m + array := s.ConvertToINodeArray(m) + return GenerateTree(array, nil) +} diff --git a/pkg/util/token_util.go b/pkg/util/token_util.go new file mode 100644 index 0000000..5f72dec --- /dev/null +++ b/pkg/util/token_util.go @@ -0,0 +1,58 @@ +package util + +import ( + "cutego/core/api/v1/response" + "cutego/core/dao" + "cutego/core/entity" + "cutego/pkg/common" + "cutego/pkg/config" + "cutego/pkg/jwt" + "github.com/gin-gonic/gin" + "strings" +) + +type UserUtils struct { +} + +// GetUserInfo 通过jwt获取当前登录用户 +func GetUserInfo(c *gin.Context) *response.UserResponse { + token := c.Request.Header.Get("Authorization") + s := strings.Split(token, " ") + // parseToken 解析token包含的信息 + claims, err := jwt.ParseToken(s[1]) + if err != nil { + common.ErrorLog(err) + } + info := claims.UserInfo + return &info +} + +// CheckLockToken 校验多终端登录锁 +func CheckLockToken(c *gin.Context) bool { + if config.AppEnvConfig.Login.Single { + // 获取redis中的token数据 + info := GetUserInfo(c) + get, err := dao.RedisDB.GET(info.UserName) + if err != nil { + common.ErrorLog(err) + return false + } + token := c.Request.Header.Get(config.AppEnvConfig.Jwt.Header) + s := strings.Split(token, " ") + if get == s[1] { + return true + } else { + return false + } + } + return true +} + +// CheckIsAdmin 判断是否是超级管理员 +func CheckIsAdmin(user *entity.SysUser) bool { + if user.UserId == 1 { + return true + } else { + return false + } +} diff --git a/pkg/websocket/index.go b/pkg/websocket/index.go new file mode 100644 index 0000000..a7f6b11 --- /dev/null +++ b/pkg/websocket/index.go @@ -0,0 +1,106 @@ +package websocket + +import ( + "cutego/pkg/common" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "net/http" +) + +// 连接例子 +// + +// 定义回调接口(消息类型、内容) +type OnReceiveMessage func(messageType int, content []byte) error + +// 分割符号 +const SignalSplitSymbol = "=_=" + +// 用户名 <--> websocket通道 +var OnlineUserMap = make(map[string]*websocket.Conn) + +var upGrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +// webSocket请求ping 返回pong +func HandleWebSocketMessage(c *gin.Context) { + userValue, userExist := c.GetQuery("user") + codeValue, codeExist := c.GetQuery("code") + + if !userExist && !codeExist { + return + } + + // 升级get请求为webSocket协议 + ws, err := upGrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + return + } + // defer ws.Close() + cacheKey := userValue + SignalSplitSymbol + codeValue + // 如果存在则踢掉之前的通道 + if OnlineUserMap[cacheKey] != nil { + // 函数结束后关闭 + tmpConn := OnlineUserMap[cacheKey] + defer tmpConn.Close() + } + OnlineUserMap[cacheKey] = ws + // 回收监听消息 + go ListenWebSocketMessage(userValue, codeValue, HandleAdminNotice) +} + +// 回调函数的具体实现 +func HandleAdminNotice(messageType int, content []byte) error { + common.InfoLog("messageType=%d\n", messageType) + common.InfoLog("content=%s\n", string(content)) + return nil +} + +// 监听到消息 +// onReceiveMessage为函数提供的回调接口, 让外部去实现 +func ListenWebSocketMessage(user string, code string, onReceiveMessage OnReceiveMessage) { + cacheKey := user + SignalSplitSymbol + code + ws := OnlineUserMap[cacheKey] + if ws != nil { + for { + // 读取ws中的数据 + mt, message, err := ws.ReadMessage() + if err != nil { + break + } + // 处理心跳 + if string(message) == "ping" { + message = []byte("pong") + // 写入ws数据 + err = ws.WriteMessage(mt, message) + if err != nil { + break + } + continue + } + // 处理接收到的数据 + err = onReceiveMessage(mt, message) + if err != nil { + break + } + } + } +}