前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【云+社区年度征文】年终技术福利博客~

【云+社区年度征文】年终技术福利博客~

原创
作者头像
猿码优创
修改2020-12-18 15:03:38
8452
修改2020-12-18 15:03:38
举报
文章被收录于专栏:猿码优创猿码优创
哈喽小伙伴们~,2020年还剩最后15天
file
file

今天我们来一个年终福利文章。

社会层面:

file
file
2020年不过是一个普通的年份,却经历了与往常大不相同的12月,366天,8784小时,5207040分,31622400秒,世界人民一起经历了比2003年非典严重狡猾的病毒,直到现在世界疫情依然很严重!从任何方面,2020都是坎坷的一年,不论是是个人,或者是国家。通过这次疫情我更加热爱我的祖国,我们祖国的疫情全面得到控制,而国外却愈演愈烈,他们面对疫情不知所措乱成一锅粥。
今年国庆我很荣幸到了天安门广场,说不出来的一种自豪和骄傲。我们中国人民真的站起来了,我为我是中国人而骄傲!
file
file
己亥末,庚子春,荆楚大疫,染者数万计,众惶恐,举国防,皆闭户,南山镇守江南都,率白衣郎中数万抗之,且九州一心,月余,疫尽去,国泰民安。-摘自网络。

生活层面:

小伙伴,一直催我更新技术文章,最近实在是太忙,不瞒大家说,我开始带团队了,已经不是一个人在战斗了。一个人的时候可以没事的时候写写博客,那个时候感觉有很多很多时间也有很多很多精力去给大家更新博客,而现在多多少少有些力不从心了。只要我更新出来的东西,必须给读者负责,前一段时间我一个朋友在某博客学习某一个技术点,结果就是不出结果,最后一看写的博客少一行代码。我们既然敢写,就是敢负责到底。

福利分享:

今天给大家分享由我呢吧完全自主搭建一个完整技术工程,上才艺~
file
file
Java技术栈
  • Spring Boot
  • MyBatis Plus(含逆向工程)
  • MySql(建议用TencentDB for MySQL )
  • Redis(建议用TencentDB for Redis )
  • WebSocket
  • Quartz
  • Influx DB时序数据库
  • Shiro+JWT
  • P6Spy+SL4J
  • fastDfs文件上传
  • Swagger接口文档
  • Hutool工具类
  • 自定义注解
  • 自定义异常

一、猿码优创工程简单使用:

1、swagger-ui使用:

file
file
1.1、访问页面地址:http://127.0.0.1:12001/swagger-ui.html
1.2、使用方法:
file
file
代码语言:txt
复制
接口注释:
 @ApiOperation(value = "首页", notes = "访问测试", httpMethod = "GET")
参数注释:
 @ApiParam(name = "名字", value = "名字", required = true) String name
1.3、配置方法:
file
file
代码语言:txt
复制
 private ApiInfo apiInfo() {
    return new ApiInfo(
            "猿码优创管理API文档",
            "猿码优创管理API文档",
            "2.0",
            null,
            new Contact("kingyifan", "https://cnbuilder.cn/", "itw@tom.com"),
            "Apache 2.0", "https://www.apache.org/licenses/LICENSE-2.0.html", Collections.emptyList());
    }

2、权限使用(流程:用户登录===》获取token===》请求头携带token===》请求接口===》校验权限===》返回结果):

2.0、Token配置文件:
file
file
2.1、登录:
file
file
请求参数:
代码语言:txt
复制
form-data:
  account=admin
  password=admin
