mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 04:39:00 +08:00
Merge branch 'dev'
This commit is contained in:
commit
b9268e4393
525 files changed
+7448
-5836
No files matched your search
@@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
## Project Overview
|
||||
|
||||
PigX is a proprietary enterprise-grade microservices platform built on Spring Cloud Alibaba, focusing on AI knowledge
|
||||
management, document processing, and intelligent Q&A capabilities. The current version is 5.10.0.
|
||||
management, document processing, and intelligent Q&A capabilities. The current version is 5.11.0.
|
||||
|
||||
**Important**: This is NOT an open-source project. All rights reserved by the author. Commercial use requires proper
|
||||
licensing.
|
||||
@@ -13,8 +13,8 @@ licensing.
|
||||
## Technology Stack
|
||||
|
||||
- **Java Version**: Java 17
|
||||
- **Spring Boot**: 3.5.6
|
||||
- **Spring Cloud**: 2025.0.0
|
||||
- **Spring Boot**: 3.5.9
|
||||
- **Spring Cloud**: 2025.0.1
|
||||
- **Spring Cloud Alibaba**: 2023.0.3.3
|
||||
- **AI/ML Libraries**:
|
||||
- LangChain4j: 1.6.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
|
||||
协议和授权
|
||||
pigX并非一个开源软件,作者保留全部的权利。
|
||||
|
||||
+4
-3
@@ -591,7 +591,7 @@ INSERT INTO `sys_menu` VALUES (2210,'参数管理',NULL,'/admin/param/index',NUL
|
||||
INSERT INTO `sys_menu` VALUES (2211,'参数新增','sys_syspublicparam_add',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:36',' ','2020-03-24 08:57:11','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2212,'参数删除','sys_syspublicparam_del',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:55',' ','2020-03-24 08:57:12','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2213,'参数编辑','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_view',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2300,'代码生成',NULL,'/gen/table/index',NULL,9000,'iconfont icon-daimashengcheng','1',2,'0','0','0','','2018-01-20 13:17:19','admin','2023-02-20 13:54:35','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2400,'终端管理',NULL,'/admin/client/index',NULL,2000,'iconfont icon-shouji','1',9,'1',NULL,'0','','2018-01-20 13:17:19','admin','2023-02-16 15:25:28','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2401,'客户端新增','sys_client_add',NULL,NULL,2400,'1','1',1,'0',NULL,'1',' ','2018-05-15 21:35:18',' ','2021-05-25 03:12:55','0',1);
|
||||
@@ -1311,7 +1311,7 @@ CREATE TABLE `sys_tenant` (
|
||||
-- Records of sys_tenant
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `sys_tenant` VALUES (1, '北京分公司', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250);
|
||||
INSERT INTO `sys_tenant` VALUES (1, 'PIGX ADMIN', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250);
|
||||
COMMIT;
|
||||
|
||||
|
||||
@@ -1344,6 +1344,7 @@ CREATE TABLE `sys_user` (
|
||||
`osc_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开源中国标识',
|
||||
`wx_cp_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '企业微信唯一ID',
|
||||
`wx_ding_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '钉钉唯一ID',
|
||||
`dept_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '主部门ID',
|
||||
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户ID',
|
||||
PRIMARY KEY (`user_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
|
||||
@@ -1352,7 +1353,7 @@ CREATE TABLE `sys_user` (
|
||||
-- Records of sys_user
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1);
|
||||
INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1,1);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
+4
-3
@@ -573,7 +573,7 @@ INSERT INTO `sys_menu` VALUES (2210,'参数管理',NULL,'/admin/param/index',NUL
|
||||
INSERT INTO `sys_menu` VALUES (2211,'参数新增','sys_syspublicparam_add',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:36',' ','2020-03-24 08:57:11','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2212,'参数删除','sys_syspublicparam_del',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:55',' ','2020-03-24 08:57:12','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2213,'参数编辑','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_view',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2300,'代码生成',NULL,'/gen/table/index',NULL,9000,'iconfont icon-daimashengcheng','1',2,'0','0','0','','2018-01-20 13:17:19','admin','2023-02-20 13:54:35','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2400,'终端管理',NULL,'/admin/client/index',NULL,2000,'iconfont icon-shouji','1',9,'1',NULL,'0','','2018-01-20 13:17:19','admin','2023-02-16 15:25:28','0',1);
|
||||
INSERT INTO `sys_menu` VALUES (2401,'客户端新增','sys_client_add',NULL,NULL,2400,'1','1',1,'0',NULL,'1',' ','2018-05-15 21:35:18',' ','2021-05-25 03:12:55','0',1);
|
||||
@@ -1295,7 +1295,7 @@ CREATE TABLE `sys_tenant` (
|
||||
-- Records of sys_tenant
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `sys_tenant` VALUES (1, '北京分公司', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250);
|
||||
INSERT INTO `sys_tenant` VALUES (1, 'PIGX ADMIN', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250);
|
||||
COMMIT;
|
||||
|
||||
|
||||
@@ -1328,6 +1328,7 @@ CREATE TABLE `sys_user` (
|
||||
`osc_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开源中国标识',
|
||||
`wx_cp_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '企业微信唯一ID',
|
||||
`wx_ding_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '钉钉唯一ID',
|
||||
`dept_id` bigint(20) DEFAULT NULL COMMENT '主部门ID',
|
||||
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户ID',
|
||||
PRIMARY KEY (`user_id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
|
||||
@@ -1336,7 +1337,7 @@ CREATE TABLE `sys_user` (
|
||||
-- Records of sys_user
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1);
|
||||
INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1,1);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-app-server</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-app-server-api</artifactId>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.pig4cloud.pigx.app.api.enums;
|
||||
|
||||
/**
|
||||
* App Server 模块错误编码
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2022/3/30
|
||||
*/
|
||||
public interface AppErrorCodes {
|
||||
|
||||
// === App 用户相关 ===
|
||||
String APP_USER_USERINFO_EMPTY = "app.user.userInfo.empty";
|
||||
|
||||
String APP_USER_USERNAME_EXISTING = "app.user.username.existing";
|
||||
|
||||
String APP_USER_IMPORT_SUCCEED = "app.user.import.succeed";
|
||||
|
||||
String APP_USER_QUERY_ERROR = "app.user.query.error";
|
||||
|
||||
// === App 角色相关 ===
|
||||
String APP_ROLE_ROLENAME_INEXISTENCE = "app.role.roleName.inexistence";
|
||||
|
||||
String APP_ROLE_NAMEORCODE_EXISTING = "app.role.nameOrCode.existing";
|
||||
|
||||
// === App 短信相关 ===
|
||||
String APP_SMS_OFTEN = "app.sms.often";
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# App Server Module i18n Messages - English
|
||||
|
||||
# App user related
|
||||
app.user.userInfo.empty=App user information is empty for {0}
|
||||
app.user.username.existing=Username {0} already exists
|
||||
app.user.import.succeed=User import succeeded; default password is the phone number
|
||||
app.user.query.error=Failed to fetch current user information
|
||||
|
||||
# App role related
|
||||
app.role.roleName.inexistence=Role name {0} does not exist
|
||||
app.role.nameOrCode.existing=Role name or code already exists ({0} {1})
|
||||
|
||||
# App SMS related
|
||||
app.sms.often=Verification code sent too frequently
|
||||
@@ -0,0 +1,14 @@
|
||||
# App Server 模块国际化消息 - 中文
|
||||
|
||||
# App 用户相关
|
||||
app.user.userInfo.empty={0} \u7528\u6237\u4FE1\u606F\u4E3A\u7A7A
|
||||
app.user.username.existing={0} \u7528\u6237\u540D\u5DF2\u5B58\u5728
|
||||
app.user.import.succeed=\u7528\u6237\u5BFC\u5165\u6210\u529F\uFF0C\u9ED8\u8BA4\u5BC6\u7801\u4E3A\u624B\u673A\u53F7
|
||||
app.user.query.error=\u83B7\u53D6\u5F53\u524D\u7528\u6237\u4FE1\u606F\u5931\u8D25
|
||||
|
||||
# App 角色相关
|
||||
app.role.roleName.inexistence={0} \u89D2\u8272\u540D\u79F0\u4E0D\u5B58\u5728
|
||||
app.role.nameOrCode.existing={0} {1} \u89D2\u8272\u540D\u6216\u89D2\u8272\u7F16\u7801\u5DF2\u7ECF\u5B58\u5728
|
||||
|
||||
# App 短信相关
|
||||
app.sms.often=\u9A8C\u8BC1\u7801\u53D1\u9001\u8FC7\u9891\u7E41
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-app-server</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-app-server-biz</artifactId>
|
||||
@@ -112,9 +112,6 @@
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<loaderImplementation>CLASSIC</loaderImplementation>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+143
-135
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -21,11 +21,11 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pigx.app.api.dto.AppUserDTO;
|
||||
import com.pig4cloud.pigx.app.api.entity.AppUser;
|
||||
import com.pig4cloud.pigx.app.api.enums.AppErrorCodes;
|
||||
import com.pig4cloud.pigx.app.api.vo.AppUserExcelVO;
|
||||
import com.pig4cloud.pigx.app.service.AppUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.exception.ErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
|
||||
@@ -58,150 +58,158 @@ import java.util.List;
|
||||
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
||||
public class AppUserController {
|
||||
|
||||
private final AppUserService appUserService;
|
||||
private final AppUserService appUserService;
|
||||
|
||||
@Inner
|
||||
@GetMapping("/info/{username}")
|
||||
public R info(@PathVariable String username) {
|
||||
AppUser user = appUserService.getOne(Wrappers.<AppUser>query()
|
||||
.lambda()
|
||||
.eq(AppUser::getUsername, username)
|
||||
.or()
|
||||
.eq(AppUser::getPhone, username));
|
||||
if (user == null) {
|
||||
return R.failed(MsgUtils.getMessage(ErrorCodes.APP_USER_USERINFO_EMPTY, username));
|
||||
}
|
||||
return R.ok(appUserService.findUserInfo(user));
|
||||
}
|
||||
@Inner
|
||||
@GetMapping("/info/{username}")
|
||||
public R info(@PathVariable String username) {
|
||||
AppUser user = appUserService.getOne(Wrappers.<AppUser>query()
|
||||
.lambda()
|
||||
.eq(AppUser::getUsername, username)
|
||||
.or()
|
||||
.eq(AppUser::getPhone, username));
|
||||
if (user == null) {
|
||||
return R.failed(MsgUtils.getMessage(AppErrorCodes.APP_USER_USERINFO_EMPTY, username));
|
||||
}
|
||||
return R.ok(appUserService.findUserInfo(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户全部信息
|
||||
* @return 用户信息
|
||||
*/
|
||||
@GetMapping(value = { "/info" })
|
||||
public R info() {
|
||||
String username = SecurityUtils.getUser().getUsername();
|
||||
AppUser user = appUserService.getOne(Wrappers.<AppUser>query().lambda().eq(AppUser::getUsername, username));
|
||||
if (user == null) {
|
||||
return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_QUERY_ERROR));
|
||||
}
|
||||
return R.ok(appUserService.findUserInfo(user));
|
||||
}
|
||||
/**
|
||||
* 获取当前用户全部信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
@GetMapping(value = {"/info"})
|
||||
public R info() {
|
||||
String username = SecurityUtils.getUser().getUsername();
|
||||
AppUser user = appUserService.getOne(Wrappers.<AppUser>query().lambda().eq(AppUser::getUsername, username));
|
||||
if (user == null) {
|
||||
return R.failed(MsgUtils.getMessage(AppErrorCodes.APP_USER_QUERY_ERROR));
|
||||
}
|
||||
return R.ok(appUserService.findUserInfo(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param page 分页对象
|
||||
* @param appUserDTO app用户表
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "分页查询", description = "分页查询")
|
||||
@GetMapping("/page")
|
||||
public R getAppUserPage(Page page, AppUserDTO appUserDTO) {
|
||||
return R.ok(appUserService.getUsersWithRolePage(page, appUserDTO));
|
||||
}
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param appUserDTO app用户表
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "分页查询", description = "分页查询")
|
||||
@GetMapping("/page")
|
||||
public R getAppUserPage(Page page, AppUserDTO appUserDTO) {
|
||||
return R.ok(appUserService.getUsersWithRolePage(page, appUserDTO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询app用户表
|
||||
* @param userId id
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过id查询", description = "通过id查询")
|
||||
@GetMapping("/details/{userId}")
|
||||
public R getById(@PathVariable("userId") Long userId) {
|
||||
return R.ok(appUserService.selectUserVoById(userId));
|
||||
}
|
||||
/**
|
||||
* 通过id查询app用户表
|
||||
*
|
||||
* @param userId id
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过id查询", description = "通过id查询")
|
||||
@GetMapping("/details/{userId}")
|
||||
public R getById(@PathVariable("userId") Long userId) {
|
||||
return R.ok(appUserService.selectUserVoById(userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过userName查询", description = "通过userName查询")
|
||||
@GetMapping("/details")
|
||||
public R getByUserName(AppUser user) {
|
||||
AppUser one = appUserService.getOne(Wrappers.query(user), false);
|
||||
return R.ok(one == null ? null : CommonConstants.SUCCESS);
|
||||
}
|
||||
/**
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过userName查询", description = "通过userName查询")
|
||||
@GetMapping("/details")
|
||||
public R getByUserName(AppUser user) {
|
||||
AppUser one = appUserService.getOne(Wrappers.query(user), false);
|
||||
return R.ok(one == null ? null : CommonConstants.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增app用户表
|
||||
* @param appUser app用户表
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "新增app用户表", description = "新增app用户表")
|
||||
@SysLog("新增app用户表")
|
||||
@PostMapping
|
||||
@HasPermission("app_appuser_add")
|
||||
public R save(@RequestBody AppUserDTO appUser) {
|
||||
appUserService.saveUser(appUser);
|
||||
return R.ok();
|
||||
}
|
||||
/**
|
||||
* 新增app用户表
|
||||
*
|
||||
* @param appUser app用户表
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "新增app用户表", description = "新增app用户表")
|
||||
@SysLog("新增app用户表")
|
||||
@PostMapping
|
||||
@HasPermission("app_appuser_add")
|
||||
public R save(@RequestBody AppUserDTO appUser) {
|
||||
appUserService.saveUser(appUser);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改app用户表
|
||||
* @param appUser app用户表
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "修改app用户表", description = "修改app用户表")
|
||||
@SysLog("修改app用户表")
|
||||
@PutMapping
|
||||
@HasPermission("app_appuser_edit")
|
||||
public R updateById(@RequestBody AppUserDTO appUser) {
|
||||
return R.ok(appUserService.updateUser(appUser));
|
||||
}
|
||||
/**
|
||||
* 修改app用户表
|
||||
*
|
||||
* @param appUser app用户表
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "修改app用户表", description = "修改app用户表")
|
||||
@SysLog("修改app用户表")
|
||||
@PutMapping
|
||||
@HasPermission("app_appuser_edit")
|
||||
public R updateById(@RequestBody AppUserDTO appUser) {
|
||||
return R.ok(appUserService.updateUser(appUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除app用户表
|
||||
* @param ids userIds
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过ids删除app用户表", description = "通过ids删除app用户表")
|
||||
@SysLog("通过id删除app用户表")
|
||||
@DeleteMapping
|
||||
@HasPermission("app_appuser_del")
|
||||
public R removeById(@RequestBody Long[] ids) {
|
||||
return R.ok(appUserService.deleteAppUserByIds(ids));
|
||||
}
|
||||
/**
|
||||
* 通过id删除app用户表
|
||||
*
|
||||
* @param ids userIds
|
||||
* @return R
|
||||
*/
|
||||
@Operation(summary = "通过ids删除app用户表", description = "通过ids删除app用户表")
|
||||
@SysLog("通过id删除app用户表")
|
||||
@DeleteMapping
|
||||
@HasPermission("app_appuser_del")
|
||||
public R removeById(@RequestBody Long[] ids) {
|
||||
return R.ok(appUserService.deleteAppUserByIds(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel 表格
|
||||
* @param appUser 查询条件
|
||||
* @return excel 文件流
|
||||
*/
|
||||
@ResponseExcel
|
||||
@GetMapping("/export")
|
||||
@HasPermission("app_appuser_export")
|
||||
public List<AppUserExcelVO> export(AppUserDTO appUser) {
|
||||
return appUserService.listUser(appUser);
|
||||
}
|
||||
/**
|
||||
* 导出excel 表格
|
||||
*
|
||||
* @param appUser 查询条件
|
||||
* @return excel 文件流
|
||||
*/
|
||||
@ResponseExcel
|
||||
@GetMapping("/export")
|
||||
@HasPermission("app_appuser_export")
|
||||
public List<AppUserExcelVO> export(AppUserDTO appUser) {
|
||||
return appUserService.listUser(appUser);
|
||||
}
|
||||
|
||||
@SysLog("修改个人信息")
|
||||
@PutMapping("/edit")
|
||||
public R updateUserInfo(@Valid @RequestBody AppUserDTO userDto) {
|
||||
if (UserTypeEnum.TOC.getStatus().equals(SecurityUtils.getUser().getUserType())) {
|
||||
userDto.setUserId(SecurityUtils.getUser().getId());
|
||||
// TOC 以手机号为key
|
||||
userDto.setUsername(SecurityUtils.getUser().getUsername());
|
||||
}
|
||||
return appUserService.updateUserInfo(userDto);
|
||||
}
|
||||
@SysLog("修改个人信息")
|
||||
@PutMapping("/edit")
|
||||
public R updateUserInfo(@Valid @RequestBody AppUserDTO userDto) {
|
||||
if (UserTypeEnum.TOC.getStatus().equals(SecurityUtils.getUser().getUserType())) {
|
||||
userDto.setUserId(SecurityUtils.getUser().getId());
|
||||
// TOC 以手机号为key
|
||||
userDto.setUsername(SecurityUtils.getUser().getUsername());
|
||||
}
|
||||
return appUserService.updateUserInfo(userDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入用户
|
||||
* @param excelVOList 用户列表
|
||||
* @param bindingResult 错误信息列表
|
||||
* @return R
|
||||
*/
|
||||
@PostMapping("/import")
|
||||
@HasPermission("app_appuser_export")
|
||||
public R importUser(@RequestExcel List<AppUserExcelVO> excelVOList, BindingResult bindingResult) {
|
||||
return appUserService.importUser(excelVOList, bindingResult);
|
||||
}
|
||||
/**
|
||||
* 导入用户
|
||||
*
|
||||
* @param excelVOList 用户列表
|
||||
* @param bindingResult 错误信息列表
|
||||
* @return R
|
||||
*/
|
||||
@PostMapping("/import")
|
||||
@HasPermission("app_appuser_export")
|
||||
public R importUser(@RequestExcel List<AppUserExcelVO> excelVOList, BindingResult bindingResult) {
|
||||
return appUserService.importUser(excelVOList, bindingResult);
|
||||
}
|
||||
|
||||
@Operation(summary = "注册app用户表", description = "注册app用户表")
|
||||
@SysLog("注册app用户表")
|
||||
@Inner(value = false)
|
||||
@PostMapping("/register")
|
||||
public R register(@RequestBody AppUserDTO appUser) {
|
||||
return appUserService.registerAppUser(appUser);
|
||||
}
|
||||
@Operation(summary = "注册app用户表", description = "注册app用户表")
|
||||
@SysLog("注册app用户表")
|
||||
@Inner(value = false)
|
||||
@PostMapping("/register")
|
||||
public R register(@RequestBody AppUserDTO appUser) {
|
||||
return appUserService.registerAppUser(appUser);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+20
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -20,11 +20,11 @@ package com.pig4cloud.pigx.app.service.impl;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.pig4cloud.pigx.app.api.enums.AppErrorCodes;
|
||||
import com.pig4cloud.pigx.app.service.AppMobileService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.exception.ErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.data.cache.RedisUtils;
|
||||
@@ -43,24 +43,25 @@ import org.springframework.stereotype.Service;
|
||||
@AllArgsConstructor
|
||||
public class AppMobileServiceImpl implements AppMobileService {
|
||||
|
||||
/**
|
||||
* 发送手机验证码 TODO: 调用短信网关发送验证码,测试返回前端
|
||||
* @param mobile mobile
|
||||
* @return code
|
||||
*/
|
||||
@Override
|
||||
public R<Boolean> sendSmsCode(String mobile) {
|
||||
String codeObj = RedisUtils.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile);
|
||||
/**
|
||||
* 发送手机验证码 TODO: 调用短信网关发送验证码,测试返回前端
|
||||
*
|
||||
* @param mobile mobile
|
||||
* @return code
|
||||
*/
|
||||
@Override
|
||||
public R<Boolean> sendSmsCode(String mobile) {
|
||||
String codeObj = RedisUtils.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile);
|
||||
|
||||
if (StrUtil.isNotBlank(codeObj)) {
|
||||
log.info("手机号验证码未过期:{},{}", mobile, codeObj);
|
||||
return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_OFTEN));
|
||||
}
|
||||
if (StrUtil.isNotBlank(codeObj)) {
|
||||
log.info("手机号验证码未过期:{},{}", mobile, codeObj);
|
||||
return R.ok(Boolean.FALSE, MsgUtils.getMessage(AppErrorCodes.APP_SMS_OFTEN));
|
||||
}
|
||||
|
||||
String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE));
|
||||
log.debug("手机号生成验证码成功:{},{}", mobile, code);
|
||||
RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile, code, SecurityConstants.CODE_TIME);
|
||||
return R.ok(Boolean.TRUE);
|
||||
}
|
||||
String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE));
|
||||
log.debug("手机号生成验证码成功:{},{}", mobile, code);
|
||||
RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile, code, SecurityConstants.CODE_TIME);
|
||||
return R.ok(Boolean.TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
+75
-64
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -17,16 +17,20 @@
|
||||
package com.pig4cloud.pigx.app.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
|
||||
import com.pig4cloud.pigx.app.api.entity.AppRole;
|
||||
import com.pig4cloud.pigx.app.api.enums.AppErrorCodes;
|
||||
import com.pig4cloud.pigx.app.api.enums.PageTypeEnums;
|
||||
import com.pig4cloud.pigx.app.api.vo.AppRoleExcelVO;
|
||||
import com.pig4cloud.pigx.app.mapper.AppPageMapper;
|
||||
import com.pig4cloud.pigx.app.mapper.AppRoleMapper;
|
||||
import com.pig4cloud.pigx.app.service.AppRoleService;
|
||||
import com.pig4cloud.pigx.common.core.exception.ErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
||||
@@ -35,79 +39,86 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* app角色表
|
||||
* APP角色服务实现类
|
||||
* <p>
|
||||
* 提供对APP角色相关的业务操作实现。
|
||||
*
|
||||
* @author aeizzz
|
||||
* @date 2022-12-07 09:52:03
|
||||
* @author lengleng
|
||||
* @date 2025/05/29
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class AppRoleServiceImpl extends ServiceImpl<AppRoleMapper, AppRole> implements AppRoleService {
|
||||
|
||||
@Override
|
||||
public List<AppRole> findRolesByUserId(Long userId) {
|
||||
return baseMapper.listRolesByUserId(userId);
|
||||
}
|
||||
private final AppPageMapper appPageMapper;
|
||||
|
||||
/**
|
||||
* 删除用户的同时,把role_menu关系删除
|
||||
* @param ids roleIds
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteRoleByIds(Long[] ids) {
|
||||
this.removeBatchByIds(CollUtil.toList(ids));
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
private final AppRoleMapper appRoleMapper;
|
||||
|
||||
/**
|
||||
* 导入角色
|
||||
* @param excelVOList
|
||||
* @param bindingResult
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public R importRole(List<AppRoleExcelVO> excelVOList, BindingResult bindingResult) {
|
||||
// 通用校验获取失败的数据
|
||||
List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
|
||||
@Override
|
||||
public List<AppRole> findRolesByUserId(Long userId) {
|
||||
return baseMapper.listRolesByUserId(userId);
|
||||
}
|
||||
|
||||
// 个性化校验逻辑
|
||||
List<AppRole> roleList = this.list();
|
||||
/**
|
||||
* 删除用户的同时,把role_menu关系删除
|
||||
*
|
||||
* @param ids roleIds
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteRoleByIds(Long[] ids) {
|
||||
this.removeBatchByIds(CollUtil.toList(ids));
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
// 执行数据插入操作 组装 RoleDto
|
||||
for (AppRoleExcelVO excel : excelVOList) {
|
||||
Set<String> errorMsg = new HashSet<>();
|
||||
// 检验角色名称或者角色编码是否存在
|
||||
boolean existRole = roleList.stream()
|
||||
.anyMatch(appRole -> excel.getRoleName().equals(appRole.getRoleName())
|
||||
|| excel.getRoleCode().equals(appRole.getRoleCode()));
|
||||
/**
|
||||
* 导入角色
|
||||
*
|
||||
* @param excelVOList
|
||||
* @param bindingResult
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public R importRole(List<AppRoleExcelVO> excelVOList, BindingResult bindingResult) {
|
||||
// 通用校验获取失败的数据
|
||||
List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
|
||||
|
||||
if (existRole) {
|
||||
errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_NAMEORCODE_EXISTING, excel.getRoleName(),
|
||||
excel.getRoleCode()));
|
||||
}
|
||||
// 个性化校验逻辑
|
||||
List<AppRole> roleList = this.list();
|
||||
|
||||
// 数据合法情况
|
||||
if (CollUtil.isEmpty(errorMsg)) {
|
||||
insertExcelRole(excel);
|
||||
}
|
||||
else {
|
||||
// 数据不合法情况
|
||||
errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(errorMessageList)) {
|
||||
return R.failed(errorMessageList);
|
||||
}
|
||||
return R.ok();
|
||||
// 执行数据插入操作 组装 RoleDto
|
||||
for (AppRoleExcelVO excel : excelVOList) {
|
||||
Set<String> errorMsg = new HashSet<>();
|
||||
// 检验角色名称或者角色编码是否存在
|
||||
boolean existRole = roleList.stream()
|
||||
.anyMatch(appRole -> excel.getRoleName().equals(appRole.getRoleName())
|
||||
|| excel.getRoleCode().equals(appRole.getRoleCode()));
|
||||
|
||||
}
|
||||
if (existRole) {
|
||||
errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_ROLE_NAMEORCODE_EXISTING, excel.getRoleName(),
|
||||
excel.getRoleCode()));
|
||||
}
|
||||
|
||||
private void insertExcelRole(AppRoleExcelVO excel) {
|
||||
AppRole appRole = new AppRole();
|
||||
appRole.setRoleName(excel.getRoleName());
|
||||
appRole.setRoleDesc(excel.getRoleDesc());
|
||||
appRole.setRoleCode(excel.getRoleCode());
|
||||
this.save(appRole);
|
||||
}
|
||||
// 数据合法情况
|
||||
if (CollUtil.isEmpty(errorMsg)) {
|
||||
insertExcelRole(excel);
|
||||
} else {
|
||||
// 数据不合法情况
|
||||
errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(errorMessageList)) {
|
||||
return R.failed(errorMessageList);
|
||||
}
|
||||
return R.ok();
|
||||
|
||||
}
|
||||
|
||||
private void insertExcelRole(AppRoleExcelVO excel) {
|
||||
AppRole appRole = new AppRole();
|
||||
appRole.setRoleName(excel.getRoleName());
|
||||
appRole.setRoleDesc(excel.getRoleDesc());
|
||||
appRole.setRoleCode(excel.getRoleCode());
|
||||
this.save(appRole);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+293
-283
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -29,6 +29,7 @@ import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
|
||||
import com.pig4cloud.pigx.app.api.entity.AppRole;
|
||||
import com.pig4cloud.pigx.app.api.entity.AppUser;
|
||||
import com.pig4cloud.pigx.app.api.entity.AppUserRole;
|
||||
import com.pig4cloud.pigx.app.api.enums.AppErrorCodes;
|
||||
import com.pig4cloud.pigx.app.api.vo.AppUserExcelVO;
|
||||
import com.pig4cloud.pigx.app.api.vo.AppUserVO;
|
||||
import com.pig4cloud.pigx.app.mapper.AppUserMapper;
|
||||
@@ -39,7 +40,6 @@ import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.exception.ErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.data.cache.RedisUtils;
|
||||
@@ -72,328 +72,338 @@ import java.util.Set;
|
||||
@Slf4j
|
||||
public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AppUserService {
|
||||
|
||||
/**
|
||||
* 密码编码器,用于密码加密
|
||||
*/
|
||||
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
|
||||
/**
|
||||
* 密码编码器,用于密码加密
|
||||
*/
|
||||
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
|
||||
|
||||
/**
|
||||
* 用户角色关联服务
|
||||
*/
|
||||
private final AppUserRoleService appUserRoleService;
|
||||
/**
|
||||
* 用户角色关联服务
|
||||
*/
|
||||
private final AppUserRoleService appUserRoleService;
|
||||
|
||||
/**
|
||||
* 角色服务
|
||||
*/
|
||||
private final AppRoleService appRoleService;
|
||||
/**
|
||||
* 角色服务
|
||||
*/
|
||||
private final AppRoleService appRoleService;
|
||||
|
||||
/**
|
||||
* 缓存管理器
|
||||
*/
|
||||
private final CacheManager cacheManager;
|
||||
/**
|
||||
* 缓存管理器
|
||||
*/
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* @param userDTO 用户数据传输对象
|
||||
* @return 更新结果
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username")
|
||||
public Boolean updateUser(AppUserDTO userDTO) {
|
||||
// 创建用户实体并复制属性
|
||||
AppUser appUser = new AppUser();
|
||||
BeanUtils.copyProperties(userDTO, appUser);
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param userDTO 用户数据传输对象
|
||||
* @return 更新结果
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username")
|
||||
public Boolean updateUser(AppUserDTO userDTO) {
|
||||
// 创建用户实体并复制属性
|
||||
AppUser appUser = new AppUser();
|
||||
BeanUtils.copyProperties(userDTO, appUser);
|
||||
|
||||
// 如果密码不为空,进行加密处理
|
||||
if (StrUtil.isNotBlank(userDTO.getPassword())) {
|
||||
appUser.setPassword(ENCODER.encode(userDTO.getPassword()));
|
||||
}
|
||||
// 更新用户基本信息
|
||||
this.updateById(appUser);
|
||||
// 如果密码不为空,进行加密处理
|
||||
if (StrUtil.isNotBlank(userDTO.getPassword())) {
|
||||
appUser.setPassword(ENCODER.encode(userDTO.getPassword()));
|
||||
}
|
||||
// 更新用户基本信息
|
||||
this.updateById(appUser);
|
||||
|
||||
// 删除用户与角色的关联关系
|
||||
appUserRoleService.remove(Wrappers.<AppUserRole>lambdaQuery().eq(AppUserRole::getUserId, appUser.getUserId()));
|
||||
// 重新建立用户与角色的关联关系
|
||||
userDTO.getRole().forEach(roleId -> {
|
||||
AppUserRole appUserRole = new AppUserRole();
|
||||
appUserRole.setRoleId(roleId);
|
||||
appUserRole.setUserId(userDTO.getUserId());
|
||||
appUserRole.insert();
|
||||
});
|
||||
// 删除用户与角色的关联关系
|
||||
appUserRoleService.remove(Wrappers.<AppUserRole>lambdaQuery().eq(AppUserRole::getUserId, appUser.getUserId()));
|
||||
// 重新建立用户与角色的关联关系
|
||||
userDTO.getRole().forEach(roleId -> {
|
||||
AppUserRole appUserRole = new AppUserRole();
|
||||
appUserRole.setRoleId(roleId);
|
||||
appUserRole.setUserId(userDTO.getUserId());
|
||||
appUserRole.insert();
|
||||
});
|
||||
|
||||
// 清除以手机号为key的用户缓存
|
||||
AppUser appUserInfo = baseMapper.selectById(userDTO.getUserId());
|
||||
cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(appUserInfo.getPhone());
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
// 清除以手机号为key的用户缓存
|
||||
AppUser appUserInfo = baseMapper.selectById(userDTO.getUserId());
|
||||
cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(appUserInfo.getPhone());
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户
|
||||
* @param userDTO 用户数据传输对象
|
||||
* @return 保存结果
|
||||
*/
|
||||
@Override
|
||||
public Boolean saveUser(AppUserDTO userDTO) {
|
||||
// 创建用户实体并复制属性
|
||||
AppUser appUser = new AppUser();
|
||||
BeanUtils.copyProperties(userDTO, appUser);
|
||||
// 设置删除标志为正常状态
|
||||
appUser.setDelFlag(CommonConstants.STATUS_NORMAL);
|
||||
// 对密码进行加密
|
||||
appUser.setPassword(ENCODER.encode(userDTO.getPassword()));
|
||||
// 插入用户记录
|
||||
baseMapper.insert(appUser);
|
||||
/**
|
||||
* 新增用户
|
||||
*
|
||||
* @param userDTO 用户数据传输对象
|
||||
* @return 保存结果
|
||||
*/
|
||||
@Override
|
||||
public Boolean saveUser(AppUserDTO userDTO) {
|
||||
// 创建用户实体并复制属性
|
||||
AppUser appUser = new AppUser();
|
||||
BeanUtils.copyProperties(userDTO, appUser);
|
||||
// 设置删除标志为正常状态
|
||||
appUser.setDelFlag(CommonConstants.STATUS_NORMAL);
|
||||
// 对密码进行加密
|
||||
appUser.setPassword(ENCODER.encode(userDTO.getPassword()));
|
||||
// 插入用户记录
|
||||
baseMapper.insert(appUser);
|
||||
|
||||
// 如果角色列表不为空,建立用户与角色的关联关系
|
||||
if (CollUtil.isNotEmpty(userDTO.getRole())) {
|
||||
List<AppUserRole> userRoleList = userDTO.getRole().stream().map(roleId -> {
|
||||
AppUserRole userRole = new AppUserRole();
|
||||
userRole.setUserId(appUser.getUserId());
|
||||
userRole.setRoleId(roleId);
|
||||
return userRole;
|
||||
}).toList();
|
||||
// 批量保存用户角色关联
|
||||
appUserRoleService.saveBatch(userRoleList);
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
// 如果角色列表不为空,建立用户与角色的关联关系
|
||||
if (CollUtil.isNotEmpty(userDTO.getRole())) {
|
||||
List<AppUserRole> userRoleList = userDTO.getRole().stream().map(roleId -> {
|
||||
AppUserRole userRole = new AppUserRole();
|
||||
userRole.setUserId(appUser.getUserId());
|
||||
userRole.setRoleId(roleId);
|
||||
return userRole;
|
||||
}).toList();
|
||||
// 批量保存用户角色关联
|
||||
appUserRoleService.saveBatch(userRoleList);
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询全部用户(用于Excel导出)
|
||||
* @param appUser 查询条件
|
||||
* @return 用户Excel视图对象列表
|
||||
*/
|
||||
@Override
|
||||
public List<AppUserExcelVO> listUser(AppUserDTO appUser) {
|
||||
// 查询所有用户
|
||||
List<AppUser> appUsers = baseMapper.selectList(null);
|
||||
// 将用户实体转换为Excel视图对象
|
||||
List<AppUserExcelVO> appUserExcelVOS = appUsers.stream().map(item -> {
|
||||
AppUserExcelVO appUserExcelVO = new AppUserExcelVO();
|
||||
BeanUtils.copyProperties(item, appUserExcelVO);
|
||||
return appUserExcelVO;
|
||||
}).toList();
|
||||
return appUserExcelVOS;
|
||||
}
|
||||
/**
|
||||
* 查询全部用户(用于Excel导出)
|
||||
*
|
||||
* @param appUser 查询条件
|
||||
* @return 用户Excel视图对象列表
|
||||
*/
|
||||
@Override
|
||||
public List<AppUserExcelVO> listUser(AppUserDTO appUser) {
|
||||
// 查询所有用户
|
||||
List<AppUser> appUsers = baseMapper.selectList(null);
|
||||
// 将用户实体转换为Excel视图对象
|
||||
List<AppUserExcelVO> appUserExcelVOS = appUsers.stream().map(item -> {
|
||||
AppUserExcelVO appUserExcelVO = new AppUserExcelVO();
|
||||
BeanUtils.copyProperties(item, appUserExcelVO);
|
||||
return appUserExcelVO;
|
||||
}).toList();
|
||||
return appUserExcelVOS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询用户(包含角色信息)
|
||||
* @param page 分页参数
|
||||
* @param appUserDTO 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Override
|
||||
public IPage getUsersWithRolePage(Page page, AppUserDTO appUserDTO) {
|
||||
return baseMapper.getUserVosPage(page, appUserDTO);
|
||||
}
|
||||
/**
|
||||
* 分页查询用户(包含角色信息)
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param appUserDTO 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Override
|
||||
public IPage getUsersWithRolePage(Page page, AppUserDTO appUserDTO) {
|
||||
return baseMapper.getUserVosPage(page, appUserDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户详细信息(包含角色和权限)
|
||||
* @param user 用户对象
|
||||
* @return 用户信息对象
|
||||
*/
|
||||
@Override
|
||||
public AppUserInfo findUserInfo(AppUser user) {
|
||||
// 创建用户信息对象
|
||||
AppUserInfo info = new AppUserInfo();
|
||||
info.setAppUser(user);
|
||||
/**
|
||||
* 查询用户详细信息(包含角色和权限)
|
||||
*
|
||||
* @param user 用户对象
|
||||
* @return 用户信息对象
|
||||
*/
|
||||
@Override
|
||||
public AppUserInfo findUserInfo(AppUser user) {
|
||||
// 创建用户信息对象
|
||||
AppUserInfo info = new AppUserInfo();
|
||||
info.setAppUser(user);
|
||||
|
||||
// 设置角色列表(ID数组)
|
||||
List<Long> roleIds = appRoleService.findRolesByUserId(user.getUserId())
|
||||
.stream()
|
||||
.map(AppRole::getRoleId)
|
||||
.toList();
|
||||
info.setRoles(ArrayUtil.toArray(roleIds, Long.class));
|
||||
// 设置角色列表(ID数组)
|
||||
List<Long> roleIds = appRoleService.findRolesByUserId(user.getUserId())
|
||||
.stream()
|
||||
.map(AppRole::getRoleId)
|
||||
.toList();
|
||||
info.setRoles(ArrayUtil.toArray(roleIds, Long.class));
|
||||
|
||||
// 设置权限列表(暂为空)
|
||||
Set<String> permissions = new HashSet<>();
|
||||
info.setPermissions(ArrayUtil.toArray(permissions, String.class));
|
||||
return info;
|
||||
}
|
||||
// 设置权限列表(暂为空)
|
||||
Set<String> permissions = new HashSet<>();
|
||||
info.setPermissions(ArrayUtil.toArray(permissions, String.class));
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户个人信息
|
||||
* @param userDTO 用户数据传输对象
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username")
|
||||
public R updateUserInfo(AppUserDTO userDTO) {
|
||||
// 获取当前登录用户
|
||||
PigxUser user = SecurityUtils.getUser();
|
||||
/**
|
||||
* 更新用户个人信息
|
||||
*
|
||||
* @param userDTO 用户数据传输对象
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username")
|
||||
public R updateUserInfo(AppUserDTO userDTO) {
|
||||
// 获取当前登录用户
|
||||
PigxUser user = SecurityUtils.getUser();
|
||||
|
||||
// C端客户修改手机号需要验证短信验证码
|
||||
if (UserTypeEnum.TOC.getStatus().equals(user.getUserType()) && StrUtil.isNotBlank(userDTO.getPhone())) {
|
||||
// 构建验证码缓存key
|
||||
String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT
|
||||
+ userDTO.getPhone();
|
||||
// 获取缓存中的验证码
|
||||
String codeObj = RedisUtils.get(key);
|
||||
// C端客户修改手机号需要验证短信验证码
|
||||
if (UserTypeEnum.TOC.getStatus().equals(user.getUserType()) && StrUtil.isNotBlank(userDTO.getPhone())) {
|
||||
// 构建验证码缓存key
|
||||
String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT
|
||||
+ userDTO.getPhone();
|
||||
// 获取缓存中的验证码
|
||||
String codeObj = RedisUtils.get(key);
|
||||
|
||||
// 验证码不匹配,返回错误
|
||||
if (!userDTO.getMobileCode().equals(codeObj)) {
|
||||
return R.failed("验证码错误");
|
||||
}
|
||||
}
|
||||
// 验证码不匹配,返回错误
|
||||
if (!userDTO.getMobileCode().equals(codeObj)) {
|
||||
return R.failed("验证码错误");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新密码(如果提供了新密码)
|
||||
if (StrUtil.isNotBlank(userDTO.getPassword())) {
|
||||
userDTO.setPassword(ENCODER.encode(userDTO.getPassword()));
|
||||
}
|
||||
// 更新密码(如果提供了新密码)
|
||||
if (StrUtil.isNotBlank(userDTO.getPassword())) {
|
||||
userDTO.setPassword(ENCODER.encode(userDTO.getPassword()));
|
||||
}
|
||||
|
||||
// 获取原用户信息并更新
|
||||
AppUser appUser = baseMapper.selectById(userDTO.getUserId());
|
||||
BeanUtils.copyProperties(userDTO, appUser);
|
||||
// 获取原用户信息并更新
|
||||
AppUser appUser = baseMapper.selectById(userDTO.getUserId());
|
||||
BeanUtils.copyProperties(userDTO, appUser);
|
||||
|
||||
// 清除以手机号为key的用户缓存
|
||||
cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(user.getPhone());
|
||||
return R.ok(this.updateById(appUser));
|
||||
}
|
||||
// 清除以手机号为key的用户缓存
|
||||
cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(user.getPhone());
|
||||
return R.ok(this.updateById(appUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查询用户视图对象
|
||||
* @param userId 用户ID
|
||||
* @return 用户视图对象
|
||||
*/
|
||||
@Override
|
||||
public AppUserVO selectUserVoById(Long userId) {
|
||||
return baseMapper.getUserVoById(userId);
|
||||
}
|
||||
/**
|
||||
* 根据用户ID查询用户视图对象
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户视图对象
|
||||
*/
|
||||
@Override
|
||||
public AppUserVO selectUserVoById(Long userId) {
|
||||
return baseMapper.getUserVoById(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除用户
|
||||
* @param ids 用户ID数组
|
||||
* @return 删除结果
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteAppUserByIds(Long[] ids) {
|
||||
// 获取缓存对象
|
||||
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI);
|
||||
// 清除用户缓存
|
||||
for (AppUser appUser : baseMapper.selectByIds(CollUtil.toList(ids))) {
|
||||
cache.evict(appUser.getUsername());
|
||||
}
|
||||
/**
|
||||
* 批量删除用户
|
||||
*
|
||||
* @param ids 用户ID数组
|
||||
* @return 删除结果
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteAppUserByIds(Long[] ids) {
|
||||
// 获取缓存对象
|
||||
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI);
|
||||
// 清除用户缓存
|
||||
for (AppUser appUser : baseMapper.selectByIds(CollUtil.toList(ids))) {
|
||||
cache.evict(appUser.getUsername());
|
||||
}
|
||||
|
||||
// 删除用户角色关联关系
|
||||
this.appUserRoleService
|
||||
.remove(Wrappers.<AppUserRole>lambdaQuery().in(AppUserRole::getUserId, CollUtil.toList(ids)));
|
||||
// 删除用户角色关联关系
|
||||
this.appUserRoleService
|
||||
.remove(Wrappers.<AppUserRole>lambdaQuery().in(AppUserRole::getUserId, CollUtil.toList(ids)));
|
||||
|
||||
// 删除用户记录
|
||||
this.removeBatchByIds(CollUtil.toList(ids));
|
||||
// 删除用户记录
|
||||
this.removeBatchByIds(CollUtil.toList(ids));
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册APP用户
|
||||
* @param appUser 用户数据传输对象
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Override
|
||||
public R registerAppUser(AppUserDTO appUser) {
|
||||
// 检查手机号是否已被注册
|
||||
List<AppUser> appUserList = baseMapper
|
||||
.selectList(Wrappers.<AppUser>lambdaQuery().eq(AppUser::getPhone, appUser.getPhone()));
|
||||
/**
|
||||
* 注册APP用户
|
||||
*
|
||||
* @param appUser 用户数据传输对象
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Override
|
||||
public R registerAppUser(AppUserDTO appUser) {
|
||||
// 检查手机号是否已被注册
|
||||
List<AppUser> appUserList = baseMapper
|
||||
.selectList(Wrappers.<AppUser>lambdaQuery().eq(AppUser::getPhone, appUser.getPhone()));
|
||||
|
||||
if (CollUtil.isNotEmpty(appUserList)) {
|
||||
return R.failed("手机号已注册,请使用验证码直接登录");
|
||||
}
|
||||
if (CollUtil.isNotEmpty(appUserList)) {
|
||||
return R.failed("手机号已注册,请使用验证码直接登录");
|
||||
}
|
||||
|
||||
// 验证短信验证码
|
||||
String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT
|
||||
+ appUser.getPhone();
|
||||
String codeObj = RedisUtils.get(key);
|
||||
// 验证短信验证码
|
||||
String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT
|
||||
+ appUser.getPhone();
|
||||
String codeObj = RedisUtils.get(key);
|
||||
|
||||
if (!appUser.getMobileCode().equals(codeObj)) {
|
||||
return R.failed("验证码错误");
|
||||
}
|
||||
if (!appUser.getMobileCode().equals(codeObj)) {
|
||||
return R.failed("验证码错误");
|
||||
}
|
||||
|
||||
// 创建用户并保存
|
||||
AppUser app = new AppUser();
|
||||
BeanUtils.copyProperties(appUser, app);
|
||||
appUser.setUsername(app.getPhone()); // 默认使用手机号作为用户名
|
||||
appUser.setDelFlag(CommonConstants.STATUS_NORMAL);
|
||||
appUser.setPassword(ENCODER.encode(appUser.getPassword()));
|
||||
baseMapper.insert(appUser);
|
||||
return null;
|
||||
}
|
||||
// 创建用户并保存
|
||||
AppUser app = new AppUser();
|
||||
BeanUtils.copyProperties(appUser, app);
|
||||
appUser.setUsername(app.getPhone()); // 默认使用手机号作为用户名
|
||||
appUser.setDelFlag(CommonConstants.STATUS_NORMAL);
|
||||
appUser.setPassword(ENCODER.encode(appUser.getPassword()));
|
||||
baseMapper.insert(appUser);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入用户(从Excel导入)
|
||||
* @param excelVOList Excel数据列表
|
||||
* @param bindingResult 数据校验结果
|
||||
* @return 导入结果
|
||||
*/
|
||||
@Override
|
||||
public R importUser(List<AppUserExcelVO> excelVOList, BindingResult bindingResult) {
|
||||
// 获取通用校验失败的数据
|
||||
List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
|
||||
/**
|
||||
* 导入用户(从Excel导入)
|
||||
*
|
||||
* @param excelVOList Excel数据列表
|
||||
* @param bindingResult 数据校验结果
|
||||
* @return 导入结果
|
||||
*/
|
||||
@Override
|
||||
public R importUser(List<AppUserExcelVO> excelVOList, BindingResult bindingResult) {
|
||||
// 获取通用校验失败的数据
|
||||
List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
|
||||
|
||||
// 获取现有用户和角色列表,用于校验
|
||||
List<AppUser> userList = this.list();
|
||||
List<AppRole> roleList = appRoleService.list();
|
||||
// 获取现有用户和角色列表,用于校验
|
||||
List<AppUser> userList = this.list();
|
||||
List<AppRole> roleList = appRoleService.list();
|
||||
|
||||
// 遍历Excel数据进行处理
|
||||
for (AppUserExcelVO excel : excelVOList) {
|
||||
Set<String> errorMsg = new HashSet<>();
|
||||
// 遍历Excel数据进行处理
|
||||
for (AppUserExcelVO excel : excelVOList) {
|
||||
Set<String> errorMsg = new HashSet<>();
|
||||
|
||||
// 校验用户名是否已存在
|
||||
boolean exsitUserName = userList.stream()
|
||||
.anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername()));
|
||||
// 校验用户名是否已存在
|
||||
boolean exsitUserName = userList.stream()
|
||||
.anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername()));
|
||||
|
||||
if (exsitUserName) {
|
||||
errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername()));
|
||||
}
|
||||
if (exsitUserName) {
|
||||
errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_USER_USERNAME_EXISTING, excel.getUsername()));
|
||||
}
|
||||
|
||||
// 校验角色名称是否合法
|
||||
List<String> roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA);
|
||||
List<AppRole> roleCollList = roleList.stream()
|
||||
.filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name)))
|
||||
.toList();
|
||||
// 校验角色名称是否合法
|
||||
List<String> roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA);
|
||||
List<AppRole> roleCollList = roleList.stream()
|
||||
.filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name)))
|
||||
.toList();
|
||||
|
||||
if (roleCollList.size() != roleNameList.size()) {
|
||||
errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList()));
|
||||
}
|
||||
if (roleCollList.size() != roleNameList.size()) {
|
||||
errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList()));
|
||||
}
|
||||
|
||||
// 数据合法时执行插入
|
||||
if (CollUtil.isEmpty(errorMsg)) {
|
||||
insertExcelUser(excel, roleCollList);
|
||||
}
|
||||
else {
|
||||
// 记录不合法数据的错误信息
|
||||
errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
|
||||
}
|
||||
}
|
||||
// 数据合法时执行插入
|
||||
if (CollUtil.isEmpty(errorMsg)) {
|
||||
insertExcelUser(excel, roleCollList);
|
||||
} else {
|
||||
// 记录不合法数据的错误信息
|
||||
errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
|
||||
}
|
||||
}
|
||||
|
||||
// 存在错误信息时返回失败
|
||||
if (CollUtil.isNotEmpty(errorMessageList)) {
|
||||
return R.failed(errorMessageList);
|
||||
}
|
||||
return R.ok(null, MsgUtils.getMessage(ErrorCodes.SYS_USER_IMPORT_SUCCEED));
|
||||
}
|
||||
// 存在错误信息时返回失败
|
||||
if (CollUtil.isNotEmpty(errorMessageList)) {
|
||||
return R.failed(errorMessageList);
|
||||
}
|
||||
return R.ok(null, MsgUtils.getMessage(AppErrorCodes.APP_USER_IMPORT_SUCCEED));
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入Excel导入的用户数据
|
||||
* @param excel Excel用户数据
|
||||
* @param roleCollList 角色列表
|
||||
*/
|
||||
private void insertExcelUser(AppUserExcelVO excel, List<AppRole> roleCollList) {
|
||||
// 创建用户DTO并设置基本信息
|
||||
AppUserDTO userDTO = new AppUserDTO();
|
||||
userDTO.setUsername(excel.getUsername());
|
||||
userDTO.setPhone(excel.getPhone());
|
||||
userDTO.setNickname(excel.getNickname());
|
||||
userDTO.setName(excel.getName());
|
||||
userDTO.setEmail(excel.getEmail());
|
||||
// 批量导入时初始密码设置为手机号
|
||||
userDTO.setPassword(userDTO.getPhone());
|
||||
/**
|
||||
* 插入Excel导入的用户数据
|
||||
*
|
||||
* @param excel Excel用户数据
|
||||
* @param roleCollList 角色列表
|
||||
*/
|
||||
private void insertExcelUser(AppUserExcelVO excel, List<AppRole> roleCollList) {
|
||||
// 创建用户DTO并设置基本信息
|
||||
AppUserDTO userDTO = new AppUserDTO();
|
||||
userDTO.setUsername(excel.getUsername());
|
||||
userDTO.setPhone(excel.getPhone());
|
||||
userDTO.setNickname(excel.getNickname());
|
||||
userDTO.setName(excel.getName());
|
||||
userDTO.setEmail(excel.getEmail());
|
||||
// 批量导入时初始密码设置为手机号
|
||||
userDTO.setPassword(userDTO.getPhone());
|
||||
|
||||
// 获取角色ID列表
|
||||
List<Long> roleIdList = roleCollList.stream().map(AppRole::getRoleId).toList();
|
||||
userDTO.setRole(roleIdList);
|
||||
// 获取角色ID列表
|
||||
List<Long> roleIdList = roleCollList.stream().map(AppRole::getRoleId).toList();
|
||||
userDTO.setRole(roleIdList);
|
||||
|
||||
// 保存用户
|
||||
this.saveUser(userDTO);
|
||||
}
|
||||
// 保存用户
|
||||
this.saveUser(userDTO);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-app-server</artifactId>
|
||||
|
||||
+2
-5
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-auth</artifactId>
|
||||
@@ -128,9 +128,6 @@
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<loaderImplementation>CLASSIC</loaderImplementation>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
+113
-93
@@ -30,6 +30,7 @@ import com.pig4cloud.pigx.auth.support.password.OAuth2ResourceOwnerPasswordAuthe
|
||||
import com.pig4cloud.pigx.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationConverter;
|
||||
import com.pig4cloud.pigx.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.security.component.PigxBootCorsProperties;
|
||||
import com.pig4cloud.pigx.common.security.handler.FormAuthenticationFailureHandler;
|
||||
import com.pig4cloud.pigx.common.security.handler.SsoLogoutSuccessHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -56,6 +57,8 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -69,114 +72,131 @@ import java.util.Arrays;
|
||||
@RequiredArgsConstructor
|
||||
public class AuthorizationServerConfiguration {
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final PasswordDecoderFilter passwordDecoderFilter;
|
||||
private final PasswordDecoderFilter passwordDecoderFilter;
|
||||
|
||||
private final ValidateCodeFilter validateCodeFilter;
|
||||
private final ValidateCodeFilter validateCodeFilter;
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
|
||||
PigxAuthenticationSuccessEventHandler successEventHandler,
|
||||
PigxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
|
||||
// 配置授权服务器的安全策略,只有/oauth2/**的请求才会走如下的配置
|
||||
http.securityMatcher("/oauth2/**");
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
|
||||
private final PigxBootCorsProperties corsProperties;
|
||||
|
||||
// 增加验证码过滤器
|
||||
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 增加密码解密过滤器
|
||||
http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
|
||||
PigxAuthenticationSuccessEventHandler successEventHandler,
|
||||
PigxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
|
||||
// 配置授权服务器的安全策略,只有/oauth2/**的请求才会走如下的配置
|
||||
http.securityMatcher("/oauth2/**");
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
|
||||
|
||||
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
|
||||
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter
|
||||
.accessTokenResponseHandler(successEventHandler) // 登录成功处理器
|
||||
.errorResponseHandler(failureEventHandler);// 登录失败处理器
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常
|
||||
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面
|
||||
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())
|
||||
.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated());
|
||||
// 增加验证码过滤器
|
||||
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 增加密码解密过滤器
|
||||
http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
// 设置 Token 存储的策略
|
||||
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
|
||||
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter
|
||||
.accessTokenResponseHandler(successEventHandler) // 登录成功处理器
|
||||
.errorResponseHandler(failureEventHandler);// 登录失败处理器
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常
|
||||
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面
|
||||
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())
|
||||
.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated());
|
||||
|
||||
// 设置授权码模式登录页面
|
||||
http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults());
|
||||
DefaultSecurityFilterChain securityFilterChain = http.build();
|
||||
// 设置 Token 存储的策略
|
||||
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
// 注入自定义授权模式实现
|
||||
addCustomOAuth2GrantAuthenticationProvider(http);
|
||||
return securityFilterChain;
|
||||
}
|
||||
// 设置授权码模式登录页面
|
||||
http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults());
|
||||
|
||||
/**
|
||||
* 令牌生成规则实现 </br>
|
||||
* client:username:uuid
|
||||
*
|
||||
* @return OAuth2TokenGenerator
|
||||
*/
|
||||
@Bean
|
||||
public OAuth2TokenGenerator oAuth2TokenGenerator() {
|
||||
CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator();
|
||||
// 注入Token 增加关联用户信息
|
||||
accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer());
|
||||
return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator());
|
||||
}
|
||||
// 单体版本:根据配置启用CORS跨域支持(仅在security.cors.enabled=true时生效)
|
||||
if (Boolean.TRUE.equals(corsProperties.getEnabled())) {
|
||||
http.cors(cors -> cors.configurationSource(request -> {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsProperties.getAllowedOriginPatterns().forEach(corsConfiguration::addAllowedOriginPattern);
|
||||
corsProperties.getAllowedHeaders().forEach(corsConfiguration::addAllowedHeader);
|
||||
corsProperties.getAllowedMethods().forEach(corsConfiguration::addAllowedMethod);
|
||||
corsConfiguration.setAllowCredentials(corsProperties.getAllowCredentials());
|
||||
source.registerCorsConfiguration(corsProperties.getPathPattern(), corsConfiguration);
|
||||
return corsConfiguration;
|
||||
}));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationFailureHandler authenticationFailureHandler() {
|
||||
return new FormAuthenticationFailureHandler();
|
||||
}
|
||||
DefaultSecurityFilterChain securityFilterChain = http.build();
|
||||
|
||||
@Bean
|
||||
public LogoutSuccessHandler logoutSuccessHandler() {
|
||||
return new SsoLogoutSuccessHandler();
|
||||
}
|
||||
// 注入自定义授权模式实现
|
||||
addCustomOAuth2GrantAuthenticationProvider(http);
|
||||
return securityFilterChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* request -> xToken 注入请求转换器
|
||||
*
|
||||
* @return DelegatingAuthenticationConverter
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationConverter accessTokenRequestConverter() {
|
||||
return new DelegatingAuthenticationConverter(
|
||||
Arrays.asList(new OAuth2ResourceOwnerPasswordAuthenticationConverter(),
|
||||
new OAuth2ResourceOwnerSmsAuthenticationConverter(),
|
||||
new PigxOAuth2RefreshTokenAuthenticationConverter(),
|
||||
new OAuth2ClientCredentialsAuthenticationConverter(),
|
||||
new OAuth2AuthorizationCodeAuthenticationConverter(),
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationConverter()));
|
||||
}
|
||||
/**
|
||||
* 令牌生成规则实现 </br>
|
||||
* client:username:uuid
|
||||
*
|
||||
* @return OAuth2TokenGenerator
|
||||
*/
|
||||
@Bean
|
||||
public OAuth2TokenGenerator oAuth2TokenGenerator() {
|
||||
CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator();
|
||||
// 注入Token 增加关联用户信息
|
||||
accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer());
|
||||
return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入授权模式实现提供方
|
||||
* <p>
|
||||
* 1. 密码模式 </br>
|
||||
* 2. 短信登录 </br>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
|
||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
|
||||
@Bean
|
||||
public AuthenticationFailureHandler authenticationFailureHandler() {
|
||||
return new FormAuthenticationFailureHandler();
|
||||
}
|
||||
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator());
|
||||
@Bean
|
||||
public LogoutSuccessHandler logoutSuccessHandler() {
|
||||
return new SsoLogoutSuccessHandler();
|
||||
}
|
||||
|
||||
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator());
|
||||
/**
|
||||
* request -> xToken 注入请求转换器
|
||||
*
|
||||
* @return DelegatingAuthenticationConverter
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationConverter accessTokenRequestConverter() {
|
||||
return new DelegatingAuthenticationConverter(
|
||||
Arrays.asList(new OAuth2ResourceOwnerPasswordAuthenticationConverter(),
|
||||
new OAuth2ResourceOwnerSmsAuthenticationConverter(),
|
||||
new PigxOAuth2RefreshTokenAuthenticationConverter(),
|
||||
new OAuth2ClientCredentialsAuthenticationConverter(),
|
||||
new OAuth2AuthorizationCodeAuthenticationConverter(),
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationConverter()));
|
||||
}
|
||||
|
||||
// 处理 UsernamePasswordAuthenticationToken
|
||||
http.authenticationProvider(new PigxDaoAuthenticationProvider());
|
||||
// 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
|
||||
// 处理 OAuth2ResourceOwnerSmsAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
|
||||
}
|
||||
/**
|
||||
* 注入授权模式实现提供方
|
||||
* <p>
|
||||
* 1. 密码模式 </br>
|
||||
* 2. 短信登录 </br>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
|
||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
|
||||
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator());
|
||||
|
||||
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator());
|
||||
|
||||
// 处理 UsernamePasswordAuthenticationToken
|
||||
http.authenticationProvider(new PigxDaoAuthenticationProvider());
|
||||
// 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
|
||||
// 处理 OAuth2ResourceOwnerSmsAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import cn.hutool.core.lang.Validator;
|
||||
import com.anji.captcha.model.vo.CaptchaVO;
|
||||
import com.anji.captcha.service.CaptchaService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
@@ -59,9 +58,9 @@ public class ImageCodeEndpoint {
|
||||
*/
|
||||
@SneakyThrows
|
||||
@GetMapping("/create")
|
||||
public R behavior() {
|
||||
public R behavior(String captchaType) {
|
||||
CaptchaVO vo = new CaptchaVO();
|
||||
vo.setCaptchaType(CommonConstants.IMAGE_CODE_TYPE);
|
||||
vo.setCaptchaType(captchaType);
|
||||
CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class);
|
||||
return R.ok(captchaService.get(vo));
|
||||
}
|
||||
@@ -75,7 +74,7 @@ public class ImageCodeEndpoint {
|
||||
CaptchaVO vo = new CaptchaVO();
|
||||
vo.setPointJson(request.getParameter("pointJson"));
|
||||
vo.setToken(request.getParameter("token"));
|
||||
vo.setCaptchaType(CommonConstants.IMAGE_CODE_TYPE);
|
||||
vo.setCaptchaType(request.getParameter("captchaType"));
|
||||
|
||||
CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class);
|
||||
return R.ok(captchaService.check(vo));
|
||||
|
||||
@@ -26,9 +26,11 @@ import com.pig4cloud.pigx.admin.api.entity.SysTenant;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteTenantService;
|
||||
import com.pig4cloud.pigx.admin.api.vo.TokenVO;
|
||||
import com.pig4cloud.pigx.auth.support.core.AuthErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.core.util.RetOps;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
@@ -48,6 +50,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.security.authentication.event.LogoutSuccessEvent;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
@@ -91,6 +94,7 @@ public class PigxTokenEndpoint {
|
||||
@GetMapping("/token/login")
|
||||
public ModelAndView require(ModelAndView modelAndView, @RequestParam(required = false) String error) {
|
||||
modelAndView.setViewName("ftl/login");
|
||||
// Note: XSS prevention is handled by FreeMarker template using ?html directive
|
||||
modelAndView.addObject("error", error);
|
||||
|
||||
R<List<SysTenant>> tenantList = tenantService.list();
|
||||
@@ -110,9 +114,11 @@ public class PigxTokenEndpoint {
|
||||
@RequestParam(OAuth2ParameterNames.STATE) String state) {
|
||||
SysOauthClientDetails clientDetails = RetOps.of(clientDetailsService.getClientDetailsById(clientId))
|
||||
.getData()
|
||||
.orElseThrow(() -> new OAuthClientException("clientId 不合法"));
|
||||
.orElseThrow(() -> new OAuthClientException(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
MsgUtils.getMessage(AuthErrorCodes.AUTH_CLIENT_INVALID)));
|
||||
|
||||
Set<String> authorizedScopes = StringUtils.commaDelimitedListToSet(clientDetails.getScope());
|
||||
// Note: XSS prevention is handled by FreeMarker template using ?html directive
|
||||
modelAndView.addObject("clientId", clientId);
|
||||
modelAndView.addObject("state", state);
|
||||
modelAndView.addObject("scopeList", authorizedScopes);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.pig4cloud.pigx.auth.support.core;
|
||||
|
||||
/**
|
||||
* Auth 模块错误编码
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2022/3/30
|
||||
*/
|
||||
public interface AuthErrorCodes {
|
||||
|
||||
// === 验证码相关 ===
|
||||
String AUTH_CAPTCHA_EMPTY = "auth.captcha.empty";
|
||||
|
||||
String AUTH_CAPTCHA_INVALID = "auth.captcha.invalid";
|
||||
|
||||
// === 客户端相关 ===
|
||||
String AUTH_CLIENT_INVALID = "auth.client.invalid";
|
||||
}
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
package com.pig4cloud.pigx.auth.support.core;
|
||||
|
||||
import com.pig4cloud.pigx.auth.support.handler.FormAuthenticationFailureHandler;
|
||||
import com.pig4cloud.pigx.auth.support.handler.SsoLogoutSuccessHandler;
|
||||
import com.pig4cloud.pigx.auth.support.handler.TenantSavedRequestAwareAuthenticationSuccessHandler;
|
||||
import com.pig4cloud.pigx.common.security.handler.SsoLogoutSuccessHandler;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
|
||||
|
||||
+104
-101
@@ -12,11 +12,13 @@ import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.anji.captcha.model.vo.CaptchaVO;
|
||||
import com.anji.captcha.service.CaptchaService;
|
||||
import com.pig4cloud.pigx.auth.support.core.AuthErrorCodes;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.CaptchaFlagTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.exception.ValidateCodeException;
|
||||
import com.pig4cloud.pigx.common.core.util.MsgUtils;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
import com.pig4cloud.pigx.common.core.util.WebUtils;
|
||||
import com.pig4cloud.pigx.common.data.cache.RedisUtils;
|
||||
@@ -45,129 +47,130 @@ import java.io.IOException;
|
||||
@RequiredArgsConstructor
|
||||
public class ValidateCodeFilter extends OncePerRequestFilter {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
String requestUrl = request.getServletPath();
|
||||
String requestUrl = request.getServletPath();
|
||||
|
||||
// 不是登录URL 请求直接跳过
|
||||
if (!SecurityConstants.OAUTH_TOKEN_URL.equals(requestUrl)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
// 不是登录URL 请求直接跳过
|
||||
if (!SecurityConstants.OAUTH_TOKEN_URL.equals(requestUrl)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果登录URL 但是刷新token的请求,直接向下执行
|
||||
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (StrUtil.containsAny(grantType, SecurityConstants.REFRESH_TOKEN)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
// 如果登录URL 但是刷新token的请求,直接向下执行
|
||||
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (StrUtil.containsAny(grantType, SecurityConstants.REFRESH_TOKEN)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// mobile模式, 如果请求不包含mobile 参数直接
|
||||
String mobile = request.getParameter(SecurityConstants.GRANT_MOBILE);
|
||||
if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && StrUtil.isBlank(mobile)) {
|
||||
throw new OAuth2AuthenticationException(SecurityConstants.GRANT_MOBILE);
|
||||
}
|
||||
// mobile模式, 如果请求不包含mobile 参数直接
|
||||
String mobile = request.getParameter(SecurityConstants.GRANT_MOBILE);
|
||||
if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && StrUtil.isBlank(mobile)) {
|
||||
throw new OAuth2AuthenticationException(SecurityConstants.GRANT_MOBILE);
|
||||
}
|
||||
|
||||
// mobile模式, 社交登录模式不校验验证码直接跳过
|
||||
if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && !StrUtil.contains(mobile, "SMS")) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
// mobile模式, 社交登录模式不校验验证码直接跳过
|
||||
if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && !StrUtil.contains(mobile, "SMS")) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断客户端是否跳过检验
|
||||
if (!isCheckCaptchaClient(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
// 判断客户端是否跳过检验
|
||||
if (!isCheckCaptchaClient(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验验证码 1. 客户端开启验证码 2. 短信模式
|
||||
try {
|
||||
checkCode();
|
||||
filterChain.doFilter(request, response);
|
||||
// 校验验证码 1. 客户端开启验证码 2. 短信模式
|
||||
try {
|
||||
checkCode();
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (ValidateCodeException validateCodeException) {
|
||||
throw new OAuth2AuthenticationException(validateCodeException.getMessage());
|
||||
}
|
||||
}
|
||||
throw new OAuth2AuthenticationException(validateCodeException.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*/
|
||||
private void checkCode() throws ValidateCodeException {
|
||||
HttpServletRequest request = WebUtils.getRequest();
|
||||
String code = request.getParameter("code");
|
||||
/**
|
||||
* 校验验证码
|
||||
*/
|
||||
private void checkCode() throws ValidateCodeException {
|
||||
HttpServletRequest request = WebUtils.getRequest();
|
||||
String code = request.getParameter("code");
|
||||
|
||||
if (StrUtil.isBlank(code)) {
|
||||
throw new ValidateCodeException("验证码不能为空");
|
||||
}
|
||||
if (StrUtil.isBlank(code)) {
|
||||
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_EMPTY));
|
||||
}
|
||||
|
||||
String randomStr = request.getParameter("randomStr");
|
||||
String randomStr = request.getParameter("randomStr");
|
||||
|
||||
// https://gitee.com/log4j/pig/issues/IWA0D
|
||||
String mobile = request.getParameter("mobile");
|
||||
if (StrUtil.isNotBlank(mobile)) {
|
||||
randomStr = mobile;
|
||||
}
|
||||
// https://gitee.com/log4j/pig/issues/IWA0D
|
||||
String mobile = request.getParameter("mobile");
|
||||
if (StrUtil.isNotBlank(mobile)) {
|
||||
randomStr = mobile;
|
||||
}
|
||||
|
||||
// 若是滑块登录
|
||||
if (CommonConstants.IMAGE_CODE_TYPE.equalsIgnoreCase(randomStr)) {
|
||||
CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class);
|
||||
CaptchaVO vo = new CaptchaVO();
|
||||
vo.setCaptchaVerification(code);
|
||||
vo.setCaptchaType(CommonConstants.IMAGE_CODE_TYPE);
|
||||
if (!captchaService.verification(vo).isSuccess()) {
|
||||
throw new ValidateCodeException("验证码不能为空");
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 若是滑块登录
|
||||
if (StrUtil.equalsAnyIgnoreCase(randomStr, CommonConstants.IMAGE_CODE_BLOCK_PUZZLE, CommonConstants.IMAGE_CODE_CLICK_WORD)) {
|
||||
CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class);
|
||||
CaptchaVO vo = new CaptchaVO();
|
||||
vo.setCaptchaVerification(code);
|
||||
vo.setCaptchaType(randomStr);
|
||||
if (!captchaService.verification(vo).isSuccess()) {
|
||||
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_EMPTY));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String key = CacheConstants.DEFAULT_CODE_KEY + randomStr;
|
||||
if (!RedisUtils.hasKey(key)) {
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
String key = CacheConstants.DEFAULT_CODE_KEY + randomStr;
|
||||
if (!RedisUtils.hasKey(key)) {
|
||||
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID));
|
||||
}
|
||||
|
||||
String codeObj = RedisUtils.get(key);
|
||||
String codeObj = RedisUtils.get(key);
|
||||
|
||||
if (codeObj == null) {
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
if (codeObj == null) {
|
||||
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(codeObj)) {
|
||||
RedisUtils.delete(key);
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
if (StrUtil.isBlank(codeObj)) {
|
||||
RedisUtils.delete(key);
|
||||
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID));
|
||||
}
|
||||
|
||||
if (!StrUtil.equals(codeObj, code)) {
|
||||
RedisUtils.delete(key);
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
if (!StrUtil.equals(codeObj, code)) {
|
||||
RedisUtils.delete(key);
|
||||
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID));
|
||||
}
|
||||
|
||||
RedisUtils.delete(key);
|
||||
}
|
||||
RedisUtils.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要校验客户端,根据client 查询客户端配置
|
||||
* @param request 请求
|
||||
* @return true 需要校验, false 不需要校验
|
||||
*/
|
||||
private boolean isCheckCaptchaClient(HttpServletRequest request) {
|
||||
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
String clientId = WebUtils.extractClientId(header).orElse(null);
|
||||
// 获取租户拼接区分租户的key
|
||||
String tenantId = request.getHeader(CommonConstants.TENANT_ID);
|
||||
String key = String.format("%s:%s:%s", StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1 : tenantId,
|
||||
CacheConstants.CLIENT_FLAG, clientId);
|
||||
/**
|
||||
* 是否需要校验客户端,根据client 查询客户端配置
|
||||
*
|
||||
* @param request 请求
|
||||
* @return true 需要校验, false 不需要校验
|
||||
*/
|
||||
private boolean isCheckCaptchaClient(HttpServletRequest request) {
|
||||
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
String clientId = WebUtils.extractClientId(header).orElse(null);
|
||||
// 获取租户拼接区分租户的key
|
||||
String tenantId = request.getHeader(CommonConstants.TENANT_ID);
|
||||
String key = String.format("%s:%s:%s", StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1 : tenantId,
|
||||
CacheConstants.CLIENT_FLAG, clientId);
|
||||
|
||||
String val = RedisUtils.get(key);
|
||||
String val = RedisUtils.get(key);
|
||||
|
||||
// 当配置不存在时,不用校验
|
||||
if (val == null) {
|
||||
return false;
|
||||
}
|
||||
// 当配置不存在时,不用校验
|
||||
if (val == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSONObject information = JSONUtil.parseObj(val);
|
||||
return !StrUtil.equals(CaptchaFlagTypeEnum.OFF.getType(), information.getStr(CommonConstants.CAPTCHA_FLAG));
|
||||
}
|
||||
JSONObject information = JSONUtil.parseObj(val);
|
||||
return !StrUtil.equals(CaptchaFlagTypeEnum.OFF.getType(), information.getStr(CommonConstants.CAPTCHA_FLAG));
|
||||
}
|
||||
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package com.pig4cloud.pigx.auth.support.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022-06-02
|
||||
* <p>
|
||||
* sso 退出功能 ,根据客户端传入跳转
|
||||
*/
|
||||
public class SsoLogoutSuccessHandler implements LogoutSuccessHandler {
|
||||
|
||||
private static final String REDIRECT_URL = "redirect_url";
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws IOException {
|
||||
if (response == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取请求参数中是否包含 回调地址
|
||||
String redirectUrl = request.getParameter(REDIRECT_URL);
|
||||
if (StrUtil.isNotBlank(redirectUrl)) {
|
||||
response.sendRedirect(redirectUrl);
|
||||
}
|
||||
else if (StrUtil.isNotBlank(request.getHeader(HttpHeaders.REFERER))) {
|
||||
// 默认跳转referer 地址
|
||||
String referer = request.getHeader(HttpHeaders.REFERER);
|
||||
response.sendRedirect(referer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Auth Module i18n Messages - English
|
||||
# Captcha related
|
||||
auth.captcha.empty=Captcha cannot be empty
|
||||
auth.captcha.invalid=Invalid captcha
|
||||
auth.client.invalid=Invalid client
|
||||
@@ -0,0 +1,5 @@
|
||||
# Auth \u6A21\u5757\u56FD\u9645\u5316\u6D88\u606F - \u4E2D\u6587
|
||||
# \u9A8C\u8BC1\u7801\u76F8\u5173
|
||||
auth.captcha.empty=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
|
||||
auth.captcha.invalid=\u9A8C\u8BC1\u7801\u4E0D\u5408\u6CD5
|
||||
auth.client.invalid=\u65E0\u6548\u7684\u5BA2\u6237\u7AEF
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<#else>
|
||||
<a href="https://pig4cloud.com"
|
||||
class="text-purple-600 dark:text-purple-400 hover:text-purple-700 dark:hover:text-purple-300 font-medium transition-colors">
|
||||
${principalName}
|
||||
${principalName?html}
|
||||
</a>
|
||||
</#if>
|
||||
</div>
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
<form id='confirmationForm' name='confirmationForm'
|
||||
action="<#if request??>${request.contextPath!}</#if>/oauth2/authorize" method='post'>
|
||||
<input type="hidden" name="client_id" value="${clientId}">
|
||||
<input type="hidden" name="state" value="${state}">
|
||||
<input type="hidden" name="client_id" value="${clientId?html}">
|
||||
<input type="hidden" name="state" value="${state?html}">
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="mb-4">
|
||||
@@ -29,9 +29,9 @@
|
||||
<div class="space-y-3 bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700 transition-all">
|
||||
<#list scopeList as scope>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" checked="checked" name="scope" value="${scope}"
|
||||
<input type="checkbox" checked="checked" name="scope" value="${scope?html}"
|
||||
class="h-4 w-4 text-purple-600 focus:ring-purple-500 dark:focus:ring-purple-400 border-gray-300 dark:border-gray-600 rounded transition-colors">
|
||||
<label class="ml-3 text-gray-600 dark:text-gray-400 transition-colors">${scope}</label>
|
||||
<label class="ml-3 text-gray-600 dark:text-gray-400 transition-colors">${scope?html}</label>
|
||||
</div>
|
||||
</#list>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<select class="w-full text-sm px-4 py-3 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:border-purple-400 dark:focus:border-purple-500 dark:text-gray-300 transition-colors"
|
||||
name="TENANT-ID">
|
||||
<#list tenantList as tenant>
|
||||
<option value="${tenant.id}">${tenant.name}</option>
|
||||
<option value="${tenant.id?c}">${tenant.name?html}</option>
|
||||
</#list>
|
||||
</select>
|
||||
</#if>
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<#if error??>
|
||||
<div class="relative text-center">
|
||||
<span class="text-red-600 dark:text-red-400">${error}</span>
|
||||
<span class="text-red-600 dark:text-red-400">${error?html}</span>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-boot</artifactId>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-common-audit</artifactId>
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
<description>pigx 公共版本控制,在此pom定义的依赖,全局业务微服务不需要指定版本。</description>
|
||||
|
||||
<properties>
|
||||
<pigx.version>5.10.0</pigx.version>
|
||||
<mybatis-plus.version>3.5.14</mybatis-plus.version>
|
||||
<mybatis-plus-join.version>1.5.4</mybatis-plus-join.version>
|
||||
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
||||
<pigx.version>5.11.0</pigx.version>
|
||||
<mybatis-plus.version>3.5.15</mybatis-plus.version>
|
||||
<mybatis-plus-join.version>1.5.5</mybatis-plus-join.version>
|
||||
<dynamic-ds.version>4.5.0</dynamic-ds.version>
|
||||
<nacos.client.version>3.1.0</nacos.client.version>
|
||||
<druid.version>1.2.23</druid.version>
|
||||
<hutool.version>5.8.41</hutool.version>
|
||||
<mysql.connector.version>8.3.0</mysql.connector.version>
|
||||
<hutool.version>5.8.42</hutool.version>
|
||||
<mysql.connector.version>9.5.0</mysql.connector.version>
|
||||
<oracle.version>21.3.0.0</oracle.version>
|
||||
<sqlserver.version>12.10.0.jre8</sqlserver.version>
|
||||
<dm.version>8.1.3.140</dm.version>
|
||||
@@ -26,8 +26,10 @@
|
||||
<knife4j.version>3.0.5</knife4j.version>
|
||||
<springdoc.version>2.8.5</springdoc.version>
|
||||
<swagger.core.version>2.2.28</swagger.core.version>
|
||||
<mp.weixin.version>4.6.7.B</mp.weixin.version>
|
||||
<ijpay.version>2.9.10-17</ijpay.version>
|
||||
<!-- FIX Quartz https://github.com/spring-projects/spring-boot/issues/48012 -->
|
||||
<quartz.version>2.5.2</quartz.version>
|
||||
<mp.weixin.version>4.7.8-20251028.120007</mp.weixin.version>
|
||||
<ijpay.version>2.9.12.1-17</ijpay.version>
|
||||
<groovy.version>3.0.3</groovy.version>
|
||||
<javax.version>4.0.1</javax.version>
|
||||
<jsoup.version>1.15.3</jsoup.version>
|
||||
@@ -42,7 +44,7 @@
|
||||
<seata.version>1.7.0</seata.version>
|
||||
<asm.version>7.1</asm.version>
|
||||
<sensitive.word.version>0.21.0</sensitive.word.version>
|
||||
<log4j2.version>2.17.1</log4j2.version>
|
||||
<log4j2.version>2.25.2</log4j2.version>
|
||||
<grpc.version>1.59.1</grpc.version>
|
||||
<docker.plugin.version>0.33.0</docker.plugin.version>
|
||||
<cloud.plugin.version>1.0.0</cloud.plugin.version>
|
||||
@@ -52,7 +54,7 @@
|
||||
<deepseek.version>1.4.7</deepseek.version>
|
||||
<jakarta.mail.version>2.0.1</jakarta.mail.version>
|
||||
<dashscope.version>2.20.1</dashscope.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<velocity.version>2.4.1</velocity.version>
|
||||
<common.io.version>2.16.1</common.io.version>
|
||||
<velocity.tool.version>3.1</velocity.tool.version>
|
||||
</properties>
|
||||
@@ -186,6 +188,22 @@
|
||||
<artifactId>pigx-flow-api</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-milvus</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-mcp</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-neo4j</artifactId>
|
||||
<version>${pigx.version}</version>
|
||||
</dependency>
|
||||
<!-- chat ai -->
|
||||
<dependency>
|
||||
<groupId>io.github.pig-mesh.ai</groupId>
|
||||
@@ -463,6 +481,12 @@
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.client.version}</version>
|
||||
</dependency>
|
||||
<!-- fix springboot 3.5.8 bug,后续版本注意移除-->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<version>${quartz.version}</version>
|
||||
</dependency>
|
||||
<!-- 指定 log4j 版本-->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-common-core</artifactId>
|
||||
|
||||
+134
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -17,25 +17,149 @@
|
||||
|
||||
package com.pig4cloud.pigx.common.core.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/11/14
|
||||
* @author Hccake
|
||||
* @date 2025-12-12
|
||||
* <p>
|
||||
* 国际化配置
|
||||
* 国际化配置 - 支持多 JAR 包扫描
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class MessageSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
public MessageSource pigxMessageSource() {
|
||||
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
|
||||
messageSource.setBasename("classpath:i18n/messages");
|
||||
return messageSource;
|
||||
}
|
||||
/**
|
||||
* 创建并配置国际化消息源Bean
|
||||
*
|
||||
* @return 配置好的消息源实例,支持通配符资源加载
|
||||
*/
|
||||
@Bean
|
||||
public MessageSource pigxMessageSource() {
|
||||
WildcardReloadableResourceBundleMessageSource messageSource = new WildcardReloadableResourceBundleMessageSource();
|
||||
// 设置基础包名,支持通配符扫描多个 JAR
|
||||
messageSource.setBasename("i18n/messages");
|
||||
messageSource.setDefaultEncoding("UTF-8");
|
||||
messageSource.setCacheSeconds(3600);
|
||||
return messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持 classpath*: 通配符的 MessageSource 实现 能够扫描所有 JAR 包中的 i18n 资源文件
|
||||
*/
|
||||
@Slf4j
|
||||
static class WildcardReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {
|
||||
private static final String PROPERTIES_SUFFIX = ".properties";
|
||||
|
||||
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
private static final Pattern LOCALE_PROPERTIES_FILE_NAME_PATTERN = Pattern.compile(".*_([a-z]{2}(_[A-Z]{2})?(_[A-Z]+)?)\\.properties$");
|
||||
|
||||
public WildcardReloadableResourceBundleMessageSource() {
|
||||
super.setResourceLoader(this.resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算给定bundle基名和Locale的所有文件名。会计算指定Locale、系统Locale(如果适用)和默认文件的文件名。
|
||||
* 当基名包含通配符时,会移除基名并添加通配符匹配到的资源文件路径。
|
||||
*
|
||||
* @param basename bundle的基名
|
||||
* @param locale 区域设置
|
||||
* @return 要检查的文件名列表
|
||||
* @see #setFallbackToSystemLocale
|
||||
* @see #calculateFilenamesForLocale
|
||||
*/
|
||||
@Override
|
||||
protected List<String> calculateAllFilenames(String basename, Locale locale) {
|
||||
// 父类默认的方法会将 basename 也放入 filenames 列表
|
||||
List<String> filenames = super.calculateAllFilenames(basename, locale);
|
||||
// 当 basename 有匹配符时,从 filenames 中移除,否则扫描文件将抛出 Illegal char <*> 的异常
|
||||
if (containsWildcard(basename)) {
|
||||
filenames.remove(basename);
|
||||
try {
|
||||
Resource[] resources = getResources(basename);
|
||||
for (Resource resource : resources) {
|
||||
String resourceUriStr = resource.getURI().toString();
|
||||
// 根据通配符匹配到的多个 basename 对应的文件添加到文件列表末尾,作为兜底匹配
|
||||
if (!LOCALE_PROPERTIES_FILE_NAME_PATTERN.matcher(resourceUriStr).matches()) {
|
||||
String sourcePath = resourceUriStr.replace(PROPERTIES_SUFFIX, "");
|
||||
filenames.add(sourcePath);
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("读取国际化信息文件异常", ex);
|
||||
}
|
||||
}
|
||||
return filenames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算指定基名和区域设置的资源文件名列表
|
||||
*
|
||||
* @param basename 资源文件基名
|
||||
* @param locale 区域设置
|
||||
* @return 存在的资源文件名列表(不包含.properties后缀)
|
||||
*/
|
||||
@Override
|
||||
protected List<String> calculateFilenamesForLocale(String basename, Locale locale) {
|
||||
// 资源文件名
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
// 获取到待匹配的国际化信息文件名集合
|
||||
List<String> matchFilenames = super.calculateFilenamesForLocale(basename, locale);
|
||||
for (String matchFilename : matchFilenames) {
|
||||
try {
|
||||
Resource[] resources = getResources(matchFilename);
|
||||
for (Resource resource : resources) {
|
||||
if (resource.exists()) {
|
||||
String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, "");
|
||||
fileNames.add(sourcePath);
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("读取国际化信息文件异常", ex);
|
||||
}
|
||||
}
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据资源路径模式获取资源数组
|
||||
*
|
||||
* @param resourceLocationPattern 资源路径模式(支持用"."表示文件层级)
|
||||
* @return 匹配的资源数组
|
||||
* @throws IOException 获取资源过程中发生IO异常时抛出
|
||||
*/
|
||||
private Resource[] getResources(String resourceLocationPattern) throws IOException {
|
||||
// 支持用 . 表示文件层级
|
||||
resourceLocationPattern = resourceLocationPattern.replace(".", "/");
|
||||
return this.resolver.getResources("classpath*:" + resourceLocationPattern + PROPERTIES_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否包含通配符
|
||||
*
|
||||
* @param str 要检查的字符串
|
||||
* @return 如果字符串包含通配符返回true,否则返回false
|
||||
*/
|
||||
private boolean containsWildcard(String str) {
|
||||
if (str.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
|
||||
str = str.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
|
||||
}
|
||||
return str.contains("*");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+94
-89
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -25,114 +25,119 @@ package com.pig4cloud.pigx.common.core.constant;
|
||||
*/
|
||||
public interface CommonConstants {
|
||||
|
||||
/**
|
||||
* header 中租户ID
|
||||
*/
|
||||
String TENANT_ID = "TENANT-ID";
|
||||
/**
|
||||
* header 中租户ID
|
||||
*/
|
||||
String TENANT_ID = "TENANT-ID";
|
||||
|
||||
/**
|
||||
* header 中版本信息
|
||||
*/
|
||||
String VERSION = "VERSION";
|
||||
/**
|
||||
* header 中版本信息
|
||||
*/
|
||||
String VERSION = "VERSION";
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
Long TENANT_ID_1 = 1L;
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
Long TENANT_ID_1 = 1L;
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
String STATUS_DEL = "1";
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
String STATUS_DEL = "1";
|
||||
|
||||
/**
|
||||
* 正常
|
||||
*/
|
||||
String STATUS_NORMAL = "0";
|
||||
/**
|
||||
* 正常
|
||||
*/
|
||||
String STATUS_NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 锁定
|
||||
*/
|
||||
String STATUS_LOCK = "9";
|
||||
/**
|
||||
* 锁定
|
||||
*/
|
||||
String STATUS_LOCK = "9";
|
||||
|
||||
/**
|
||||
* 菜单树根节点
|
||||
*/
|
||||
Long MENU_TREE_ROOT_ID = -1L;
|
||||
/**
|
||||
* 菜单树根节点
|
||||
*/
|
||||
Long MENU_TREE_ROOT_ID = -1L;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
String UTF8 = "UTF-8";
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
String UTF8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* 前端工程名
|
||||
*/
|
||||
String FRONT_END_PROJECT = "pigx-ui";
|
||||
/**
|
||||
* 前端工程名
|
||||
*/
|
||||
String FRONT_END_PROJECT = "pigx-ui";
|
||||
|
||||
/**
|
||||
* 移动端工程名
|
||||
*/
|
||||
String UNI_END_PROJECT = "pigx-app";
|
||||
/**
|
||||
* 移动端工程名
|
||||
*/
|
||||
String UNI_END_PROJECT = "pigx-app";
|
||||
|
||||
/**
|
||||
* 后端工程名
|
||||
*/
|
||||
String BACK_END_PROJECT = "pigx";
|
||||
/**
|
||||
* 后端工程名
|
||||
*/
|
||||
String BACK_END_PROJECT = "pigx";
|
||||
|
||||
/**
|
||||
* 公共参数
|
||||
*/
|
||||
String PIG_PUBLIC_PARAM_KEY = "PIG_PUBLIC_PARAM_KEY";
|
||||
/**
|
||||
* 公共参数
|
||||
*/
|
||||
String PIG_PUBLIC_PARAM_KEY = "PIG_PUBLIC_PARAM_KEY";
|
||||
|
||||
/**
|
||||
* 成功标记
|
||||
*/
|
||||
Integer SUCCESS = 0;
|
||||
/**
|
||||
* 成功标记
|
||||
*/
|
||||
Integer SUCCESS = 0;
|
||||
|
||||
/**
|
||||
* 失败标记
|
||||
*/
|
||||
Integer FAIL = 1;
|
||||
/**
|
||||
* 失败标记
|
||||
*/
|
||||
Integer FAIL = 1;
|
||||
|
||||
/**
|
||||
* 默认存储bucket
|
||||
*/
|
||||
String BUCKET_NAME = "lengleng";
|
||||
/**
|
||||
* 默认存储bucket
|
||||
*/
|
||||
String BUCKET_NAME = "lengleng";
|
||||
|
||||
/**
|
||||
* 滑块验证码
|
||||
*/
|
||||
String IMAGE_CODE_TYPE = "blockPuzzle";
|
||||
/**
|
||||
* 滑块验证码
|
||||
*/
|
||||
String IMAGE_CODE_BLOCK_PUZZLE = "blockPuzzle";
|
||||
|
||||
/**
|
||||
* 验证码开关
|
||||
*/
|
||||
String CAPTCHA_FLAG = "captcha_flag";
|
||||
/**
|
||||
* 点击文字验证码类型
|
||||
*/
|
||||
String IMAGE_CODE_CLICK_WORD = "clickWord";
|
||||
|
||||
/**
|
||||
* 密码传输是否加密
|
||||
*/
|
||||
String ENC_FLAG = "enc_flag";
|
||||
/**
|
||||
* 验证码开关
|
||||
*/
|
||||
String CAPTCHA_FLAG = "captcha_flag";
|
||||
|
||||
/**
|
||||
* 客户端允许同时在线数量
|
||||
*/
|
||||
String ONLINE_QUANTITY = "online_quantity";
|
||||
/**
|
||||
* 密码传输是否加密
|
||||
*/
|
||||
String ENC_FLAG = "enc_flag";
|
||||
|
||||
/**
|
||||
* 请求开始时间
|
||||
*/
|
||||
String REQUEST_START_TIME = "REQUEST-START-TIME";
|
||||
/**
|
||||
* 客户端允许同时在线数量
|
||||
*/
|
||||
String ONLINE_QUANTITY = "online_quantity";
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
String CURRENT = "current";
|
||||
/**
|
||||
* 请求开始时间
|
||||
*/
|
||||
String REQUEST_START_TIME = "REQUEST-START-TIME";
|
||||
|
||||
/**
|
||||
* size
|
||||
*/
|
||||
String SIZE = "size";
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
String CURRENT = "current";
|
||||
|
||||
/**
|
||||
* size
|
||||
*/
|
||||
String SIZE = "size";
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.core.exception;
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2022/3/30
|
||||
*/
|
||||
public interface ErrorCodes {
|
||||
|
||||
/**
|
||||
* 系统编码错误
|
||||
*/
|
||||
String SYS_PARAM_CONFIG_ERROR = "sys.param.config.error";
|
||||
|
||||
/**
|
||||
* 系统内置参数不能删除
|
||||
*/
|
||||
String SYS_PARAM_DELETE_SYSTEM = "sys.param.delete.system";
|
||||
|
||||
/**
|
||||
* 用户已存在
|
||||
*/
|
||||
String SYS_USER_USERNAME_EXISTING = "sys.user.username.existing";
|
||||
|
||||
/**
|
||||
* 用户已存在
|
||||
*/
|
||||
String SYS_USER_PHONE_EXISTING = "sys.user.phone.existing";
|
||||
|
||||
/**
|
||||
* 用户原密码错误,修改失败
|
||||
*/
|
||||
String SYS_USER_UPDATE_PASSWORDERROR = "sys.user.update.passwordError";
|
||||
|
||||
/**
|
||||
* 用户信息为空
|
||||
*/
|
||||
String SYS_USER_USERINFO_EMPTY = "sys.user.userInfo.empty";
|
||||
|
||||
/**
|
||||
* 获取当前用户信息失败
|
||||
*/
|
||||
String SYS_USER_QUERY_ERROR = "sys.user.query.error";
|
||||
|
||||
String SYS_USER_IMPORT_SUCCEED = "sys.user.import.succeed";
|
||||
|
||||
/**
|
||||
* 部门名称不存在
|
||||
*/
|
||||
String SYS_DEPT_DEPTNAME_INEXISTENCE = "sys.dept.deptName.inexistence";
|
||||
|
||||
/**
|
||||
* 岗位名称不存在
|
||||
*/
|
||||
String SYS_POST_POSTNAME_INEXISTENCE = "sys.post.postName.inexistence";
|
||||
|
||||
/**
|
||||
* 岗位名称或编码已经存在
|
||||
*/
|
||||
String SYS_POST_NAMEORCODE_EXISTING = "sys.post.nameOrCode.existing";
|
||||
|
||||
/**
|
||||
* 角色名称不存在
|
||||
*/
|
||||
String SYS_ROLE_ROLENAME_INEXISTENCE = "sys.role.roleName.inexistence";
|
||||
|
||||
/**
|
||||
* 角色名或角色编码已经存在
|
||||
*/
|
||||
String SYS_ROLE_NAMEORCODE_EXISTING = "sys.role.nameOrCode.existing";
|
||||
|
||||
/**
|
||||
* 菜单存在下级节点 删除失败
|
||||
*/
|
||||
String SYS_MENU_DELETE_EXISTING = "sys.menu.delete.existing";
|
||||
|
||||
/**
|
||||
* 系统内置字典不允许删除
|
||||
*/
|
||||
String SYS_DICT_DELETE_SYSTEM = "sys.dict.delete.system";
|
||||
|
||||
/**
|
||||
* 系统内置字典不能修改
|
||||
*/
|
||||
String SYS_DICT_UPDATE_SYSTEM = "sys.dict.update.system";
|
||||
|
||||
/**
|
||||
* 验证码发送频繁
|
||||
*/
|
||||
String SYS_APP_SMS_OFTEN = "sys.app.sms.often";
|
||||
|
||||
/**
|
||||
* 手机号未注册
|
||||
*/
|
||||
String SYS_APP_PHONE_UNREGISTERED = "sys.app.phone.unregistered";
|
||||
|
||||
/**
|
||||
* 企微调用接口错误
|
||||
*/
|
||||
String SYS_CONNECT_CP_DEPT_SYNC_ERROR = "sys.connect.cp.dept.sync.error";
|
||||
|
||||
/**
|
||||
* 企微调用接口错误
|
||||
*/
|
||||
String SYS_CONNECT_CP_USER_SYNC_ERROR = "sys.connect.cp.user.sync.error";
|
||||
|
||||
/**
|
||||
* 用户信息为空
|
||||
*/
|
||||
String APP_USER_USERINFO_EMPTY = "app.user.userInfo.empty";
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+50
-27
@@ -2,6 +2,8 @@ package com.pig4cloud.pigx.common.core.util;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.NoSuchMessageException;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -14,34 +16,55 @@ import java.util.Locale;
|
||||
@UtilityClass
|
||||
public class MsgUtils {
|
||||
|
||||
/**
|
||||
* 通过code 获取中文错误信息
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public String getMessage(String code) {
|
||||
MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource");
|
||||
return messageSource.getMessage(code, null, Locale.CHINA);
|
||||
}
|
||||
/**
|
||||
* 通过code 获取中文错误信息
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public String getMessage(String code) {
|
||||
MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource");
|
||||
return resolveMessage(messageSource, code, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过code 和参数获取中文错误信息
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public String getMessage(String code, Object... objects) {
|
||||
MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource");
|
||||
return messageSource.getMessage(code, objects, Locale.CHINA);
|
||||
}
|
||||
/**
|
||||
* 通过code 和参数获取中文错误信息
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public String getMessage(String code, Object... objects) {
|
||||
MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource");
|
||||
return resolveMessage(messageSource, code, objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* security 通过code 和参数获取中文错误信息
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public String getSecurityMessage(String code, Object... objects) {
|
||||
MessageSource messageSource = SpringContextHolder.getBean("securityMessageSource");
|
||||
return messageSource.getMessage(code, objects, Locale.CHINA);
|
||||
}
|
||||
/**
|
||||
* 通过错误码和参数获取安全相关的中文错误信息
|
||||
*
|
||||
* @param code 错误码
|
||||
* @param objects 格式化参数
|
||||
* @return 格式化后的错误信息
|
||||
*/
|
||||
public String getSecurityMessage(String code, Object... objects) {
|
||||
MessageSource messageSource = SpringContextHolder.getBean("securityMessageSource");
|
||||
return resolveMessage(messageSource, code, objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析消息,根据当前区域设置获取消息,如果找不到消息则使用中国区域设置重试
|
||||
*
|
||||
* @param messageSource 消息源
|
||||
* @param code 消息代码
|
||||
* @param objects 消息参数
|
||||
* @return 解析后的消息
|
||||
*/
|
||||
private String resolveMessage(MessageSource messageSource, String code, Object[] objects) {
|
||||
Locale locale = LocaleContextHolder.getLocale();
|
||||
try {
|
||||
return messageSource.getMessage(code, objects, locale);
|
||||
} catch (NoSuchMessageException ex) {
|
||||
return messageSource.getMessage(code, objects, Locale.CHINA);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~
|
||||
~ Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
~ Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pigx-common-data</artifactId>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+21
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -71,6 +71,16 @@ public class DataScope extends HashMap {
|
||||
*/
|
||||
private DataScopeFuncEnum func = DataScopeFuncEnum.ALL;
|
||||
|
||||
/**
|
||||
* 逻辑组合模式,默认OR(向后兼容)
|
||||
* 当同时有本人权限(username)和部门权限(deptList)时的逻辑组合方式
|
||||
* <ul>
|
||||
* <li>OR: 满足任一条件即可(默认)</li>
|
||||
* <li>AND: 需同时满足所有条件</li>
|
||||
* </ul>
|
||||
*/
|
||||
private DataScopeLogicEnum logicMode = DataScopeLogicEnum.OR;
|
||||
|
||||
/**
|
||||
* of 获取实例
|
||||
*/
|
||||
@@ -93,4 +103,14 @@ public class DataScope extends HashMap {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置逻辑组合模式
|
||||
* @param logicMode ��辑模式(AND/OR)
|
||||
* @return DataScope
|
||||
*/
|
||||
public DataScope logic(DataScopeLogicEnum logicMode) {
|
||||
this.logicMode = logicMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+7
-2
@@ -63,8 +63,13 @@ public class DataScopeInnerInterceptor implements DataScopeInterceptor {
|
||||
// 2.如果为本人权限 + 部门权限控制
|
||||
else if (StrUtil.isNotBlank(dataScope.getUsername()) && CollUtil.isNotEmpty(deptIds)) {
|
||||
String join = CollectionUtil.join(deptIds, ",");
|
||||
originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s' OR temp_data_scope.%s IN (%s)",
|
||||
dataScope.getFunc().getType(), originalSql, dataScope.getScopeUserName(), dataScope.getUsername(), dataScope.getScopeDeptName(), join);
|
||||
// 使用配置的组合模式(AND/OR),默认OR保持向后兼容
|
||||
String logicKeyword = dataScope.getLogicMode() != null ? dataScope.getLogicMode().getKeyword()
|
||||
: DataScopeLogicEnum.OR.getKeyword();
|
||||
originalSql = String.format(
|
||||
"SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s' %s temp_data_scope.%s IN (%s)",
|
||||
dataScope.getFunc().getType(), originalSql, dataScope.getScopeUserName(), dataScope.getUsername(),
|
||||
logicKeyword, dataScope.getScopeDeptName(), join);
|
||||
}
|
||||
// 3. 如果为本人
|
||||
else if (StrUtil.isNotBlank(dataScope.getUsername())) {
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.data.datascope;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 数据权限逻辑组合类型
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2025-12-10
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataScopeLogicEnum {
|
||||
|
||||
/**
|
||||
* OR逻辑 - 满足任一条件即可(默认) 示例: WHERE create_by='user' OR dept_id IN (1,2,3)
|
||||
*/
|
||||
OR("OR", "或"),
|
||||
|
||||
/**
|
||||
* AND逻辑 - 需同时满足所有条件 示例: WHERE create_by='user' AND dept_id IN (1,2,3)
|
||||
*/
|
||||
AND("AND", "且");
|
||||
|
||||
/**
|
||||
* SQL关键字
|
||||
*/
|
||||
private final String keyword;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+17
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,11 +22,13 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysDept;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysRole;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteDataScopeService;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.util.RetOps;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUser;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -65,7 +67,12 @@ public class PigxDefaultDataScopeHandle implements DataScopeHandle {
|
||||
}
|
||||
|
||||
// 获取用户角色ID列表
|
||||
List<Long> roleIdList = user.getRoleIds();
|
||||
List<String> roleIdList = user.getAuthorities()
|
||||
.stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.filter(authority -> authority.startsWith(SecurityConstants.ROLE))
|
||||
.map(authority -> authority.split(StrUtil.UNDERLINE)[1])
|
||||
.toList();
|
||||
if (CollUtil.isEmpty(roleIdList)) {
|
||||
return false;
|
||||
}
|
||||
@@ -128,24 +135,20 @@ public class PigxDefaultDataScopeHandle implements DataScopeHandle {
|
||||
* 处理本级及下级数据权限
|
||||
*/
|
||||
private void handleOwnChildLevelScope(PigxUser user, List<Long> deptList) {
|
||||
|
||||
for (Long deptId : user.getDeptIds()) {
|
||||
List<Long> descendantDeptIds = RetOps.of(dataScopeService.getDescendantList(deptId))
|
||||
.getData()
|
||||
.orElseGet(Collections::emptyList)
|
||||
.stream()
|
||||
.map(SysDept::getDeptId)
|
||||
.toList();
|
||||
deptList.addAll(descendantDeptIds);
|
||||
}
|
||||
|
||||
List<Long> descendantDeptIds = RetOps.of(dataScopeService.getDescendantList(user.getDeptId()))
|
||||
.getData()
|
||||
.orElseGet(Collections::emptyList)
|
||||
.stream()
|
||||
.map(SysDept::getDeptId)
|
||||
.toList();
|
||||
deptList.addAll(descendantDeptIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理本级数据权限
|
||||
*/
|
||||
private void handleOwnLevelScope(PigxUser user, List<Long> deptList) {
|
||||
deptList.addAll(user.getDeptIds());
|
||||
deptList.add(user.getDeptId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
* Copyright (c) 2018-2026, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -48,13 +48,15 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 租户上下文过滤器:处理租户ID的提取、验证和上下文设置
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2018/9/13
|
||||
* @date 2025/12/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Order(-104) // OrderedRequestContextFilter 之后执行
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class TenantContextHolderFilter extends GenericFilterBean {
|
||||
|
||||
private final static String UNDEFINED_STR = "undefined";
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>pigx-common</artifactId>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<version>5.10.0</version>
|
||||
<version>5.11.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
Loaded 100 of 525 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user