feat: 新增微信小程序登录功能及第三方账号绑定表
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,4 +17,3 @@ docker/*/data/
|
||||
docker/minio/config
|
||||
docker/xxljob/logs
|
||||
application-youlai.yml
|
||||
youlai_admin_template.sql
|
||||
12
pom.xml
12
pom.xml
@@ -44,7 +44,6 @@
|
||||
<aliyun-sdk-oss.version>3.16.3</aliyun-sdk-oss.version>
|
||||
|
||||
<!-- redisson 分布式锁 -->
|
||||
<!-- Spring Boot 4.x 必须使用更新的版本 -->
|
||||
<redisson.version>4.1.0</redisson.version>
|
||||
|
||||
<!-- 自动代码生成 -->
|
||||
@@ -62,6 +61,8 @@
|
||||
|
||||
<!-- 阿里 TransmittableThreadLocal (支持异步场景的ThreadLocal传递) -->
|
||||
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
|
||||
|
||||
<weixin-java-miniapp.version>4.8.1.B</weixin-java-miniapp.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -271,13 +272,20 @@
|
||||
<version>${caffeine.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 微信小程序登录 -->
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-miniapp</artifactId>
|
||||
<version>${weixin.java.miniapp.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 动态多数据源 -->
|
||||
<!--<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||
<version>${dynamic-datasource.version}</version>
|
||||
</dependency>-->
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -10,7 +10,7 @@ CREATE DATABASE IF NOT EXISTS youlai_admin CHARACTER SET utf8mb4 DEFAULT COLLATE
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 2. 创建表 && 数据初始化
|
||||
-- 2. 创建表 && 数据初始化
|
||||
-- ----------------------------
|
||||
USE youlai_admin;
|
||||
|
||||
@@ -557,3 +557,25 @@ INSERT INTO `sys_user_notice` VALUES (7, 7, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (8, 8, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (9, 9, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (10, 10, 2, 1, NULL, now(), now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user_social
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_social`;
|
||||
CREATE TABLE `sys_user_social` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`platform` varchar(20) NOT NULL COMMENT '平台类型(WECHAT_MINI/WECHAT_MP/ALIPAY/QQ/APPLE)',
|
||||
`openid` varchar(64) NOT NULL COMMENT '平台openid',
|
||||
`unionid` varchar(64) DEFAULT NULL COMMENT '微信unionid',
|
||||
`nickname` varchar(64) DEFAULT NULL COMMENT '第三方昵称',
|
||||
`avatar` varchar(255) DEFAULT NULL COMMENT '第三方头像URL',
|
||||
`session_key` varchar(128) DEFAULT NULL COMMENT '微信session_key',
|
||||
`verified` tinyint(1) DEFAULT 1 COMMENT '是否已验证(1-已验证 0-未验证)',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '绑定时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_platform_openid` (`platform`, `openid`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_unionid` (`unionid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户第三方账号绑定表';
|
||||
|
||||
520
sql/mysql/youlai_admin_template.sql
Normal file
520
sql/mysql/youlai_admin_template.sql
Normal file
@@ -0,0 +1,520 @@
|
||||
# YouLai_Admin 数据库(MySQL 5.7 ~ MySQL 8.x)
|
||||
# Copyright (c) 2021-present, youlai.tech
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 1. 创建数据库
|
||||
-- ----------------------------
|
||||
CREATE DATABASE IF NOT EXISTS youlai_admin_template CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 2. 创建表 && 数据初始化
|
||||
-- ----------------------------
|
||||
USE youlai_admin_template;
|
||||
|
||||
SET NAMES utf8mb4; # 设置字符集
|
||||
SET FOREIGN_KEY_CHECKS = 0; # 关闭外键检查,加快导入速度
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dept`;
|
||||
CREATE TABLE `sys_dept` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`name` varchar(100) NOT NULL COMMENT '部门名称',
|
||||
`code` varchar(100) NOT NULL COMMENT '部门编号',
|
||||
`parent_id` bigint DEFAULT 0 COMMENT '父节点id',
|
||||
`tree_path` varchar(255) NOT NULL COMMENT '父节点id路径',
|
||||
`sort` smallint DEFAULT 0 COMMENT '显示顺序',
|
||||
`status` tinyint DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`create_by` bigint NULL COMMENT '创建人ID',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_by` bigint NULL COMMENT '修改人ID',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT 0 COMMENT '逻辑删除标识(1-已删除 0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_code`(`code` ASC) USING BTREE COMMENT '部门编号唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '部门管理表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_dept
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dept` VALUES (1, '有来技术', 'YOULAI', 0, '0', 1, 1, 1, NULL, 1, now(), 0);
|
||||
INSERT INTO `sys_dept` VALUES (2, '研发部门', 'RD001', 1, '0,1', 1, 1, 2, NULL, 2, now(), 0);
|
||||
INSERT INTO `sys_dept` VALUES (3, '测试部门', 'QA001', 1, '0,1', 1, 1, 2, NULL, 2, now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dict
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dict`;
|
||||
CREATE TABLE `sys_dict` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键 ',
|
||||
`dict_code` varchar(50) COMMENT '类型编码',
|
||||
`name` varchar(50) COMMENT '类型名称',
|
||||
`status` tinyint(1) DEFAULT '0' COMMENT '状态(0:正常;1:禁用)',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
`is_deleted` tinyint DEFAULT '0' COMMENT '是否删除(1-删除,0-未删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_dict_code` (`dict_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典类型表';
|
||||
-- ----------------------------
|
||||
-- Records of sys_dict
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dict` VALUES (1, 'gender', '性别', 1, NULL, now() , 1,now(), 1,0);
|
||||
INSERT INTO `sys_dict` VALUES (2, 'notice_type', '通知类型', 1, NULL, now(), 1,now(), 1,0);
|
||||
INSERT INTO `sys_dict` VALUES (3, 'notice_level', '通知级别', 1, NULL, now(), 1,now(), 1,0);
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_dict_item
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_dict_item`;
|
||||
CREATE TABLE `sys_dict_item` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`dict_code` varchar(50) COMMENT '关联字典编码,与sys_dict表中的dict_code对应',
|
||||
`value` varchar(50) COMMENT '字典项值',
|
||||
`label` varchar(100) COMMENT '字典项标签',
|
||||
`tag_type` varchar(50) COMMENT '标签类型,用于前端样式展示(如success、warning等)',
|
||||
`status` tinyint DEFAULT '0' COMMENT '状态(1-正常,0-禁用)',
|
||||
`sort` int DEFAULT '0' COMMENT '排序',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典项表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_dict_item
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_dict_item` VALUES (1, 'gender', '1', '男', 'primary', 1, 1, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (2, 'gender', '2', '女', 'danger', 1, 2, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (3, 'gender', '0', '保密', 'info', 1, 3, NULL, now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (4, 'notice_type', '1', '系统升级', 'success', 1, 1, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (5, 'notice_type', '2', '系统维护', 'primary', 1, 2, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (6, 'notice_type', '3', '安全警告', 'danger', 1, 3, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (7, 'notice_type', '4', '假期通知', 'success', 1, 4, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (8, 'notice_type', '5', '公司新闻', 'primary', 1, 5, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (9, 'notice_type', '99', '其他', 'info', 1, 99, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (10, 'notice_level', 'L', '低', 'info', 1, 1, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (11, 'notice_level', 'M', '中', 'warning', 1, 2, '', now(), 1,now(),1);
|
||||
INSERT INTO `sys_dict_item` VALUES (12, 'notice_level', 'H', '高', 'danger', 1, 3, '', now(), 1,now(),1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_menu`;
|
||||
CREATE TABLE `sys_menu` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`parent_id` bigint NOT NULL COMMENT '父菜单ID',
|
||||
`tree_path` varchar(255) COMMENT '父节点ID路径',
|
||||
`name` varchar(64) NOT NULL COMMENT '菜单名称',
|
||||
`type` char(1) NOT NULL COMMENT '菜单类型(C-目录 M-菜单 B-按钮)',
|
||||
`route_name` varchar(255) COMMENT '路由名称(Vue Router 中用于命名路由)',
|
||||
`route_path` varchar(128) COMMENT '路由路径(Vue Router 中定义的 URL 路径)',
|
||||
`component` varchar(128) COMMENT '组件路径(组件页面完整路径,相对于 src/views/,缺省后缀 .vue)',
|
||||
`perm` varchar(128) COMMENT '【按钮】权限标识',
|
||||
`always_show` tinyint DEFAULT 0 COMMENT '【目录】只有一个子路由是否始终显示(1-是 0-否)',
|
||||
`keep_alive` tinyint DEFAULT 0 COMMENT '【菜单】是否开启页面缓存(1-是 0-否)',
|
||||
`visible` tinyint(1) DEFAULT 1 COMMENT '显示状态(1-显示 0-隐藏)',
|
||||
`sort` int DEFAULT 0 COMMENT '排序',
|
||||
`icon` varchar(64) COMMENT '菜单图标',
|
||||
`redirect` varchar(128) COMMENT '跳转路径',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`params` varchar(255) NULL COMMENT '路由参数',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统菜单表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_menu
|
||||
-- ----------------------------
|
||||
-- 顶级目录:系统管理/代码生成/平台文档/接口文档
|
||||
INSERT INTO `sys_menu` VALUES (1, 0, '0', '系统管理', 'C', '', '/system', 'Layout', NULL, NULL, NULL, 1, 1, 'system', '/system/user', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2, 0, '0', '代码生成', 'C', '', '/codegen', 'Layout', NULL, NULL, NULL, 1, 2, 'code', '/codegen/index', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (4, 0, '0', '平台文档', 'C', '', '/doc', 'Layout', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (5, 0, '0', '接口文档', 'C', '', '/api', 'Layout', NULL, NULL, NULL, 1, 5, 'api', '', now(), now(), NULL);
|
||||
|
||||
-- 系统管理
|
||||
INSERT INTO `sys_menu` VALUES (210, 1, '0,1', '用户管理', 'M', 'User', 'user', 'system/user/index', NULL, NULL, 1, 1, 1, 'el-icon-User', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2101, 210, '0,1,210', '用户查询', 'B', NULL, '', NULL, 'sys:user:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2102, 210, '0,1,210', '用户新增', 'B', NULL, '', NULL, 'sys:user:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2103, 210, '0,1,210', '用户编辑', 'B', NULL, '', NULL, 'sys:user:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2104, 210, '0,1,210', '用户删除', 'B', NULL, '', NULL, 'sys:user:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2105, 210, '0,1,210', '重置密码', 'B', NULL, '', NULL, 'sys:user:reset-password', NULL, NULL, 1, 5, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2106, 210, '0,1,210', '用户导入', 'B', NULL, '', NULL, 'sys:user:import', NULL, NULL, 1, 6, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2107, 210, '0,1,210', '用户导出', 'B', NULL, '', NULL, 'sys:user:export', NULL, NULL, 1, 7, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (220, 1, '0,1', '角色管理', 'M', 'Role', 'role', 'system/role/index', NULL, NULL, 1, 1, 2, 'role', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2201, 220, '0,1,220', '角色查询', 'B', NULL, '', NULL, 'sys:role:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2202, 220, '0,1,220', '角色新增', 'B', NULL, '', NULL, 'sys:role:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2203, 220, '0,1,220', '角色编辑', 'B', NULL, '', NULL, 'sys:role:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2204, 220, '0,1,220', '角色删除', 'B', NULL, '', NULL, 'sys:role:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2205, 220, '0,1,220', '角色分配权限', 'B', NULL, '', NULL, 'sys:role:assign', NULL, NULL, 1, 5, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (230, 1, '0,1', '菜单管理', 'M', 'SysMenu', 'menu', 'system/menu/index', NULL, NULL, 1, 1, 3, 'menu', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2301, 230, '0,1,230', '菜单查询', 'B', NULL, '', NULL, 'sys:menu:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2302, 230, '0,1,230', '菜单新增', 'B', NULL, '', NULL, 'sys:menu:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2303, 230, '0,1,230', '菜单编辑', 'B', NULL, '', NULL, 'sys:menu:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2304, 230, '0,1,230', '菜单删除', 'B', NULL, '', NULL, 'sys:menu:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (240, 1, '0,1', '部门管理', 'M', 'Dept', 'dept', 'system/dept/index', NULL, NULL, 1, 1, 4, 'tree', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2401, 240, '0,1,240', '部门查询', 'B', NULL, '', NULL, 'sys:dept:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2402, 240, '0,1,240', '部门新增', 'B', NULL, '', NULL, 'sys:dept:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2403, 240, '0,1,240', '部门编辑', 'B', NULL, '', NULL, 'sys:dept:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2404, 240, '0,1,240', '部门删除', 'B', NULL, '', NULL, 'sys:dept:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (250, 1, '0,1', '字典管理', 'M', 'Dict', 'dict', 'system/dict/index', NULL, NULL, 1, 1, 5, 'dict', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2501, 250, '0,1,250', '字典查询', 'B', NULL, '', NULL, 'sys:dict:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2502, 250, '0,1,250', '字典新增', 'B', NULL, '', NULL, 'sys:dict:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2503, 250, '0,1,250', '字典编辑', 'B', NULL, '', NULL, 'sys:dict:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2504, 250, '0,1,250', '字典删除', 'B', NULL, '', NULL, 'sys:dict:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (251, 1, '0,1', '字典项', 'M', 'DictItem', 'dict-item', 'system/dict/dict-item', NULL, 0, 1, 0, 6, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2511, 251, '0,1,251', '字典项查询', 'B', NULL, '', NULL, 'sys:dict-item:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2512, 251, '0,1,251', '字典项新增', 'B', NULL, '', NULL, 'sys:dict-item:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2513, 251, '0,1,251', '字典项编辑', 'B', NULL, '', NULL, 'sys:dict-item:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2514, 251, '0,1,251', '字典项删除', 'B', NULL, '', NULL, 'sys:dict-item:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (260, 1, '0,1', '系统日志', 'M', 'Log', 'log', 'system/log/index', NULL, 0, 1, 1, 7, 'document', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (270, 1, '0,1', '系统配置', 'M', 'Config', 'config', 'system/config/index', NULL, 0, 1, 1, 8, 'setting', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2701, 270, '0,1,270', '系统配置查询', 'B', NULL, '', NULL, 'sys:config:list', 0, 1, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2702, 270, '0,1,270', '系统配置新增', 'B', NULL, '', NULL, 'sys:config:create', 0, 1, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2703, 270, '0,1,270', '系统配置修改', 'B', NULL, '', NULL, 'sys:config:update', 0, 1, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2704, 270, '0,1,270', '系统配置删除', 'B', NULL, '', NULL, 'sys:config:delete', 0, 1, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2705, 270, '0,1,270', '系统配置刷新', 'B', NULL, '', NULL, 'sys:config:refresh', 0, 1, 1, 5, '', NULL, now(), now(), NULL);
|
||||
|
||||
INSERT INTO `sys_menu` VALUES (280, 1, '0,1', '通知公告', 'M', 'Notice', 'notice', 'system/notice/index', NULL, NULL, NULL, 1, 9, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2801, 280, '0,1,280', '通知查询', 'B', NULL, '', NULL, 'sys:notice:list', NULL, NULL, 1, 1, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2802, 280, '0,1,280', '通知新增', 'B', NULL, '', NULL, 'sys:notice:create', NULL, NULL, 1, 2, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2803, 280, '0,1,280', '通知编辑', 'B', NULL, '', NULL, 'sys:notice:update', NULL, NULL, 1, 3, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2804, 280, '0,1,280', '通知删除', 'B', NULL, '', NULL, 'sys:notice:delete', NULL, NULL, 1, 4, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2805, 280, '0,1,280', '通知发布', 'B', NULL, '', NULL, 'sys:notice:publish', 0, 1, 1, 5, '', NULL, now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (2806, 280, '0,1,280', '通知撤回', 'B', NULL, '', NULL, 'sys:notice:revoke', 0, 1, 1, 6, '', NULL, now(), now(), NULL);
|
||||
|
||||
-- 代码生成
|
||||
INSERT INTO `sys_menu` VALUES (310, 2, '0,2', '代码生成', 'M', 'Codegen', 'codegen', 'codegen/index', NULL, NULL, 1, 1, 1, 'code', NULL, now(), now(), NULL);
|
||||
|
||||
-- 平台文档(外链通过 route_path 识别)
|
||||
INSERT INTO `sys_menu` VALUES (501, 4, '0,4', '平台文档(外链)', 'M', NULL, 'https://juejin.cn/post/7228990409909108793', '', NULL, NULL, NULL, 1, 1, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (502, 4, '0,4', '后端文档', 'M', NULL, 'https://youlai.blog.csdn.net/article/details/145178880', '', NULL, NULL, NULL, 1, 2, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (503, 4, '0,4', '移动端文档', 'M', NULL, 'https://youlai.blog.csdn.net/article/details/143222890', '', NULL, NULL, NULL, 1, 3, 'document', '', now(), now(), NULL);
|
||||
INSERT INTO `sys_menu` VALUES (504, 4, '0,4', '内部文档', 'M', NULL, 'internal-doc', 'demo/internal-doc', NULL, NULL, NULL, 1, 4, 'document', '', now(), now(), NULL);
|
||||
|
||||
-- 接口文档
|
||||
INSERT INTO `sys_menu` VALUES (601, 5, '0,5', 'Apifox', 'M', 'Apifox', 'apifox', 'demo/api/apifox', NULL, NULL, 1, 1, 1, 'api', '', now(), now(), NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role`;
|
||||
CREATE TABLE `sys_role` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(64) NOT NULL COMMENT '角色名称',
|
||||
`code` varchar(32) NOT NULL COMMENT '角色编码',
|
||||
`sort` int NULL COMMENT '显示顺序',
|
||||
`status` tinyint(1) DEFAULT 1 COMMENT '角色状态(1-正常 0-停用)',
|
||||
`data_scope` tinyint NULL COMMENT '数据权限(1-所有数据 2-部门及子部门数据 3-本部门数据 4-本人数据 5-自定义部门数据)',
|
||||
`create_by` bigint NULL COMMENT '创建人 ID',
|
||||
`create_time` datetime NULL COMMENT '创建时间',
|
||||
`update_by` bigint NULL COMMENT '更新人ID',
|
||||
`update_time` datetime NULL COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_name`(`name` ASC) USING BTREE COMMENT '角色名称唯一索引',
|
||||
UNIQUE INDEX `uk_code`(`code` ASC) USING BTREE COMMENT '角色编码唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统角色表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_role` VALUES (1, '超级管理员', 'ROOT', 1, 1, 1, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (2, '系统管理员', 'ADMIN', 2, 1, 1, NULL, now(), NULL, NULL, 0);
|
||||
INSERT INTO `sys_role` VALUES (3, '访问游客', 'GUEST', 3, 1, 3, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (4, '部门主管', 'DEPT_MANAGER', 4, 1, 2, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (5, '部门成员', 'DEPT_MEMBER', 5, 1, 3, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (6, '普通员工', 'EMPLOYEE', 6, 1, 4, NULL, now(), NULL, now(), 0);
|
||||
INSERT INTO `sys_role` VALUES (7, '自定义权限用户', 'CUSTOM_USER', 7, 1, 5, NULL, now(), NULL, now(), 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role_menu`;
|
||||
CREATE TABLE `sys_role_menu` (
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`menu_id` bigint NOT NULL COMMENT '菜单ID',
|
||||
UNIQUE INDEX `uk_roleid_menuid`(`role_id` ASC, `menu_id` ASC) USING BTREE COMMENT '角色菜单唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '角色菜单关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_role_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_role_dept`;
|
||||
CREATE TABLE `sys_role_dept` (
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`dept_id` bigint NOT NULL COMMENT '部门ID',
|
||||
UNIQUE INDEX `uk_roleid_deptid`(`role_id` ASC, `dept_id` ASC) USING BTREE COMMENT '角色部门唯一索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '角色部门关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_role_dept
|
||||
-- ----------------------------
|
||||
INSERT IGNORE INTO `sys_role_dept` VALUES (7, 1);
|
||||
INSERT IGNORE INTO `sys_role_dept` VALUES (7, 2);
|
||||
|
||||
-- ============================================
|
||||
-- 系统管理员角色菜单权限(role_id=2)
|
||||
-- 顶级目录
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 1), (2, 2), (2, 4), (2, 5);
|
||||
-- 系统管理
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 210), (2, 2101), (2, 2102), (2, 2103), (2, 2104), (2, 2105), (2, 2106), (2, 2107);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 220), (2, 2201), (2, 2202), (2, 2203), (2, 2204), (2, 2205);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 230), (2, 2301), (2, 2302), (2, 2303), (2, 2304);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 240), (2, 2401), (2, 2402), (2, 2403), (2, 2404);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 250), (2, 2501), (2, 2502), (2, 2503), (2, 2504);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 251), (2, 2511), (2, 2512), (2, 2513), (2, 2514);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 260);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 270), (2, 2701), (2, 2702), (2, 2703), (2, 2704), (2, 2705);
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 280), (2, 2801), (2, 2802), (2, 2803), (2, 2804), (2, 2805), (2, 2806);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 210), (4, 2101), (4, 2102), (4, 2103), (4, 2104), (4, 2105), (4, 2106), (4, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (4, 220), (4, 2201), (4, 2202), (4, 2203), (4, 2204), (4, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 210), (5, 2101), (5, 2102), (5, 2103), (5, 2104), (5, 2105), (5, 2106), (5, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (5, 220), (5, 2201), (5, 2202), (5, 2203), (5, 2204), (5, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 210), (6, 2101), (6, 2102), (6, 2103), (6, 2104), (6, 2105), (6, 2106), (6, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (6, 220), (6, 2201), (6, 2202), (6, 2203), (6, 2204), (6, 2205);
|
||||
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 1);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 210), (7, 2101), (7, 2102), (7, 2103), (7, 2104), (7, 2105), (7, 2106), (7, 2107);
|
||||
INSERT IGNORE INTO `sys_role_menu` VALUES (7, 220), (7, 2201), (7, 2202), (7, 2203), (7, 2204), (7, 2205);
|
||||
-- 代码生成
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 310);
|
||||
-- 平台文档
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 501), (2, 502), (2, 503), (2, 504);
|
||||
-- 接口文档
|
||||
INSERT INTO `sys_role_menu` VALUES (2, 601);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user`;
|
||||
CREATE TABLE `sys_user` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(64) COMMENT '用户名',
|
||||
`nickname` varchar(64) COMMENT '昵称',
|
||||
`gender` tinyint(1) DEFAULT 1 COMMENT '性别((1-男 2-女 0-保密)',
|
||||
`password` varchar(100) COMMENT '密码',
|
||||
`dept_id` int COMMENT '部门ID',
|
||||
`avatar` varchar(255) COMMENT '用户头像',
|
||||
`mobile` varchar(20) COMMENT '联系方式',
|
||||
`status` tinyint(1) DEFAULT 1 COMMENT '状态(1-正常 0-禁用)',
|
||||
`email` varchar(128) COMMENT '用户邮箱',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '修改人ID',
|
||||
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '系统用户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_user` VALUES (1, 'root', '有来技术', 0, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', NULL, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345677', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (2, 'admin', '系统管理员', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345678', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (3, 'test', '测试小用户', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 3, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345679', 1, 'youlaitech@163.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (4, 'dept_manager', '部门主管', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345680', 1, 'manager@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (5, 'dept_member', '部门成员', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 1, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345681', 1, 'member@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (6, 'employee', '普通员工', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 2, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345682', 1, 'employee@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
INSERT INTO `sys_user` VALUES (7, 'custom_user', '自定义权限用户', 1, '$2a$10$xVWsNOhHrCxh5UbpCE7/HuJ.PAOKcYAqRxD2CO2nVnJS.IAXkr5aq', 3, 'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif', '18812345683', 1, 'custom@youlaitech.com', now(), NULL, now(), NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_user_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_role`;
|
||||
CREATE TABLE `sys_user_role` (
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
PRIMARY KEY (`user_id`, `role_id`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '用户角色关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sys_user_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_user_role` VALUES (1, 1);
|
||||
INSERT INTO `sys_user_role` VALUES (2, 2);
|
||||
INSERT INTO `sys_user_role` VALUES (3, 3);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (4, 4);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (5, 5);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (6, 6);
|
||||
INSERT IGNORE INTO `sys_user_role` VALUES (7, 7);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_log`;
|
||||
CREATE TABLE `sys_log` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`module` varchar(50) NOT NULL COMMENT '日志模块',
|
||||
`request_method` varchar(64) NOT NULL COMMENT '请求方式',
|
||||
`request_params` text COMMENT '请求参数(批量请求参数可能会超过text)',
|
||||
`response_content` mediumtext COMMENT '返回参数',
|
||||
`content` varchar(255) NOT NULL COMMENT '日志内容',
|
||||
`request_uri` varchar(255) COMMENT '请求路径',
|
||||
`method` varchar(255) COMMENT '方法名',
|
||||
`ip` varchar(45) COMMENT 'IP地址',
|
||||
`province` varchar(100) COMMENT '省份',
|
||||
`city` varchar(100) COMMENT '城市',
|
||||
`execution_time` bigint COMMENT '执行时间(ms)',
|
||||
`browser` varchar(100) COMMENT '浏览器',
|
||||
`browser_version` varchar(100) COMMENT '浏览器版本',
|
||||
`os` varchar(100) COMMENT '终端系统',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COMMENT='系统操作日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for gen_table
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `gen_table`;
|
||||
CREATE TABLE `gen_table` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`table_name` varchar(100) NOT NULL COMMENT '表名',
|
||||
`module_name` varchar(100) COMMENT '模块名',
|
||||
`package_name` varchar(255) NOT NULL COMMENT '包名',
|
||||
`business_name` varchar(100) NOT NULL COMMENT '业务名',
|
||||
`entity_name` varchar(100) NOT NULL COMMENT '实体类名',
|
||||
`author` varchar(50) NOT NULL COMMENT '作者',
|
||||
`parent_menu_id` bigint COMMENT '上级菜单ID,对应sys_menu的id ',
|
||||
`remove_table_prefix` varchar(20) COMMENT '要移除的表前缀,如: sys_',
|
||||
`page_type` varchar(20) COMMENT '页面类型(classic|curd)',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint(4) DEFAULT 0 COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tablename` (`table_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代码生成配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for gen_table_column
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `gen_table_column`;
|
||||
CREATE TABLE `gen_table_column` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`table_id` bigint NOT NULL COMMENT '关联的表配置ID',
|
||||
`column_name` varchar(100) ,
|
||||
`column_type` varchar(50) ,
|
||||
`column_length` int ,
|
||||
`field_name` varchar(100) NOT NULL COMMENT '字段名称',
|
||||
`field_type` varchar(100) COMMENT '字段类型',
|
||||
`field_sort` int COMMENT '字段排序',
|
||||
`field_comment` varchar(255) COMMENT '字段描述',
|
||||
`max_length` int ,
|
||||
`is_required` tinyint(1) COMMENT '是否必填',
|
||||
`is_show_in_list` tinyint(1) DEFAULT '0' COMMENT '是否在列表显示',
|
||||
`is_show_in_form` tinyint(1) DEFAULT '0' COMMENT '是否在表单显示',
|
||||
`is_show_in_query` tinyint(1) DEFAULT '0' COMMENT '是否在查询条件显示',
|
||||
`query_type` tinyint COMMENT '查询方式',
|
||||
`form_type` tinyint COMMENT '表单类型',
|
||||
`dict_type` varchar(50) COMMENT '字典类型',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_table_id` (`table_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='代码生成字段配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 系统配置表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_config`;
|
||||
CREATE TABLE `sys_config` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`config_name` varchar(50) NOT NULL COMMENT '配置名称',
|
||||
`config_key` varchar(50) NOT NULL COMMENT '配置key',
|
||||
`config_value` varchar(100) NOT NULL COMMENT '配置值',
|
||||
`remark` varchar(255) COMMENT '备注',
|
||||
`create_time` datetime COMMENT '创建时间',
|
||||
`create_by` bigint COMMENT '创建人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`update_by` bigint COMMENT '更新人ID',
|
||||
`is_deleted` tinyint(4) DEFAULT '0' NOT NULL COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='系统配置表';
|
||||
|
||||
INSERT INTO `sys_config` VALUES (1, '系统限流QPS', 'IP_QPS_THRESHOLD_LIMIT', '10', '单个IP请求的最大每秒查询数(QPS)阈值Key', now(), 1, NULL, NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- 通知公告表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_notice`;
|
||||
CREATE TABLE `sys_notice` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(50) COMMENT '通知标题',
|
||||
`content` text COMMENT '通知内容',
|
||||
`type` tinyint NOT NULL COMMENT '通知类型(关联字典编码:notice_type)',
|
||||
`level` varchar(5) NOT NULL COMMENT '通知等级(字典code:notice_level)',
|
||||
`target_type` tinyint NOT NULL COMMENT '目标类型(1: 全体, 2: 指定)',
|
||||
`target_user_ids` varchar(255) COMMENT '目标人ID集合(多个使用英文逗号,分割)',
|
||||
`publisher_id` bigint COMMENT '发布人ID',
|
||||
`publish_status` tinyint DEFAULT '0' COMMENT '发布状态(0: 未发布, 1: 已发布, -1: 已撤回)',
|
||||
`publish_time` datetime COMMENT '发布时间',
|
||||
`revoke_time` datetime COMMENT '撤回时间',
|
||||
`create_by` bigint NOT NULL COMMENT '创建人ID',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_by` bigint COMMENT '更新人ID',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除(0: 未删除, 1: 已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统通知公告表';
|
||||
|
||||
INSERT INTO `sys_notice` VALUES (1, 'v3.0.0 版本发布 - 多租户功能上线', '<p>🎉 新版本发布,主要更新内容:</p><p>1. 新增多租户功能,支持租户隔离和数据管理</p><p>2. 优化系统性能,提升响应速度</p><p>3. 完善权限管理,增强安全性</p><p>4. 修复已知问题,提升系统稳定性</p>', 1, 'H', 1, NULL, 1, 1, '2024-12-15 10:00:00', NULL, 1, '2024-12-15 10:00:00', 1, '2024-12-15 10:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (2, '系统维护通知 - 2024年12月20日', '<p>⏰ 系统维护通知</p><p>系统将于 <strong>2024年12月20日(本周五)凌晨 2:00-4:00</strong> 进行例行维护升级。</p><p>维护期间系统将暂停服务,请提前做好数据备份工作。</p><p>给您带来的不便,敬请谅解!</p>', 2, 'H', 1, NULL, 1, 1, '2024-12-18 14:30:00', NULL, 1, '2024-12-18 14:30:00', 1, '2024-12-18 14:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (3, '安全提醒 - 防范钓鱼邮件', '<p>⚠️ 安全提醒</p><p>近期发现有不法分子通过钓鱼邮件进行网络攻击,请大家提高警惕:</p><p>1. 不要点击来源不明的邮件链接</p><p>2. 不要下载可疑附件</p><p>3. 遇到可疑邮件请及时联系IT部门</p><p>4. 定期修改密码,使用强密码策略</p>', 3, 'H', 1, NULL, 1, 1, '2024-12-10 09:00:00', NULL, 1, '2024-12-10 09:00:00', 1, '2024-12-10 09:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (4, '元旦假期安排通知', '<p>📅 元旦假期安排</p><p>根据国家法定节假日安排,公司元旦假期时间为:</p><p><strong>2024年12月30日(周一)至 2025年1月1日(周三)</strong>,共3天。</p><p>2024年12月29日(周日)正常上班。</p><p>祝大家元旦快乐,假期愉快!</p>', 4, 'M', 1, NULL, 1, 1, '2024-12-25 16:00:00', NULL, 1, '2024-12-25 16:00:00', 1, '2024-12-25 16:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (5, '新产品发布会邀请', '<p>🎊 新产品发布会邀请</p><p>公司将于 <strong>2025年1月15日下午14:00</strong> 在总部会议室举办新产品发布会。</p><p>届时将展示最新研发的产品和技术成果,欢迎全体员工参加。</p><p>请各部门提前安排好工作,准时参加。</p>', 5, 'M', 1, NULL, 1, 1, '2024-12-28 11:00:00', NULL, 1, '2024-12-28 11:00:00', 1, '2024-12-28 11:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (6, 'v2.16.1 版本更新', '<p>✨ 版本更新</p><p>v2.16.1 版本已发布,主要修复内容:</p><p>1. 修复 WebSocket 重复连接导致的后台线程阻塞问题</p><p>2. 优化通知公告功能,提升用户体验</p><p>3. 修复部分已知bug</p><p>建议尽快更新到最新版本。</p>', 1, 'M', 1, NULL, 1, 1, '2024-12-05 15:30:00', NULL, 1, '2024-12-05 15:30:00', 1, '2024-12-05 15:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (7, '年终总结会议通知', '<p>📋 年终总结会议通知</p><p>各部门年终总结会议将于 <strong>2024年12月30日上午9:00</strong> 召开。</p><p>请各部门负责人提前准备好年度工作总结和下年度工作计划。</p><p>会议地点:总部大会议室</p>', 5, 'M', 2, '1,2', 1, 1, '2024-12-22 10:00:00', NULL, 1, '2024-12-22 10:00:00', 1, '2024-12-22 10:00:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (8, '系统功能优化完成', '<p>✅ 系统功能优化</p><p>已完成以下功能优化:</p><p>1. 优化用户管理界面,提升操作体验</p><p>2. 增强数据导出功能,支持更多格式</p><p>3. 优化搜索功能,提升查询效率</p><p>4. 修复部分界面显示问题</p>', 1, 'L', 1, NULL, 1, 1, '2024-12-12 14:20:00', NULL, 1, '2024-12-12 14:20:00', 1, '2024-12-12 14:20:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (9, '员工培训计划', '<p>📚 员工培训计划</p><p>为提升员工专业技能,公司将于 <strong>2025年1月8日-10日</strong> 组织技术培训。</p><p>培训内容:</p><p>1. 新技术框架应用</p><p>2. 代码规范与最佳实践</p><p>3. 系统架构设计</p><p>请各部门合理安排工作,确保培训顺利进行。</p>', 5, 'M', 1, NULL, 1, 1, '2024-12-20 09:30:00', NULL, 1, '2024-12-20 09:30:00', 1, '2024-12-20 09:30:00', 0);
|
||||
INSERT INTO `sys_notice` VALUES (10, '数据备份提醒', '<p>💾 数据备份提醒</p><p>请各部门注意定期备份重要数据,建议每周至少备份一次。</p><p>备份方式:</p><p>1. 使用系统自带备份功能</p><p>2. 手动导出重要数据</p><p>3. 联系IT部门协助备份</p><p>数据安全,人人有责!</p>', 3, 'L', 1, NULL, 1, 1, '2024-12-08 08:00:00', NULL, 1, '2024-12-08 08:00:00', 1, '2024-12-08 08:00:00', 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- 用户通知公告表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sys_user_notice`;
|
||||
CREATE TABLE `sys_user_notice` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`notice_id` bigint NOT NULL COMMENT '公共通知id',
|
||||
`user_id` bigint NOT NULL COMMENT '用户id',
|
||||
`is_read` bigint DEFAULT '0' COMMENT '读取状态(0: 未读, 1: 已读)',
|
||||
`read_time` datetime COMMENT '阅读时间',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_time` datetime COMMENT '更新时间',
|
||||
`is_deleted` tinyint DEFAULT '0' COMMENT '逻辑删除(0: 未删除, 1: 已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户通知公告关联表';
|
||||
|
||||
INSERT INTO `sys_user_notice` VALUES (1, 1, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (2, 2, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (3, 3, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (4, 4, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (5, 5, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (6, 6, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (7, 7, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (8, 8, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (9, 9, 2, 1, NULL, now(), now(), 0);
|
||||
INSERT INTO `sys_user_notice` VALUES (10, 10, 2, 1, NULL, now(), now(), 0);
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.youlai.boot.auth.controller;
|
||||
|
||||
import com.youlai.boot.auth.model.vo.CaptchaVO;
|
||||
import com.youlai.boot.auth.model.vo.WechatLoginResult;
|
||||
import com.youlai.boot.auth.model.dto.LoginRequest;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
import com.youlai.boot.core.web.Result;
|
||||
@@ -67,6 +68,39 @@ public class AuthController {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "微信小程序登录(个人小程序)")
|
||||
@PostMapping("/wechat-miniapp/login")
|
||||
@Log(value = "微信小程序登录", module = LogModuleEnum.LOGIN)
|
||||
public Result<WechatLoginResult> loginByWechatMini(
|
||||
@Parameter(description = "微信登录code", example = "xxx") @RequestParam String code
|
||||
) {
|
||||
WechatLoginResult result = authService.loginByWechatMini(code);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "微信小程序一键登录(企业小程序)")
|
||||
@PostMapping("/wechat-miniapp/phone-login")
|
||||
@Log(value = "微信小程序一键登录", module = LogModuleEnum.LOGIN)
|
||||
public Result<AuthenticationToken> loginByWechatMiniWithPhone(
|
||||
@Parameter(description = "微信登录code", example = "xxx") @RequestParam String loginCode,
|
||||
@Parameter(description = "手机号授权code", example = "xxx") @RequestParam String phoneCode
|
||||
) {
|
||||
AuthenticationToken result = authService.wechatMiniLoginWithPhone(loginCode, phoneCode);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "微信小程序绑定手机号")
|
||||
@PostMapping("/wechat-miniapp/bind-mobile")
|
||||
@Log(value = "微信小程序绑定手机号", module = LogModuleEnum.LOGIN)
|
||||
public Result<AuthenticationToken> bindMobileForWechatMini(
|
||||
@Parameter(description = "微信openid") @RequestParam String openid,
|
||||
@Parameter(description = "手机号", example = "18812345678") @RequestParam String mobile,
|
||||
@Parameter(description = "短信验证码", example = "1234") @RequestParam String code
|
||||
) {
|
||||
AuthenticationToken result = authService.bindMobileForWechatMini(openid, mobile, code);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "退出登录")
|
||||
@DeleteMapping("/logout")
|
||||
@Log(value = "退出登录", module = LogModuleEnum.LOGIN)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.youlai.boot.auth.model.vo;
|
||||
|
||||
import com.youlai.boot.security.model.AuthenticationToken;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 微信小程序登录结果
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "微信小程序登录结果")
|
||||
public class WechatLoginResult {
|
||||
|
||||
@Schema(description = "是否新用户")
|
||||
private Boolean isNewUser;
|
||||
|
||||
@Schema(description = "是否需要绑定手机号")
|
||||
private Boolean needBindMobile;
|
||||
|
||||
@Schema(description = "微信openid(绑定手机号时需要)")
|
||||
private String openid;
|
||||
|
||||
@Schema(description = "访问令牌")
|
||||
private String accessToken;
|
||||
|
||||
@Schema(description = "刷新令牌")
|
||||
private String refreshToken;
|
||||
|
||||
@Schema(description = "令牌类型")
|
||||
private String tokenType;
|
||||
|
||||
@Schema(description = "过期时间(秒)")
|
||||
private Long expiresIn;
|
||||
|
||||
/**
|
||||
* 创建需要绑定手机号的结果
|
||||
*/
|
||||
public static WechatLoginResult needBindMobile(String openid) {
|
||||
WechatLoginResult result = new WechatLoginResult();
|
||||
result.setIsNewUser(true);
|
||||
result.setNeedBindMobile(true);
|
||||
result.setOpenid(openid);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建登录成功的结果
|
||||
*/
|
||||
public static WechatLoginResult success(AuthenticationToken token) {
|
||||
WechatLoginResult result = new WechatLoginResult();
|
||||
result.setIsNewUser(false);
|
||||
result.setNeedBindMobile(false);
|
||||
result.setAccessToken(token.getAccessToken());
|
||||
result.setRefreshToken(token.getRefreshToken());
|
||||
result.setTokenType(token.getTokenType());
|
||||
result.setExpiresIn(token.getExpiresIn());
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.youlai.boot.auth.service;
|
||||
|
||||
import com.youlai.boot.auth.model.vo.CaptchaVO;
|
||||
import com.youlai.boot.auth.model.vo.WechatLoginResult;
|
||||
import com.youlai.boot.security.model.AuthenticationToken;
|
||||
|
||||
/**
|
||||
@@ -55,4 +56,31 @@ public interface AuthService {
|
||||
* @return 登录结果
|
||||
*/
|
||||
AuthenticationToken loginBySms(String mobile, String code);
|
||||
|
||||
/**
|
||||
* 微信小程序登录(个人小程序)
|
||||
*
|
||||
* @param code 微信登录code
|
||||
* @return 登录结果
|
||||
*/
|
||||
WechatLoginResult loginByWechatMini(String code);
|
||||
|
||||
/**
|
||||
* 微信小程序一键登录(企业小程序)
|
||||
*
|
||||
* @param loginCode 微信登录code
|
||||
* @param phoneCode 手机号授权code
|
||||
* @return 登录结果
|
||||
*/
|
||||
AuthenticationToken wechatMiniLoginWithPhone(String loginCode, String phoneCode);
|
||||
|
||||
/**
|
||||
* 微信小程序绑定手机号
|
||||
*
|
||||
* @param openid 微信openid
|
||||
* @param mobile 手机号
|
||||
* @param smsCode 短信验证码
|
||||
* @return 登录结果
|
||||
*/
|
||||
AuthenticationToken bindMobileForWechatMini(String openid, String mobile, String smsCode);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
package com.youlai.boot.auth.service.impl;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.hutool.captcha.AbstractCaptcha;
|
||||
import cn.hutool.captcha.CaptchaUtil;
|
||||
import cn.hutool.captcha.generator.CodeGenerator;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.boot.auth.model.vo.CaptchaVO;
|
||||
import com.youlai.boot.auth.model.vo.WechatLoginResult;
|
||||
import com.youlai.boot.auth.service.AuthService;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.common.enums.CaptchaTypeEnum;
|
||||
import com.youlai.boot.config.property.CaptchaProperties;
|
||||
import com.youlai.boot.support.sms.enums.SmsTypeEnum;
|
||||
import com.youlai.boot.support.sms.service.SmsService;
|
||||
import com.youlai.boot.security.exception.NeedBindMobileException;
|
||||
import com.youlai.boot.security.model.AuthenticationToken;
|
||||
import com.youlai.boot.security.model.SmsAuthenticationToken;
|
||||
import com.youlai.boot.security.model.SysUserDetails;
|
||||
import com.youlai.boot.security.model.WechatMiniAuthenticationToken;
|
||||
import com.youlai.boot.security.token.TokenManager;
|
||||
import com.youlai.boot.security.util.SecurityUtils;
|
||||
import com.youlai.boot.support.sms.enums.SmsTypeEnum;
|
||||
import com.youlai.boot.support.sms.service.SmsService;
|
||||
import com.youlai.boot.system.enums.SocialPlatformEnum;
|
||||
import com.youlai.boot.system.model.entity.User;
|
||||
import com.youlai.boot.system.service.UserSocialService;
|
||||
import com.youlai.boot.system.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@@ -24,8 +33,10 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.awt.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -50,6 +61,9 @@ public class AuthServiceImpl implements AuthService {
|
||||
|
||||
private final SmsService smsService;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final UserSocialService userSocialService;
|
||||
private final UserService userService;
|
||||
private final WxMaService wxMaService;
|
||||
|
||||
/**
|
||||
* 用户名密码登录
|
||||
@@ -198,4 +212,150 @@ public class AuthServiceImpl implements AuthService {
|
||||
return tokenManager.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序登录(个人小程序)
|
||||
*/
|
||||
@Override
|
||||
public WechatLoginResult loginByWechatMini(String code) {
|
||||
WechatMiniAuthenticationToken token = new WechatMiniAuthenticationToken(code);
|
||||
|
||||
try {
|
||||
Authentication authentication = authenticationManager.authenticate(token);
|
||||
AuthenticationToken authToken = tokenManager.generateToken(authentication);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
return WechatLoginResult.success(authToken);
|
||||
} catch (NeedBindMobileException e) {
|
||||
return WechatLoginResult.needBindMobile(e.getOpenid());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序一键登录(企业小程序)
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AuthenticationToken wechatMiniLoginWithPhone(String loginCode, String phoneCode) {
|
||||
// 1. 用 loginCode 换取 openid
|
||||
cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult session;
|
||||
try {
|
||||
session = wxMaService.jsCode2SessionInfo(loginCode);
|
||||
} catch (Exception e) {
|
||||
log.error("微信小程序一键登录失败:获取openid异常,loginCode={}", loginCode, e);
|
||||
throw new IllegalArgumentException("微信登录失败:" + e.getMessage());
|
||||
}
|
||||
String openid = session.getOpenid();
|
||||
|
||||
// 2. 用 phoneCode 换取手机号
|
||||
cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo phoneInfo;
|
||||
try {
|
||||
phoneInfo = wxMaService.getUserService().getPhoneNoInfo(phoneCode);
|
||||
} catch (Exception e) {
|
||||
log.error("微信小程序一键登录失败:获取手机号异常,phoneCode={}", phoneCode, e);
|
||||
throw new IllegalArgumentException("获取手机号失败:" + e.getMessage());
|
||||
}
|
||||
String mobile = phoneInfo.getPhoneNumber();
|
||||
|
||||
log.info("微信小程序一键登录:openid={}, mobile={}", openid, mobile);
|
||||
|
||||
// 3. 查询或创建用户
|
||||
User user = userService.lambdaQuery()
|
||||
.eq(User::getMobile, mobile)
|
||||
.one();
|
||||
|
||||
if (user == null) {
|
||||
user = new User();
|
||||
user.setMobile(mobile);
|
||||
user.setUsername("wx_" + IdUtil.fastSimpleUUID().substring(0, 8));
|
||||
user.setNickname(phoneInfo.getNickName() != null ? phoneInfo.getNickName() : "微信用户");
|
||||
user.setAvatar(phoneInfo.getAvatarUrl());
|
||||
user.setStatus(1);
|
||||
user.setIsDeleted(0);
|
||||
user.setCreateTime(LocalDateTime.now());
|
||||
user.setUpdateTime(LocalDateTime.now());
|
||||
userService.save(user);
|
||||
log.info("微信小程序一键登录:创建新用户,mobile={}, userId={}", mobile, user.getId());
|
||||
}
|
||||
|
||||
// 4. 绑定 openid
|
||||
userSocialService.bindOrUpdate(
|
||||
user.getId(),
|
||||
SocialPlatformEnum.WECHAT_MINI,
|
||||
openid,
|
||||
session.getUnionid(),
|
||||
user.getNickname(),
|
||||
user.getAvatar(),
|
||||
session.getSessionKey()
|
||||
);
|
||||
|
||||
// 5. 生成 token
|
||||
SysUserDetails userDetails = new SysUserDetails(userService.getAuthInfoByMobile(mobile));
|
||||
AuthenticationToken authToken = tokenManager.generateToken(userDetails);
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
|
||||
);
|
||||
|
||||
log.info("微信小程序一键登录成功:mobile={}, openid={}", mobile, openid);
|
||||
|
||||
return authToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序绑定手机号
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AuthenticationToken bindMobileForWechatMini(String openid, String mobile, String smsCode) {
|
||||
// 1. 验证短信验证码
|
||||
String cacheKey = StrUtil.format(RedisConstants.Captcha.SMS_LOGIN_CODE, mobile);
|
||||
String cachedCode = (String) redisTemplate.opsForValue().get(cacheKey);
|
||||
|
||||
if (!StrUtil.equals(smsCode, cachedCode)) {
|
||||
throw new IllegalArgumentException("验证码错误");
|
||||
}
|
||||
|
||||
// 删除验证码
|
||||
redisTemplate.delete(cacheKey);
|
||||
|
||||
// 2. 查询或创建用户
|
||||
User user = userService.lambdaQuery()
|
||||
.eq(User::getMobile, mobile)
|
||||
.one();
|
||||
|
||||
if (user == null) {
|
||||
// 创建新用户
|
||||
user = new User();
|
||||
user.setMobile(mobile);
|
||||
user.setUsername("wx_" + IdUtil.fastSimpleUUID().substring(0, 8));
|
||||
user.setNickname("微信用户");
|
||||
user.setStatus(1);
|
||||
user.setIsDeleted(0);
|
||||
user.setCreateTime(LocalDateTime.now());
|
||||
user.setUpdateTime(LocalDateTime.now());
|
||||
userService.save(user);
|
||||
log.info("微信小程序绑定手机号:创建新用户,mobile={}, userId={}", mobile, user.getId());
|
||||
}
|
||||
|
||||
// 3. 绑定第三方账号
|
||||
userSocialService.bindOrUpdate(
|
||||
user.getId(),
|
||||
SocialPlatformEnum.WECHAT_MINI,
|
||||
openid,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 4. 生成token
|
||||
SysUserDetails userDetails = new SysUserDetails(userService.getAuthInfoByMobile(mobile));
|
||||
AuthenticationToken authToken = tokenManager.generateToken(userDetails);
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
|
||||
);
|
||||
|
||||
log.info("微信小程序绑定手机号成功:mobile={}, openid={}", mobile, openid);
|
||||
|
||||
return authToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.youlai.boot.config;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.hutool.captcha.generator.CodeGenerator;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.youlai.boot.config.property.SecurityProperties;
|
||||
@@ -9,9 +10,11 @@ import com.youlai.boot.security.filter.TokenAuthenticationFilter;
|
||||
import com.youlai.boot.security.handler.MyAccessDeniedHandler;
|
||||
import com.youlai.boot.security.handler.MyAuthenticationEntryPoint;
|
||||
import com.youlai.boot.security.provider.SmsAuthenticationProvider;
|
||||
import com.youlai.boot.security.provider.WechatMiniAuthenticationProvider;
|
||||
import com.youlai.boot.security.token.TokenManager;
|
||||
import com.youlai.boot.security.service.SysUserDetailsService;
|
||||
import com.youlai.boot.system.service.ConfigService;
|
||||
import com.youlai.boot.system.service.UserSocialService;
|
||||
import com.youlai.boot.system.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -54,6 +57,9 @@ public class SecurityConfig {
|
||||
private final ConfigService configService;
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
private final WxMaService wxMaService;
|
||||
private final UserSocialService userSocialService;
|
||||
|
||||
/**
|
||||
* 配置安全过滤链 SecurityFilterChain
|
||||
*/
|
||||
@@ -128,17 +134,27 @@ public class SecurityConfig {
|
||||
return new SmsAuthenticationProvider(userService, redisTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序认证 Provider
|
||||
*/
|
||||
@Bean
|
||||
public WechatMiniAuthenticationProvider wechatMiniAuthenticationProvider() {
|
||||
return new WechatMiniAuthenticationProvider(wxMaService, userSocialService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证管理器
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(
|
||||
DaoAuthenticationProvider daoAuthenticationProvider,
|
||||
SmsAuthenticationProvider smsAuthenticationProvider
|
||||
SmsAuthenticationProvider smsAuthenticationProvider,
|
||||
WechatMiniAuthenticationProvider wechatMiniAuthenticationProvider
|
||||
) {
|
||||
return new ProviderManager(
|
||||
daoAuthenticationProvider,
|
||||
smsAuthenticationProvider
|
||||
smsAuthenticationProvider,
|
||||
wechatMiniAuthenticationProvider
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
31
src/main/java/com/youlai/boot/config/WxMaConfiguration.java
Normal file
31
src/main/java/com/youlai/boot/config/WxMaConfiguration.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.youlai.boot.config;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 微信小程序配置
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(WxMaProperties.class)
|
||||
public class WxMaConfiguration {
|
||||
|
||||
@Bean
|
||||
public WxMaService wxMaService(WxMaProperties properties) {
|
||||
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
|
||||
config.setAppid(properties.getAppid());
|
||||
config.setSecret(properties.getSecret());
|
||||
config.setToken(properties.getToken());
|
||||
config.setAesKey(properties.getAesKey());
|
||||
config.setMsgDataFormat(properties.getMsgDataFormat());
|
||||
|
||||
WxMaService service = new WxMaServiceImpl();
|
||||
service.setWxMaConfig(config);
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
38
src/main/java/com/youlai/boot/config/WxMaProperties.java
Normal file
38
src/main/java/com/youlai/boot/config/WxMaProperties.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.youlai.boot.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* 微信小程序配置属性
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "wx.miniapp")
|
||||
public class WxMaProperties {
|
||||
|
||||
/**
|
||||
* 小程序appid
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 小程序Secret
|
||||
*/
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 小程序token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 小程序EncodingAESKey
|
||||
*/
|
||||
private String aesKey;
|
||||
|
||||
/**
|
||||
* 消息格式
|
||||
*/
|
||||
private String msgDataFormat;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.youlai.boot.security.exception;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
* 需要绑定手机号异常
|
||||
*/
|
||||
public class NeedBindMobileException extends AuthenticationException {
|
||||
|
||||
private final String openid;
|
||||
|
||||
private final String sessionKey;
|
||||
|
||||
public NeedBindMobileException(String openid, String sessionKey) {
|
||||
super("需要绑定手机号");
|
||||
this.openid = openid;
|
||||
this.sessionKey = sessionKey;
|
||||
}
|
||||
|
||||
public String getOpenid() {
|
||||
return openid;
|
||||
}
|
||||
|
||||
public String getSessionKey() {
|
||||
return sessionKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.youlai.boot.security.model;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 微信小程序认证 Token
|
||||
*/
|
||||
public class WechatMiniAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 622L;
|
||||
|
||||
/**
|
||||
* 认证信息
|
||||
* 未认证时:微信code
|
||||
* 已认证时:SysUserDetails 用户详情
|
||||
*/
|
||||
private final Object principal;
|
||||
|
||||
/**
|
||||
* 凭证信息
|
||||
* 未认证时:null
|
||||
* 已认证时:null
|
||||
*/
|
||||
private final Object credentials;
|
||||
|
||||
/**
|
||||
* 创建未认证的 Token
|
||||
*
|
||||
* @param code 微信小程序code
|
||||
*/
|
||||
public WechatMiniAuthenticationToken(String code) {
|
||||
super(AuthorityUtils.NO_AUTHORITIES);
|
||||
this.principal = code;
|
||||
this.credentials = null;
|
||||
setAuthenticated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建已认证的 Token
|
||||
*
|
||||
* @param principal 用户详情(SysUserDetails)
|
||||
* @param authorities 授权信息
|
||||
*/
|
||||
public WechatMiniAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
this.credentials = null;
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建已认证的 Token(静态工厂方法)
|
||||
*/
|
||||
public static WechatMiniAuthenticationToken authenticated(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
return new WechatMiniAuthenticationToken(principal, authorities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.youlai.boot.security.provider;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.youlai.boot.security.exception.NeedBindMobileException;
|
||||
import com.youlai.boot.security.model.SysUserDetails;
|
||||
import com.youlai.boot.security.model.UserAuthInfo;
|
||||
import com.youlai.boot.security.model.WechatMiniAuthenticationToken;
|
||||
import com.youlai.boot.system.enums.SocialPlatformEnum;
|
||||
import com.youlai.boot.system.model.entity.UserSocial;
|
||||
import com.youlai.boot.system.service.UserSocialService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
/**
|
||||
* 微信小程序认证 Provider
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class WechatMiniAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private final WxMaService wxMaService;
|
||||
private final UserSocialService userSocialService;
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
String code = (String) authentication.getPrincipal();
|
||||
|
||||
if (code == null || code.isEmpty()) {
|
||||
log.warn("微信小程序登录失败:code为空");
|
||||
throw new IllegalArgumentException("code不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 用 code 换取 openid
|
||||
WxMaJscode2SessionResult session = wxMaService.jsCode2SessionInfo(code);
|
||||
String openid = session.getOpenid();
|
||||
String sessionKey = session.getSessionKey();
|
||||
|
||||
log.info("微信小程序登录:openid={}", openid);
|
||||
|
||||
// 2. 根据 openid 查询绑定信息
|
||||
UserSocial userSocial = userSocialService.getByPlatformAndOpenid(SocialPlatformEnum.WECHAT_MINI, openid);
|
||||
|
||||
if (userSocial == null) {
|
||||
// 未绑定,抛出异常提示需要绑定手机号
|
||||
log.info("微信小程序登录:用户未绑定手机号,openid={}", openid);
|
||||
throw new NeedBindMobileException(openid, sessionKey);
|
||||
}
|
||||
|
||||
// 3. 获取用户认证信息
|
||||
UserAuthInfo userAuthInfo = userSocialService.getAuthInfoByOpenid(SocialPlatformEnum.WECHAT_MINI, openid);
|
||||
|
||||
if (userAuthInfo == null) {
|
||||
log.warn("微信小程序登录失败:用户不存在,openid={}", openid);
|
||||
throw new UsernameNotFoundException("用户不存在");
|
||||
}
|
||||
|
||||
// 4. 检查用户状态
|
||||
if (ObjectUtil.notEqual(userAuthInfo.getStatus(), 1)) {
|
||||
log.warn("微信小程序登录失败:用户已禁用,username={}", userAuthInfo.getUsername());
|
||||
throw new DisabledException("用户已被禁用");
|
||||
}
|
||||
|
||||
// 5. 更新 session_key
|
||||
userSocialService.updateSessionKey(userSocial.getId(), sessionKey);
|
||||
|
||||
// 6. 构建已认证 Token
|
||||
SysUserDetails userDetails = new SysUserDetails(userAuthInfo);
|
||||
|
||||
log.info("微信小程序登录成功:username={}, openid={}", userAuthInfo.getUsername(), openid);
|
||||
|
||||
return WechatMiniAuthenticationToken.authenticated(userDetails, userDetails.getAuthorities());
|
||||
|
||||
} catch (WxErrorException e) {
|
||||
log.error("微信小程序登录失败:调用微信接口异常,code={}", code, e);
|
||||
throw new IllegalArgumentException("微信登录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return WechatMiniAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.youlai.boot.system.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.youlai.boot.common.base.IBaseEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 第三方登录平台类型枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum SocialPlatformEnum implements IBaseEnum<String> {
|
||||
|
||||
WECHAT_MINI("WECHAT_MINI", "微信小程序"),
|
||||
WECHAT_MP("WECHAT_MP", "微信公众号"),
|
||||
WECHAT_OPEN("WECHAT_OPEN", "微信开放平台"),
|
||||
ALIPAY("ALIPAY", "支付宝"),
|
||||
QQ("QQ", "QQ"),
|
||||
APPLE("APPLE", "Apple ID");
|
||||
|
||||
@EnumValue
|
||||
private final String value;
|
||||
|
||||
private final String label;
|
||||
|
||||
SocialPlatformEnum(String value, String label) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.youlai.boot.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.youlai.boot.security.model.UserAuthInfo;
|
||||
import com.youlai.boot.system.model.entity.UserSocial;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 用户第三方账号绑定持久层
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserSocialMapper extends BaseMapper<UserSocial> {
|
||||
|
||||
/**
|
||||
* 根据用户ID获取认证信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 认证信息
|
||||
*/
|
||||
UserAuthInfo getAuthInfoByUserId(Long userId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.youlai.boot.system.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.youlai.boot.common.base.BaseEntity;
|
||||
import com.youlai.boot.system.enums.SocialPlatformEnum;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户第三方账号绑定实体
|
||||
*/
|
||||
@TableName("sys_user_social")
|
||||
@Getter
|
||||
@Setter
|
||||
public class UserSocial {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 平台类型
|
||||
*/
|
||||
private SocialPlatformEnum platform;
|
||||
|
||||
/**
|
||||
* 平台openid
|
||||
*/
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* 微信unionid
|
||||
*/
|
||||
private String unionid;
|
||||
|
||||
/**
|
||||
* 第三方昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 第三方头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 微信session_key
|
||||
*/
|
||||
private String sessionKey;
|
||||
|
||||
/**
|
||||
* 是否已验证(1-已验证 0-未验证)
|
||||
*/
|
||||
private Integer verified;
|
||||
|
||||
/**
|
||||
* 绑定时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.youlai.boot.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.boot.security.model.UserAuthInfo;
|
||||
import com.youlai.boot.system.enums.SocialPlatformEnum;
|
||||
import com.youlai.boot.system.model.entity.UserSocial;
|
||||
|
||||
/**
|
||||
* 用户第三方账号绑定业务接口
|
||||
*/
|
||||
public interface UserSocialService extends IService<UserSocial> {
|
||||
|
||||
/**
|
||||
* 根据平台和openid查询绑定信息
|
||||
*
|
||||
* @param platform 平台类型
|
||||
* @param openid openid
|
||||
* @return 绑定信息
|
||||
*/
|
||||
UserSocial getByPlatformAndOpenid(SocialPlatformEnum platform, String openid);
|
||||
|
||||
/**
|
||||
* 根据unionid查询绑定信息
|
||||
*
|
||||
* @param unionid unionid
|
||||
* @return 绑定信息
|
||||
*/
|
||||
UserSocial getByUnionid(String unionid);
|
||||
|
||||
/**
|
||||
* 绑定或更新第三方账号
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param platform 平台类型
|
||||
* @param openid openid
|
||||
* @param unionid unionid
|
||||
* @param nickname 昵称
|
||||
* @param avatar 头像
|
||||
* @param sessionKey session_key
|
||||
* @return 绑定信息
|
||||
*/
|
||||
UserSocial bindOrUpdate(Long userId, SocialPlatformEnum platform, String openid, String unionid, String nickname, String avatar, String sessionKey);
|
||||
|
||||
/**
|
||||
* 解绑第三方账号
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param platform 平台类型
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean unbind(Long userId, SocialPlatformEnum platform);
|
||||
|
||||
/**
|
||||
* 根据openid获取用户认证信息
|
||||
*
|
||||
* @param platform 平台类型
|
||||
* @param openid openid
|
||||
* @return 用户认证信息
|
||||
*/
|
||||
UserAuthInfo getAuthInfoByOpenid(SocialPlatformEnum platform, String openid);
|
||||
|
||||
/**
|
||||
* 更新session_key
|
||||
*
|
||||
* @param id 绑定记录ID
|
||||
* @param sessionKey session_key
|
||||
*/
|
||||
void updateSessionKey(Long id, String sessionKey);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.youlai.boot.system.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.boot.security.model.UserAuthInfo;
|
||||
import com.youlai.boot.system.enums.SocialPlatformEnum;
|
||||
import com.youlai.boot.system.mapper.UserSocialMapper;
|
||||
import com.youlai.boot.system.model.entity.UserSocial;
|
||||
import com.youlai.boot.system.service.UserSocialService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户第三方账号绑定业务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserSocialServiceImpl extends ServiceImpl<UserSocialMapper, UserSocial> implements UserSocialService {
|
||||
|
||||
@Override
|
||||
public UserSocial getByPlatformAndOpenid(SocialPlatformEnum platform, String openid) {
|
||||
return getOne(new LambdaQueryWrapper<UserSocial>()
|
||||
.eq(UserSocial::getPlatform, platform)
|
||||
.eq(UserSocial::getOpenid, openid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSocial getByUnionid(String unionid) {
|
||||
if (StrUtil.isBlank(unionid)) {
|
||||
return null;
|
||||
}
|
||||
return getOne(new LambdaQueryWrapper<UserSocial>()
|
||||
.eq(UserSocial::getUnionid, unionid));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public UserSocial bindOrUpdate(Long userId, SocialPlatformEnum platform, String openid, String unionid, String nickname, String avatar, String sessionKey) {
|
||||
UserSocial userSocial = getByPlatformAndOpenid(platform, openid);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
if (userSocial == null) {
|
||||
userSocial = new UserSocial();
|
||||
userSocial.setUserId(userId);
|
||||
userSocial.setPlatform(platform);
|
||||
userSocial.setOpenid(openid);
|
||||
userSocial.setUnionid(unionid);
|
||||
userSocial.setNickname(nickname);
|
||||
userSocial.setAvatar(avatar);
|
||||
userSocial.setSessionKey(sessionKey);
|
||||
userSocial.setVerified(1);
|
||||
userSocial.setCreateTime(now);
|
||||
userSocial.setUpdateTime(now);
|
||||
save(userSocial);
|
||||
log.info("第三方账号绑定成功:userId={}, platform={}, openid={}", userId, platform, openid);
|
||||
} else {
|
||||
userSocial.setUserId(userId);
|
||||
userSocial.setUnionid(unionid);
|
||||
userSocial.setNickname(nickname);
|
||||
userSocial.setAvatar(avatar);
|
||||
userSocial.setSessionKey(sessionKey);
|
||||
userSocial.setUpdateTime(now);
|
||||
updateById(userSocial);
|
||||
log.info("第三方账号更新成功:userId={}, platform={}, openid={}", userId, platform, openid);
|
||||
}
|
||||
|
||||
return userSocial;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean unbind(Long userId, SocialPlatformEnum platform) {
|
||||
boolean removed = remove(new LambdaQueryWrapper<UserSocial>()
|
||||
.eq(UserSocial::getUserId, userId)
|
||||
.eq(UserSocial::getPlatform, platform));
|
||||
if (removed) {
|
||||
log.info("第三方账号解绑成功:userId={}, platform={}", userId, platform);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthInfo getAuthInfoByOpenid(SocialPlatformEnum platform, String openid) {
|
||||
UserSocial userSocial = getByPlatformAndOpenid(platform, openid);
|
||||
if (userSocial == null) {
|
||||
return null;
|
||||
}
|
||||
return baseMapper.getAuthInfoByUserId(userSocial.getUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSessionKey(Long id, String sessionKey) {
|
||||
UserSocial userSocial = getById(id);
|
||||
if (userSocial != null) {
|
||||
userSocial.setSessionKey(sessionKey);
|
||||
userSocial.setUpdateTime(LocalDateTime.now());
|
||||
updateById(userSocial);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -85,13 +85,10 @@ public class CodegenServiceImpl implements CodegenService {
|
||||
return templateConfig.getTemplatePath();
|
||||
}
|
||||
if ("API".equals(templateName)) {
|
||||
return "codegen/api.js.vm";
|
||||
return "codegen/frontend/js/api.js.vm";
|
||||
}
|
||||
if ("VIEW".equals(templateName)) {
|
||||
return "codegen/index.js.vue.vm";
|
||||
}
|
||||
if ("API_TYPES".equals(templateName)) {
|
||||
return "codegen/api-types.js.vm";
|
||||
return "codegen/frontend/js/index.js.vue.vm";
|
||||
}
|
||||
return templateConfig.getTemplatePath();
|
||||
}
|
||||
|
||||
@@ -210,8 +210,11 @@ captcha:
|
||||
# 验证码有效期(秒)
|
||||
expire-seconds: 120
|
||||
|
||||
# 微信小程配置
|
||||
# 微信小程序配置
|
||||
wx:
|
||||
miniapp:
|
||||
app-id: xxxxxx
|
||||
app-secret: xxxxxx
|
||||
appid: your-app-id
|
||||
secret: your-app-secret
|
||||
token: your-token
|
||||
aes-key: your-aes-key
|
||||
msg-data-format: JSON
|
||||
|
||||
@@ -17,45 +17,45 @@ codegen:
|
||||
## 模板配置
|
||||
templateConfigs:
|
||||
API:
|
||||
templatePath: codegen/api.ts.vm
|
||||
templatePath: codegen/frontend/ts/api.ts.vm
|
||||
subpackageName: api
|
||||
extension: .ts
|
||||
API_TYPES:
|
||||
templatePath: codegen/api-types.ts.vm
|
||||
templatePath: codegen/frontend/ts/api-types.ts.vm
|
||||
subpackageName: types
|
||||
extension: .ts
|
||||
VIEW:
|
||||
templatePath: codegen/index.vue.vm
|
||||
templatePath: codegen/frontend/ts/index.vue.vm
|
||||
subpackageName: views
|
||||
extension: .vue
|
||||
Controller:
|
||||
templatePath: codegen/controller.java.vm
|
||||
templatePath: codegen/backend/controller.java.vm
|
||||
subpackageName: controller
|
||||
Service:
|
||||
templatePath: codegen/service.java.vm
|
||||
templatePath: codegen/backend/service.java.vm
|
||||
subpackageName: service
|
||||
ServiceImpl:
|
||||
templatePath: codegen/serviceImpl.java.vm
|
||||
templatePath: codegen/backend/serviceImpl.java.vm
|
||||
subpackageName: service.impl
|
||||
Mapper:
|
||||
templatePath: codegen/mapper.java.vm
|
||||
templatePath: codegen/backend/mapper.java.vm
|
||||
subpackageName: mapper
|
||||
MapperXml:
|
||||
templatePath: codegen/mapper.xml.vm
|
||||
templatePath: codegen/backend/mapper.xml.vm
|
||||
subpackageName: mapper
|
||||
extension: .xml
|
||||
Converter:
|
||||
templatePath: codegen/converter.java.vm
|
||||
templatePath: codegen/backend/converter.java.vm
|
||||
subpackageName: converter
|
||||
Query:
|
||||
templatePath: codegen/query.java.vm
|
||||
templatePath: codegen/backend/query.java.vm
|
||||
subpackageName: model.query
|
||||
Form:
|
||||
templatePath: codegen/form.java.vm
|
||||
templatePath: codegen/backend/form.java.vm
|
||||
subpackageName: model.form
|
||||
Vo:
|
||||
templatePath: codegen/vo.java.vm
|
||||
templatePath: codegen/backend/vo.java.vm
|
||||
subpackageName: model.vo
|
||||
Entity:
|
||||
templatePath: codegen/entity.java.vm
|
||||
templatePath: codegen/backend/entity.java.vm
|
||||
subpackageName: model.entity
|
||||
|
||||
39
src/main/resources/mapper/system/UserSocialMapper.xml
Normal file
39
src/main/resources/mapper/system/UserSocialMapper.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.youlai.boot.system.mapper.UserSocialMapper">
|
||||
|
||||
<!-- 用户认证信息映射 -->
|
||||
<resultMap id="AuthCredentialsMap" type="com.youlai.boot.security.model.UserAuthInfo">
|
||||
<id property="userId" column="userId" jdbcType="BIGINT"/>
|
||||
<result property="username" column="username" jdbcType="VARCHAR"/>
|
||||
<result property="nickname" column="nickname" jdbcType="VARCHAR"/>
|
||||
<result property="deptId" column="dept_id" jdbcType="BIGINT"/>
|
||||
<result property="password" column="password" jdbcType="VARCHAR"/>
|
||||
<result property="status" column="status" jdbcType="BOOLEAN"/>
|
||||
<collection property="roles" ofType="string" javaType="java.util.Set">
|
||||
<result column="code"/>
|
||||
</collection>
|
||||
</resultMap>
|
||||
|
||||
<!-- 根据用户ID获取认证信息 -->
|
||||
<select id="getAuthInfoByUserId" resultMap="AuthCredentialsMap">
|
||||
SELECT
|
||||
u.id AS userId,
|
||||
u.username,
|
||||
u.nickname,
|
||||
u.dept_id,
|
||||
u.password,
|
||||
u.status,
|
||||
r.code
|
||||
FROM
|
||||
sys_user u
|
||||
LEFT JOIN sys_user_role ur ON u.id = ur.user_id
|
||||
LEFT JOIN sys_role r ON ur.role_id = r.id AND r.is_deleted = 0
|
||||
WHERE
|
||||
u.id = #{userId}
|
||||
AND u.is_deleted = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -17,4 +17,4 @@ public interface ${entityName}Converter{
|
||||
${entityName}Form toForm(${entityName} entity);
|
||||
|
||||
${entityName} toEntity(${entityName}Form formData);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-container">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="filter-section">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="auto">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1)
|
||||
<el-form-item label="$fieldConfig.fieldComment" prop="$fieldConfig.fieldName">
|
||||
@@ -95,29 +95,34 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-card shadow="never">
|
||||
<div class="mb-10px">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:create']"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleOpenDialog()"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
:disabled="removeIds.length === 0"
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
>删除</el-button>
|
||||
<el-card shadow="hover" class="table-section">
|
||||
<div class="table-section__toolbar">
|
||||
<div class="table-section__toolbar--actions">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:create']"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleCreateClick()"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
:disabled="!hasSelection"
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
>删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="dataTableRef"
|
||||
v-loading="loading"
|
||||
:data="pageData"
|
||||
highlight-current-row
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
class="table-section__content"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
@@ -148,7 +153,7 @@
|
||||
size="small"
|
||||
link
|
||||
icon="edit"
|
||||
@click="handleOpenDialog(String(scope.row.id))"
|
||||
@click="handleEditClick(String(scope.row.id))"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
@@ -171,7 +176,7 @@
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="handleQuery()"
|
||||
@pagination="fetchList"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
@@ -180,7 +185,7 @@
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
@close="handleCloseDialog"
|
||||
@close="closeDialog"
|
||||
>
|
||||
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
@@ -260,7 +265,7 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit()">确定</el-button>
|
||||
<el-button @click="handleCloseDialog()">取消</el-button>
|
||||
<el-button @click="closeDialog()">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -268,18 +273,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useTableSelection } from "@/composables";
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
|
||||
defineOptions({
|
||||
name: "${entityName}",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
|
||||
const queryFormRef = ref();
|
||||
const dataFormRef = ref();
|
||||
|
||||
const loading = ref(false);
|
||||
const removeIds = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive({
|
||||
@@ -290,7 +297,6 @@
|
||||
// $!{businessName}表格数据
|
||||
const pageData = ref([]);
|
||||
|
||||
// 弹窗
|
||||
const dialog = reactive({
|
||||
title: "",
|
||||
visible: false,
|
||||
@@ -310,42 +316,57 @@
|
||||
#end
|
||||
});
|
||||
|
||||
const { selectedIds, hasSelection, handleSelectionChange } = useTableSelection();
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await ${entityName}API.getPage(queryParams);
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询$!{businessName} */
|
||||
function handleQuery() {
|
||||
loading.value = true;
|
||||
${entityName}API.getPage(queryParams)
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
queryParams.pageNum = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
/** 重置$!{businessName}查询 */
|
||||
function handleResetQuery() {
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 行复选框选中记录选中ID集合 */
|
||||
function handleSelectionChange(selection) {
|
||||
removeIds.value = selection.map((item) => String(item.id));
|
||||
function openDialog() {
|
||||
dialog.visible = true;
|
||||
}
|
||||
|
||||
/** 打开$!{businessName}弹窗 */
|
||||
function handleOpenDialog(id) {
|
||||
dialog.visible = true;
|
||||
if (id) {
|
||||
dialog.title = "修改$!{businessName}";
|
||||
${entityName}API.getFormData(id).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增$!{businessName}";
|
||||
}
|
||||
function closeDialog() {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
formData.id = undefined;
|
||||
}
|
||||
|
||||
async function handleCreateClick() {
|
||||
dialog.title = "新增$!{businessName}";
|
||||
openDialog();
|
||||
}
|
||||
|
||||
async function handleEditClick(id) {
|
||||
dialog.title = "修改$!{businessName}";
|
||||
${entityName}API.getFormData(id).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
openDialog();
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交$!{businessName}表单 */
|
||||
@@ -358,7 +379,7 @@
|
||||
${entityName}API.update(id, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
handleCloseDialog();
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -366,7 +387,7 @@
|
||||
${entityName}API.create(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
handleCloseDialog();
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -375,17 +396,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭$!{businessName}弹窗 */
|
||||
function handleCloseDialog() {
|
||||
dialog.visible = false;
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
formData.id = undefined;
|
||||
}
|
||||
|
||||
/** 删除$!{businessName} */
|
||||
function handleDelete(id) {
|
||||
const ids = [id || removeIds.value].join(",");
|
||||
const ids = [id || selectedIds.value].join(",");
|
||||
if (!ids) {
|
||||
ElMessage.warning("请勾选删除项");
|
||||
return;
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-container">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<div class="filter-section">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="auto">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1)
|
||||
<el-form-item label="$fieldConfig.fieldComment" prop="$fieldConfig.fieldName">
|
||||
@@ -95,29 +95,34 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-card shadow="never">
|
||||
<div class="mb-10px">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:create']"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleOpenDialog()"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
:disabled="removeIds.length === 0"
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
>删除</el-button>
|
||||
<el-card shadow="hover" class="table-section">
|
||||
<div class="table-section__toolbar">
|
||||
<div class="table-section__toolbar--actions">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:create']"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleCreateClick()"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
:disabled="!hasSelection"
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
>删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="dataTableRef"
|
||||
v-loading="loading"
|
||||
:data="pageData"
|
||||
highlight-current-row
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
class="table-section__content"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
@@ -148,7 +153,7 @@
|
||||
size="small"
|
||||
link
|
||||
icon="edit"
|
||||
@click="handleOpenDialog(String(scope.row.id))"
|
||||
@click="handleEditClick(String(scope.row.id))"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
@@ -171,7 +176,7 @@
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="handleQuery()"
|
||||
@pagination="fetchList"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
@@ -180,7 +185,7 @@
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
@close="handleCloseDialog"
|
||||
@close="closeDialog"
|
||||
>
|
||||
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
@@ -193,7 +198,7 @@
|
||||
/>
|
||||
#elseif($fieldConfig.formType == "SELECT")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<Dict v-model="formData.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-select v-model="formData.$fieldConfig.fieldName" placeholder="请选择$fieldConfig.fieldComment">
|
||||
<el-option :value="0" label="选项一"/>
|
||||
@@ -202,7 +207,7 @@
|
||||
#end
|
||||
#elseif($fieldConfig.formType == "RADIO")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<Dict v-model="queryParams.$fieldConfig.fieldName" type="radio" code="$fieldConfig.dictType" />
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" type="radio" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-radio-group v-model="formData.$fieldConfig.fieldName">
|
||||
<el-radio :value="0">选项一</el-radio>
|
||||
@@ -211,7 +216,7 @@
|
||||
#end
|
||||
#elseif($fieldConfig.formType == "CHECK_BOX")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<Dict v-model="queryParams.$fieldConfig.fieldName" type="checkbox" code="$fieldConfig.dictType" />
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" type="checkbox" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-checkbox-group v-model="formData.$fieldConfig.fieldName">
|
||||
<el-checkbox :value="0">选项一</el-checkbox>
|
||||
@@ -260,7 +265,7 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit()">确定</el-button>
|
||||
<el-button @click="handleCloseDialog()">取消</el-button>
|
||||
<el-button @click="closeDialog()">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -268,25 +273,27 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from "element-plus";
|
||||
import { useTableSelection } from "@/composables";
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
import type { ${entityName}Item, ${entityName}Form, ${entityName}QueryParams } from "@/types/api";
|
||||
|
||||
defineOptions({
|
||||
name: "${entityName}",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
import type { ${entityName}Item, ${entityName}Form, ${entityName}QueryParams } from "@/types/api";
|
||||
|
||||
const queryFormRef = ref();
|
||||
const dataFormRef = ref();
|
||||
const queryFormRef = ref<FormInstance>();
|
||||
const dataFormRef = ref<FormInstance>();
|
||||
|
||||
const loading = ref(false);
|
||||
const removeIds = ref<string[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive<${entityName}QueryParams>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
} as ${entityName}QueryParams);
|
||||
|
||||
// $!{businessName}表格数据
|
||||
const pageData = ref<${entityName}Item[]>([]);
|
||||
@@ -298,10 +305,10 @@
|
||||
});
|
||||
|
||||
// $!{businessName}表单数据
|
||||
const formData = reactive<${entityName}Form>({});
|
||||
const formData = reactive<${entityName}Form>({} as ${entityName}Form);
|
||||
|
||||
// $!{businessName}表单校验规则
|
||||
const rules = reactive({
|
||||
const rules: FormRules = {
|
||||
#if($fieldConfigs)
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInForm && $fieldConfig.isRequired)
|
||||
@@ -309,49 +316,64 @@
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
});
|
||||
};
|
||||
|
||||
const { selectedIds, hasSelection, handleSelectionChange } = useTableSelection<${entityName}Item>();
|
||||
|
||||
async function fetchList(): Promise<void> {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await ${entityName}API.getPage(queryParams);
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询$!{businessName} */
|
||||
function handleQuery() {
|
||||
loading.value = true;
|
||||
${entityName}API.getPage(queryParams)
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
function handleQuery(): void {
|
||||
queryParams.pageNum = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
/** 重置$!{businessName}查询 */
|
||||
function handleResetQuery() {
|
||||
queryFormRef.value!.resetFields();
|
||||
queryParams.pageNum = 1;
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 行复选框选中记录选中ID集合 */
|
||||
function handleSelectionChange(selection: any) {
|
||||
removeIds.value = selection.map((item: any) => String(item.id));
|
||||
function openDialog(): void {
|
||||
dialog.visible = true;
|
||||
}
|
||||
|
||||
/** 打开$!{businessName}弹窗 */
|
||||
function handleOpenDialog(id?: string) {
|
||||
dialog.visible = true;
|
||||
if (id) {
|
||||
dialog.title = "修改$!{businessName}";
|
||||
${entityName}API.getFormData(id).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增$!{businessName}";
|
||||
}
|
||||
function closeDialog(): void {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
function resetForm(): void {
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
formData.id = undefined;
|
||||
}
|
||||
|
||||
async function handleCreateClick(): Promise<void> {
|
||||
dialog.title = "新增$!{businessName}";
|
||||
openDialog();
|
||||
}
|
||||
|
||||
async function handleEditClick(id: string): Promise<void> {
|
||||
dialog.title = "修改$!{businessName}";
|
||||
${entityName}API.getFormData(id).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
openDialog();
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交$!{businessName}表单 */
|
||||
function handleSubmit() {
|
||||
dataFormRef.value.validate((valid: any) => {
|
||||
dataFormRef.value?.validate((valid: any) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
const id = formData.id;
|
||||
@@ -359,7 +381,7 @@
|
||||
${entityName}API.update(id, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
handleCloseDialog();
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -367,7 +389,7 @@
|
||||
${entityName}API.create(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
handleCloseDialog();
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -376,17 +398,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭$!{businessName}弹窗 */
|
||||
function handleCloseDialog() {
|
||||
dialog.visible = false;
|
||||
dataFormRef.value.resetFields();
|
||||
dataFormRef.value.clearValidate();
|
||||
formData.id = undefined;
|
||||
}
|
||||
|
||||
/** 删除$!{businessName} */
|
||||
function handleDelete(id?: string) {
|
||||
const ids = [id || removeIds.value].join(",");
|
||||
const ids = [id || selectedIds.value].join(",");
|
||||
if (!ids) {
|
||||
ElMessage.warning("请勾选删除项");
|
||||
return;
|
||||
Reference in New Issue
Block a user