返回参数:
代码语言:txt
复制
{
    "msg": "登录成功(Login Success.)",
    "code": 200,
    "data": {
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjdXJyZW50VGltZU1pbGxpcyI6IjE2MDY0NDg3MDM1NzciLCJleHAiOjE2MDY0OTE5MDMsImFjY291bnQiOiJhZG1pbiJ9.nI_J4K7UtuHISwM3weGYKd65p3VhD7LSqcuCnqKa8A0"
    }
}
登录以后会自动加请求头响应参数:
file
file
登录代码:
代码语言:txt
复制
  /**
     * 登录授权
     *
     * @param account
     * @param password
     * @author 猿码优创 website:https://cnbuilder.cn/ Email:itw@tom.com
     */
    @PostMapping("/login")
    @ApiOperation(value = "用户", notes = "登录", httpMethod = "GET")
    public R login(
            @ApiParam(name = "account", value = "账号", required = true) @RequestParam(value = "account", required = true) String account,
            @ApiParam(name = "password", value = "密码", required = true) @RequestParam(value = "password", required = true) String password,
            HttpServletResponse httpServletResponse) {

        Map reMap = new HashMap<>();
        // 查询数据库中的帐号信息account
        User userTemp = userService.getOne(new QueryWrapper<User>().eq("account", account));
        if (userTemp == null) {
            return R.error(500, "该帐号不存在(The account does not exist.)");
        }
        // 密码进行AES解密
        String key = AesCipherUtil.deCrypto(userTemp.getPassword());
        // 因为密码加密是以帐号+密码的形式进行加密的,所以解密后的对比是帐号+密码
        if (key.equals(account + password)) {
            // 清除可能存在的Shiro权限信息缓存
            if (JedisUtil.exists(Constant.PREFIX_SHIRO_CACHE + account)) {
                JedisUtil.delKey(Constant.PREFIX_SHIRO_CACHE + account);
            }
            //判斷redis是否存在 存在直接用相同的。这个可以避免同样的账号两个人登录 把另一个人给踢出 TODO:如果一个账号只能一个登录可以去掉这段代码~
            String rtoken = (String) JedisUtil.getObject(Constant.EXISTENCE_TOKEN + account);
            if (rtoken != null) {
                httpServletResponse.setHeader("Authorization", rtoken);
                httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
                reMap.put("token", rtoken);
                return R.ok(HttpStatus.OK.value(), "登录成功(Login Success.)", reMap);
            }

            // 设置RefreshToken,时间戳为当前时间戳,直接设置即可(不用先删后设,会覆盖已有的RefreshToken)
            String currentTimeMillis = String.valueOf(System.currentTimeMillis());
            JedisUtil.setObject(Constant.PREFIX_SHIRO_REFRESH_TOKEN + account, currentTimeMillis, Integer.parseInt(refreshTokenExpireTime));

            // 从Header中Authorization返回AccessToken,时间戳为当前时间戳
            String token = JwtUtil.sign(account, currentTimeMillis);
            //存放redis
            JedisUtil.setObject(Constant.EXISTENCE_TOKEN + account, token, Integer.parseInt(accessTokenExpireTime));

            reMap.put("token", token);
            httpServletResponse.setHeader("Authorization", token);
            httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
            return R.ok("登录成功(Login Success.)").data(reMap);
        }
        return R.error(500, "帐号或密码错误(Account or Password Error.)");
    }
游客模式(不带Token也可以访问接口):
file
file
需要Token放行接口:
file
file
2.2、请求API接口:
不传Token:
file
file
传输错误Token(项目封装了异常捕获统一返回,如果不喜欢这样提示这样可以自行更改哈。):
file
file
传输正确Token
没有权限:
file
file
DB赋值权限(目前没做admin全部权限,有兴趣大家可以自己做一下哈):
file
file
有权限访问:
file
file
代码:
代码语言:txt
复制
加一行注解即可:自己匹配权限
@RequiresPermissions(logical = Logical.AND, value = {"user:menus"})

