├── vue ├── public │ ├── favicon.ico │ └── index.html ├── babel.config.js ├── src │ ├── assets │ │ ├── logo.png │ │ ├── img │ │ │ ├── logo.jpg │ │ │ └── background.jpg │ │ ├── globalvariable │ │ │ └── constant.js │ │ ├── login │ │ │ └── js │ │ │ │ ├── loginAssets.js │ │ │ │ ├── password.js │ │ │ │ └── cookie.js │ │ └── token │ │ │ └── token.js │ ├── utils │ │ ├── index.js │ │ ├── request.js │ │ ├── recursion-router.js │ │ ├── permission.js │ │ └── library.js │ ├── pages │ │ ├── page3.vue │ │ ├── page4.vue │ │ ├── page5.vue │ │ ├── page2.vue │ │ └── page1.vue │ ├── views │ │ ├── user │ │ │ ├── user.vue │ │ │ ├── superAdmin.vue │ │ │ └── admin.vue │ │ ├── index.vue │ │ └── login │ │ │ ├── register.vue │ │ │ └── login.vue │ ├── api │ │ ├── menu │ │ │ └── menuApi.js │ │ └── user │ │ │ └── userApi.js │ ├── App.vue │ ├── store │ │ ├── getters.js │ │ ├── index.js │ │ └── modules │ │ │ ├── permission.js │ │ │ └── user.js │ ├── main.js │ ├── components │ │ └── side │ │ │ └── SideItem.vue │ ├── plugins │ │ └── axios.js │ ├── permission.js │ └── router │ │ └── index.js ├── .gitignore ├── jsconfig.json ├── README.md ├── vue.config.js └── package.json ├── explain ├── 项目类详细介绍.assets │ ├── image-20220710083300766.png │ ├── image-20220710091147822.png │ └── image-20220710104456857.png └── 功能模块开发流程.md ├── .gitignore ├── redis ├── src │ └── main │ │ └── java │ │ └── yj │ │ └── sansui │ │ ├── version1 │ │ ├── RedisConstant.java │ │ ├── RedisConfig.java │ │ └── RedisUtil.java │ │ └── version2 │ │ └── RedisConstant.java ├── pom.xml └── redis.iml ├── shop-user ├── src │ └── main │ │ ├── java │ │ └── yj │ │ │ └── sansui │ │ │ ├── mapper │ │ │ └── ShopUserMapper.java │ │ │ ├── ShopUserApplication.java │ │ │ ├── service │ │ │ ├── ShopUserService.java │ │ │ └── impl │ │ │ │ └── ShopUserServiceImpl.java │ │ │ └── controller │ │ │ └── ShopUserController.java │ │ └── resources │ │ ├── application.yml │ │ ├── sql │ │ └── shop_user.sql │ │ └── logback-spring.xml └── pom.xml ├── common ├── src │ └── main │ │ └── java │ │ └── yj │ │ └── sansui │ │ ├── result │ │ ├── StatusCode.java │ │ ├── ResultCode.java │ │ └── Result.java │ │ ├── bean │ │ ├── dto │ │ │ ├── RoleMenuDTO.java │ │ │ ├── UserRoleDTO.java │ │ │ └── UserDTO.java │ │ └── entity │ │ │ ├── UserEntity.java │ │ │ ├── Role.java │ │ │ ├── Menu.java │ │ │ └── User.java │ │ ├── pack │ │ ├── NotControllerResponse.java │ │ └── ResponsePackInterceptor.java │ │ ├── exception │ │ ├── CommonException.java │ │ ├── ExceptionInterceptor.java │ │ └── ExceptionCode.java │ │ └── utils │ │ ├── TkipUtil.java │ │ ├── MailUtils.java │ │ └── VerifyCode.java ├── pom.xml └── common.iml ├── user ├── src │ └── main │ │ ├── java │ │ └── yj │ │ │ └── sansui │ │ │ ├── UserApplication.java │ │ │ ├── constant │ │ │ └── Constant.java │ │ │ ├── mapper │ │ │ ├── UserMapper.java │ │ │ ├── RoleMapper.java │ │ │ └── MenuMapper.java │ │ │ ├── service │ │ │ ├── MenuService.java │ │ │ ├── RoleService.java │ │ │ ├── UserService.java │ │ │ └── impl │ │ │ │ ├── PermissionRoleImpl.java │ │ │ │ ├── RoleServiceImpl.java │ │ │ │ ├── MenuServiceImpl.java │ │ │ │ ├── UserServiceImpl.java │ │ │ │ └── SaTokenRedisServiceImpl.java │ │ │ ├── controller │ │ │ ├── RoleController.java │ │ │ ├── MenuController.java │ │ │ └── UserController.java │ │ │ └── config │ │ │ ├── CorsConfig.java │ │ │ └── ServerConfig.java │ │ └── resources │ │ ├── application.yml │ │ └── sql │ │ └── sa-token.sql └── pom.xml ├── jwt ├── src │ └── main │ │ └── java │ │ └── yj │ │ └── sansui │ │ ├── annotation │ │ └── PassVerify.java │ │ ├── JwtInterceptorConfig.java │ │ ├── JwtAuthenticationInterceptor.java │ │ └── JwtUtil.java ├── pom.xml └── jwt.iml ├── README.md ├── CommonLibrary.iml └── pom.xml /vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DreamNextHeart/CommonLibrary/HEAD/vue/public/favicon.ico -------------------------------------------------------------------------------- /vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DreamNextHeart/CommonLibrary/HEAD/vue/src/assets/logo.png -------------------------------------------------------------------------------- /vue/src/utils/index.js: -------------------------------------------------------------------------------- 1 | import permission from "./permission" 2 | export default { 3 | permission 4 | } 5 | -------------------------------------------------------------------------------- /vue/src/assets/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DreamNextHeart/CommonLibrary/HEAD/vue/src/assets/img/logo.jpg -------------------------------------------------------------------------------- /vue/src/assets/img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DreamNextHeart/CommonLibrary/HEAD/vue/src/assets/img/background.jpg -------------------------------------------------------------------------------- /explain/项目类详细介绍.assets/image-20220710083300766.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DreamNextHeart/CommonLibrary/HEAD/explain/项目类详细介绍.assets/image-20220710083300766.png -------------------------------------------------------------------------------- /explain/项目类详细介绍.assets/image-20220710091147822.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DreamNextHeart/CommonLibrary/HEAD/explain/项目类详细介绍.assets/image-20220710091147822.png -------------------------------------------------------------------------------- /explain/项目类详细介绍.assets/image-20220710104456857.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DreamNextHeart/CommonLibrary/HEAD/explain/项目类详细介绍.assets/image-20220710104456857.png -------------------------------------------------------------------------------- /vue/src/pages/page3.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是Page3 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /vue/src/pages/page4.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是page4 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /vue/src/pages/page5.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是page5 3 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /shop-user/src/main/resources/log 3 | /vue/node_modules 4 | 5 | #各模块的target无需提交推送 6 | /common/target 7 | /jwt/target 8 | /redis/target 9 | /shop-user/target 10 | /user/target -------------------------------------------------------------------------------- /vue/src/views/user/user.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是user页面 3 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /vue/src/views/user/superAdmin.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是superAdmin页面 3 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /vue/src/assets/globalvariable/constant.js: -------------------------------------------------------------------------------- 1 | let tokenKey='satoken'; 2 | let phone='phone'; 3 | let remember='remember'; 4 | 5 | export default { 6 | expires: 7, 7 | tokenKey, 8 | phone, 9 | remember 10 | } 11 | -------------------------------------------------------------------------------- /vue/src/api/menu/menuApi.js: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | 3 | //获取树状菜单栏 4 | export function getMenuTree(){ 5 | return request({ 6 | url: '/getMenuTree', 7 | method: 'get' 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /vue/src/pages/page2.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是page2 3 | 4 | 5 | 6 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /vue/src/views/user/admin.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是admin页面 3 | 4 | 5 | 6 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /vue/src/pages/page1.vue: -------------------------------------------------------------------------------- 1 | 2 | 我是page1 3 | 4 | 5 | 6 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /vue/src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters={ 2 | token: state=> state.user.token, 3 | username: state=>state.user.username, 4 | phone: state=>state.user.phone, 5 | roles: state=>state.user.roles, 6 | permissions: state=>state.user.permissions 7 | } 8 | 9 | export default getters 10 | -------------------------------------------------------------------------------- /redis/src/main/java/yj/sansui/version1/RedisConstant.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.version1; 2 | 3 | /** 4 | * RedisConstant,redis的key常量 5 | * @author sansui 6 | */ 7 | public class RedisConstant { 8 | public static String All_User_Key = "allUser"; 9 | public static String User_Id_Key = "id:"; 10 | } 11 | -------------------------------------------------------------------------------- /vue/src/assets/login/js/loginAssets.js: -------------------------------------------------------------------------------- 1 | import {checkCode} from "@/api/user/userApi"; 2 | 3 | 4 | export function checkCodeAssets(code){ 5 | checkCode(code).then(res => { 6 | if (res.data.code !== 200) { 7 | this.$message.error('验证码错误'); 8 | } else { 9 | this.clearCookie(); 10 | } 11 | }) 12 | } 13 | 14 | -------------------------------------------------------------------------------- /vue/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /vue/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vuex from 'vuex' 2 | import user from './modules/user' 3 | import getters from './getters' 4 | import permission from "@/store/modules/permission"; 5 | 6 | // Vue.use(Vuex) 7 | 8 | const store=new Vuex.Store({ 9 | modules: { 10 | user, 11 | permission 12 | }, 13 | getters 14 | }) 15 | export default store 16 | -------------------------------------------------------------------------------- /vue/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /shop-user/src/main/java/yj/sansui/mapper/ShopUserMapper.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.mapper; 2 | 3 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import yj.sansui.bean.entity.UserEntity; 7 | 8 | /** 9 | * @author sansui 10 | */ 11 | @Mapper 12 | public interface ShopUserMapper extends BaseMapper{ 13 | } 14 | -------------------------------------------------------------------------------- /vue/src/assets/token/token.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | import constant from "@/assets/globalvariable/constant"; 3 | export function getToken() { 4 | return Cookies.get(constant.tokenKey) 5 | } 6 | 7 | export function setToken(token) { 8 | return Cookies.set(constant.tokenKey, token) 9 | } 10 | 11 | export function removeToken() { 12 | return Cookies.remove(constant.tokenKey) 13 | } 14 | -------------------------------------------------------------------------------- /shop-user/src/main/java/yj/sansui/ShopUserApplication.java: -------------------------------------------------------------------------------- 1 | package yj.sansui; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ShopUserApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(ShopUserApplication.class,args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/result/StatusCode.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.result; 2 | 3 | /** 4 | * StatusCode,获取状态码和状态信息 5 | * 6 | * @author sansui 7 | */ 8 | public interface StatusCode { 9 | /** 10 | * 获取状态码 11 | * 12 | * @return code 13 | */ 14 | Integer getCode(); 15 | 16 | /** 17 | * 获取状态信息 18 | * 19 | * @return message 20 | */ 21 | String getMessage(); 22 | } 23 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/UserApplication.java: -------------------------------------------------------------------------------- 1 | package yj.sansui; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author sansui 8 | */ 9 | @SpringBootApplication 10 | public class UserApplication { 11 | public static void main(String[] args) { 12 | SpringApplication.run(UserApplication.class,args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /vue/README.md: -------------------------------------------------------------------------------- 1 | # demo 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your unit tests 19 | ``` 20 | npm run test:unit 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /shop-user/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | 4 | spring: 5 | datasource: 6 | driver-class-name: com.mysql.cj.jdbc.Driver 7 | url: jdbc:mysql://localhost:3306/shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false 8 | username: root 9 | password: Poison0116 10 | 11 | jwt: 12 | expires-time: 30 13 | 14 | logging: 15 | config: 16 | classpath: logback-spring.xml 17 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/constant/Constant.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.constant; 2 | 3 | import yj.sansui.config.ServerConfig; 4 | 5 | import javax.annotation.Resource; 6 | 7 | /** 8 | * @author sansui 9 | */ 10 | public class Constant { 11 | /** 12 | * ACTIVE_URL,激活 13 | */ 14 | public static String ACTIVE_URL = ServerConfig.getBaseUrl()+"/active?code="; 15 | public static String LOGIN_URL=ServerConfig.getAddress()+":8080"; 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/bean/dto/RoleMenuDTO.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.bean.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * RoleMenuDTO,角色菜单对应关系 10 | * @author zpli 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class RoleMenuDTO { 17 | private Integer roleId; 18 | private Integer menuId; 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/bean/dto/UserRoleDTO.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.bean.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * UserRoleDTO,用户角色对应关系 10 | * @author zpli 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class UserRoleDTO { 17 | private Integer userId; 18 | private Integer roleId; 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/pack/NotControllerResponse.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.pack; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * NotControllerResponse,自定义注解,无需封装 10 | * 11 | * @author sansui 12 | */ 13 | @Target({ElementType.METHOD}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface NotControllerResponse { 16 | } 17 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.*; 5 | import yj.sansui.bean.entity.User; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author sansui 11 | */ 12 | @Mapper 13 | public interface UserMapper extends BaseMapper { 14 | @Select("select * from user where user.user_id = #{id}") 15 | User getUser(@Param("id") Integer id); 16 | } 17 | -------------------------------------------------------------------------------- /vue/src/main.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { createApp } from 'vue' 3 | import App from './App.vue' 4 | import router from './router' 5 | import store from './store' 6 | import ElementPlus from 'element-plus' 7 | import 'element-plus/dist/index.css' 8 | import request from "@/utils/request"; 9 | import './permission' 10 | 11 | 12 | 13 | createApp(App) 14 | .use(store) 15 | .use(router) 16 | .use(ElementPlus) 17 | .use("axios",axios) 18 | .use(request) 19 | .mount('#app') 20 | -------------------------------------------------------------------------------- /jwt/src/main/java/yj/sansui/annotation/PassVerify.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 如果接口带这个注解,跳过token认证 10 | * @author Sansui 11 | */ 12 | @Target({ElementType.METHOD, ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface PassVerify { 15 | boolean required() default true; 16 | } 17 | -------------------------------------------------------------------------------- /vue/src/assets/login/js/password.js: -------------------------------------------------------------------------------- 1 | export function getRandomPassword (len) { 2 | const $chars1 = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz' /** 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1**/ 3 | const $chars2='2345678!,.?' 4 | const maxPos1 = $chars1.length 5 | const maxPos2 = $chars2.length 6 | let pwd = '' 7 | for (let i = 0; i < len/2; i++) { 8 | pwd += $chars1.charAt(Math.floor(Math.random() * maxPos1)) 9 | } 10 | for (let i = 0; i < len/2; i++) { 11 | pwd += $chars2.charAt(Math.floor(Math.random() * maxPos2)) 12 | } 13 | return pwd 14 | } 15 | -------------------------------------------------------------------------------- /vue/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | module.exports = defineConfig({ 3 | transpileDependencies: true, 4 | // devServer: { 5 | // open: true, 6 | // port: 8081, 7 | // //以上的ip和端口是我们本机的;下面为需要跨域的 8 | // proxy: { //配置跨域 9 | // '/apis': { 10 | // target: 'http://localhost:8082/', //这里后台的地址模拟的;应该填写你们真实的后台接口 11 | // ws: true, 12 | // changOrigin: true, //允许跨域 13 | // pathRewrite: { 14 | // '^/apis': '' //请求的时候使用这个api就可以 15 | // } 16 | // } 17 | // } 18 | // }, 19 | }) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## CommonLibrary 2 | 3 | > 项目地址:https://github.com/DreamNextHeart/CommonLibrary 4 | 5 | ### 项目介绍 6 | 7 | 一个项目中有许多方法,相应的也有许多的结果返回,同时在业务逻辑中还有着相应的异常响应及参数校验等情况,对于初学项目者而言极度不友好,因此创建了本项目想要做一个简单的小轮子,以最容易理解的解决方案来解决这种情况。 8 | 9 | 因此本项目提供 10 | 11 | - 统一结果返回 12 | - 统一异常处理 13 | - 统一参数检验 14 | - 快速集成第三方框架 15 | 16 | 补充:本项目适用于初学者 17 | 18 | ### 项目层次包 19 | 20 |  21 | 22 | ### Version 23 | 24 | - 1.0.0:统一封装、返回结果,统一异常处理,统一参数处理、集成JWT框架、集成redis框架、加入项目类详细介绍 25 | 26 | 该版本使用请使用模块shop-user进行测试 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/bean/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.bean.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * UserDTO,登录信息 10 | * @author sansui 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class UserDTO { 17 | private String username; 18 | private String email; 19 | private String phone; 20 | private String password; 21 | private String code; 22 | private String token; 23 | private String activeCode; 24 | } 25 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/service/MenuService.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import yj.sansui.bean.entity.Menu; 5 | 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | public interface MenuService extends IService { 10 | /** 11 | * getChildrenList 获取子菜单 12 | * @param menu 菜单 13 | * @param menuSet 菜单列表 14 | * @return Menu 15 | */ 16 | Menu getChildren(Menu menu, Set menuSet); 17 | 18 | /** 19 | * getMenuTree 20 | * @return 21 | */ 22 | Set getMenuTree(); 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /redis/src/main/java/yj/sansui/version2/RedisConstant.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.version2; 2 | 3 | /** 4 | * RedisConstant,redis的key常量 5 | * @author sansui 6 | */ 7 | public class RedisConstant { 8 | /** time是作为时间常量 */ 9 | public static long time= 2592000; 10 | 11 | public static String LAST_ACTIVITY="satoken:login:last-activity:"; 12 | public static String SESSION="satoken:login:session:"; 13 | 14 | /** 常量,表示一个key永不过期 (在一个key被标注为永远不过期时返回此值) */ 15 | public static final long NEVER_EXPIRE = -1; 16 | 17 | /** 常量,表示系统中不存在这个缓存 (在对不存在的key获取剩余存活时间时返回此值) */ 18 | public static final long NOT_VALUE_EXPIRE = -2; 19 | } 20 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/service/RoleService.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import yj.sansui.bean.entity.Role; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * @author zpli 10 | */ 11 | public interface RoleService extends IService { 12 | /** 13 | * getRoleByUser,根据User的id获取Role 14 | * @return Role 15 | */ 16 | Role getRoleByUserId(Integer id); 17 | 18 | /** 19 | * getPermsList 获取权限列表 20 | * @param id user的id 21 | * @return getPermsList 22 | */ 23 | ArrayList getPermsList(Integer id); 24 | } 25 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/mapper/RoleMapper.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.apache.ibatis.annotations.Select; 7 | import yj.sansui.bean.entity.Role; 8 | 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | @Mapper 13 | public interface RoleMapper extends BaseMapper { 14 | @Select("select role.* from user_role,role where user_role.user_id = #{user_id} and role.role_id = user_role.role_id") 15 | Set returnRole(@Param("user_id") Integer id); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/controller/RoleController.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.controller; 2 | import org.springframework.web.bind.annotation.GetMapping; 3 | import org.springframework.web.bind.annotation.RestController; 4 | import yj.sansui.bean.entity.User; 5 | import yj.sansui.service.RoleService; 6 | 7 | import javax.annotation.Resource; 8 | import java.util.ArrayList; 9 | 10 | 11 | /** 12 | * @author sansui 13 | */ 14 | 15 | public class RoleController { 16 | @Resource 17 | RoleService roleService; 18 | @GetMapping("/getPermsList") 19 | public ArrayList getPermsList(Integer id){ 20 | return roleService.getPermsList(id); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /user/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | 4 | spring: 5 | datasource: 6 | driver-class-name: com.mysql.cj.jdbc.Driver 7 | url: jdbc:mysql://localhost:3306/sa-token?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false 8 | username: root 9 | password: Poison0116 10 | # mybatis-plus: 11 | # configuration: 12 | # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 13 | 14 | sa-token: 15 | # 设置7天活跃有效期,自动续签 16 | activity-timeout: 604800 17 | # 永久 18 | timeout: -1 19 | token-style: simple-uuid 20 | 21 | mybatis-plus: 22 | configuration: 23 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 24 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/mapper/MenuMapper.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.apache.ibatis.annotations.Select; 7 | import yj.sansui.bean.entity.Menu; 8 | 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | /** 13 | * @author zpli 14 | */ 15 | @Mapper 16 | public interface MenuMapper extends BaseMapper { 17 | @Select("select menu.* from role_menu,menu where role_menu.role_id = #{role_id} and menu.menu_id = role_menu.menu_id") 18 | Set returnMenu(@Param("role_id") Integer id); 19 | } 20 | -------------------------------------------------------------------------------- /vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue. 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/controller/MenuController.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.controller; 2 | 3 | import org.springframework.web.bind.annotation.CrossOrigin; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | import yj.sansui.bean.entity.Menu; 7 | import yj.sansui.service.MenuService; 8 | import javax.annotation.Resource; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | 13 | @RestController 14 | public class MenuController { 15 | @Resource 16 | MenuService menuService; 17 | 18 | @GetMapping("/getMenuTree") 19 | public Set getMenuTree(){ 20 | return menuService.getMenuTree(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/bean/entity/UserEntity.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.bean.entity; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import javax.validation.constraints.Min; 9 | import javax.validation.constraints.NotNull; 10 | 11 | 12 | /** 13 | * UserEntity 用户实体类,对应版本1.0.0 14 | * @author Sansui 15 | */ 16 | @Data 17 | @TableName(value = "shop_user") 18 | public class UserEntity { 19 | /** 20 | * id 主键,用户的唯一标识,有唯一性,不可重复 21 | */ 22 | @TableId 23 | private Integer id; 24 | 25 | /** 26 | * name,用户姓名,不允许为空 27 | */ 28 | @NotNull(message = "名字不允许为空") 29 | private String name; 30 | 31 | /** 32 | * age,用户年龄,最小值为0,不允许为负数 33 | */ 34 | @Min(value = 0,message = "年龄不允许为负数") 35 | private Integer age; 36 | } 37 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | /** 8 | * CorsConfig,跨域配置 9 | * @author sansui 10 | */ 11 | @Configuration 12 | public class CorsConfig implements WebMvcConfigurer { 13 | 14 | @Override 15 | public void addCorsMappings(CorsRegistry registry){ 16 | registry.addMapping("/**") 17 | //是否发送Cookie 18 | .allowCredentials(true) 19 | //放行哪些原始域 20 | .allowedOrigins("*") 21 | .allowedMethods("GET", "POST", "PUT", "DELETE") 22 | .allowedHeaders("*") 23 | .exposedHeaders("*"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/bean/entity/Role.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.bean.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import javax.validation.constraints.NotNull; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | /** 16 | * Role,角色表 17 | * @author sansui 18 | */ 19 | @Data 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | public class Role { 24 | /** 25 | * roleId:角色id 26 | * roleName:角色名 27 | */ 28 | @TableId(value = "role_id",type = IdType.AUTO) 29 | private Integer roleId; 30 | 31 | @NotNull(message = "角色名不允许为空") 32 | private String roleName; 33 | } 34 | -------------------------------------------------------------------------------- /jwt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | CommonLibrary 7 | yj.sansui 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | jwt 13 | 14 | 15 | 16 | yj.sansui 17 | common 18 | 1.0.0 19 | 20 | 21 | com.auth0 22 | java-jwt 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/result/ResultCode.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.result; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * ResultCode,枚举类,自定义响应结果的响应值和响应信息 7 | * @author Sansui 8 | * @annotation Getter,自动根据字段生成get方法 9 | */ 10 | @Getter 11 | public enum ResultCode implements StatusCode{ 12 | /** 13 | * SUCCESS:请求成功 14 | * FAILED:请求失败 15 | * VALIDATE_ERROR:参数校验错误 16 | * RESPONSE_PACK_ERROR:response结果封装错误 17 | */ 18 | SUCCESS(200,"请求成功"), 19 | FAILED(201,"请求失败"), 20 | VALIDATE_ERROR(202,"参数校验错误"); 21 | 22 | /** 23 | * code,状态码 24 | */ 25 | private Integer code; 26 | 27 | /** 28 | * message,响应信息 29 | */ 30 | private String message; 31 | 32 | /** 33 | * 全参构造方法 34 | * @param code Integer 35 | * @param message String 36 | */ 37 | ResultCode(Integer code,String message){ 38 | this.code=code; 39 | this.message=message; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /vue/src/utils/request.js: -------------------------------------------------------------------------------- 1 | /*引入axios*/ 2 | import axios from 'axios' 3 | import {getToken} from "@/assets/token/token"; 4 | 5 | 6 | // 是否显示重新登录 7 | export let isRelogin = {show: false}; 8 | 9 | axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' 10 | 11 | const request = axios.create({ 12 | baseURL: 'http://localhost:8082', // 基础路径,将统一的部分全部封装 13 | timeout: 10000, 14 | withCredentials: true 15 | }) 16 | 17 | // request请求拦截器 18 | request.interceptors.request.use(config => { 19 | console.log("进入request拦截器") 20 | const token = getToken() 21 | if (token) { 22 | config.headers.Authorization = 'Bearer ' + token; 23 | } 24 | 25 | return config; 26 | }, error => Promise.reject(error) 27 | ); 28 | 29 | //前端采用export.default,在写后端代码时用module.export 30 | export default request 31 | 32 | export function get(url){ 33 | return function (params){ 34 | return axios.get(url,{ 35 | params 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vue/src/utils/recursion-router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 方法一:比对路由权限 3 | * 方法二:指定返回的默认路由 4 | */ 5 | 6 | /** 7 | * 8 | * @param {Array} userRouter 后台返回的路由权限json 9 | * @param {Array} allRouter 前端配置好的路由权限数据 10 | * @return {Array} realRoutes 过滤之后的符合条件的路由 11 | */ 12 | 13 | export function recursionRouter(userRouter = [],allRouter = []){ 14 | const realRoutes = []; 15 | allRouter.forEach((v,i) =>{ 16 | userRouter.forEach((item,index) =>{ 17 | if(item.name === v.meta.name){ 18 | if(item.children && item.children.length > 0){ 19 | v.children = recursionRouter(item.children,v.children); 20 | } 21 | realRoutes.push(v) 22 | } 23 | }) 24 | }) 25 | return realRoutes; 26 | } 27 | 28 | 29 | export function setDefaultRoute(routes){ 30 | routes.forEach((v,i) =>{ 31 | if(v.children && v.children.length > 0){ 32 | v.redirect = { name : v.children[0].name} 33 | setDefaultRoute(v.children); 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /jwt/src/main/java/yj/sansui/JwtInterceptorConfig.java: -------------------------------------------------------------------------------- 1 | package yj.sansui; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 7 | 8 | /** 9 | * JwtInterceptorConfig,JWT框架配置类,配置拦截路径并注入Bean 10 | * @author sansui 11 | */ 12 | @Configuration 13 | public class JwtInterceptorConfig extends WebMvcConfigurationSupport { 14 | /** 15 | * 配置拦截路径 16 | * @param registry InterceptorRegistry 17 | */ 18 | @Override 19 | public void addInterceptors(InterceptorRegistry registry) { 20 | //默认拦截所有路径 21 | registry.addInterceptor(authenticationInterceptor()) 22 | .addPathPatterns("/**"); 23 | } 24 | 25 | /** 26 | * 注入Bean 27 | */ 28 | @Bean 29 | public JwtAuthenticationInterceptor authenticationInterceptor() { 30 | return new JwtAuthenticationInterceptor(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /vue/src/views/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{v.title}} 8 | {{vTemp.title}} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 42 | -------------------------------------------------------------------------------- /vue/src/components/side/SideItem.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{ item.meta.title }} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {{ item.meta.title }} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /shop-user/src/main/resources/sql/shop_user.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : root 5 | Source Server Type : MySQL 6 | Source Server Version : 80028 7 | Source Host : localhost:3306 8 | Source Schema : shop 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80028 12 | File Encoding : 65001 13 | 14 | Date: 12/09/2022 14:52:40 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for shop_user 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `shop_user`; 24 | CREATE TABLE `shop_user` ( 25 | `id` int(0) NOT NULL, 26 | `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, 27 | `age` int(0) NULL DEFAULT NULL, 28 | PRIMARY KEY (`id`) USING BTREE 29 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 30 | 31 | -- ---------------------------- 32 | -- Records of shop_user 33 | -- ---------------------------- 34 | INSERT INTO `shop_user` VALUES (3, '2', 1); 35 | 36 | SET FOREIGN_KEY_CHECKS = 1; 37 | -------------------------------------------------------------------------------- /vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.8.3", 12 | "element-plus": "^2.2.17", 13 | "js-cookie": "^3.0.1", 14 | "less-loader": "^11.1.0", 15 | "vue": "^3.2.13", 16 | "vue-router": "^4.0.3", 17 | "vuex": "^4.1.0" 18 | }, 19 | "devDependencies": { 20 | "@vue/cli-plugin-babel": "~5.0.0", 21 | "@vue/cli-plugin-router": "~5.0.0", 22 | "@vue/cli-plugin-unit-mocha": "~5.0.0", 23 | "@vue/cli-plugin-vuex": "~5.0.0", 24 | "@vue/cli-service": "~5.0.0", 25 | "@vue/test-utils": "^2.0.0-0", 26 | "axios": "^0.18.0", 27 | "chai": "^4.2.0", 28 | "less": "^4.1.3", 29 | "nprogress": "^0.2.0", 30 | "sass": "^1.32.7", 31 | "sass-loader": "^12.0.0", 32 | "vue-cli-plugin-axios": "^0.0.4", 33 | "vue-cli-plugin-element": "^1.0.1" 34 | }, 35 | "browserslist": [ 36 | "> 1%", 37 | "last 2 versions", 38 | "not dead", 39 | "not ie 11" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /vue/src/assets/login/js/cookie.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | import constant from "@/assets/globalvariable/constant"; 3 | 4 | 5 | 6 | export function setCookie (loginForm) { 7 | if(loginForm.remember===true){ 8 | Cookies.set(constant.phone, loginForm.phone, { expires: constant.expires }) 9 | Cookies.set(constant.tokenKey, loginForm.token, { expires: constant.expires }) 10 | Cookies.set(constant.remember, loginForm.remember, { expires: constant.expires }) 11 | }else { 12 | Cookies.set(constant.tokenKey, loginForm.token, { expires: constant.expires }) 13 | } 14 | } 15 | 16 | export function getCookie (loginForm) { 17 | loginForm.phone = Cookies.get(constant.phone) 18 | loginForm.token = Cookies.get(constant.tokenKey) 19 | loginForm.remember=Boolean(Cookies.get(constant.remember)) 20 | } 21 | 22 | 23 | export function getCookieRemember(TempForm){ 24 | TempForm.remember=Boolean(Cookies.get(constant.remember)) 25 | } 26 | 27 | export function clearCookie () { 28 | Cookies.remove(constant.phone) 29 | Cookies.remove(constant.tokenKey) 30 | Cookies.remove(constant.remember) 31 | } 32 | 33 | export function getMessage (tempForm) { 34 | tempForm.message = Cookies.get('message') 35 | } 36 | 37 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/exception/CommonException.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.exception; 2 | 3 | import lombok.Getter; 4 | import yj.sansui.result.StatusCode; 5 | 6 | /** 7 | * CommonException,通用异常类 8 | * 9 | * @author Sansui 10 | * @annotation Getter 自动生成get方法 11 | */ 12 | @Getter 13 | public class CommonException extends RuntimeException { 14 | /** 15 | * code,异常码 16 | * message,异常信息 17 | */ 18 | private Integer code; 19 | private String message; 20 | 21 | /** 22 | * 全参构造函数,用于手动设置抛出异常 23 | * 24 | * @param statusCode StatusCode 25 | * @param message String 26 | */ 27 | public CommonException(StatusCode statusCode, String message) { 28 | // message用于用户设置抛出错误详情,例如:当前价格-5,小于0,给开发人员看的 29 | super(message); 30 | // 异常码 31 | this.code = statusCode.getCode(); 32 | // 异常码配套的msg 33 | this.message = statusCode.getMessage(); 34 | } 35 | 36 | /** 37 | * 构造函数,默认异常使用BUSINESS_ERROR状态码 38 | * 39 | * @param message String 40 | */ 41 | public CommonException(String message) { 42 | super(message); 43 | this.code = ExceptionCode.BUSINESS_EXCEPTION.getCode(); 44 | this.message = message; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/service/UserService.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import yj.sansui.bean.dto.UserDTO; 6 | import yj.sansui.bean.entity.Role; 7 | import yj.sansui.bean.entity.User; 8 | import yj.sansui.result.Result; 9 | 10 | import java.util.Map; 11 | 12 | /** 13 | * @author sansui 14 | */ 15 | public interface UserService extends IService { 16 | /** 17 | * loginInNotRemember,登录服务,不记住密码,检验账号密码 18 | * @param userDTO 19 | * @return 20 | */ 21 | Map loginInNotRemember(UserDTO userDTO); 22 | 23 | /** 24 | * loginInRemember,登录服务,记住密码,检验账号和token 25 | * @param userDTO 26 | * @return 27 | */ 28 | Map loginInRemember(UserDTO userDTO); 29 | 30 | /** 31 | * register,注册服务 32 | * @param userDTO 33 | * @return 34 | */ 35 | Result register(UserDTO userDTO); 36 | 37 | /** 38 | * active,激活服务 39 | * @param code 激活码 40 | * @return 41 | */ 42 | Result active(String code); 43 | 44 | /** 45 | * getUserInfo,根据token获取用户信息 46 | * @param token 47 | * @return User对象 48 | */ 49 | User getUserInfo(String token); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /redis/src/main/java/yj/sansui/version1/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.version1; 2 | 3 | 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; 8 | import org.springframework.data.redis.serializer.StringRedisSerializer; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * RedisConfigBySansui redis配置类,配置key、hashKey、value、hashValue的序列化方式 14 | * @author Sansui 15 | */ 16 | @Configuration 17 | public class RedisConfig { 18 | @Resource 19 | private RedisTemplate redisTemplate; 20 | 21 | @Bean 22 | public RedisTemplate redisTemplateInit() { 23 | //序列化key的实例化对象 24 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 25 | //序列化hashKey的实例化对象 26 | redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 27 | //序列化value的实例化对象 28 | redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); 29 | //序列化hashValue的实例化对象 30 | redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); 31 | return redisTemplate; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/bean/entity/Menu.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.bean.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import javax.validation.constraints.NotNull; 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | /** 17 | * @author zpli 18 | */ 19 | @Data 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @TableName("menu") 24 | public class Menu { 25 | /** 26 | * menuId:菜单id 27 | * menuName:菜单名 28 | * parentId:父级菜单id 29 | * perms:权限 30 | * childrenList: 树状菜单导航栏 31 | */ 32 | @TableId(value = "menu_id",type = IdType.AUTO) 33 | private Integer menuId; 34 | 35 | @NotNull(message = "菜单名不允许为空") 36 | private String menuName; 37 | 38 | @NotNull(message = "父菜单id不允许为空") 39 | private Integer parentId; 40 | 41 | @NotNull(message = "组件不允许为空") 42 | private String compoent; 43 | 44 | @NotNull(message = "权限不允许为空") 45 | private String perms; 46 | 47 | @TableField(exist = false) 48 | private Set childrenSet = new HashSet<>(); 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/service/impl/PermissionRoleImpl.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service.impl; 2 | 3 | import cn.dev33.satoken.stp.StpInterface; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * PermissionRoleImpl,获取权限角色列表 11 | * @author sansui 12 | */ 13 | @Service 14 | public class PermissionRoleImpl implements StpInterface { 15 | 16 | /** 17 | * 返回指定账号id所拥有的权限码集合 18 | * 19 | * @param loginId 账号id 20 | * @param loginType 账号类型 21 | * @return 该账号id具有的权限码集合 22 | */ 23 | @Override 24 | public List getPermissionList(Object loginId, String loginType) { 25 | List list=new ArrayList<>(); 26 | list.add("101"); 27 | list.add("user-add"); 28 | list.add("user-delete"); 29 | list.add("user-update"); 30 | list.add("user-get"); 31 | list.add("article-get"); 32 | return list; 33 | } 34 | 35 | /** 36 | * 返回指定账号id所拥有的角色标识集合 37 | * 38 | * @param loginId 账号id 39 | * @param loginType 账号类型 40 | * @return 该账号id具有的角色标识集合 41 | */ 42 | @Override 43 | public List getRoleList(Object loginId, String loginType) { 44 | List list = new ArrayList<>(); 45 | list.add("admin"); 46 | list.add("super-admin"); 47 | return list; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /redis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | CommonLibrary 7 | yj.sansui 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | redis 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-data-redis 18 | 19 | 20 | org.apache.commons 21 | commons-pool2 22 | 23 | 24 | com.fasterxml.jackson.core 25 | jackson-annotations 26 | 27 | 28 | com.fasterxml.jackson.core 29 | jackson-databind 30 | 31 | 32 | com.fasterxml.jackson.datatype 33 | jackson-datatype-jsr310 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /vue/src/api/user/userApi.js: -------------------------------------------------------------------------------- 1 | import request, {get} from '@/utils/request' 2 | 3 | // 获取验证码 4 | export function getCodeImg () { 5 | return request({ 6 | url: '/getCode', 7 | method: 'get', 8 | responseType: 'blob' 9 | }) 10 | } 11 | 12 | 13 | // 登录不记住密码 14 | export function loginInNotRemember (loginForm) { 15 | // const data={ 16 | // phone, 17 | // password, 18 | // code 19 | // } 20 | return request({ 21 | url: '/loginInNotRemember', 22 | method: 'post', 23 | params: loginForm 24 | }) 25 | } 26 | 27 | // 登录记住密码 28 | export function loginInRemember (loginForm) { 29 | // const data={ 30 | // phone, 31 | // token 32 | // } 33 | return request({ 34 | url: '/loginInRemember', 35 | method: 'post', 36 | params: loginForm 37 | }) 38 | } 39 | 40 | //匹配权限 41 | export function fetchPermission(){ 42 | return request({ 43 | url: '/getPermission', 44 | method: 'get' 45 | }) 46 | } 47 | 48 | //获取用户信息 49 | export function getUserInfo(token){ 50 | return request({ 51 | url: '/getUserInfo', 52 | method: 'get', 53 | params: { 54 | token: token 55 | } 56 | }) 57 | } 58 | 59 | 60 | //登出 61 | export function logout(token){ 62 | return request({ 63 | url: '/logout', 64 | method: 'get', 65 | params: token 66 | }) 67 | } 68 | 69 | //注册 70 | export function registerApi(registerForm){ 71 | return request({ 72 | url: '/register', 73 | method: 'post', 74 | params: registerForm 75 | }) 76 | } 77 | 78 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/exception/ExceptionInterceptor.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.exception; 2 | 3 | 4 | import org.springframework.validation.BindException; 5 | import org.springframework.validation.ObjectError; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.RestControllerAdvice; 8 | import yj.sansui.result.Result; 9 | import yj.sansui.result.ResultCode; 10 | 11 | 12 | /** 13 | * ExceptionInterceptor,异常拦截器 14 | * 拦截异常后封装成Result返回结果 15 | * 16 | * @author Sansui 17 | * @annotation RestControllerAdvice 默认全局拦截器 18 | */ 19 | @RestControllerAdvice 20 | public class ExceptionInterceptor { 21 | 22 | /** 23 | * methodArgumentNotValidExceptionHandler,方法参数无效异常处理器(可重载,参数为异常类) 24 | * 25 | * @param e BindException 26 | * @return Result 27 | * @annotation ExceptionHandler 异常处理器 28 | */ 29 | @ExceptionHandler({BindException.class}) 30 | public Result methodArgumentNotValidExceptionHandler(BindException e) { 31 | // 从异常对象中拿到ObjectError对象 32 | ObjectError objectError = e.getBindingResult().getAllErrors().get(0); 33 | return new Result(ResultCode.VALIDATE_ERROR, objectError.getDefaultMessage()); 34 | } 35 | 36 | /** 37 | * commonExceptionHandler,通用异常处理器 38 | * 39 | * @param e CommonException 40 | * @return Result 41 | */ 42 | @ExceptionHandler(CommonException.class) 43 | public Result commonExceptionHandler(CommonException e) { 44 | return new Result(e.getCode(), e.getMessage()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | CommonLibrary 7 | yj.sansui 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | common 13 | 14 | 15 | 16 | yj.sansui 17 | redis 18 | 1.0.0 19 | 20 | 21 | org.projectlombok 22 | lombok 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | com.baomidou 30 | mybatis-plus-boot-starter 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-validation 35 | 36 | 37 | com.sun.mail 38 | javax.mail 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /vue/src/plugins/axios.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import Vue from 'vue'; 4 | import axios from "axios"; 5 | 6 | // Full config: https://github.com/axios/axios#request-config 7 | // axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || ''; 8 | // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; 9 | // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; 10 | 11 | let config = { 12 | // baseURL: process.env.baseURL || process.env.apiUrl || "" 13 | // timeout: 60 * 1000, // Timeout 14 | // withCredentials: true, // Check cross-site Access-Control 15 | }; 16 | 17 | const _axios = axios.create(config); 18 | 19 | _axios.interceptors.request.use( 20 | function(config) { 21 | // Do something before request is sent 22 | return config; 23 | }, 24 | function(error) { 25 | // Do something with request error 26 | return Promise.reject(error); 27 | } 28 | ); 29 | 30 | // Add a response interceptor 31 | _axios.interceptors.response.use( 32 | function(response) { 33 | // Do something with response data 34 | return response; 35 | }, 36 | function(error) { 37 | // Do something with response error 38 | return Promise.reject(error); 39 | } 40 | ); 41 | 42 | Plugin.install = function(Vue, options) { 43 | Vue.axios = _axios; 44 | window.axios = _axios; 45 | Object.defineProperties(Vue.prototype, { 46 | axios: { 47 | get() { 48 | return _axios; 49 | } 50 | }, 51 | $axios: { 52 | get() { 53 | return _axios; 54 | } 55 | }, 56 | }); 57 | }; 58 | 59 | Vue.use(Plugin) 60 | 61 | export default Plugin; 62 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/utils/TkipUtil.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.utils; 2 | 3 | import java.math.BigInteger; 4 | import java.nio.charset.StandardCharsets; 5 | import java.security.MessageDigest; 6 | import java.util.Random; 7 | 8 | /** 9 | * TkipUtil,加密加盐工具类 10 | * @author sansui 11 | */ 12 | public class TkipUtil { 13 | 14 | /** 15 | * degst,加密算法 16 | * @param str String,接收传入的明文密码 17 | * @return 输出密文 18 | */ 19 | public static String degst(String str){ 20 | try { 21 | MessageDigest md5 = MessageDigest.getInstance("md5"); 22 | byte[] bb = md5.digest(str.getBytes(StandardCharsets.UTF_8)); 23 | // 1:不需要正负号,32:输出三十二进制数据 24 | String r = new BigInteger(1, bb).toString(32); 25 | System.out.println("第一次加密"+r); 26 | return r; 27 | } catch (Exception e) { 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | 32 | /** 33 | * getSalt获取盐值 34 | * @return salt 盐值 35 | */ 36 | public static String getSalt(){ 37 | String salt=""; 38 | int saltNumber=5; 39 | String[] saltStr={"0","1","2","3","4","5","6","7","8","9", 40 | "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z", 41 | "A","B","C", "D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}; 42 | for(int i=0;i roles; 63 | 64 | @TableField(exist = false) 65 | private Set perms; 66 | } 67 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/exception/ExceptionCode.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.exception; 2 | 3 | import lombok.Getter; 4 | import yj.sansui.result.StatusCode; 5 | 6 | 7 | /** 8 | * ExceptionCode 异常码枚举类 9 | * 10 | * @author Sansui 11 | * @annotation Getter 自动生成get方法 12 | */ 13 | @Getter 14 | public enum ExceptionCode implements StatusCode { 15 | /** 16 | * BUSINESS_EXCEPTION:业务异常 17 | * PRICE_EXCEPTION:价格异常 18 | * ID_NULL_EXCEPTION:id空异常 19 | * SIGNATURE_NOT_MATCH:签名不匹配 20 | */ 21 | BUSINESS_EXCEPTION(300, "业务异常"), 22 | PRICE_EXCEPTION(301, "价格异常"), 23 | ID_NULL_EXCEPTION(302, "id空异常"), 24 | SIGNATURE_NOT_MATCH(303, "签名不匹配"), 25 | SIGNATURE_IS_NULL(304, "签名为空"), 26 | SIGNATURE_DECODE_EXCEPTION(305, "签名解析失败"), 27 | DELETE_EXCEPTION(306,"删除失败"), 28 | SAVE_EXCEPTION(307,"保存失败"), 29 | UPDATE_EXCEPTION(308,"更新失败"), 30 | SELECT_EXCEPTION(309,"查询失败"), 31 | CODE_EXCEPTION(310,"验证码错误"), 32 | USERNAME_EXCEPTION(311,"用户名或密码错误"), 33 | TOKEN_OUT_OF_DATE(312,"token已过期"), 34 | PHONE_EXIST(313,"手机号码已被注册"), 35 | SEND_MAIL_ERROR(314,"邮件发送失败"), 36 | ACTIVE_CODE_INEXISTENCE(315,"激活码不存在"), 37 | RESPONSE_PACK_ERROR(316,"response结果封装错误"), 38 | ACCOUNT_UN_ACTIVE(317,"账号未激活"); 39 | 40 | 41 | /** 42 | * code,异常码,用于标识异常类型 43 | * message,异常信息,用于解释异常详细信息 44 | */ 45 | private Integer code; 46 | private String message; 47 | 48 | /** 49 | * 全参构造函数 50 | * 51 | * @param code Integer 52 | * @param message String 53 | */ 54 | ExceptionCode(Integer code, String message) { 55 | this.code = code; 56 | this.message = message; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /vue/src/utils/permission.js: -------------------------------------------------------------------------------- 1 | import {fetchPermission} from "@/api/user/userApi"; 2 | import {recursionRouter, setDefaultRoute} from "@/utils/recursion-router"; 3 | import dynamicRoutes from "@/router/dynamic-router"; 4 | import router, {DynamicRoutes} from "@/router"; 5 | 6 | export default { 7 | namespace: true, 8 | state: { 9 | permissionList: null, 10 | sidebarMenu: [],//导航菜单 11 | currentMenu: ''//高亮选中 12 | }, 13 | getters: {}, 14 | mutations: { 15 | SET_PERMISSION(state,routes){ 16 | state.permissionList = routes; 17 | }, 18 | CLEAR_PERMSSION(state){ 19 | state.permissionList = null; 20 | }, 21 | SET_MENU(state,menu){ 22 | state.sidebarMenu = menu; 23 | }, 24 | CLEAR_MENU(state){ 25 | state.sidebarMenu = [] 26 | } 27 | }, 28 | 29 | //异步访问 30 | actions: { 31 | async FETCH_PERMISSION({commit,state}){ 32 | const permissionList = await fetchPermission(); 33 | //筛选 34 | const routes=recursionRouter(permissionList,dynamicRoutes); 35 | const MainContainer=DynamicRoutes.find(v=> v.path===""); 36 | const children=MainContainer.children; 37 | children.push(...routes); 38 | 39 | //生成菜单 40 | commit("SET_MENU",children); 41 | 42 | //设置默认路由 43 | setDefaultRoute([MainContainer]); 44 | 45 | // 初始化路由 46 | const initialRoutes = router.options.routes; 47 | router.addRoutes(DynamicRoutes); 48 | 49 | commit("SET_PERMISSION",[ ...initialRoutes , ...DynamicRoutes]) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /shop-user/src/main/java/yj/sansui/service/ShopUserService.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import yj.sansui.bean.entity.UserEntity; 6 | import yj.sansui.result.Result; 7 | 8 | import java.util.List; 9 | 10 | 11 | /** 12 | * UserService,定义User业务方法逻辑的方法接口 13 | * @author Sansui 14 | */ 15 | public interface ShopUserService extends IService { 16 | /** 17 | * selectById,输入id,查找userEntity 18 | * @param id Integer 19 | * @return Result 20 | */ 21 | Result selectById(Integer id); 22 | 23 | /** 24 | * saveUser,输入User,保存到数据库中 25 | * @param userEntity UserEntity 26 | * @return true/false 27 | */ 28 | boolean saveUser(UserEntity userEntity); 29 | 30 | /** 31 | * getUserById,输入id,查找userEntity 32 | * @param id Integer 33 | * @return UserEntity 34 | */ 35 | UserEntity getUserById(Integer id); 36 | 37 | /** 38 | * saveUserEntity,保存User,带Redis 39 | * @param userEntity UserEntity 40 | * @return true/false 41 | */ 42 | boolean saveUserEntity(UserEntity userEntity); 43 | 44 | /** 45 | * deleteById,根据id删除User,带Redis 46 | * @param id Integer 47 | * @return true/false 48 | */ 49 | boolean deleteById(Integer id); 50 | 51 | /** 52 | * updateUser,更新User资料,带Redis 53 | * @param userEntity UserEntity 54 | * @return true/false 55 | */ 56 | boolean updateUser(UserEntity userEntity); 57 | 58 | /** 59 | * selectAllSmall,查找全部,小厂可用,QPS<=1000 60 | * @return List 61 | */ 62 | List selectAllSmall(); 63 | 64 | /** 65 | * selectAllBig,查找全部,大厂可用,QPS>1000 66 | * @return List 67 | */ 68 | List selectAllBig(); 69 | 70 | } 71 | -------------------------------------------------------------------------------- /CommonLibrary.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/service/impl/RoleServiceImpl.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import org.springframework.stereotype.Service; 5 | import yj.sansui.bean.entity.Menu; 6 | import yj.sansui.bean.entity.Role; 7 | import yj.sansui.bean.entity.User; 8 | import yj.sansui.mapper.MenuMapper; 9 | import yj.sansui.mapper.RoleMapper; 10 | import yj.sansui.mapper.UserMapper; 11 | import yj.sansui.service.RoleService; 12 | 13 | import javax.annotation.Resource; 14 | import java.util.ArrayList; 15 | import java.util.HashSet; 16 | import java.util.Set; 17 | 18 | @Service 19 | public class RoleServiceImpl extends ServiceImpl implements RoleService { 20 | @Resource 21 | public UserMapper userMapper; 22 | @Resource 23 | public RoleMapper roleMapper; 24 | @Resource 25 | public MenuMapper menuMapper; 26 | 27 | /** 28 | * getRoleByUser,根据User的id获取Role 29 | * 30 | * @return Role 31 | */ 32 | @Override 33 | public Role getRoleByUserId(Integer id) { 34 | return null; 35 | } 36 | 37 | /** 38 | * getPermsList 获取权限列表 39 | * @param id user的id 40 | * @return getPermsList 41 | */ 42 | @Override 43 | public ArrayList getPermsList(Integer id) { 44 | User user= userMapper.getUser(id); 45 | Set roles=roleMapper.returnRole(user.getUserId()); 46 | Set menuSet=new HashSet<>(); 47 | ArrayList permsList=new ArrayList<>(); 48 | for(Role role: roles){ 49 | menuSet.addAll(menuMapper.returnMenu(role.getRoleId())); 50 | } 51 | ArrayList menuList=new ArrayList(menuSet); 52 | 53 | for(int temp=0;temp { 15 | @Override 16 | public void onApplicationEvent(WebServerInitializedEvent event) { 17 | // 项目启动获取启动的端口号 18 | ServerConfig.serverPort = event.getWebServer().getPort(); 19 | } 20 | 21 | /** 22 | * 当前服务使用的端口号 23 | */ 24 | private static int serverPort; 25 | 26 | public static int getServerPort() { 27 | return serverPort; 28 | } 29 | 30 | public void setServerPort(int serverPort) { 31 | ServerConfig.serverPort = serverPort; 32 | } 33 | 34 | /** 35 | * 获取请求基地址 36 | * 37 | * @return String 请求基地址 38 | **/ 39 | public static String getBaseUrl() { 40 | InetAddress address = null; 41 | try { 42 | address = InetAddress.getLocalHost(); 43 | } catch (UnknownHostException e) { 44 | e.printStackTrace(); 45 | } 46 | assert address != null; 47 | return "http://"+address.getHostAddress() +":"+ getServerPort(); 48 | } 49 | 50 | /** 51 | * getAddress,获取ip地址 52 | * 53 | * @return String 请求ip地址 54 | **/ 55 | public static String getAddress() { 56 | InetAddress address = null; 57 | try { 58 | address = InetAddress.getLocalHost(); 59 | } catch (UnknownHostException e) { 60 | e.printStackTrace(); 61 | } 62 | assert address != null; 63 | return "http://"+address.getHostAddress(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /user/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | CommonLibrary 7 | yj.sansui 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | user 13 | jar 14 | 15 | 16 | 17 | yj.sansui 18 | common 19 | 1.0.0 20 | 21 | 22 | yj.sansui 23 | redis 24 | 1.0.0 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | com.baomidou 32 | mybatis-plus-boot-starter 33 | 34 | 35 | mysql 36 | mysql-connector-java 37 | 38 | 39 | org.slf4j 40 | slf4j-api 41 | 42 | 43 | cn.dev33 44 | sa-token-spring-boot-starter 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-maven-plugin 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /vue/src/permission.js: -------------------------------------------------------------------------------- 1 | import router, {dynamicRouter, errorRouter} from "@/router"; 2 | import {getToken} from "@/assets/token/token"; 3 | import store from "@/store"; 4 | import {isRelogin} from "@/utils/request"; 5 | 6 | 7 | router.beforeEach((to, from, next) => { 8 | console.log("进入路由守卫") 9 | const token=getToken() 10 | console.log(token) 11 | const excludePath = ['error'] 12 | //存在token 13 | if(token!==null &&excludePath.indexOf(to.name) === -1){ 14 | if(to.path==='/login'||to.path==='/register'){ 15 | console.log("在登陆") 16 | next({path: '/'}) 17 | }else { 18 | console.log("不在登录") 19 | // 判断当前用户是否已拉取完user_info信息 20 | if(store.getters.roles.length===0){ 21 | isRelogin.show = true 22 | //判断当前用户是否已经拉取完user信息 23 | store.dispatch('getInfo').then(response=>{ 24 | console.log(response.data.data.roles) 25 | const roles=response.data.data.roles 26 | isRelogin.show = false 27 | 28 | store.dispatch('GenerateRoutes',{roles}).then(accessedRouters=>{ 29 | console.log("原") 30 | console.log(router.getRoutes()) 31 | errorRouter.forEach(temp => router.addRoute(temp)) 32 | accessedRouters.forEach(temp=>router.addRoute(temp)) 33 | let menuList=router.options.routes[0].children 34 | menuList=menuList.concat(accessedRouters[0].children) 35 | router.options.routes=menuList 36 | console.log(router.options.routes) 37 | next({...to,replace: true}) 38 | }) 39 | }).catch(error=>{ 40 | console.log(error) 41 | }) 42 | }else { 43 | next() 44 | } 45 | } 46 | }else { 47 | next('/login'); 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/result/Result.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.result; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Result 统一响应结果 7 | * 一个通用的响应结果格式应该具备以下信息: 8 | * 1. 响应码:200,404,500等; 9 | * 2. 响应信息:请求成功,查询成功,删除失败等; 10 | * 3. 响应数据:一般为实体对象 11 | * @author Sansui 12 | * @annotation Data,自动根据字段生成get/set、toString等方法 13 | */ 14 | @Data 15 | public class Result { 16 | /** 17 | * code,Integer,响应码 18 | * message,String,响应信息 19 | * data,Object,响应对象 20 | */ 21 | private Integer code; 22 | private String message; 23 | private Object data; 24 | 25 | /** 26 | * 构造器公有 27 | */ 28 | public Result() { 29 | } 30 | 31 | /** 32 | * Result全参构造函数 33 | * @param code Integer 34 | * @param message String 35 | * @param data Object 36 | */ 37 | public Result(Integer code, String message, Object data) { 38 | this.code = code; 39 | this.message = message; 40 | this.data = data; 41 | } 42 | 43 | /** 44 | * 默认构造函数 45 | * 返回成功响应码和响应信息 46 | * @param data Object 47 | */ 48 | public Result(Object data) { 49 | this.code = ResultCode.SUCCESS.getCode(); 50 | this.message = ResultCode.SUCCESS.getMessage(); 51 | this.data = data; 52 | } 53 | 54 | /** 55 | * 返回指定响应码,数据对象 56 | * @param statusCode StatusCode 57 | * @param data Object 58 | */ 59 | public Result(StatusCode statusCode, Object data) { 60 | this.code = statusCode.getCode(); 61 | this.message = statusCode.getMessage(); 62 | this.data = data; 63 | } 64 | 65 | /** 66 | * 只返回响应码 67 | * @param statusCode StatusCode 68 | */ 69 | public Result(StatusCode statusCode) { 70 | this.code = statusCode.getCode(); 71 | this.message = statusCode.getMessage(); 72 | this.data = null; 73 | } 74 | 75 | /** 76 | * 针对抛出异常进行封装返回 77 | * @param code Integer 78 | * @param message String 79 | */ 80 | public Result(Integer code, String message) { 81 | this.code = code; 82 | this.message = message; 83 | this.data = null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /shop-user/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | CommonLibrary 7 | yj.sansui 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | shop-user 13 | 14 | 15 | 16 | yj.sansui 17 | common 18 | 1.0.0 19 | 20 | 21 | yj.sansui 22 | jwt 23 | 1.0.0 24 | 25 | 26 | yj.sansui 27 | redis 28 | 1.0.0 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | com.baomidou 36 | mybatis-plus-boot-starter 37 | 38 | 39 | mysql 40 | mysql-connector-java 41 | 42 | 43 | org.slf4j 44 | slf4j-api 45 | 46 | 47 | ch.qos.logback 48 | logback-core 49 | 50 | 51 | ch.qos.logback 52 | logback-classic 53 | 54 | 55 | com.alibaba 56 | fastjson 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.controller; 2 | 3 | import cn.dev33.satoken.stp.StpUtil; 4 | import org.springframework.web.bind.annotation.*; 5 | import yj.sansui.bean.dto.UserDTO; 6 | import yj.sansui.bean.entity.User; 7 | import yj.sansui.constant.Constant; 8 | import yj.sansui.result.Result; 9 | import yj.sansui.service.UserService; 10 | import yj.sansui.utils.VerifyCode; 11 | 12 | import javax.annotation.Resource; 13 | import javax.servlet.http.Cookie; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | import java.util.Map; 18 | 19 | /** 20 | * @author sansui 21 | */ 22 | 23 | @RestController 24 | public class UserController { 25 | @Resource 26 | public UserService userService; 27 | 28 | @GetMapping("/getCode") 29 | public void getCode(HttpServletResponse response, HttpServletRequest request) throws IOException { 30 | VerifyCode.createCode(response, request); 31 | } 32 | 33 | @PostMapping("/loginInNotRemember") 34 | public Map loginInNotRemember(UserDTO userDTO){ 35 | return userService.loginInNotRemember(userDTO); 36 | } 37 | 38 | @PostMapping("/loginInRemember") 39 | public Map loginInRemember(UserDTO userDTO){ 40 | return userService.loginInRemember(userDTO); 41 | } 42 | 43 | @PostMapping("/register") 44 | public Result register(UserDTO userDTO){ 45 | return userService.register(userDTO); 46 | } 47 | 48 | @GetMapping("/active") 49 | public void active(String code, HttpServletResponse response)throws Exception{ 50 | Result result=userService.active(code); 51 | if(result.getCode()==200){ 52 | Cookie cookie=new Cookie("message","激活成功,请登录"); 53 | response.addCookie(cookie); 54 | response.sendRedirect(Constant.LOGIN_URL); 55 | }else if(result.getCode()==203){ 56 | Cookie cookie=new Cookie("message","账号已激活,请登录"); 57 | response.addCookie(cookie); 58 | response.sendRedirect(Constant.LOGIN_URL); 59 | }else { 60 | response.sendRedirect(""); 61 | } 62 | } 63 | 64 | @GetMapping("/getUserInfo") 65 | public User getUserInfo(String token){ 66 | return userService.getUserInfo(token); 67 | } 68 | 69 | @GetMapping("/") 70 | public String getHealth(){ 71 | return "health"; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/utils/MailUtils.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.utils; 2 | 3 | 4 | import yj.sansui.exception.CommonException; 5 | 6 | import javax.mail.*; 7 | import javax.mail.internet.InternetAddress; 8 | import javax.mail.internet.MimeMessage; 9 | import java.util.Properties; 10 | 11 | /** 12 | * 发邮件工具类 13 | * @author sansui 14 | */ 15 | public final class MailUtils { 16 | /** 17 | * USER,发件人称号,同邮箱地址,根据自己的邮箱账号进行更改 18 | * PASSWORD,授权码,自行百度邮箱授权码如何获取 19 | */ 20 | private static final String USER = "yjsansui@163.com"; 21 | private static final String PASSWORD = "YDCSDEEBTAIUSZAF"; 22 | 23 | /** 24 | * sendMail,发送验证信息的邮件 25 | * @param to 收件人邮箱 26 | * @param text 邮件正文 27 | * @param title 标题 28 | */ 29 | public static boolean sendMail(String to, String text, String title){ 30 | try { 31 | final Properties props = new Properties(); 32 | props.put("mail.smtp.auth", "true"); 33 | //根据自己的邮箱选择路径 34 | props.put("mail.smtp.host", "smtp.163.com"); 35 | 36 | // 发件人的账号 37 | props.put("mail.user", USER); 38 | //发件人的密码 39 | props.put("mail.password", PASSWORD); 40 | 41 | // 构建授权信息,用于进行SMTP进行身份验证 42 | Authenticator authenticator = new Authenticator() { 43 | @Override 44 | protected PasswordAuthentication getPasswordAuthentication() { 45 | // 用户名、密码 46 | String userName = props.getProperty("mail.user"); 47 | String password = props.getProperty("mail.password"); 48 | return new PasswordAuthentication(userName, password); 49 | } 50 | }; 51 | // 使用环境属性和授权信息,创建邮件会话 52 | Session mailSession = Session.getInstance(props, authenticator); 53 | // 创建邮件消息 54 | MimeMessage message = new MimeMessage(mailSession); 55 | // 设置发件人 56 | String username = props.getProperty("mail.user"); 57 | InternetAddress form = new InternetAddress(username); 58 | message.setFrom(form); 59 | 60 | // 设置收件人 61 | InternetAddress toAddress = new InternetAddress(to); 62 | message.setRecipient(Message.RecipientType.TO, toAddress); 63 | 64 | // 设置邮件标题 65 | message.setSubject(title); 66 | 67 | // 设置邮件的内容体 68 | message.setContent(text, "text/html;charset=UTF-8"); 69 | // 发送邮件 70 | Transport.send(message); 71 | return true; 72 | }catch (Exception e){ 73 | e.printStackTrace(); 74 | } 75 | return false; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/service/impl/MenuServiceImpl.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service.impl; 2 | 3 | 4 | import cn.dev33.satoken.stp.StpUtil; 5 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 6 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import org.springframework.stereotype.Service; 9 | import yj.sansui.bean.entity.Menu; 10 | import yj.sansui.bean.entity.Role; 11 | import yj.sansui.bean.entity.User; 12 | import yj.sansui.mapper.MenuMapper; 13 | import yj.sansui.mapper.RoleMapper; 14 | import yj.sansui.mapper.UserMapper; 15 | import yj.sansui.service.MenuService; 16 | import yj.sansui.service.UserService; 17 | import yj.sansui.utils.LibraryUtil; 18 | 19 | import javax.annotation.Resource; 20 | import java.util.ArrayList; 21 | import java.util.HashSet; 22 | import java.util.List; 23 | import java.util.Set; 24 | import java.util.stream.Collectors; 25 | 26 | /** 27 | * @author sansui 28 | */ 29 | @Service 30 | public class MenuServiceImpl extends ServiceImpl implements MenuService { 31 | @Resource 32 | UserMapper userMapper; 33 | 34 | @Resource 35 | RoleMapper roleMapper; 36 | 37 | @Resource 38 | MenuMapper menuMapper; 39 | 40 | @Resource 41 | UserService userService; 42 | 43 | public Set getMenu() { 44 | String phone=(String) StpUtil.getLoginId(); 45 | QueryWrapper wrapper=new QueryWrapper(); 46 | wrapper.eq("phone",phone); 47 | User user=userService.getOne(wrapper); 48 | Set roles = roleMapper.returnRole(user.getUserId()); 49 | Set menuSet = new HashSet<>(); 50 | for (Role role : roles) { 51 | menuSet.addAll(menuMapper.returnMenu(role.getRoleId())); 52 | } 53 | return menuSet; 54 | } 55 | 56 | @Override 57 | public Set getMenuTree() { 58 | Set menuSet = getMenu(); 59 | Set topMenuSet = menuSet.stream().filter( 60 | s -> s.getParentId().equals(0)).collect(Collectors.toSet()); 61 | for (Menu menu : topMenuSet) { 62 | getChildren(menu, menuSet); 63 | } 64 | return topMenuSet; 65 | } 66 | 67 | /** 68 | * getChildrenList 获取子菜单 69 | * 70 | * @param menu 菜单 71 | * @param menuSet 菜单列表 72 | * @return Menu 73 | */ 74 | @Override 75 | public Menu getChildren(Menu menu, Set menuSet) { 76 | for (Menu tempMenu : menuSet) { 77 | if (LibraryUtil.equals(tempMenu.getParentId(), menu.getMenuId())) { 78 | menu.getChildrenSet().add(getChildren(tempMenu, menuSet)); 79 | } 80 | } 81 | return menu; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /vue/src/store/modules/permission.js: -------------------------------------------------------------------------------- 1 | import router, {constantRouter, dynamicRouter} from "@/router"; 2 | 3 | const permission = { 4 | state: { 5 | router: constantRouter, 6 | addRouter: [], 7 | 8 | routes: [], 9 | addRoutes: [], 10 | defaultRoutes: [], 11 | topBarRouters: [], 12 | sideBarRouters: [] 13 | }, 14 | mutations: { 15 | SET_ROUTERS: (state, router) => { 16 | state.addRouter = router; 17 | state.router = constantRouter.concat(router); 18 | }, 19 | 20 | SET_ROUTES: (state, routes) => { 21 | state.addRoutes = routes 22 | //追加、合并、去重数组 23 | state.routes = constantRouter.concat(routes) 24 | }, 25 | SET_DEFAULT_ROUTES: (state, routes) => { 26 | state.defaultRoutes = constantRouter.concat(routes) 27 | }, 28 | SET_TOPBAR_ROUTES: (state, routes) => { 29 | state.topBarRouters = routes 30 | }, 31 | SET_SIDEBAR_ROUTERS: (state, routes) => { 32 | state.sideBarRouters = routes 33 | }, 34 | }, 35 | actions: { 36 | //生成路由 37 | GenerateRoutes({commit}, data) { 38 | return new Promise(resolve => { 39 | const {roles} = data; 40 | const accessedRouters = dynamicRouter.filter(v => { 41 | if (roles.indexOf('super_admin') >= 0) { 42 | console.log("super_admin") 43 | return true; 44 | } 45 | if (hasPermission(roles, v)) { 46 | if (v.children && v.children.length > 0) { 47 | v.children = v.children.filter(child => { 48 | if (hasPermission(roles, child)) { 49 | return child 50 | } 51 | return false 52 | }); 53 | return v 54 | } else { 55 | return v 56 | } 57 | } 58 | return false 59 | }); 60 | commit('SET_ROUTERS', accessedRouters) 61 | resolve(accessedRouters); 62 | }) 63 | } 64 | 65 | } 66 | } 67 | 68 | function hasPermission(roles, route) { 69 | if (route.meta && route.meta.roles) { 70 | /** 71 | * some的作用是检测数组中是否存在指定条件的元素; 72 | * 若存在指定的元素则返回的结果是true, 73 | * 若不存在指定的元素则返回的结果是false 74 | */ 75 | return roles.some(role => route.meta.roles.indexOf(role.roleName) >= 0) 76 | } else { 77 | return true 78 | } 79 | } 80 | 81 | 82 | export default permission 83 | -------------------------------------------------------------------------------- /explain/功能模块开发流程.md: -------------------------------------------------------------------------------- 1 | ## 功能模块流程 2 | 3 | > 本文用于编写各功能模块的开发流程 4 | 5 | ### 注册 6 | 7 | #### 前端 8 | 9 | - 注册表单 10 | - 用户名——用户名字 11 | - 手机号码——用户登录账号 12 | - 邮箱——用户激活的标识 13 | - 密码 14 | - 确认密码 15 | - 验证码 16 | 17 | - rules: 18 | - 用户名 19 | - 不能为空 20 | - 手机号码 21 | - 不能为空 22 | - 11位 23 | - 邮箱 24 | - 不能为空 25 | - xxx@xxx.xxx 26 | - 密码 27 | - 不能为空 28 | - 字母、数字和标点符号至少包含两种 29 | - 确认密码 30 | - 不能为空 31 | - 字母、数字和标点符号至少包含两种 32 | - 与密码相等 33 | 34 | - created() 35 | - getCodeImg():获取验证码图片 36 | - methods() 37 | - handleRegister():注册 38 | - 校验规则需全部通过 39 | - 验证码通过 40 | - 手机号码未被注册 41 | - 注册成功后前往邮箱激活账号 42 | 43 | #### 后端 44 | 45 | - UserServiceImpl 46 | - register 47 | - 校验验证码 48 | - 校验手机号吗是否注册 49 | - 生成激活码 50 | - 生成注册用户 51 | - 保存用户 52 | - 发送激活邮件 53 | 54 | ### 激活 55 | 56 | #### 前端 57 | 58 | - 激活成功 59 | - 重定向登陆界面 60 | - Cookie设置message,激活成功,请登录、账号已激活,请登录 61 | - 激活失败 62 | - 重定向激活失败页面,重新发送邮件 63 | - Cookie设置message,激活失败,请重新激活 64 | 65 | #### 后端 66 | 67 | - 根据激活码查询用户 68 | - 修改字段active(0为未激活,1为激活) 69 | - 设置response的跳转和Cookie 70 | 71 | ### 登录 72 | 73 | #### 前端 74 | 75 | - 表单 76 | - 用户名 77 | - 密码 78 | - 验证码 79 | - 记住密码 80 | - loginForm.remember和tempForm.remember的区别 81 | - loginForm.remember为前端登陆表单中用户选择是否记住密码 82 | - tempForm.remember为每次进入登陆页面查询该设备是否记住密码 83 | - created() 84 | - getMessage():获取信息 85 | - getCookieRemember():是否记住密码 86 | - getCodeImg():获取验证码图片 87 | 88 | - methods(): 89 | - getMessage():获取信息 90 | - 判定有无信息,有则显示,无则无反应 91 | - getCodeImg():获取验证码图片 92 | - 接收后端传来的图片流 93 | - getCookieRemember():是否记住密码 94 | - 判定是否记住密码(tempForm.remember) 95 | - 记住密码(true) 96 | - 从cookie获取用户名,记住密码 97 | - 随机生成10位密码,包括数字、字母、字符三种其中两种 98 | - 关闭验证码 99 | - 不记住密码(undefined) 100 | - 保持原表单 101 | - handleLogin():登录 102 | - tempForm.remember为false 103 | - 调用loginInNotRemember的Api接口 104 | - 310——验证码错误,清空验证码,重新获取验证码 105 | - 311——用户名或密码错误,清空验证码,重新获取验证码 106 | - 317——账号未激活,需要到邮箱处激活 107 | - 200——登陆成功,将token写入cookie 108 | - tempForm.remember为true 109 | - 200——登陆成功,直接跳转 110 | - 除200外均为token过期,清空所有数据,表单恢复原装 111 | 112 | #### 后端 113 | 114 | - loginInNotRemember:登录,不记住密码 115 | 116 | - 判定验证码是否正确 117 | - 判定是否存在该用户 118 | - 判定是否已激活,若未激活则发送邮件给响应邮箱进行激活 119 | - 判定盐值加密算法是否相等 120 | - 登录 121 | - 获取token 122 | - 获取权限 123 | - 获取角色 124 | - 返回map 125 | 126 | 本方法主要是针对验证码、账号、是否激活、密码进行校验 127 | 128 | - loginInRemember:登录,记住密码 129 | 130 | - 判定账号和token的有效性 131 | - 登录 132 | - 获取权限 133 | - 获取角色 134 | - 返回map 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /jwt/src/main/java/yj/sansui/JwtAuthenticationInterceptor.java: -------------------------------------------------------------------------------- 1 | package yj.sansui; 2 | 3 | import org.springframework.web.method.HandlerMethod; 4 | import org.springframework.web.servlet.HandlerInterceptor; 5 | import org.springframework.web.servlet.ModelAndView; 6 | import yj.sansui.exception.CommonException; 7 | import yj.sansui.exception.ExceptionCode; 8 | import yj.sansui.annotation.PassVerify; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.lang.reflect.Method; 13 | import java.util.Objects; 14 | 15 | /** 16 | * JwtAuthenticationInterceptor,JWT框架的拦截器,校验token 17 | * @author sansui 18 | */ 19 | public class JwtAuthenticationInterceptor implements HandlerInterceptor { 20 | /** 21 | * 重写preHandle方法,对请求进行拦截并从请求头中Authorization取出token进行校验 22 | * @param httpServletRequest HttpServletRequest 23 | * @param httpServletResponse HttpServletResponse 24 | * @param object Object 25 | * @return ture/false 26 | * @throws Exception 异常 27 | */ 28 | @Override 29 | public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { 30 | // 从请求头中取出 token 这里需要和前端约定好把jwt放到请求头一个叫token的地方 31 | String token = httpServletRequest.getHeader("Authorization"); 32 | // 如果不是映射到方法直接通过 33 | if (!(object instanceof HandlerMethod)) { 34 | return true; 35 | } 36 | HandlerMethod handlerMethod = (HandlerMethod) object; 37 | Method method = handlerMethod.getMethod(); 38 | //检查是否有passtoken注释,有则跳过认证 39 | if (method.isAnnotationPresent(PassVerify.class)) { 40 | PassVerify passVerify = method.getAnnotation(PassVerify.class); 41 | if (passVerify.required()) { 42 | return true; 43 | } 44 | } 45 | //默认全部检查 46 | else { 47 | // 执行认证 48 | if (token == null) { 49 | // 没传token,不通过 50 | throw new CommonException(ExceptionCode.SIGNATURE_IS_NULL,"签名为空"); 51 | } 52 | String[] tokenSplit = token.split(" "); 53 | if (!Objects.equals("Bearer", tokenSplit[0])) { 54 | throw new CommonException(ExceptionCode.SIGNATURE_NOT_MATCH,"签名不匹配"); 55 | } 56 | 57 | // 验证 token 58 | JwtUtil.verifyToken(tokenSplit[1]); 59 | 60 | return true; 61 | } 62 | return true; 63 | } 64 | 65 | @Override 66 | public void postHandle(HttpServletRequest httpServletRequest, 67 | HttpServletResponse httpServletResponse, 68 | Object o, ModelAndView modelAndView) throws Exception { 69 | 70 | } 71 | 72 | @Override 73 | public void afterCompletion(HttpServletRequest httpServletRequest, 74 | HttpServletResponse httpServletResponse, 75 | Object o, Exception e) throws Exception { 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/pack/ResponsePackInterceptor.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.pack; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.http.converter.HttpMessageConverter; 8 | import org.springframework.http.server.ServerHttpRequest; 9 | import org.springframework.http.server.ServerHttpResponse; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 12 | import yj.sansui.exception.CommonException; 13 | import yj.sansui.exception.ExceptionCode; 14 | import yj.sansui.result.Result; 15 | import yj.sansui.result.ResultCode; 16 | 17 | 18 | /** 19 | * ResponsePackInterceptor 响应结果拦截器 20 | * 拦截响应结果后统一封装成Result返回 21 | * 流程如下: 22 | * 1.当获取请求进行返回值前,该拦截器将返回值拦截进行判断 23 | * 2.supports方法判断返回值是否为Result(已统一封装)或拥有自定义注解@NotControllerResponse,是则直接返回,否便进行下一步 24 | * 3.beforeBodyWrite方法将原本的返回值封装进Result返回,若返回值为String类型,需要将String类型转换为json串再封装进Result返回 25 | * @author Sansui 26 | * @annotation RestControllerAdvice 默认全局拦截器,可自定义针对特定包进行拦截 27 | */ 28 | @RestControllerAdvice(basePackages = {"yj.sansui"}) 29 | public class ResponsePackInterceptor implements ResponseBodyAdvice { 30 | /** 31 | * 实现supports方法 32 | * 判断返回值是否为Result(已统一封装)或拥有自定义注解@NotControllerResponse 33 | * @param methodParameter MethodParameter 34 | * @param aClass Class 35 | * @return true/false 36 | */ 37 | @Override 38 | public boolean supports(MethodParameter methodParameter, Class extends HttpMessageConverter>> aClass) { 39 | // response是Result类型,或者注释了NotControllerResponse都不进行封装 40 | return !(methodParameter.getParameterType().isAssignableFrom(Result.class) 41 | || methodParameter.hasMethodAnnotation(NotControllerResponse.class)); 42 | } 43 | 44 | /** 45 | * 实现beforeBodyWrite方法 46 | * 将原本的返回值封装进Result返回,若返回值为String类型,需要将String类型转换为json串再封装进Result返回 47 | * 针对该方法的参数做备注,方法参数最多为3个,超出三个封装成对象作为方法参数,这里是对应源码方法 48 | * @param data Object 49 | * @param returnType MethodParameter 50 | * @param mediaType MediaType 51 | * @param aClass Class 52 | * @param request ServerHttpRequest 53 | * @param response ServerHttpResponse 54 | * @return new Result(data) 55 | */ 56 | @Override 57 | public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class extends HttpMessageConverter>> aClass, ServerHttpRequest request, ServerHttpResponse response) { 58 | // String类型不能直接封装 59 | if (returnType.getGenericParameterType().equals(String.class)) { 60 | ObjectMapper objectMapper = new ObjectMapper(); 61 | try { 62 | // 将数据封装在Result里后转换为json串进行返回 63 | return objectMapper.writeValueAsString(new Result(data)); 64 | } catch (JsonProcessingException e) { 65 | throw new CommonException(ExceptionCode.RESPONSE_PACK_ERROR, e.getMessage()); 66 | } 67 | } 68 | // 否则直接封装成Result返回 69 | return new Result(data); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /shop-user/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n 17 | 18 | 19 | 20 | 21 | 22 | 23 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n 24 | 25 | 26 | 27 | 34 | 35 | ${logPath}/EmsServer.log 36 | 37 | 38 | ${logPath}/EmsServers.%i.log.zip 39 | 1 40 | 10 41 | 42 | 43 | 44 | 5MB 45 | 46 | 47 | 48 | 49 | 50 | 51 | ERROR 52 | 53 | 54 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n 55 | 56 | 57 | ${logPath}/error/%d.log 58 | 59 | 60 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /jwt/src/main/java/yj/sansui/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package yj.sansui; 2 | 3 | 4 | import com.auth0.jwt.JWT; 5 | import com.auth0.jwt.JWTVerifier; 6 | import com.auth0.jwt.algorithms.Algorithm; 7 | import com.auth0.jwt.exceptions.JWTDecodeException; 8 | import com.auth0.jwt.interfaces.Claim; 9 | import com.auth0.jwt.interfaces.DecodedJWT; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Component; 12 | import yj.sansui.exception.CommonException; 13 | import yj.sansui.exception.ExceptionCode; 14 | 15 | 16 | import java.util.Calendar; 17 | import java.util.Date; 18 | 19 | /** 20 | * JwtUtil,JWT工具类 21 | * 22 | * @author sansui 23 | * @annotation Component 将JwtUtil注入Ioc容器管理 24 | */ 25 | @Component 26 | public class JwtUtil { 27 | /** 28 | * secret 自定义密钥 29 | */ 30 | private static String secret = "Sansui"; 31 | 32 | /** 33 | * token有效时间 34 | */ 35 | private static Integer jwtExpiresTime; 36 | 37 | @Value("${jwt.expires-time}") 38 | public void setJwtExpiresTime(Integer time) { 39 | jwtExpiresTime = time; 40 | } 41 | 42 | /** 43 | * createToken 生成token 44 | * 签发对象:账号 45 | * 签发时间:现在 46 | * 有效时间:配置文件可设置 47 | * 载荷内容:账号 密码 48 | * 加密密钥:这个人的id加上一串字符串 49 | * 50 | * @param passport String 51 | * @param password String 52 | * @return token 53 | */ 54 | public static String createToken(String passport, String password) { 55 | Calendar nowTime = Calendar.getInstance(); 56 | nowTime.add(Calendar.MINUTE, jwtExpiresTime); 57 | Date expiresDate = nowTime.getTime(); 58 | 59 | /* 60 | create 创建token 61 | withAudience 签发对象 62 | withIssuedAt 发行时间 63 | withExpiresAt 有效时间 64 | withClaim 载荷(不固定) 65 | sign 加密 66 | */ 67 | return JWT.create().withAudience(passport) 68 | .withIssuedAt(new Date()) 69 | .withExpiresAt(expiresDate) 70 | .withClaim("passport", passport) 71 | .withClaim("password", password) 72 | .sign(Algorithm.HMAC256(secret)); 73 | } 74 | 75 | /** 76 | * 检验合法性,其中secret参数就应该传入的是用户的id 77 | * 78 | * @param token String 79 | * @throws CommonException 通用异常类 80 | */ 81 | public static Boolean verifyToken(String token) throws CommonException { 82 | boolean temp = false; 83 | DecodedJWT jwt = null; 84 | try { 85 | JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build(); 86 | jwt = verifier.verify(token); 87 | return temp = true; 88 | } catch (Exception e) { 89 | //效验失败 90 | //这里抛出的异常是我自定义的一个异常,你也可以写成别的 91 | throw new CommonException(ExceptionCode.SIGNATURE_NOT_MATCH, "签名不匹配"); 92 | } 93 | 94 | } 95 | 96 | /** 97 | * getAudience 获取签发对象 98 | * @param token String 99 | * @return audience签发对象 100 | * @throws CommonException 异常类 101 | */ 102 | public static String getAudience(String token) throws CommonException { 103 | String audience = null; 104 | try { 105 | audience = JWT.decode(token).getAudience().get(0); 106 | } catch (Exception e) { 107 | //这里是token解析失败 108 | throw new CommonException(ExceptionCode.SIGNATURE_DECODE_EXCEPTION,"签名解析失败"); 109 | } 110 | return audience; 111 | } 112 | 113 | 114 | /** 115 | * getClaimByName 通过载荷名字获取载荷的值 116 | * @param token String 117 | * @param name String 118 | * @return 载荷 119 | */ 120 | public static Claim getClaimByName(String token, String name) { 121 | return JWT.decode(token).getClaim(name); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /vue/src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import {loginInNotRemember, loginInRemember, logout} from "@/api/user/userApi"; 2 | import {getUserInfo} from "@/api/user/userApi" 3 | import {removeToken, setToken} from "@/assets/token/token"; 4 | 5 | import {getToken} from "@/assets/token/token" 6 | 7 | 8 | 9 | const user = { 10 | state: { 11 | token: getToken(), 12 | phone: '', 13 | roles: [], 14 | permissions: [] 15 | }, 16 | mutations: { 17 | SET_TOKEN: (state, token) => { 18 | state.token = token 19 | }, 20 | SET_USERNAME: (state, username) => { 21 | state.username = username 22 | }, 23 | SET_PHONE: (state, phone) => { 24 | state.phone = phone 25 | }, 26 | SET_ROLES: (state, roles) => { 27 | state.roles = roles 28 | }, 29 | SET_PERMISSIONS: (state, permissions) => { 30 | state.permissions = permissions 31 | } 32 | }, 33 | actions: { 34 | //用户登录 35 | loginInNotRemember({commit}, userInfo) { 36 | // trim():从字符串中删除开头和结尾的空格和行结束符。 37 | const phone = userInfo.phone.trim() 38 | const password = userInfo.password 39 | const code = userInfo.code 40 | return new Promise((resolve, reject) => { 41 | loginInNotRemember(phone, password, code).then(response => { 42 | setToken(response.token) 43 | commit('SET_TOKEN', response.token) 44 | resolve() 45 | }).catch(error => { 46 | reject(error) 47 | }) 48 | }) 49 | }, 50 | loginInRemember({commit}, userInfo) { 51 | // trim():从字符串中删除开头和结尾的空格和行结束符。 52 | const phone = userInfo.phone.trim() 53 | const token = userInfo.token; 54 | return new Promise(((resolve, reject) => { 55 | loginInRemember(phone,token).then(response=>{ 56 | commit('SET_TOKEN', response.token) 57 | resolve() 58 | }).catch(error => { 59 | reject(error) 60 | }) 61 | })) 62 | }, 63 | 64 | //获取用户信息 65 | getInfo({commit, state}) { 66 | return new Promise((resolve, reject) => { 67 | getUserInfo(state.token).then(response => { 68 | const user =response.data.data 69 | //验证返回的roles是否是一个非空数组 70 | if(user.roles&&user.roles.length>0){ 71 | commit('SET_ROLES', user.roles); 72 | commit('SET_PERMISSIONS', user.permissions); 73 | }else { 74 | commit('SET_ROLES', ['ROLE_DEFAULT']) 75 | } 76 | commit('SET_USERNAME', user.username); 77 | commit('SET_PHONE', user.phone); 78 | resolve(response); 79 | }).catch(error => { 80 | reject(error); 81 | }) 82 | }) 83 | }, 84 | 85 | //用户登出 86 | logout({commit, state}) { 87 | return new Promise((resolve, reject) => { 88 | logout(state.token).then(() => { 89 | commit('SET_TOKEN', ''); 90 | commit('SET_USERNAME', ''); 91 | commit('SET_PHONE', '') 92 | commit('SET_ROLES', []); 93 | removeToken(); 94 | resolve(); 95 | }).catch(error => { 96 | reject(error); 97 | }) 98 | }) 99 | }, 100 | 101 | //重置token 102 | resetToken({commit}) { 103 | return new Promise(resolve => { 104 | commit('SET_TOKEN', ''); 105 | commit('SET_USERNAME', ''); 106 | commit('SET_PHONE', '') 107 | commit('SET_ROLES', []); 108 | }) 109 | } 110 | } 111 | 112 | } 113 | 114 | export default user 115 | -------------------------------------------------------------------------------- /vue/src/router/index.js: -------------------------------------------------------------------------------- 1 | import {createRouter, createWebHistory} from 'vue-router' 2 | 3 | 4 | export const constantRouter = [ 5 | { 6 | path: '/', 7 | name: 'index', 8 | title: '首页', 9 | component: () => import("@/views/index"), 10 | children: [ 11 | { 12 | path: '/page1', 13 | name: 'page1', 14 | title: '第一页', 15 | component: () => import("@/pages/page1"), 16 | meta: { 17 | title: '第一页' 18 | }, 19 | children: [ 20 | { 21 | path: '/page3', 22 | name: 'page3', 23 | title: '第三页', 24 | component: () => import("@/pages/page3"), 25 | meta: { 26 | title: '第三页' 27 | } 28 | } 29 | ] 30 | }, 31 | { 32 | path: '/page2', 33 | name: 'page2', 34 | title: '第二页', 35 | component: () => import("@/pages/page2"), 36 | meta: { 37 | title: '第二页' 38 | }, 39 | children: [ 40 | { 41 | path: '/page4', 42 | name: 'page4', 43 | title: '第四页', 44 | component: () => import("@/pages/page4"), 45 | meta: { 46 | title: '第四页' 47 | } 48 | } 49 | ] 50 | }, 51 | { 52 | path: '/login', 53 | name: 'Login', 54 | title: '登录页', 55 | component: () => import("@/views/login/login"), 56 | meta: { 57 | title: '登录页' 58 | } 59 | }, 60 | { 61 | path: '/register', 62 | name: 'register', 63 | title: '注册页', 64 | component: () => import("@/views/login/register"), 65 | meta: { 66 | title: '注册页' 67 | } 68 | } 69 | ] 70 | } 71 | ] 72 | 73 | export const dynamicRouter = [ 74 | { 75 | path: '/index', 76 | name: 'index1', 77 | title: '首页1', 78 | component: () => import("@/views/index"), 79 | redirect: '/welcome', 80 | children: [ 81 | { 82 | path: '/admin', 83 | name: 'admin', 84 | title: '管理员页面', 85 | component: () => import("@/views/user/admin"), 86 | meta: { 87 | roles: ["admin", "super_admin"], 88 | title: '管理员页面' 89 | }, 90 | children: [ 91 | { 92 | path: '/admin/page5', 93 | name: 'page5', 94 | title: '第五页', 95 | component: () => import("@/pages/page5"), 96 | meta: { 97 | title: '第五页' 98 | } 99 | } 100 | ] 101 | }, 102 | { 103 | path: '/superAdmin', 104 | name: 'superAdmin', 105 | title: '超级管理员页面', 106 | component: () => import("@/views/user/superAdmin"), 107 | meta: { 108 | roles: ["super_admin"], 109 | title: '超级管理员页面' 110 | } 111 | }, 112 | { 113 | path: '/user', 114 | name: 'user', 115 | title: '用户页面', 116 | component: () => import("@/views/user/user"), 117 | meta: { 118 | roles: ["user", "admin", "super_admin"], 119 | title: '用户页面' 120 | } 121 | } 122 | ] 123 | } 124 | 125 | ] 126 | 127 | export const errorRouter = [ 128 | { 129 | path: '/:pathMatch(.*)', redirect: '/404', hidden: true 130 | } 131 | ] 132 | 133 | 134 | const router = createRouter({ 135 | history: createWebHistory(process.env.BASE_URL), 136 | routes: constantRouter 137 | }) 138 | 139 | 140 | export default router 141 | -------------------------------------------------------------------------------- /shop-user/src/main/java/yj/sansui/service/impl/ShopUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Service; 6 | import yj.sansui.bean.entity.UserEntity; 7 | import yj.sansui.exception.CommonException; 8 | import yj.sansui.exception.ExceptionCode; 9 | import yj.sansui.mapper.ShopUserMapper; 10 | import yj.sansui.result.Result; 11 | import yj.sansui.service.ShopUserService; 12 | import yj.sansui.version1.RedisConstant; 13 | import yj.sansui.version1.RedisUtil; 14 | 15 | import javax.annotation.Resource; 16 | import java.util.List; 17 | 18 | /** 19 | * @author Sansui 20 | */ 21 | @Service 22 | @Slf4j 23 | public class ShopUserServiceImpl extends ServiceImpl implements ShopUserService { 24 | 25 | @Resource 26 | private ShopUserServiceImpl userService; 27 | 28 | /** 29 | * selectById,输入id,获取该id的UserEntity 30 | * @param id Integer 31 | * @return Result 32 | */ 33 | @Override 34 | public Result selectById(Integer id) { 35 | return new Result(userService.getById(id)); 36 | } 37 | 38 | /** 39 | * saveUser,保存User 40 | * @param userEntity UserEntity 41 | * @return true/false 42 | */ 43 | @Override 44 | public boolean saveUser(UserEntity userEntity){ 45 | return userService.save(userEntity); 46 | } 47 | 48 | /** 49 | * getUserById,输入id,获取该id的UserEntity 50 | * @param id Integer 51 | * @return UserEntity 52 | */ 53 | @Override 54 | public UserEntity getUserById(Integer id){ 55 | return userService.getById(id); 56 | } 57 | 58 | /** 59 | * saveUserEntity,保存User,带Redis 60 | * @param userEntity UserEntity 61 | * @return true/false 62 | */ 63 | @Override 64 | public boolean saveUserEntity(UserEntity userEntity){ 65 | if(userService.saveUser(userEntity)){ 66 | String key= RedisConstant.User_Id_Key+userEntity.getId(); 67 | RedisUtil.setKeyValue(key,userEntity); 68 | return true; 69 | }else{ 70 | throw new CommonException(ExceptionCode.SAVE_EXCEPTION,"保存id为"+userEntity.getId()+"的User失败"); 71 | } 72 | } 73 | 74 | /** 75 | * deleteById,根据id删除User,带Redis 76 | * @param id Integer 77 | * @return true/false 78 | */ 79 | @Override 80 | public boolean deleteById(Integer id){ 81 | if(userService.removeById(id)){ 82 | String key= RedisConstant.User_Id_Key+id; 83 | RedisUtil.del(key); 84 | return true; 85 | }else { 86 | throw new CommonException(ExceptionCode.DELETE_EXCEPTION,"删除id为"+id+"的User失败"); 87 | } 88 | } 89 | 90 | /** 91 | * updateUser,更新UserEntity,带Redis 92 | * @param userEntity UserEntity 93 | * @return true/false 94 | */ 95 | @Override 96 | public boolean updateUser(UserEntity userEntity){ 97 | if(userService.updateById(userEntity)){ 98 | String key=RedisConstant.User_Id_Key+userEntity.getId(); 99 | RedisUtil.setKeyValue(key,userEntity); 100 | return true; 101 | }else { 102 | throw new CommonException(ExceptionCode.UPDATE_EXCEPTION,"更新id为"+userEntity.getId()+"的User失败"); 103 | } 104 | } 105 | 106 | /** 107 | * selectAllSmall,小厂可用,查询所有User,QPS<=1000 108 | * @return List 109 | */ 110 | @Override 111 | public List selectAllSmall(){ 112 | //先查缓存,缓存没有再查数据库 113 | List users=(List) RedisUtil.getByKey(RedisConstant.All_User_Key); 114 | if(users==null){ 115 | users=userService.list(); 116 | if(users==null){ 117 | //数据库也没有则返回异常 118 | throw new CommonException(ExceptionCode.SELECT_EXCEPTION,"查询失败"); 119 | }else { 120 | //数据库有则写入缓存再返回 121 | RedisUtil.setKeyValue(RedisConstant.All_User_Key,users); 122 | return users; 123 | } 124 | } 125 | return users; 126 | } 127 | 128 | /** 129 | * selectAllBig,大厂可用,查询所有User,QPS>1000 130 | * @return List 131 | */ 132 | @Override 133 | public List selectAllBig(){ 134 | //先查缓存,缓存没有再查数据库 135 | List users=(List) RedisUtil.getByKey(RedisConstant.All_User_Key); 136 | if(users==null){ 137 | //同步锁,确认没有线程修改 138 | synchronized (ShopUserMapper.class){ 139 | //二次查询确认 140 | users=(List) RedisUtil.getByKey(RedisConstant.All_User_Key); 141 | if(users==null){ 142 | users=userService.list(); 143 | if(users==null){ 144 | throw new CommonException(ExceptionCode.SELECT_EXCEPTION,"查询失败"); 145 | }else { 146 | RedisUtil.setKeyValue(RedisConstant.All_User_Key,users); 147 | return users; 148 | } 149 | } 150 | } 151 | } 152 | return users; 153 | } 154 | 155 | 156 | } 157 | -------------------------------------------------------------------------------- /redis/redis.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /shop-user/src/main/java/yj/sansui/controller/ShopUserController.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.controller; 2 | 3 | import org.springframework.data.redis.core.RedisTemplate; 4 | import org.springframework.validation.annotation.Validated; 5 | import org.springframework.web.bind.annotation.CrossOrigin; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | import yj.sansui.JwtUtil; 9 | import yj.sansui.annotation.PassVerify; 10 | import yj.sansui.bean.entity.UserEntity; 11 | import yj.sansui.exception.CommonException; 12 | import yj.sansui.exception.ExceptionCode; 13 | import yj.sansui.pack.NotControllerResponse; 14 | import yj.sansui.result.Result; 15 | import yj.sansui.service.impl.ShopUserServiceImpl; 16 | import yj.sansui.version1.RedisUtil; 17 | 18 | import javax.annotation.Resource; 19 | import java.util.List; 20 | 21 | 22 | /** 23 | * UserController,User的控制层,发起请求的接口 24 | * 25 | * @author Sansui 26 | */ 27 | @CrossOrigin 28 | @RestController 29 | public class ShopUserController { 30 | 31 | @Resource 32 | private ShopUserServiceImpl userService; 33 | 34 | @Resource 35 | private RedisTemplate redisTemplate; 36 | 37 | /** 38 | * selectById,根据id获取User实例 39 | * 40 | * @param id Integer 41 | * @return Result 42 | */ 43 | @GetMapping("/User/selectById") 44 | @PassVerify 45 | public Result selectById(Integer id) { 46 | return new Result(userService.selectById(id)); 47 | } 48 | 49 | /** 50 | * testNameAndAge name和age参数校验 51 | * 52 | * @param userEntity UserEntity 53 | */ 54 | @GetMapping("/User/testNameAndAge") 55 | public void testNameAndAge(UserEntity userEntity) { 56 | if (userEntity.getName() == null) { 57 | throw new CommonException("名字不允许为空"); 58 | } 59 | if (userEntity.getAge() < 0) { 60 | throw new CommonException("年龄不允许为负数"); 61 | } 62 | } 63 | 64 | /** 65 | * saveUser,传入UserEntity,保存到数据库中 66 | * 67 | * @param userEntity UserEntity 68 | * @annotation Validated, 参数检验 69 | */ 70 | @GetMapping("/User/saveUser") 71 | @PassVerify 72 | public void saveUser(@Validated UserEntity userEntity) { 73 | userService.saveUser(userEntity); 74 | } 75 | 76 | /** 77 | * selectByIdException,根据id查询UserEntity,返回空指针 78 | * 79 | * @param id Integer 80 | * @return Result 81 | */ 82 | @GetMapping("/User/selectById/exception") 83 | @PassVerify 84 | public Result selectByIdException(Integer id) { 85 | if (userService.selectById(id).getData() == null) { 86 | throw new CommonException(ExceptionCode.ID_NULL_EXCEPTION, "id:" + id + "不存在"); 87 | } else { 88 | return new Result(userService.selectById(id)); 89 | } 90 | } 91 | 92 | /** 93 | * getUserById,根据id查询UserEntity 94 | * 95 | * @param id Integer 96 | * @return UserEntity 97 | */ 98 | @GetMapping("/User/getUserById") 99 | public UserEntity getUserById(Integer id) { 100 | return userService.getUserById(id); 101 | } 102 | 103 | /** 104 | * health,检测是否健康 105 | * 106 | * @return “success” 107 | * @annotation NotControllerResponse 不封装 108 | */ 109 | @GetMapping("/User/health/pack") 110 | @NotControllerResponse 111 | @PassVerify 112 | public String health() { 113 | return "success"; 114 | } 115 | 116 | /** 117 | * jwtCreate,生成token 118 | * @param passport String 119 | * @param password String 120 | * @return token 121 | */ 122 | @GetMapping("/User/jwt/create") 123 | @PassVerify 124 | public String jwtCreate(String passport,String password){ 125 | return JwtUtil.createToken(passport,password); 126 | } 127 | 128 | /** 129 | * jwtVerify,验证token 130 | * @param token String 131 | * @return true/false 132 | */ 133 | @GetMapping("/User/jwt/verify") 134 | public Boolean jwtVerify(String token){ 135 | return JwtUtil.verifyToken(token); 136 | } 137 | 138 | /** 139 | * saveRedis,保存键对值到redis数据库 140 | * @param key String 141 | * @param value String 142 | */ 143 | @GetMapping("/User/save/redisTest") 144 | @PassVerify 145 | public void saveRedis(String key,String value){ 146 | redisTemplate.opsForValue().set(key,value); 147 | } 148 | 149 | /** 150 | * saveRedisUtil,通过RedisUtil保存redis键值对 151 | * @param key String 152 | * @param value String 153 | */ 154 | @GetMapping("/User/save/redisUtil") 155 | @PassVerify 156 | public void saveRedisUtil(String key,String value){ 157 | RedisUtil.setKeyValue(key,value); 158 | } 159 | 160 | /** 161 | * addUserEntity,保存UserEntity并存于缓存中 162 | * @param userEntity UserEntity 163 | */ 164 | @GetMapping("/User/redis/add") 165 | @PassVerify 166 | public void addUserEntity(UserEntity userEntity){ 167 | userService.saveUserEntity(userEntity); 168 | } 169 | 170 | /** 171 | * deleteById,根据id删除User 172 | * @param id Integer 173 | */ 174 | @GetMapping("/User/redis/delete") 175 | @PassVerify 176 | public void deleteById(Integer id){ 177 | userService.deleteById(id); 178 | } 179 | 180 | /** 181 | * updateUserEntity,更新User,带Redis 182 | * @param userEntity UserEntity 183 | */ 184 | @GetMapping("/User/redis/update") 185 | @PassVerify 186 | public void updateUserEntity(UserEntity userEntity){ 187 | userService.updateUser(userEntity); 188 | } 189 | 190 | /** 191 | * selectAllBig,小厂可用,查询所有User,QPS<=1000 192 | * @return List 193 | */ 194 | @GetMapping("/User/redis/selectAllSmall") 195 | @PassVerify 196 | public List selectAllSmall(){ 197 | return userService.selectAllSmall(); 198 | } 199 | 200 | /** 201 | * selectAllBig,大厂可用,查询所有User,QPS>1000 202 | * @return List 203 | */ 204 | @GetMapping("/User/redis/selectAllBig") 205 | @PassVerify 206 | public List selectAllBig(){ 207 | return userService.selectAllBig(); 208 | } 209 | 210 | 211 | } 212 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | yj.sansui 8 | CommonLibrary 9 | 1.0.0 10 | 11 | common 12 | shop-user 13 | jwt 14 | redis 15 | user 16 | 17 | pom 18 | 19 | 20 | spring-boot-starter-parent 21 | org.springframework.boot 22 | 2.3.7.RELEASE 23 | 24 | 25 | 26 | 2.3.7.RELEASE 27 | 1.31.0 28 | 8.0.28 29 | 1.18.22 30 | 2.8.0 31 | 3.18.2 32 | 0.5.10 33 | 2.3.11.RELEASE 34 | 2.2.6.RELEASE 35 | 3.3.0 36 | 1.7.25 37 | 1.2.3 38 | 1.2.56 39 | 2.9.6 40 | 1.6.2 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-security 48 | ${springboot.version} 49 | 50 | 51 | org.projectlombok 52 | lombok 53 | ${lombok.version} 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-web 59 | ${springboot.version} 60 | 61 | 62 | mysql 63 | mysql-connector-java 64 | ${mysql.version} 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-data-redis 70 | ${springboot.version} 71 | 72 | 73 | 74 | 75 | org.apache.commons 76 | commons-pool2 77 | ${pool2.version} 78 | 79 | 80 | 81 | 82 | com.baomidou 83 | mybatis-plus-boot-starter 84 | ${mybatisplus.version} 85 | 86 | 87 | com.auth0 88 | java-jwt 89 | ${jwt.version} 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-starter-validation 94 | ${validation.version} 95 | 96 | 97 | 98 | org.slf4j 99 | slf4j-api 100 | ${slf4j.version} 101 | 102 | 103 | ch.qos.logback 104 | logback-core 105 | ${logback.version} 106 | 107 | 108 | ch.qos.logback 109 | logback-classic 110 | ${logback.version} 111 | 112 | 113 | com.alibaba 114 | fastjson 115 | ${fastjson.version} 116 | 117 | 118 | net.jodah 119 | expiringmap 120 | ${jodah.version} 121 | 122 | 123 | 124 | cn.dev33 125 | sa-token-spring-boot-starter 126 | ${sa-token.version} 127 | 128 | 129 | com.fasterxml.jackson.core 130 | jackson-annotations 131 | ${jackson.version} 132 | 133 | 134 | org.springframework.boot 135 | spring-boot-maven-plugin 136 | ${springboot.version} 137 | 138 | 139 | 140 | com.sun.mail 141 | javax.mail 142 | ${mail.version} 143 | 144 | 145 | 146 | 147 | 148 | 149 | org.springframework.boot 150 | spring-boot-devtools 151 | ${devtools.version} 152 | runtime 153 | true 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /user/src/main/resources/sql/sa-token.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : root 5 | Source Server Type : MySQL 6 | Source Server Version : 80028 7 | Source Host : localhost:3306 8 | Source Schema : sa-token 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80028 12 | File Encoding : 65001 13 | 14 | Date: 16/10/2022 23:09:05 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for menu 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `menu`; 24 | CREATE TABLE `menu` ( 25 | `menu_id` int(0) NOT NULL AUTO_INCREMENT, 26 | `menu_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 27 | `parent_id` int(0) NULL DEFAULT NULL, 28 | `compoent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 29 | `perms` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 30 | PRIMARY KEY (`menu_id`) USING BTREE 31 | ) ENGINE = InnoDB AUTO_INCREMENT = 1004 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 32 | 33 | -- ---------------------------- 34 | -- Records of menu 35 | -- ---------------------------- 36 | INSERT INTO `menu` VALUES (1, '组织架构', 0, 'sys/organization/index', 'sys:organization:index'); 37 | INSERT INTO `menu` VALUES (2, '权限管控', 0, 'sys/permission/index', 'sys:permission:index'); 38 | INSERT INTO `menu` VALUES (3, '业务模块', 0, 'sys/business/index', 'sys:business:index'); 39 | INSERT INTO `menu` VALUES (101, '组织管理', 1, 'sys/organization/list', 'sys:organization:list'); 40 | INSERT INTO `menu` VALUES (102, '用户管理', 1, 'sys/user/list', 'sys:user:list'); 41 | INSERT INTO `menu` VALUES (103, '职位管理', 1, 'sys/post/list', 'sys:post:list'); 42 | INSERT INTO `menu` VALUES (201, '角色管理', 2, 'sys/role/list', 'sys:role:list'); 43 | INSERT INTO `menu` VALUES (202, '模块管理', 2, 'sys/module/list', 'sys:module:list'); 44 | INSERT INTO `menu` VALUES (203, '菜单管理', 2, 'sys/menu/list', 'sys:menu:list'); 45 | INSERT INTO `menu` VALUES (301, '食物业务', 3, 'sys/food/list', 'sys:food:list'); 46 | INSERT INTO `menu` VALUES (302, '饮料业务', 3, 'sys/beverage/list', 'sys:beverage:list'); 47 | INSERT INTO `menu` VALUES (303, '娱乐业务', 3, 'sys/recreation/list', 'sys:recreation:list'); 48 | INSERT INTO `menu` VALUES (1001, '组织审核', 101, 'organization/check/perms', 'organization:check:perms'); 49 | INSERT INTO `menu` VALUES (1002, '用户审核', 102, 'user/check/perms', 'user:check:perms'); 50 | 51 | -- ---------------------------- 52 | -- Table structure for role 53 | -- ---------------------------- 54 | DROP TABLE IF EXISTS `role`; 55 | CREATE TABLE `role` ( 56 | `role_id` int(0) NOT NULL AUTO_INCREMENT, 57 | `role_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 58 | PRIMARY KEY (`role_id`) USING BTREE 59 | ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 60 | 61 | -- ---------------------------- 62 | -- Records of role 63 | -- ---------------------------- 64 | INSERT INTO `role` VALUES (1, 'super_admin'); 65 | INSERT INTO `role` VALUES (2, 'admin'); 66 | INSERT INTO `role` VALUES (3, 'user'); 67 | 68 | -- ---------------------------- 69 | -- Table structure for role_menu 70 | -- ---------------------------- 71 | DROP TABLE IF EXISTS `role_menu`; 72 | CREATE TABLE `role_menu` ( 73 | `role_id` int(0) NOT NULL, 74 | `menu_id` int(0) NULL DEFAULT NULL 75 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 76 | 77 | -- ---------------------------- 78 | -- Records of role_menu 79 | -- ---------------------------- 80 | INSERT INTO `role_menu` VALUES (2, 1); 81 | INSERT INTO `role_menu` VALUES (2, 3); 82 | INSERT INTO `role_menu` VALUES (2, 101); 83 | INSERT INTO `role_menu` VALUES (2, 102); 84 | INSERT INTO `role_menu` VALUES (2, 103); 85 | INSERT INTO `role_menu` VALUES (2, 301); 86 | INSERT INTO `role_menu` VALUES (2, 302); 87 | INSERT INTO `role_menu` VALUES (2, 303); 88 | INSERT INTO `role_menu` VALUES (3, 3); 89 | INSERT INTO `role_menu` VALUES (3, 301); 90 | INSERT INTO `role_menu` VALUES (3, 302); 91 | INSERT INTO `role_menu` VALUES (3, 303); 92 | INSERT INTO `role_menu` VALUES (2, 1001); 93 | INSERT INTO `role_menu` VALUES (3, 102); 94 | INSERT INTO `role_menu` VALUES (3, 1002); 95 | INSERT INTO `role_menu` VALUES (2, 0); 96 | 97 | -- ---------------------------- 98 | -- Table structure for user 99 | -- ---------------------------- 100 | DROP TABLE IF EXISTS `user`; 101 | CREATE TABLE `user` ( 102 | `user_id` int(0) UNSIGNED NOT NULL AUTO_INCREMENT, 103 | `user_name` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 104 | `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 105 | `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '', 106 | `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 107 | `salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 108 | `active_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '激活码', 109 | `active` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '是否激活,1为激活,0为未激活', 110 | PRIMARY KEY (`user_id`) USING BTREE 111 | ) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 112 | 113 | -- ---------------------------- 114 | -- Records of user 115 | -- ---------------------------- 116 | INSERT INTO `user` VALUES (1, '三岁', '15767686503', 'yjsansui@163.com', '7k00qpevf1icpffc257ubas8kg', 'w7aPT', '571b2f08-e070-493e-861b-661fdeb24418', '1'); 117 | INSERT INTO `user` VALUES (2, '叫阿三的', '15767686505', 'yjsansui@163.com', '3gvjic0urea6cla69v9cp3qgd', 'QbAkx', '571b2f08-e070-493e-861b-661fdeb24419', '1'); 118 | INSERT INTO `user` VALUES (3, '四岁', '15767686501', 'yjsansui@163.com', '3gvjic0urea6cla69v9cp3qgd', 'QbAkx', '1', '0'); 119 | INSERT INTO `user` VALUES (24, 'String', 'String', 'String', '4e9197vpv9r5v4ot77o6scic0q', 'nc3XE', 'String', '\0'); 120 | 121 | -- ---------------------------- 122 | -- Table structure for user_role 123 | -- ---------------------------- 124 | DROP TABLE IF EXISTS `user_role`; 125 | CREATE TABLE `user_role` ( 126 | `user_id` int(0) NOT NULL, 127 | `role_id` int(0) NULL DEFAULT NULL 128 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 129 | 130 | -- ---------------------------- 131 | -- Records of user_role 132 | -- ---------------------------- 133 | INSERT INTO `user_role` VALUES (1, 1); 134 | INSERT INTO `user_role` VALUES (2, 2); 135 | INSERT INTO `user_role` VALUES (3, 3); 136 | INSERT INTO `user_role` VALUES (2, 3); 137 | 138 | SET FOREIGN_KEY_CHECKS = 1; 139 | -------------------------------------------------------------------------------- /vue/src/views/login/register.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommonLibrary 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | 注 册 85 | 注 册 中... 86 | 87 | 88 | 已有账号?立即登录 89 | 90 | 91 | 92 | 93 | 94 | 97 | 98 | 99 | 100 | 193 | 194 | 260 | -------------------------------------------------------------------------------- /vue/src/utils/library.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 通用js方法封装处理 3 | */ 4 | 5 | // 日期格式化 6 | export function parseTime(time, pattern) { 7 | if (arguments.length === 0 || !time) { 8 | return null 9 | } 10 | const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' 11 | let date 12 | if (typeof time === 'object') { 13 | date = time 14 | } else { 15 | if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { 16 | time = parseInt(time) 17 | } else if (typeof time === 'string') { 18 | time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), ''); 19 | } 20 | if ((typeof time === 'number') && (time.toString().length === 10)) { 21 | time = time * 1000 22 | } 23 | date = new Date(time) 24 | } 25 | const formatObj = { 26 | y: date.getFullYear(), 27 | m: date.getMonth() + 1, 28 | d: date.getDate(), 29 | h: date.getHours(), 30 | i: date.getMinutes(), 31 | s: date.getSeconds(), 32 | a: date.getDay() 33 | } 34 | const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { 35 | let value = formatObj[key] 36 | // Note: getDay() returns 0 on Sunday 37 | if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } 38 | if (result.length > 0 && value < 10) { 39 | value = '0' + value 40 | } 41 | return value || 0 42 | }) 43 | return time_str 44 | } 45 | 46 | // 表单重置 47 | export function resetForm(refName) { 48 | if (this.$refs[refName]) { 49 | this.$refs[refName].resetFields(); 50 | } 51 | } 52 | 53 | // 添加日期范围 54 | export function addDateRange(params, dateRange, propName) { 55 | let search = params; 56 | search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}; 57 | dateRange = Array.isArray(dateRange) ? dateRange : []; 58 | if (typeof (propName) === 'undefined') { 59 | search.params['beginTime'] = dateRange[0]; 60 | search.params['endTime'] = dateRange[1]; 61 | } else { 62 | search.params['begin' + propName] = dateRange[0]; 63 | search.params['end' + propName] = dateRange[1]; 64 | } 65 | return search; 66 | } 67 | 68 | // 回显数据字典 69 | export function selectDictLabel(datas, value) { 70 | if (value === undefined) { 71 | return ""; 72 | } 73 | var actions = []; 74 | Object.keys(datas).some((key) => { 75 | if (datas[key].value == ('' + value)) { 76 | actions.push(datas[key].label); 77 | return true; 78 | } 79 | }) 80 | if (actions.length === 0) { 81 | actions.push(value); 82 | } 83 | return actions.join(''); 84 | } 85 | 86 | // 回显数据字典(字符串数组) 87 | export function selectDictLabels(datas, value, separator) { 88 | if (value === undefined) { 89 | return ""; 90 | } 91 | var actions = []; 92 | var currentSeparator = undefined === separator ? "," : separator; 93 | var temp = value.split(currentSeparator); 94 | Object.keys(value.split(currentSeparator)).some((val) => { 95 | var match = false; 96 | Object.keys(datas).some((key) => { 97 | if (datas[key].value == ('' + temp[val])) { 98 | actions.push(datas[key].label + currentSeparator); 99 | match = true; 100 | } 101 | }) 102 | if (!match) { 103 | actions.push(temp[val] + currentSeparator); 104 | } 105 | }) 106 | return actions.join('').substring(0, actions.join('').length - 1); 107 | } 108 | 109 | // 字符串格式化(%s ) 110 | export function sprintf(str) { 111 | var args = arguments, flag = true, i = 1; 112 | str = str.replace(/%s/g, function () { 113 | var arg = args[i++]; 114 | if (typeof arg === 'undefined') { 115 | flag = false; 116 | return ''; 117 | } 118 | return arg; 119 | }); 120 | return flag ? str : ''; 121 | } 122 | 123 | // 转换字符串,undefined,null等转化为"" 124 | export function parseStrEmpty(str) { 125 | if (!str || str == "undefined" || str == "null") { 126 | return ""; 127 | } 128 | return str; 129 | } 130 | 131 | // 数据合并 132 | export function mergeRecursive(source, target) { 133 | for (var p in target) { 134 | try { 135 | if (target[p].constructor == Object) { 136 | source[p] = mergeRecursive(source[p], target[p]); 137 | } else { 138 | source[p] = target[p]; 139 | } 140 | } catch (e) { 141 | source[p] = target[p]; 142 | } 143 | } 144 | return source; 145 | }; 146 | 147 | /** 148 | * 构造树型结构数据 149 | * @param {*} data 数据源 150 | * @param {*} id id字段 默认 'id' 151 | * @param {*} parentId 父节点字段 默认 'parentId' 152 | * @param {*} children 孩子节点字段 默认 'children' 153 | */ 154 | export function handleTree(data, id, parentId, children) { 155 | let config = { 156 | id: id || 'id', 157 | parentId: parentId || 'parentId', 158 | childrenList: children || 'children' 159 | }; 160 | 161 | var childrenListMap = {}; 162 | var nodeIds = {}; 163 | var tree = []; 164 | 165 | for (let d of data) { 166 | let parentId = d[config.parentId]; 167 | if (childrenListMap[parentId] == null) { 168 | childrenListMap[parentId] = []; 169 | } 170 | nodeIds[d[config.id]] = d; 171 | childrenListMap[parentId].push(d); 172 | } 173 | 174 | for (let d of data) { 175 | let parentId = d[config.parentId]; 176 | if (nodeIds[parentId] == null) { 177 | tree.push(d); 178 | } 179 | } 180 | 181 | for (let t of tree) { 182 | adaptToChildrenList(t); 183 | } 184 | 185 | function adaptToChildrenList(o) { 186 | if (childrenListMap[o[config.id]] !== null) { 187 | o[config.childrenList] = childrenListMap[o[config.id]]; 188 | } 189 | if (o[config.childrenList]) { 190 | for (let c of o[config.childrenList]) { 191 | adaptToChildrenList(c); 192 | } 193 | } 194 | } 195 | return tree; 196 | } 197 | 198 | /** 199 | * 参数处理 200 | * @param {*} params 参数 201 | */ 202 | export function tansParams(params) { 203 | let result = '' 204 | for (const propName of Object.keys(params)) { 205 | const value = params[propName]; 206 | var part = encodeURIComponent(propName) + "="; 207 | if (value !== null && value !== "" && typeof (value) !== "undefined") { 208 | if (typeof value === 'object') { 209 | for (const key of Object.keys(value)) { 210 | if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { 211 | let params = propName + '[' + key + ']'; 212 | var subPart = encodeURIComponent(params) + "="; 213 | result += subPart + encodeURIComponent(value[key]) + "&"; 214 | } 215 | } 216 | } else { 217 | result += part + encodeURIComponent(value) + "&"; 218 | } 219 | } 220 | } 221 | return result 222 | } 223 | 224 | // 验证是否为blob格式 225 | export async function blobValidate(data) { 226 | try { 227 | const text = await data.text(); 228 | JSON.parse(text); 229 | return false; 230 | } catch (error) { 231 | return true; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /vue/src/views/login/login.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CommonLibrary 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | 27 | 28 | 29 | 30 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | 登 录 57 | 登 录 中... 58 | 59 | 60 | 没有账号?立即注册 61 | 62 | 63 | 64 | 65 | 66 | 69 | 70 | 71 | 72 | 201 | 202 | 268 | -------------------------------------------------------------------------------- /common/src/main/java/yj/sansui/utils/VerifyCode.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.utils; 2 | 3 | 4 | 5 | import lombok.Data; 6 | import org.springframework.http.HttpHeaders; 7 | import org.springframework.http.ResponseCookie; 8 | import yj.sansui.exception.CommonException; 9 | import yj.sansui.exception.ExceptionCode; 10 | import yj.sansui.version2.RedisUtil; 11 | 12 | 13 | import javax.imageio.ImageIO; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import javax.servlet.http.HttpSession; 17 | import java.awt.*; 18 | import java.awt.geom.AffineTransform; 19 | import java.awt.image.BufferedImage; 20 | import java.io.IOException; 21 | import java.io.OutputStream; 22 | import java.time.Duration; 23 | import java.util.Locale; 24 | import java.util.Random; 25 | import java.lang.*; 26 | 27 | /** 28 | * VerifyUtil 生成图形验证码的工具类 29 | * 30 | * @author Sansui 31 | */ 32 | public class VerifyCode { 33 | /** 34 | * codes,验证码字符集 35 | * size,验证码字符数量 36 | * lines,干扰线数量 37 | * width,验证码图片宽度 38 | * height,验证码图片高度 39 | * tilt,字符倾斜程度 40 | * backgroundColor,验证码图片背景颜色 41 | */ 42 | private static final char[] CODES = { 43 | '1', '2', '3', '4', '5', '6', '7', '8', '9', 44 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 45 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' 46 | }; 47 | private Integer size; 48 | private Integer lines; 49 | private Integer width; 50 | private Integer height; 51 | private Integer fontSize; 52 | private boolean tilt; 53 | private Color backgroundColor; 54 | 55 | /** 56 | * Builder 构造器对象 57 | */ 58 | @Data 59 | public static class Builder { 60 | /** 61 | * size,验证码字符数量 62 | * lines,干扰线数量 63 | * width,图片宽度 64 | * height,图片高度 65 | * fontSize,字体大小 66 | * tilt,是否倾斜 67 | * backgroundColor,背景颜色 68 | */ 69 | private Integer size = 4; 70 | private Integer lines = 5; 71 | private Integer width = 80; 72 | private Integer height = 35; 73 | private Integer fontSize = 25; 74 | private boolean tilt = true; 75 | private Color backgroundColor = Color.LIGHT_GRAY; 76 | public VerifyCode build(){ 77 | return new VerifyCode(this); 78 | } 79 | } 80 | 81 | /** 82 | * 实例化构造器对象 83 | * @return new Builder() 84 | */ 85 | public static Builder newBuilder() { 86 | return new Builder(); 87 | } 88 | 89 | /** 90 | * 初始化基础参数 91 | * @param builder Builder 92 | */ 93 | private VerifyCode(Builder builder) { 94 | size=builder.size; 95 | lines=builder.lines; 96 | width=builder.width; 97 | height= builder.height; 98 | fontSize=builder.fontSize; 99 | tilt=builder.tilt; 100 | backgroundColor=builder.backgroundColor; 101 | } 102 | 103 | /** 104 | * getRandomColor,获取随机颜色 105 | * @return color 106 | */ 107 | public Color getRandomColor(){ 108 | Random ran=new Random(); 109 | Color color=new Color(ran.nextInt(25),ran.nextInt(255),ran.nextInt(255)); 110 | return color; 111 | } 112 | 113 | public Object[] createImage(){ 114 | StringBuffer sb=new StringBuffer(); 115 | //创建空白图片 116 | BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); 117 | //获取图片画笔 118 | Graphics2D graphics=image.createGraphics(); 119 | //设置抗锯齿 120 | graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); 121 | //设置画笔颜色 122 | graphics.setColor(backgroundColor); 123 | //绘制矩形背景 124 | graphics.fillRect(0,0,width,height); 125 | //获取随机字符 126 | Random ran=new Random(); 127 | //计算每个字符占的宽度,预留一个字符宽度用于左右边距 128 | int codeWidth=width/(size+1); 129 | //字符y轴的坐标 130 | int y=height*3/4; 131 | for(int i=0;i implements UserService { 41 | @Resource 42 | UserService userService; 43 | 44 | @Resource 45 | RoleMapper roleMapper; 46 | 47 | @Resource 48 | MenuMapper menuMapper; 49 | 50 | 51 | /** 52 | * loginIn,登录方法 53 | * 54 | * @param userDTO UserDTO 55 | * @return 验证码正确/不正确,用户不存在/存在,用户名,token,权限/角色 56 | */ 57 | @Override 58 | public Map loginInNotRemember(UserDTO userDTO) { 59 | //验证码校验 60 | VerifyCode.checkCode(userDTO.getCode()); 61 | //确认数据库存在该用户 62 | QueryWrapper wrapper = new QueryWrapper(); 63 | wrapper.eq("phone", userDTO.getPhone()); 64 | User user = userService.getOne(wrapper); 65 | if (LibraryUtil.isEmpty(user)) { 66 | //不存在该用户 67 | throw new CommonException(ExceptionCode.USERNAME_EXCEPTION, "账号:" + userDTO.getPhone() + "不存在"); 68 | } else if(LibraryUtil.equals(user.getActive(),'0')){ 69 | String content = "点击激活【CommonLibrary账号】"; 70 | MailUtils.sendMail(user.getEmail(), content, "CommonLibrary激活邮件"); 71 | //账号未激活 72 | throw new CommonException(ExceptionCode.ACCOUNT_UN_ACTIVE, "账号:" + userDTO.getPhone() + "未激活"); 73 | } else { 74 | //获取盐值 75 | String salt = user.getSalt(); 76 | String password = userDTO.getPassword(); 77 | password += salt; 78 | //加密算法 79 | password = TkipUtil.degst(password); 80 | //密码验证是否相等 81 | if (LibraryUtil.equals(password, user.getPassword())) { 82 | //用户登录 83 | StpUtil.login(user.getPhone()); 84 | //获取token 85 | String token = StpUtil.getTokenValue(); 86 | //获取权限 87 | List permissionList = StpUtil.getPermissionList(); 88 | //获取角色 89 | List roleList = StpUtil.getRoleList(); 90 | Map map = new HashMap<>(4); 91 | map.put("user", userDTO.getPhone()); 92 | map.put("token", token); 93 | map.put("permission", permissionList); 94 | map.put("role", roleList); 95 | return map; 96 | } else { 97 | //密码错误 98 | throw new CommonException(ExceptionCode.USERNAME_EXCEPTION, "用户名:" + userDTO.getPhone() + "密码错误"); 99 | } 100 | } 101 | } 102 | 103 | 104 | /** 105 | * loginInRemember,登录服务,记住密码,检验账号和token 106 | * 107 | * @param userDTO 108 | * @return 109 | */ 110 | @Override 111 | public Map loginInRemember(UserDTO userDTO) { 112 | String phone = userDTO.getPhone(); 113 | String token = userDTO.getToken(); 114 | //验证phone和token是否存在 115 | if (RedisUtil.hasStringKey(RedisConstant.SESSION + phone) && RedisUtil.hasStringKey(RedisConstant.LAST_ACTIVITY + token)) { 116 | //用户登录 117 | StpUtil.login(phone); 118 | //获取权限 119 | List permissionList = StpUtil.getPermissionList(); 120 | //获取角色 121 | List roleList = StpUtil.getRoleList(); 122 | Map map = new HashMap<>(4); 123 | map.put("user", phone); 124 | map.put("token", token); 125 | map.put("permission", permissionList); 126 | map.put("role", roleList); 127 | return map; 128 | } else { 129 | throw new CommonException(ExceptionCode.TOKEN_OUT_OF_DATE, "token已过期,请重新登录"); 130 | } 131 | } 132 | 133 | /** 134 | * register,注册服务 135 | * 136 | * @param userDTO 137 | * @return 138 | */ 139 | @Override 140 | public Result register(UserDTO userDTO) { 141 | //0.判定验证码 142 | VerifyCode.checkCode(userDTO.getCode()); 143 | QueryWrapper wrapper=new QueryWrapper(); 144 | wrapper.eq("phone",userDTO.getPhone()); 145 | //1.判定手机号是否已经注册,空为未注册,有为已注册 146 | if(LibraryUtil.isEmpty(userService.getOne(wrapper))){ 147 | String password = userDTO.getPassword(); 148 | String salt = TkipUtil.getSalt(); 149 | password += salt; 150 | password = TkipUtil.degst(password); 151 | userDTO.setPassword(password); 152 | //2.生成激活码 153 | String code = UUID.randomUUID().toString(); 154 | //3.生成用户 155 | User user = User.builder() 156 | .userName(userDTO.getUsername()) 157 | .phone(userDTO.getPhone()) 158 | .password(userDTO.getPassword()) 159 | .email(userDTO.getEmail()) 160 | .salt(salt) 161 | .activeCode(code) 162 | .active('0') 163 | .build(); 164 | //4.保存用户 165 | userService.save(user); 166 | //5.发送激活邮件 167 | String content = "点击激活【CommonLibrary账号】"; 168 | MailUtils.sendMail(user.getEmail(), content, "CommonLibrary激活邮件"); 169 | return new Result(200, "注册成功"); 170 | }else { 171 | throw new CommonException(ExceptionCode.PHONE_EXIST,"手机号码:"+userDTO.getPhone()+"已经注册"); 172 | } 173 | } 174 | 175 | /** 176 | * active,激活服务 177 | * 178 | * @param code 激活码 179 | * @return 180 | */ 181 | @Override 182 | public Result active(String code) { 183 | QueryWrapper wrapper=new QueryWrapper(); 184 | wrapper.eq("activeCode",code); 185 | User user=userService.getOne(wrapper); 186 | //账号为空 187 | if(LibraryUtil.isEmpty(user)){ 188 | throw new CommonException(ExceptionCode.ACTIVE_CODE_INEXISTENCE,"激活码:"+code+"不存在"); 189 | //账号已激活 190 | }else if(LibraryUtil.equals(user.getActive(),'1')){ 191 | return new Result(203,"手机号码:"+user.getPhone()+"已激活"); 192 | }else { 193 | //账号激活成功 194 | user.setActive('1'); 195 | userService.updateById(user); 196 | return new Result(200,"激活成功"); 197 | } 198 | } 199 | 200 | /** 201 | * getUserInfo,根据token获取用户信息 202 | * 203 | * @param token String 204 | * @return User对象 205 | */ 206 | @Override 207 | public User getUserInfo(String token) { 208 | String phone=(String) StpUtil.getLoginIdByToken(token); 209 | QueryWrapper wrapper=new QueryWrapper(); 210 | wrapper.eq("phone",phone); 211 | User user=userService.getOne(wrapper); 212 | Set roles=roleMapper.returnRole(user.getUserId()); 213 | Set menu=menuMapper.returnMenu(user.getUserId()); 214 | Set perms=new HashSet<>(); 215 | for (Menu tempMenu : menu) { 216 | perms.add(tempMenu.getPerms()); 217 | } 218 | user.setRoles(roles); 219 | user.setPerms(perms); 220 | return user; 221 | } 222 | 223 | 224 | } 225 | -------------------------------------------------------------------------------- /common/common.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /jwt/jwt.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /user/src/main/java/yj/sansui/service/impl/SaTokenRedisServiceImpl.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.service.impl; 2 | 3 | import cn.dev33.satoken.SaManager; 4 | import cn.dev33.satoken.dao.SaTokenDao; 5 | import cn.dev33.satoken.util.SaFoxUtil; 6 | import org.springframework.stereotype.Service; 7 | import yj.sansui.version2.RedisUtil; 8 | 9 | import java.util.Iterator; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | 14 | /** 15 | * SaTokenRedisServiceImpl,Redis的操作 16 | * @author sansui 17 | */ 18 | @Service 19 | public class SaTokenRedisServiceImpl implements SaTokenDao { 20 | 21 | /** 22 | * 数据集合 23 | */ 24 | public Map dataMap = new ConcurrentHashMap(); 25 | 26 | /** 27 | * 过期时间集合 (单位: 毫秒) , 记录所有key的到期时间 [注意不是剩余存活时间] 28 | */ 29 | public Map expireMap = new ConcurrentHashMap(); 30 | 31 | /** 32 | * 构造函数 33 | */ 34 | public SaTokenRedisServiceImpl() { 35 | initRefreshThread(); 36 | } 37 | 38 | // ------------------------ String 读写操作 39 | 40 | /** 41 | * 获取Value,如无返空 42 | * 43 | * @param key 键名称 44 | * @return value 45 | */ 46 | @Override 47 | public String get(String key) { 48 | return RedisUtil.getString(key); 49 | } 50 | 51 | /** 52 | * 写入Value,并设定存活时间 (单位: 秒) 53 | * 54 | * @param key 键名称 55 | * @param value 值 56 | * @param timeout 过期时间(值大于0时限时存储,值=-1时永久存储,值=0或小于-2时不存储) 57 | */ 58 | @Override 59 | public void set(String key, String value, long timeout) { 60 | if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { 61 | return; 62 | } 63 | //判断是否永不过期 64 | if(timeout == SaTokenDao.NEVER_EXPIRE) { 65 | RedisUtil.setString(key,value); 66 | } else { 67 | RedisUtil.setStringTime(key, value, timeout); 68 | } 69 | } 70 | 71 | /** 72 | * 更新Value (过期时间不变) 73 | * 74 | * @param key 键名称 75 | * @param value 值 76 | */ 77 | @Override 78 | public void update(String key, String value) { 79 | long expire = getTimeout(key); 80 | // -2 = 无此键 81 | if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { 82 | return; 83 | } 84 | this.set(key, value, expire); 85 | } 86 | 87 | /** 88 | * 删除Value 89 | * 90 | * @param key 键名称 91 | */ 92 | @Override 93 | public void delete(String key) { 94 | RedisUtil.deleteString(key); 95 | } 96 | 97 | /** 98 | * 获取Value的剩余存活时间 (单位: 秒) 99 | * 100 | * @param key 指定key 101 | * @return 这个key的剩余存活时间 102 | */ 103 | @Override 104 | public long getTimeout(String key) { 105 | return RedisUtil.getStringTimeout(key); 106 | } 107 | 108 | /** 109 | * 修改Value的剩余存活时间 (单位: 秒) 110 | * 111 | * @param key 指定key 112 | * @param timeout 过期时间 113 | */ 114 | @Override 115 | public void updateTimeout(String key, long timeout) { 116 | RedisUtil.updateObjectTimeout(key,timeout); 117 | } 118 | 119 | //----------------Object 操作 120 | /** 121 | * 获取Object,如无返空 122 | * 123 | * @param key 键名称 124 | * @return object 125 | */ 126 | @Override 127 | public Object getObject(String key) { 128 | clearKeyByTimeout(key); 129 | return RedisUtil.getObject(key); 130 | } 131 | 132 | /** 133 | * 写入Object,并设定存活时间 (单位: 秒) 134 | * 135 | * @param key 键名称 136 | * @param object 值 137 | * @param timeout 存活时间 (值大于0时限时存储,值=-1时永久存储,值=0或小于-2时不存储) 138 | */ 139 | @Override 140 | public void setObject(String key, Object object, long timeout) { 141 | if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { 142 | return; 143 | } 144 | //判断是否永不过期 145 | if(timeout == SaTokenDao.NEVER_EXPIRE) { 146 | RedisUtil.setObject(key,object); 147 | } else { 148 | RedisUtil.setObjectTime(key, object, timeout); 149 | } 150 | } 151 | 152 | /** 153 | * 更新Object (过期时间不变) 154 | * 155 | * @param key 键名称 156 | * @param object 值 157 | */ 158 | @Override 159 | public void updateObject(String key, Object object) { 160 | long expire = getObjectTimeout(key); 161 | // -2 = 无此键 162 | if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { 163 | return; 164 | } 165 | this.setObject(key, object, expire); 166 | } 167 | 168 | /** 169 | * 删除Object 170 | * 171 | * @param key 键名称 172 | */ 173 | @Override 174 | public void deleteObject(String key) { 175 | RedisUtil.deleteObject(key); 176 | } 177 | 178 | /** 179 | * 获取Object的剩余存活时间 (单位: 秒) 180 | * 181 | * @param key 指定key 182 | * @return 这个key的剩余存活时间 183 | */ 184 | @Override 185 | public long getObjectTimeout(String key) { 186 | return RedisUtil.getObjectTimeout(key); 187 | } 188 | 189 | /** 190 | * 修改Object的剩余存活时间 (单位: 秒) 191 | * 192 | * @param key 指定key 193 | * @param timeout 过期时间 194 | */ 195 | @Override 196 | public void updateObjectTimeout(String key, long timeout) { 197 | // 判断是否想要设置为永久 198 | if(timeout == SaTokenDao.NEVER_EXPIRE) { 199 | long expire = getObjectTimeout(key); 200 | if(expire == SaTokenDao.NEVER_EXPIRE) { 201 | // 如果其已经被设置为永久,则不作任何处理 202 | } else { 203 | // 如果尚未被设置为永久,那么再次set一次 204 | this.setObject(key, this.getObject(key), timeout); 205 | } 206 | } 207 | } 208 | 209 | // ------------------------ 过期时间相关操作 210 | 211 | /** 212 | * 如果指定key已经过期,则立即清除它 213 | * @param key 指定key 214 | */ 215 | void clearKeyByTimeout(String key) { 216 | Long expirationTime = expireMap.get(key); 217 | // 清除条件:如果不为空 && 不是[永不过期] && 已经超过过期时间 218 | if(expirationTime != null && expirationTime != SaTokenDao.NEVER_EXPIRE && expirationTime < System.currentTimeMillis()) { 219 | dataMap.remove(key); 220 | expireMap.remove(key); 221 | } 222 | } 223 | 224 | /** 225 | * 获取指定key的剩余存活时间 (单位:秒) 226 | */ 227 | long getKeyTimeout(String key) { 228 | // 先检查是否已经过期 229 | clearKeyByTimeout(key); 230 | // 获取过期时间 231 | Long expire = expireMap.get(key); 232 | // 如果根本没有这个值 233 | if(expire == null) { 234 | return SaTokenDao.NOT_VALUE_EXPIRE; 235 | } 236 | // 如果被标注为永不过期 237 | if(expire == SaTokenDao.NEVER_EXPIRE) { 238 | return SaTokenDao.NEVER_EXPIRE; 239 | } 240 | // ---- 计算剩余时间并返回 241 | long timeout = (expire - System.currentTimeMillis()) / 1000; 242 | // 小于零时,视为不存在 243 | if(timeout < 0) { 244 | dataMap.remove(key); 245 | expireMap.remove(key); 246 | return SaTokenDao.NOT_VALUE_EXPIRE; 247 | } 248 | return timeout; 249 | } 250 | 251 | // --------------------- 定时清理过期数据 252 | 253 | /** 254 | * 执行数据清理的线程 255 | */ 256 | public Thread refreshThread; 257 | 258 | /** 259 | * 是否继续执行数据清理的线程标记 260 | */ 261 | public volatile boolean refreshFlag; 262 | 263 | /** 264 | * 清理所有已经过期的key 265 | */ 266 | public void refreshDataMap() { 267 | Iterator keys = expireMap.keySet().iterator(); 268 | while (keys.hasNext()) { 269 | clearKeyByTimeout(keys.next()); 270 | } 271 | } 272 | 273 | /** 274 | * 初始化定时任务 275 | */ 276 | public void initRefreshThread() { 277 | // 如果配置了<=0的值,则不启动定时清理 278 | if(SaManager.getConfig().getDataRefreshPeriod() <= 0) { 279 | return; 280 | } 281 | // 启动定时刷新 282 | this.refreshFlag = true; 283 | this.refreshThread = new Thread(() -> { 284 | for (;;) { 285 | try { 286 | try { 287 | // 如果已经被标记为结束 288 | if(refreshFlag == false) { 289 | return; 290 | } 291 | // 执行清理 292 | refreshDataMap(); 293 | } catch (Exception e) { 294 | e.printStackTrace(); 295 | } 296 | // 休眠N秒 297 | int dataRefreshPeriod = SaManager.getConfig().getDataRefreshPeriod(); 298 | if(dataRefreshPeriod <= 0) { 299 | dataRefreshPeriod = 1; 300 | } 301 | Thread.sleep(dataRefreshPeriod * 1000); 302 | } catch (Exception e) { 303 | e.printStackTrace(); 304 | } 305 | } 306 | }); 307 | this.refreshThread.start(); 308 | } 309 | 310 | /** 311 | * 结束定时任务 312 | */ 313 | public void endRefreshThread() { 314 | this.refreshFlag = false; 315 | } 316 | 317 | //------------------------------会话管理 318 | /** 319 | * 搜索数据 320 | * 321 | * @param prefix 前缀 322 | * @param keyword 关键字 323 | * @param start 开始处索引 (-1代表查询所有) 324 | * @param size 获取数量 325 | * @param sortType 排序类型(true=正序,false=反序) 326 | * @return 查询到的数据集合 327 | */ 328 | @Override 329 | public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { 330 | return SaFoxUtil.searchList(expireMap.keySet(), prefix, keyword, start, size, sortType); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /redis/src/main/java/yj/sansui/version1/RedisUtil.java: -------------------------------------------------------------------------------- 1 | package yj.sansui.version1; 2 | 3 | import org.springframework.data.redis.core.RedisTemplate; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.util.CollectionUtils; 6 | 7 | import javax.annotation.Resource; 8 | import java.util.Collection; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * RedisUtil,redis工具类,内涵各种操作redis服务器的操作方法 16 | * 17 | * @author Sansui 18 | */ 19 | @Component("RedisUtil1") 20 | public class RedisUtil { 21 | 22 | private static RedisTemplate redisTemplate; 23 | 24 | /** 25 | * setRedisTemplate,注入Bean——RedisTemplate 26 | * @param redisTemplate RedisTemplate 27 | */ 28 | @Resource 29 | public void setRedisTemplate(RedisTemplate redisTemplate) { 30 | RedisUtil.redisTemplate = redisTemplate; 31 | } 32 | 33 | /** 34 | * del 删除缓存 35 | * @param key String(可多个参数) 36 | */ 37 | public static void del(String... key) { 38 | if (key != null && key.length > 0) { 39 | if (key.length == 1) { 40 | redisTemplate.delete(key[0]); 41 | } else { 42 | redisTemplate.delete( 43 | (Collection) CollectionUtils.arrayToList(key)); 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * expire,指定缓存失效时间 50 | * @param key String 51 | * @param time long 52 | * @return true/false 53 | */ 54 | public boolean expire(String key, long time) { 55 | try { 56 | if (time > 0) { 57 | redisTemplate.expire(key, time, TimeUnit.SECONDS); 58 | } 59 | return true; 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | return false; 63 | } 64 | } 65 | 66 | 67 | /** 68 | * getExpire,根据key获取过期时间 69 | * @param key String 70 | * @return redisTemplate.getExpire(key,TimeUnit.SECONDS); 71 | */ 72 | public long getExpire(String key){ 73 | return redisTemplate.getExpire(key,TimeUnit.SECONDS); 74 | } 75 | 76 | /** 77 | * hasKey,判断key是否存在 78 | * @param key String 79 | * @return true/false 80 | */ 81 | public static boolean hasKey(String key){ 82 | try { 83 | return redisTemplate.hasKey(key); 84 | }catch(Exception e){ 85 | e.printStackTrace(); 86 | return false; 87 | } 88 | } 89 | 90 | 91 | /** 92 | * getByKey,根据key获取value 93 | * @param key String 94 | * @return null/value 95 | */ 96 | public static Object getByKey(String key) { 97 | return key == null ? null : redisTemplate.opsForValue().get(key); 98 | } 99 | 100 | 101 | /** 102 | * setKeyValue,往redis存入key和value的值 103 | * @param key String 104 | * @param value Object 105 | * @return ture/false 106 | */ 107 | public static boolean setKeyValue(String key, Object value) { 108 | try { 109 | redisTemplate.opsForValue().set(key, value); 110 | return true; 111 | } catch (Exception e) { 112 | e.printStackTrace(); 113 | return false; 114 | } 115 | 116 | } 117 | 118 | 119 | 120 | /** 121 | * setKeyValueTime,设置key,value和对应过期时间 122 | * @param key 123 | * @param value 124 | * @param time 125 | * @return true/false 126 | */ 127 | public static boolean setKeyValueTime(String key, Object value, long time) { 128 | try { 129 | if (time > 0) { 130 | redisTemplate.opsForValue() 131 | .set(key, value, time, TimeUnit.SECONDS); 132 | } else { 133 | setKeyValue(key, value); 134 | } 135 | return true; 136 | }catch(Exception e){ 137 | e.printStackTrace(); 138 | return false; 139 | } 140 | } 141 | 142 | 143 | 144 | /** 145 | * incr,递增 146 | * 从key开始,递增因子是delta 147 | * @param key 148 | * @param delta 149 | * @return Exception/value 150 | */ 151 | public long incr(String key,long delta){ 152 | if(delta<0){ 153 | throw new RuntimeException("递增因子必须大于0"); 154 | } 155 | return redisTemplate.opsForValue().increment(key,delta); 156 | } 157 | 158 | /** 159 | * decr,递减 160 | * 从key开始,递减因子是delta 161 | * @param key 162 | * @param delta 163 | * @return Exception/value 164 | */ 165 | public long decr(String key, long delta){ 166 | if(delta<0){ 167 | throw new RuntimeException("递减因子必须大于0"); 168 | } 169 | return redisTemplate.opsForValue().increment(key,-delta); 170 | } 171 | 172 | 173 | 174 | /** 175 | * hashGetKeyValue,hash获取键值对 176 | * @param key 177 | * @param value 178 | * @return redisTemplate.opsForHash().get(key,value) 179 | */ 180 | public Object hashGetKeyValue(String key,String value){ 181 | return redisTemplate.opsForHash().get(key,value); 182 | } 183 | 184 | 185 | 186 | /** 187 | * hashGetKey,获取hashKey对应的所有键值 188 | * @param key 189 | * @return 190 | */ 191 | public static Map hashGetKey(String key){ 192 | return redisTemplate.opsForHash().entries(key); 193 | } 194 | 195 | 196 | 197 | /** 198 | * hmset HashSet 199 | * @param key String 200 | * @param map Map 201 | * @return true/fasle 202 | */ 203 | public boolean hmset(String key,Map map){ 204 | try{ 205 | redisTemplate.opsForHash().putAll(key,map); 206 | return true; 207 | }catch(Exception e){ 208 | e.printStackTrace(); 209 | return false; 210 | } 211 | } 212 | 213 | // HashSet 并设置时间 214 | public boolean hmset(String key,Map map,long time){ 215 | try{ 216 | redisTemplate.opsForHash().putAll(key, map); 217 | if (time>0){ 218 | expire(key,time); 219 | } 220 | return true; 221 | }catch(Exception e){ 222 | e.printStackTrace(); 223 | return false; 224 | } 225 | } 226 | 227 | 228 | // 向一张hash表中放入数据,如果不存在将创建 229 | public static boolean hset(String key, String item, Object value){ 230 | try{ 231 | redisTemplate.opsForHash().put(key,item,value); 232 | return true; 233 | }catch(Exception e){ 234 | e.printStackTrace(); 235 | return false; 236 | } 237 | } 238 | 239 | 240 | // 向一张hash表中放入数据,如果不存在将创建 241 | public boolean hset(String key,String item,Object value,long time){ 242 | try{ 243 | redisTemplate.opsForHash().put(key,item,value); 244 | if(time>0){ 245 | expire(key,time); 246 | } 247 | return true; 248 | }catch(Exception e){ 249 | e.printStackTrace(); 250 | return false; 251 | } 252 | } 253 | 254 | // 删除hash表中的值 255 | public void hdel(String key,Object... item) { 256 | redisTemplate.opsForHash().delete(key,item); 257 | } 258 | 259 | // 判断hash表中是否有该项的值 260 | public boolean hHasKey(String key,String item) { 261 | return redisTemplate.opsForHash().hasKey(key,item); 262 | } 263 | 264 | // hash递增 如果不存在,就会创建一个 并把新增后的值返回 265 | public double hincr(String key,String item,double by) { 266 | return redisTemplate.opsForHash().increment(key,item,by); 267 | } 268 | 269 | // hash递减 270 | public double hdecr(String key,String item,double by) { 271 | return redisTemplate.opsForHash().increment(key,item,-by); 272 | } 273 | 274 | 275 | // 根据key获取Set中的所有值 276 | public Set sGet(String key) { 277 | try{ 278 | return redisTemplate.opsForSet().members(key); 279 | }catch(Exception e){ 280 | e.printStackTrace(); 281 | return null; 282 | } 283 | } 284 | 285 | // 根据value从一个set中查询,是否存在 286 | public boolean sHasKey(String key,Object value) { 287 | try{ 288 | return redisTemplate.opsForSet().isMember(key,value); 289 | }catch(Exception e){ 290 | e.printStackTrace(); 291 | return false; 292 | } 293 | } 294 | 295 | // 将数据放入set缓存 296 | public long sSet(String key,Object... values) { 297 | try{ 298 | return redisTemplate.opsForSet().add(key,values); 299 | }catch(Exception e){ 300 | e.printStackTrace(); 301 | return 0; 302 | } 303 | } 304 | 305 | // 将set数据放入缓存 306 | public long sSetAndTime(String key,long time,Object... values){ 307 | try{ 308 | Long count=redisTemplate.opsForSet().add(key,values); 309 | if (time> 0) { 310 | expire(key, time); 311 | } 312 | return count; 313 | }catch(Exception e){ 314 | e.printStackTrace(); 315 | return 0; 316 | } 317 | } 318 | 319 | 320 | // 获取set缓存的长度 321 | public long sGetSetSize(String key){ 322 | try{ 323 | return redisTemplate.opsForSet().size(key); 324 | }catch(Exception e){ 325 | e.printStackTrace(); 326 | return 0; 327 | } 328 | } 329 | 330 | // 移除值为value的 331 | public long setRemove(String key,Object... values){ 332 | try{ 333 | Long count=redisTemplate.opsForSet().remove(key,values); 334 | return count; 335 | }catch(Exception e){ 336 | e.printStackTrace(); 337 | return 0; 338 | } 339 | } 340 | 341 | 342 | // 获取list缓存的内容 343 | public List lGet(String key, long start, long end){ 344 | try{ 345 | return redisTemplate.opsForList().range(key,start,end); 346 | }catch(Exception e){ 347 | e.printStackTrace(); 348 | return null; 349 | } 350 | } 351 | 352 | // 获取list缓存的长度 353 | public long lGetListSize(String key){ 354 | try{ 355 | return redisTemplate.opsForList().size(key); 356 | }catch(Exception e){ 357 | e.printStackTrace(); 358 | return 0; 359 | } 360 | } 361 | 362 | // 通过索引 获取list中的值 363 | public Object lGetIndex(String key,long index){ 364 | try{ 365 | return redisTemplate.opsForList().index(key,index); 366 | }catch(Exception e){ 367 | e.printStackTrace(); 368 | return null; 369 | } 370 | } 371 | 372 | // 将list放入缓存 373 | public boolean lSet(String key, Object value){ 374 | try{ 375 | redisTemplate.opsForList().rightPush(key,value); 376 | return true; 377 | }catch(Exception e){ 378 | e.printStackTrace(); 379 | return false; 380 | } 381 | } 382 | 383 | 384 | // 将list放入缓存 385 | public boolean lSet(String key,Object value,long time){ 386 | try{ 387 | redisTemplate.opsForList().rightPush(key,value); 388 | if (time > 0) { 389 | expire(key, time); 390 | } 391 | return true; 392 | }catch(Exception e){ 393 | e.printStackTrace(); 394 | return false; 395 | } 396 | } 397 | 398 | 399 | // 将list放入缓存 400 | public boolean lSet(String key, List value){ 401 | try{ 402 | redisTemplate.opsForList().rightPushAll(key,value); 403 | return true; 404 | }catch(Exception e){ 405 | e.printStackTrace(); 406 | return false; 407 | } 408 | } 409 | 410 | 411 | // 将list放入缓存 412 | public boolean lSet(String key,List value,long time){ 413 | try{ 414 | redisTemplate.opsForList().rightPushAll(key,value); 415 | if(time>0) { 416 | expire(key, time); 417 | } 418 | return true; 419 | }catch(Exception e){ 420 | e.printStackTrace(); 421 | return false; 422 | } 423 | } 424 | 425 | 426 | // 根据索引修改list中的某条数据 427 | public boolean lUpdateIndex(String key,long index,Object value){ 428 | try{ 429 | redisTemplate.opsForList().set(key,index,value); 430 | return true; 431 | }catch(Exception e){ 432 | e.printStackTrace(); 433 | return false; 434 | } 435 | } 436 | 437 | 438 | // 移除N个值为value 439 | public long lRemove(String key,long count,Object value){ 440 | try{ 441 | Long remove=redisTemplate.opsForList().remove(key,count,value); 442 | return remove; 443 | }catch(Exception e){ 444 | e.printStackTrace(); 445 | return 0; 446 | } 447 | } 448 | } 449 | --------------------------------------------------------------------------------