代码样例:
@GetMapping(value = "/menus")
@ApiOperation(value = "获取菜单", notes = "获取菜单", httpMethod = "GET")
@SystemLogAnnotation(businessType = "用户", methodDetail = "获取菜单")
@RequiresPermissions(logical = Logical.AND, value = {"user:menus"})
public R menus() {
    QueryWrapper<SysMenu> qr = new QueryWrapper<>();
    //根据用户查询菜单权限
    List<SysMenu> list = iSysMenuService.list(qr);
    //配置
    TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
    // 自定义属性名 都要默认值的
    treeNodeConfig.setWeightKey("sort");
    treeNodeConfig.setIdKey("id");
    // 最大递归深度
    treeNodeConfig.setDeep(3);
    //转换器
    List<Tree<String>> treeNodes = TreeUtil.build(list, "-1", treeNodeConfig,
            (treeNode, tree) -> {
                tree.setId(treeNode.getId().toString());
                tree.setParentId(treeNode.getParentid().toString());
                tree.setWeight(treeNode.getSort());
                tree.setName(treeNode.getName());
                // 扩展属性 ...
                tree.putExtra("isJump", treeNode.getIsJump() == null || treeNode.getIsJump() == 0 ? false : true);
                tree.putExtra("icon", treeNode.getIcon() == null ? "" : treeNode.getIcon());
                tree.putExtra("url", treeNode.getUrl());
            });
    return R.ok().data(treeNodes);
}
2.3、获取用户信息(必须过jwt拦截器传Token,才可以获取。):
file
file
根据Token自己解析用户信息
代码语言:txt
复制
代码:
final注入:
 private final UserUtil userUtil;这种final方式注入需要在controller或者service加@RequiredArgsConstructor
普通注入:
 @Autowired
    private UserUtil userUtil;
直接使用:
   User user = userUtil.getUser();
ps:权限框架集成参考:https://gitee.com/dolyw/ShiroJwt

3、influxDB:

3.0、influxDB简介和配置:
简介:
file
file
配置:
file
file
3.1、influxDB使用:
新增数据:没有database(数据库)和measurement(可以理解成表)自动会创建
file
file
代码语言:txt
复制
 //新增时序数据库数据
Map<String, String> tagsMap = new HashMap<>();//默认自己加索引
Map<String, Object> fieldsMap = new HashMap<>();//默认不加索引

tagsMap.put("name", "猿码优创");
tagsMap.put("website", "www.cnbuilder.cn");
tagsMap.put("Email", "itw@tom.com");
fieldsMap.put("author", "kingyifan");
influxDBConnect.insert("user", tagsMap, fieldsMap);
保存:
/**
 * 插入
 *
 * @param measurement 表
 * @param tags        标签
 * @param fields      字段
 */
public void insert(String measurement, Map<String, String> tags, Map<String, Object> fields) {
    Point.Builder builder = Point.measurement(measurement);
    // 纳秒时会出现异常信息:partial write: points beyond retention policy dropped=1
    // builder.time(System.nanoTime(), TimeUnit.NANOSECONDS);
    builder.time(System.currentTimeMillis() / 1000, TimeUnit.SECONDS);
    builder.tag(tags);
    builder.fields(fields);
    log.info("influxDB insert data:[{}]", builder.build().toString());
    influxDB.write(database, "", builder.build());
}
插入成功:
file
file
查询数据 最后加tz('Asia/Shanghai')指定时区
file
file
代码语言:txt
复制
PS:influxDB和mysql数据库不一样。
mysql返回的数据:
    key:value 
influxDb返回数据:
    columns=[time, Email, author, name, website], 
    values=[[2020-12-17T10:16:39Z, itw@tom.com, kingyifan, 猿码优创, www.cnbuilder.cn]]]
返回数据:
file
file
解析代码:
file
file
代码语言:txt
复制
代码:
/**
 * 解析influx返回数据
 *
 * @param queryResult
 * @return
 */
public static List<Map<String, Object>> convertQueryResult(QueryResult queryResult) {
    List<Map<String, Object>> maps = new LinkedList<>();
    if (queryResult.getResults().get(0).getSeries() == null || queryResult.getResults().get(0).getSeries().size() == 0) {
        return null;
    }
    for (QueryResult.Series series : queryResult.getResults().get(0).getSeries()) {
        List<String> columns = series.getColumns();
        List<List<Object>> values = series.getValues();
        for (List<Object> objects : values) {
            Map map = new HashMap();
            for (int i = 0; i < columns.size(); i++) {
                String c = columns.get(i);
                if (c.equals("time")) {
                    map.put(c, objects.get(i).toString().replace("+08:00", ""));
                    continue;
                }
                map.put(c, objects.get(i));
            }
            if (series.getTags() != null) {
                for (String key : series.getTags().keySet()) {
                    map.put(key, series.getTags().get(key));
                }
            }
            maps.add(map);
        }
    }
    return maps;
}

4、定时任务Quartz:

4.1、使用:
file
file
代码语言:txt
复制
注解:
  @Scheduled(cron = "0/1 * * * * ? ")
ps:七子表达式在线生成地址:https://cron.qqe2.com/

5、登录拦截日志(已经实现)

5.1、使用:

拦截用户操作日志:谁在什么时间干了什么什么事最后结果是什么?没干成原因是什么?:

file
file
代码语言:txt
复制
CREATE TABLE `sys_user_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `business_type` varchar(20) DEFAULT NULL COMMENT '业务类型',
  `method_detail` varchar(50) DEFAULT NULL COMMENT '接口备注',
  `ip` varchar(20) DEFAULT NULL COMMENT '请求ip',
  `method_name` varchar(200) DEFAULT NULL COMMENT '接口方法',
  `url` varchar(200) DEFAULT NULL COMMENT '接口url',
  `params` text COMMENT '请求参数',
  `sys_user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `sys_user_name` varchar(50) DEFAULT NULL COMMENT '操作人名称',
  `excute_time` bigint(20) DEFAULT NULL COMMENT '执行耗时',
  `create_time` datetime DEFAULT NULL COMMENT '操作时间',
  `return_result` text COMMENT '返回结果',
  `error_detail` text COMMENT '错误原因',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;
用到了Spring的Aop环绕拦截(后期可以加mq)。
代码语言:txt
复制
package cn.cnbuilder.manage.common.aspect;

import cn.cnbuilder.manage.busi.service.ISysUserLogService;
import cn.cnbuilder.manage.common.entity.R;
import cn.cnbuilder.manage.shiro.util.UserUtil;
import cn.cnbuilder.manage.busi.entity.SysUserLog;
import cn.cnbuilder.manage.common.annotation.SystemLogAnnotation;
import cn.cnbuilder.manage.common.utils.IpUtil;
import cn.cnbuilder.manage.shiro.model.entity.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;

/**
 * 日志切面类
 *
 * @author 猿码优创 website:https://cnbuilder.cn/ Email:itw@tom.com
 */
@Aspect
@Component
public class LogAspect {

    @Autowired
    private UserUtil userUtil;

    @Autowired
    private ISysUserLogService sysUserLogService;


    /**
     * 定义一个切入点.
     * 解释下:
     * <p>
     * ~ 第一个 * 代表任意修饰符及任意返回值.
     * ~ 第二个 * 定义在web包或者子包
     * ~ 第三个 * 任意方法
     * ~ .. 匹配任意数量的参数.
     */
    @Pointcut("execution(* cn.cnbuilder.manage.*.controller..*(..))")
    public void logPointcut() {
    }

    @Pointcut("@annotation(cn.cnbuilder.manage.common.annotation.SystemLogAnnotation)")
    public void executePointCut() {
    }

    @org.aspectj.lang.annotation.Around("logPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        //获取请求
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        SysUserLog sysUserLog = new SysUserLog();
        long start = System.currentTimeMillis();
        Object result = null;
        //  操作模块和方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        SystemLogAnnotation annotation = signature.getMethod().getAnnotation(SystemLogAnnotation.class);
        try {
            //返回结果
            result = joinPoint.proceed();
        } catch (Throwable e) {
            //保存
            sysUserLog.setErrorDetail(e.getMessage());
            e.printStackTrace();
            //返回错误信息
            result = R.error(500, e.getMessage());
        } finally {
            try {
                //结束时间
                long end = System.currentTimeMillis();
                //执行时间
                sysUserLog.setExcuteTime(end - start);
                //y业务类型
                sysUserLog.setBusinessType(annotation == null ? "" : annotation.businessType());
                //操作时间
                sysUserLog.setCreateTime(new Date());
                //ip地址
                sysUserLog.setIp(IpUtil.getIpAddr(request));
                //接口方法
                sysUserLog.setMethodName(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
                //请求参数
                sysUserLog.setParams(Arrays.toString(joinPoint.getArgs()));
                //返回结果
                sysUserLog.setReturnResult(result == null ? "" : result.toString());
                User user = userUtil.aroundGetUser();
                //用户id
                sysUserLog.setSysUserId(user.getId());
                //用戶名
                sysUserLog.setSysUserName(user.getUsername());
                //请求url
                sysUserLog.setUrl(request.getRequestURI());
                //方法备注
                sysUserLog.setMethodDetail(annotation == null ? "" : annotation.methodDetail());
                sysUserLogService.save(sysUserLog);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //不管保存不保存日志都需要返回结果
                return result;
            }
        }

    }

}

使用了自定义注解:

代码语言:txt
复制
添加使用注解:
 @SystemLogAnnotation(businessType = "用户", methodDetail = "获取菜单")

自定义注解代码(可以自行添加别的参数):
package cn.cnbuilder.manage.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解
 *
 * @author 猿码优创 website:https://cnbuilder.cn/ Email:itw@tom.com
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLogAnnotation {

    //  业务类型
    String businessType() default "";

    //方法名称
    String methodDetail() default "";
}

6、Redis使用(目前用的redis连接池,后期可以更换成redisTemplate):

6.1、配置文件:
file
file

存值:

代码语言:txt
复制
JedisUtil.setObject("name", "wangyifan");

取值:

代码语言:txt
复制
JedisUtil.getObject("name").toString();

更多使用详见JedisUtil工具类,建议使用腾讯云版本的Redis地址:https://cloud.tencent.com/product/crs

7、WebSocket使用

file
file

正常发送消息:

file
file

群发消息:

file
file

具体配置信息我原来写过文章,请移步:https://blog.cnbuilder.cn/archives/SpringBoot_websocket

8、MyBatisPlus逆向工程

修改成自己的连接等配置,执行main方法,就会生成我们需要的controller、service、mapper、xml等文件

8.1、修改数据库配置:
代码语言:txt
复制
// ======================数据源配置========================
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDbType(DbType.MYSQL);   //设置数据库类型,
dsc.setUrl("jdbc:mysql://xxx.xxx.xxx.xxx:xx/ymyc_manage?serverTimezone=GMT%2B8");  //指定数据库
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
autoGenerator.setDataSource(dsc);
8.2、修改创建的表
代码语言:txt
复制
//设置生成的表名,这里使用exClude来进行排除,写入null,表示生成所有表。
strategyConfig.setExclude(null);
//设置表    可变个数的形参    String数组
strategyConfig.setInclude("t\_user",""xxx");
8.3、作者:
代码语言:txt
复制
 gc.setAuthor("猿码优创");// 作者
8.4、包的路径:
代码语言:txt
复制
pc.setParent("cn.cnbuilder.manage.busi");          
终、、以上奉献给你们的年终福利~

码云地址:https://gitee.com/it-wangyifan/ymyc_manage

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 社会层面:
  • 生活层面:
  • 福利分享:
  • 一、猿码优创工程简单使用:
    • 1、swagger-ui使用:
      • 2、权限使用(流程:用户登录===》获取token===》请求头携带token===》请求接口===》校验权限===》返回结果):
        • 3、influxDB:
          • 4、定时任务Quartz:
            • 5、登录拦截日志(已经实现)
              • 6、Redis使用(目前用的redis连接池,后期可以更换成redisTemplate):
                • 7、WebSocket使用
                  • 8、MyBatisPlus逆向工程
                  相关产品与服务
                  云数据库 Redis
                  腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档