入职必知必会

目录

一、slf4j+logback日志

1. 基本概念

日志级别

2. 用法

设置resources下的文件logback.xml

调用

二、navicat

1. 建表

2. 索引

3. 查找

三、Linux常用指令

1. 复制粘贴

2. 常用指令

四、idea的使用

1. 导入项目

2. 快捷键

3. debug模式

五、Git

1. 创建本地仓库

2. 新建远程仓库

3. 生成SSH公钥

本地仓库连接远程仓库

4. 在Idea中配置Git(不用命令行)

创建本地仓库

设置远程仓库

提交并推送到本地仓库

5. 版本回退和撤销修改

6. 解决冲突和补丁操作

六、解决BUG

1. bug一般的处理流程

2. 错误复现

七、Redis

1. 新项目的使用

2. 项目已有 Redis

3. Redis 的使用场景

八、本地跑项目

1. 有开发环境

2. 无开发环境

3. 设置maven

九、如何写一个接口

1. 模糊分页查询

2. 批量删除

3. 新增

十、对Stream的使用

1. 对map集合的操作

2. 对list集合的操作

十一、RabbitMQ

1. 引入依赖

2. 配置连接信息

3. 设置消息转换器(可以省略)

4. 配置文件

5. 生产者代码

6. 消费者代码


一、slf4j+logback日志

1. 基本概念

日志级别

  1. FATAL:表示需要立即被处理的系统级错误。当该错误发生时,表示服务已经出现了某种程度的不可用,系统管理员需要立即介入。这属于最严重的日志级别,因此该日志级别必须慎用,如果这种级别的日志经常出现,则该日志也失去了意义。通常情况下,一个进程的生命周期中应该只记录一次FATAL级别的日志,即该进程遇到无法恢复的错误而退出时。当然,如果某个系统的子系统遇到了不可恢复的错误,那该子系统的调用方也可以记入FATAL级别日志,以便通过日志报警提醒系统管理员修复;
  2. ERROR:该级别的错误也需要马上被处理,但是紧急程度要低于 FATAL 级别。当ERROR错误发生时,已经影响了用户的正常访问。从该意义上来说,实际上ERROR错误和FATAL错误对用户的影响是相当的。FATAL相当于服务已经挂了,而ERROR相当于好死不如赖活着,然而活着却无法提供正常的服务,只能不断地打印ERROR日志。特别需要注意的是,ERROR和FATAL都属于服务器自己的异常,是需要马上得到人工介入并处理的。而对于用户自己操作不当,如请求参数错误等等,是绝对不应该记为ERROR日志的;
  3. WARN:该日志表示系统可能出现问题,也可能没有,这种情况如网络的波动等。对于那些目前还不是错误,然而不及时处理也会变为错误的情况,也可以记为WARN日志,例如一个存储系统的磁盘使用量超过阀值,或者系统中某个用户的存储配额快用完等等。对于WARN级别的日志,虽然不需要系统管理员马上处理,也是需要及时查看并处理的。因此此种级别的日志也不应太多,能不打WARN级别的日志,就尽量不要打;
  4. INFO:该种日志记录系统的正常运行状态,例如某个子系统的初始化,某个请求的成功执行等等。通过查看INFO级别的日志,可以很快地对系统中出现的 WARN,ERROR,FATAL错误进行定位。INFO日志不宜过多,通常情况下,INFO级别的日志应该不大于TRACE日志的10%;
  5. DEBUG or TRACE:这两种日志具体的规范应该由项目组自己定义,该级别日志的主要作用是对系统每一步的运行状态进行精确的记录。通过该种日志,可以查看某一个操作每一步的执行过程,可以准确定位是何种操作,何种参数,何种顺序导致了某种错误的发生。可以保证在不重现错误的情况下,也可以通过DEBUG(或TRACE)级别的日志对问题进行诊断。需要注意的是,DEBUG日志也需要规范日志格式,应该保证除了记录日志的开发人员自己外,其他的如运维,测试人员等也可以通过 DEBUG(或TRACE)日志来定位问题;

日志级别优先级:

ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

logback中是没有fatal这个级别的,如果使用会映射成error输出。

2. 用法

SpringBoot 中默认使用 commons logging 门面(日志框架),也就是说已经引入了 commons logging 相关的依赖,我们可以选择(实现) jdk 自带的 java.util.logging 也可以选择log4j也可以选择logback。

SpringBoot 项目(使用了一个starter)中内置一个 logback 的实现,我们可以修改配置文件实现输出定制的日志格式。

设置resources下的文件logback.xml

示例:

<?xml version="1.0" encoding="UTF-8" ?>
 
<!-- logback中一共有5种有效级别,分别是TRACE、DEBUG、INFO、WARN、ERROR,优先级依次从低到高 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
 
  <property name="DIR_NAME" value="spring-helloworld"/>
 
  <!-- 将记录日志打印到控制台 -->
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
 
  <!-- RollingFileAppender begin -->
  <appender name="ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 根据时间来制定滚动策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/all.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
 
    <!-- 根据文件大小来制定滚动策略 -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>30MB</maxFileSize>
    </triggeringPolicy>
 
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
 
  <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 根据时间来制定滚动策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
 
    <!-- 根据文件大小来制定滚动策略 -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>
 
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>ERROR</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
 
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
 
  <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 根据时间来制定滚动策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/warn.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
 
    <!-- 根据文件大小来制定滚动策略 -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>
 
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>WARN</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
 
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
 
  <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 根据时间来制定滚动策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
 
    <!-- 根据文件大小来制定滚动策略 -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>
 
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>INFO</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
 
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
 
  <appendername="DEBUG"class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 根据时间来制定滚动策略 -->
    <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
 
    <!-- 根据文件大小来制定滚动策略 -->
    <triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>
 
    <filterclass="ch.qos.logback.classic.filter.LevelFilter">
      <level>DEBUG</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
 
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
 
  <appendername="TRACE"class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 根据时间来制定滚动策略 -->
    <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/trace.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
 
    <!-- 根据文件大小来制定滚动策略 -->
    <triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>
 
    <filterclass="ch.qos.logback.classic.filter.LevelFilter">
      <level>TRACE</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
 
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
 
  <appendername="SPRING"class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 根据时间来制定滚动策略 -->
    <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/springframework.%d{yyyy-MM-dd}.log
      </fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
 
    <!-- 根据文件大小来制定滚动策略 -->
    <triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>
 
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
  <!-- RollingFileAppender end -->
 
  <!-- logger begin -->
  <!-- 本项目的日志记录,分级打印 -->
  <loggername="org.zp.notes.spring"level="TRACE"additivity="false">
    <appender-refref="STDOUT"/>
    <appender-refref="ERROR"/>
    <appender-refref="WARN"/>
    <appender-refref="INFO"/>
    <appender-refref="DEBUG"/>
    <appender-refref="TRACE"/>
  </logger>
 
  <!-- SPRING框架日志 -->
  <loggername="org.springframework"level="WARN"additivity="false">
    <appender-refref="SPRING"/>
  </logger>
 
  <rootlevel="TRACE">
    <appender-refref="ALL"/>
  </root>
  <!-- logger end -->
 
</configuration>

自己常用的:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>

    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%clr([%d{HH:mm:ss.SSS}]) %msg %clr([%level=>]) %C.%M\(%F:%L\)%n</pattern>
        </encoder>
    </appender>

    <!-- 系统模块日志级别控制  -->
    <logger name="com.ruoyi" level="info" />
    <!-- Spring日志级别控制  -->
    <logger name="org.springframework" level="warn" />

    <root level="info">
        <appender-ref ref="console" />
    </root>

</configuration>

调用

  1. 使用idea在pom文件加入lombok的依赖
  2. 类上面添加@Sl4j注解,然后使用log打印日志;

二、navicat

1. 建表

  1. 直接建表
  2. 向导建表

可以使用向导功能将excel表转为数据库表,不过excel表要符合格式。

  1. sql文件建表

将原有的表转成sql文件,将sql文件放在别的环境中执行就可以得到。

2. 索引

设计表中可以设置字段索引。

3. 查找

当数据量大时使用sql查询语句会比较慢,那么直接在navicat中使用筛选功能。

可以对查询出来的数据进行处理(导出成excel表)。

三、Linux常用指令

1. 复制粘贴

Linux 系统下的复制粘贴不是 ctrl + C/V 而是 ctrl + insert(复制),shift + insert(粘贴)。

2. 常用指令

系统指令:

ifconfig 查看IP地址。

top 运行的进程和系统性能信息。

free -h 内存使用情况。

df -h 磁盘使用情况。

systemctl status firewalld 防火墙状态。

systemctl start firewalld 开启防火墙。

systemctl stop firewalld 中断防火墙。

文本编辑:

vim test.txt

vi和vim,是 linux 中的文本编辑器,用来在linux中创建、查看或者编辑文本文件。

编辑模式:在一般模式下,按 i 键或者 a 键,进入编辑模式;

可以编辑文件内容;但是不能保存编辑的内容;按 esc 键,可以回到一般模式。

命令行模式:在一般模式下,按 shift + :进入命令行模式;

输入:

wq 保存并退出编辑器
q 只是退出编辑器
q! 不保存强制退出编辑器

文件和目录相关操作:

切换目录

ll 查看当前目录下的所有文件。

cd .. 切换到上级目录。

cd /opt 切换到opt目录下。

cd bin 切换到当前目录的bin目录下。

cd test 切换到当前目录下的test目录下。

创建目录

mkdir /opt/data 在opt目录下创建目录data。

mkdir -p data/mysql 
在当前目录下创建目录data,并且在data下创建mysql(一次创建多级目录)。

复制文件

cp gateway.jar gateway-bank-2023-08-12.jar
把文件gateway.jar复制为gateway-bank-2023-08-12.jar作为备份。

删除文件

rm test.txt 提示删除 text.txt 文件。

rm -f test.txt 强制删除 text.txt 文件。

rm -r data 提示递归删除data目录。

rm -rf data 强制递归删除data目录。

查看文件

cat text.txt 查看text.txt文件内容,所有内容一次性显示出来

tail -f text.txt 实时查看text.txt文件的末尾内容

grep "mysql" text.txt //查看文件text.txt中包含mysql的内容

查找文件

find / -name text.txt 从根目录查找名称为text.txt的文件

find /opt *.java 查找opt目录下的.java文件

find /opt *.java | grep user 
搜索opt目录下,所有名称包含user的.java文件

四、idea的使用

1. 导入项目

导入项目时需要修改maven的构建,私仓地址和本地仓库地址;还有启动的脚本配置。

修改maven:

启动脚本:

2. 快捷键

ctrl + alt + l 格式化

alt + enter 补全创建变量

new Arrlist<>();

3. debug模式

  1. 打断点会造成服务一直启动不起来需要将断点全部取消掉再启动;

  1. debug模式可以添加变量到控制台可以监控其变化;
  2. 打断点时可以设置断点精确到循环的条件时停下来(i=2);

五、Git

  1. 执行克隆命令和初始化仓库命令

Git(分布式版本控制工具)-CSDN博客

知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具

1. 创建本地仓库

在项目文件夹初始化仓库

git init

2. 新建远程仓库

3. 生成SSH公钥

SSH 公钥设置 | Gitee 产品文档

  • 生成SSH公钥
    • ssh-keygen -t rsa
    • 不断回车
      • 如果公钥已经存在,则自动覆盖
  • Gitee设置账户共公钥
    • 获取公钥
      • cat ~/.ssh/id_rsa.pub

本地仓库连接远程仓库

命令: git remote add <远端名称> <仓库路径SSH>

  • 远端名称,默认是origin,取决于远端服务器设置
  • 仓库路径,从远端服务器获取此SSH,

4. 在Idea中配置Git(不用命令行)

创建本地仓库

设置远程仓库

提交并推送到本地仓库

5. 版本回退和撤销修改

  1. 已提交,没有push

1)git reset --soft 撤销commit

2)git reset --mixed 撤销commit和add两个动作

  1. 已提交,并且push

1)git reset --hard 撤销并舍弃版本号之后的提交记录。使用需要谨慎

2)git revert 撤销。但是保留了提交记录

6. 解决冲突和补丁操作

提交冲突的原因:提交者的版本库<远程库

冲突情况一:能自动合并的冲突

文件中有两个函数,分别由两个人负责开发维护,需要将另外一个人修改的代码合并在一起 (冲突为不同角色对同一内容的修改)

冲突情况二:手动解决冲突

如何实现本地同步:git pull(先拉下来,解决冲突再提交)

六、解决BUG

1. bug一般的处理流程

2. 错误复现

  1. F12打开控制台

复制地址到postman里

  1. 操作postman

将从控制台得到的body,headers,鉴权token(key-value)等请求参数填写到postman中

  1. 操作idea

复制从控制台得到的url的后面几位复制下来到idea中查找

七、Redis

1. 新项目的使用

  1. 配置 redis 信息(主要就是 redis 的地址、端口、密码)
  redis:
    host: 192.168.64.100
    port: 6379
    timeout: 1000
    jedis:
      pool:
        max-active: 8
        max-wait: 1000
        max-idle: 500
        min-idle: 0
  1. 引入 redisTemplate 依赖

  1. 写 redis 的工具类(封装好 redis 再使用)

Redis 配置类:

@Configuration
public class RedisConfiguration {

    @Bean
    public StringRedisTemplate getStringRedisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    @Bean(name = "redisSessionTemplate")
    public RedisTemplate<String, SessionContext> getredisSessionTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, SessionContext> redisTemplate = new RedisTemplate<String, SessionContext>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer()); // key的序列化类型

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value的序列化类型
        return redisTemplate;
    }

    @Bean
    public RedisSessionHelper redisSessionHelper() {
        return new RedisSessionHelper();
    }

    @Bean
    public RedisHelper redisHelper() {
        return new RedisHelper();
    }
}

Redis 方法:

public class RedisHelper {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void setObject(String prefix, String key, Object value) {
        String redisKey = getRedisKey(prefix, key);
        String serializeData = JSON.toJSONString(value);
        redisTemplate.opsForValue().set(redisKey, serializeData);
    }

    public <T> T getObject(String prefix, String key, Class<T> clazz) {
        String redisKey = getRedisKey(prefix, key);
        String serializeData = redisTemplate.opsForValue().get(redisKey);
        return JSON.parseObject(serializeData, clazz);
    }

    public void delKey(String prefix, String key) {
        String redisKey = getRedisKey(prefix, key);
        redisTemplate.delete(redisKey);
    }

    public boolean exists(String prefix, String key) {
        String redisKey = getRedisKey(prefix, key);
        return redisTemplate.hasKey(redisKey);
    }

    public void addGEO(String prefix, String key, String lng, String lat, String tripId) {
        String redisKey = getRedisKey(prefix, key);
        Point point = new Point(Float.parseFloat(lng), Float.parseFloat(lat));
        redisTemplate.opsForGeo().add(redisKey, point, tripId);
    }

    public void delGEO(String prefix, String key, String tripId) {
        String redisKey = getRedisKey(prefix, key);
        redisTemplate.opsForGeo().remove(redisKey, tripId);
    }

    /**
     * 计算两点之间距离
     *
     * @param prefix
     * @param key
     * @param startLocation
     * @param endLocation
     * @return
     */
    public float geoDistance(String prefix, String key, String startLocation, String endLocation) {
        String redisKey = getRedisKey(prefix, key);
        Distance distance = redisTemplate.opsForGeo()
                .distance(redisKey, startLocation, endLocation, RedisGeoCommands.DistanceUnit.KILOMETERS);//params: key, 地方名称1, 地方名称2, 距离单位
        return (float) distance.getNormalizedValue();
    }


    public Map<String, GeoBO> geoNearByXY(String prefix, String key, float lng, float lat) {
        String redisKey = getRedisKey(prefix, key);
        //以当前坐标为中心画圆
        Circle circle = new Circle(
                new Point(lng, lat),
                new Distance(HtichConstants.STROKE_DIAMETER_RANGE, Metrics.KILOMETERS)
        );
        //限制20条,可以根据实际情况调整
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                .includeDistance().includeCoordinates()
                .sortAscending().limit(20);
        //查找这个范围内的行程点
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo()
                .radius(redisKey, circle, args);
        return geoResultPack(results);
    }


    /**
     * 根据地址名词进行搜索
     *
     * @param prefix   前缀
     * @param key      key
     * @param location 地址:行程ID+role
     * @param isStart  是否时起始行程
     * @return
     */
    public Map<String, GeoBO> geoNearByPlace(String prefix, String key, String location, boolean isStart) {
        String redisKey = getRedisKey(prefix, key);
        Distance distance = new Distance(HtichConstants.STROKE_DIAMETER_RANGE, Metrics.KILOMETERS);//params: 距离量, 距离单位
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(20);
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo()
                .radius(redisKey, location, distance, args);//params: key, 地方名称, Circle, GeoRadiusCommandArgs
        return geoResultPack(results);

    }

    public void addHash(String prefix, String key, String hkey, String value) {
        String redisKey = getRedisKey(prefix, key);
        redisTemplate.opsForHash().put(redisKey, hkey, value);
    }

    public void delHash(String prefix, String key, String... hkeys) {
        String redisKey = getRedisKey(prefix, key);
        redisTemplate.opsForHash().delete(redisKey, hkeys);
    }

    public String getHash(String prefix, String key, String hkey) {
        String redisKey = getRedisKey(prefix, key);
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        return hashOperations.get(redisKey, hkey);
    }


    public Map<String, String> getHashByMap(String prefix, String key) {
        String redisKey = getRedisKey(prefix, key);
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        return hashOperations.entries(redisKey);
    }

    public void addZset(String prefix, String key, String value, float score) {
        String redisKey = getRedisKey(prefix, key);
        redisTemplate.opsForZSet().add(redisKey, value, score);
    }

    public List<ZsetResultBO> getZsetSortVaues(String prefix, String key) {
        String redisKey = getRedisKey(prefix, key);
        List<ZsetResultBO> zsetResultBOList = new ArrayList<>();
        //rangeByScoreWithScores(K,Smin,Smax,[offset],[count])
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().reverseRangeByScoreWithScores(redisKey, 0, 100,0,20);
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            zsetResultBOList.add(new ZsetResultBO(typedTuple.getScore().floatValue(), typedTuple.getValue()));
        }
        return zsetResultBOList;
    }

    public float getZsetScore(String prefix, String key, String value) {
        String redisKey = getRedisKey(prefix, key);
        return redisTemplate.opsForZSet().score(redisKey, value).floatValue();
    }

    /**
     * 删除zset数据
     *
     * @param prefix
     * @param key
     * @param value
     */
    public void delZsetByKey(String prefix, String key, String value) {
        String redisKey = getRedisKey(prefix, key);
        redisTemplate.opsForZSet().remove(redisKey, value);
    }

    private String getRedisKey(String prefix, String key) {
        return prefix + key;
    }


    /**
     * GEO结果集包装
     *
     * @param results
     * @return
     */
    private Map<String, GeoBO> geoResultPack(GeoResults<RedisGeoCommands.GeoLocation<String>> results) {
        Map<String, GeoBO> geoBOMap = new HashMap();
        for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : results) {
            RedisGeoCommands.GeoLocation<String> content = result.getContent();
            String name = content.getName();
            // 距离中心点的距离
            Distance dis = result.getDistance();
            Point pos = content.getPoint();
            geoBOMap.put(name, new GeoBO(name, ((float) dis.getValue()), String.valueOf(pos.getX()), String.valueOf(pos.getY())));
        }
        return geoBOMap;
    }

}

  1. 使用 redis 的工具类
RedisHelper.setObject(prefix,key);

2. 项目已有 Redis

  1. 全局搜索 redis

查看别人如何使用 redis (查看它封装的 redis)。

3. Redis 的使用场景

  1. 有些图表数据来源的表数据较多,处理过程复杂,导致接口响应慢,不符合客户需求
  2. 自定义注解,需要把校验信息存储到 redis 中
  3. 一些经常使用的数据需要在项目启动的时候就从数据库里加载到 redis 中
  4. 验证码、token信息需要加载到redis中

至于缓存一致性,直接先更新,再删除就能满足大部分的场景

八、本地跑项目

1. 有开发环境

直接使用公司的开发环境(公司有一台服务器去部署项目),不需要本地安装项目启动所需的开发环境,比如jdk、MySQL、redis等(不用担心这些的版本问题)。

• 远程配置文件(启动脚本)

将其添加在

• 本地配置文件

application.yml下

--spring.profiles.active-dev

2. 无开发环境

需要本地安装项目启动所需要的开发环境,需要安装的东西可以从项目的配置文件里面去看(保证不会出现版本问题)。

3. 设置maven

配置 maven

九、如何写一个接口

1. 模糊分页查询

SpringBoot实现分页模糊查询_spring boot 模糊查询-CSDN博客

PageHelper设置pageSize失效问题解决_使用新版本pagehelper pagehelper.startpage失效-CSDN博客

Controller层:

@RestController
@RequestMapping("/camera")
public class CameraAlarmController{
  @Autowired
  private WarehouseOutService warehouseOutService;
  /**
    * 获取设备相关信息
    * @param equipAttribute
    * @param equipType
    * @param keywords
    * @param pageNum
    * @param pageSize
    * @return
    */
  @GetMapping("/getOutboundEquipmentInfo")
  public Result getOutboundEquipmentInfo(@RequestParam(value="operationType",required=false) Integer operationType,
                                         @RequestParam(value="warehouseId",required=false Integer warehouseId,
                                         @RequestParam(value="equipAttribute",required=false) String equipAttribute,
                                         @RequestParam(value="equipTypeId",required=false) Integer equipTypeId,
                                         @RequestParam(value="keywords",required=false) String keywords,
                                         @RequestParam(value="pageNum",required=false,defaultValue="1") Integer pageNum,
                                         @RequestParam(value="pageSize",required=false,defaultValue="10") Integer pageSize){
      PageHelper.startPage(pageNum,pageSize);
      PageInfo<OutboundEquipmentInfo> pageInfo=new PageInfo<>(warehouseOutService.getOutboundEquipmentInfo(equipAttribute,
      equipTypeId.warehouseId,operationType,keywords));

      return new SuccessResult(pageInfo);
      }
}

Service层:

定义service层接口

public interface IWarehouseOutService{
  List<OutboundEquipmentInfoVo> getOutboundEquipmentInfo(String equipAttribute,
      Integer equipTypeId,Integer warehouseId,Integer operationType,String keywords);
}

service层实现

@Service
@Slf4j
public class WarehouseServiceImpl implements IWarehouseOutService{
  @Autowired
  private WmEquipInfoMapper wmEquipInfoMapper;
   
    @Override
    public List<OutboundEquipmentInfoVo> getOutboundEquipmentInfo(String equipAttribute,
                                                                  Integer equipTypeId,
                                                                  Integer warehouseId,
                                                                  Integer operationType,
                                                                  String keywords) {
        //入库操作
        if (operationType==WmConstant.EQUIPMENT_INFO_INCOME){
            List<OutboundEquipmentInfoVo> outList=wmEquipInfoMapper.getEquipmentInformation(equipAttribute,equipTypeId,keywords);
            List<WmRepertoryInfoDto> allEquip = wmRepertoryInfoMapper.getAllEquip(warehouseId);
            outList.forEach(info->{
                Optional<WmRepertoryInfoDto> wmRepertoryInfoDto = allEquip.stream().filter(all -> all.getEquipId().equals(info.getId())).findFirst();
                if (wmRepertoryInfoDto.isPresent()){
                    info.setInventoryQuantity(wmRepertoryInfoDto.get().getNum());
                    info.setDamagesNum(wmRepertoryInfoDto.get().getDamagesNum());
                }else {
                    info.setInventoryQuantity(0);
                    info.setDamagesNum(0);
                }
            });
            return outList;
        }else{
            //出库操作
            List<WmRelationEquipDto> relationEquipDtos=wmOutWarehouseMapper.getOutboundApproval(warehouseId);
            List<OutboundEquipmentInfoVo> outboundEquipmentInfo = wmEquipInfoMapper.getOutboundEquipmentInfo(equipAttribute, warehouseId, equipTypeId, keywords);
            outboundEquipmentInfo.forEach(out-> relationEquipDtos.forEach(re->{
                    if (Integer.valueOf(re.getEquipId().toString()).equals(out.getId())){
                        Integer num=out.getInventoryQuantity()-re.getNum();
                        out.setInventoryQuantity(num<0?0:num);
                    }
                })
            );
            return outboundEquipmentInfo;
        }
    }

mapper层:

2. 批量删除

3. 新增

十、对Stream的使用

1. 对map集合的操作

• map集合value总和

long sum = datas.entrySet().stream().filter(entry -> !entry.getKey().equals("1") && !entry.getKey().equals("3")
                        && !entry.getKey().equals("6") && !entry.getKey().equals("7") && !entry.getKey().equals("8")
                        && !entry.getKey().equals("10"))
                        .mapToLong(entry -> Long.parseLong(entry.getValue())).sum();
  • 合并两个map,key相同的value进行加和、
map1.forEach((key, value) -> map2.merge(key, value, Integer::sum));
//map1合并到map2,map类型<String,Integer>

2. 对list集合的操作

  • 根据时间对list集合的对象进行升序排序
 List<ThirdCloudAlarmDto> dtoList = collect.get(key).stream()
                    .sorted(Comparator.comparing(ThirdCloudAlarmDto::getOccurTime)).collect(Collectors.toList());
    • stream流根据时间筛选所需数据
List<CameraAlarmDto> collect = cameraAlarms.stream().
                        filter(e -> (e.getTimeLast().after(startDate) &&
                                e.getTimeLast().before(endDate))).collect(Collectors.toList());
    • 对list集合根据某一个元素进行过滤,并选取过滤后的集合对象中的某些属性组合成新的集合
 groups.forEach(group -> {
            List<DceEquipGroupInfoVo> dceEquipGroupInfoVos = dceEquipInfoDtos.stream().
                    filter((DceEquipGroupInfoDto d) -> group.getNodeName().equals(d.getNodeName())).
                    map(item -> {
                        DceEquipGroupInfoVo equipGroupInfoVo = new DceEquipGroupInfoVo();
                        equipGroupInfoVo.setEquipKey(item.getEquipKey()).setEquipName(item.getEquipName());
                        return equipGroupInfoVo;
                    }).distinct().collect(Collectors.toList());
      }
    • 对List集合使用分页查询
thirdCloudHostDtos = thirdCloudHostDtos.stream().skip((pageNum - 1) * pageSize)
                    .limit(pageSize).collect(Collectors.toList());
    • 对list集合中对象中属性为字符串数字比较倒叙排序
 List<MetricVO> listTop5 = list.stream().sorted(Comparator.comparing(MetricVO::getValue,
                Comparator.comparingInt(Integer::parseInt)).reversed()).limit(5).collect(Collectors.toList());
  • 使用stream流进行模糊查询
keyWordsList = dtoList.stream().filter(e -> Boolean.FALSE ? e.getServerIp().equals(keyWords)
                     || e.getServerPort().equals(keyWords) || e.getClientIp().equals(keyWords)
                     || e.getClientPort().equals(keyWords) || e.getAppProgram().equals(keyWords)
                     || e.getProtocol().equals(keyWords) :
                     (e.getServerIp().contains(keyWords)) || e.getServerPort().contains(keyWords) || e.getClientIp().contains(keyWords)
                             || e.getClientPort().contains(keyWords) || e.getAppProgram().contains(keyWords)
                             || e.getProtocol().contains(keyWords)).collect(Collectors.toList());
  • 取多个list集合的交集
dealedList = keyWordsList.stream().filter(appList::contains).filter(protocolList::contains).collect(Collectors.toList());
  • 根据对象某一字段组成类型集合
 List<String> appTypes = dtoList.stream().filter(item ->item.getAppProgram() != null)
                        .map(HistoricalFlowDto::getAppProgram).distinct().collect(Collectors.toList());  
  • 求list属性中某一属性的和
Integer totalOrders = usedStatisticsVoList.stream().mapToInt(ServiceUsedStatisticsVo :: getWorkOrderNum).sum();  //list中存储的是对象
 Long totalOrders = mapList.stream().mapToLong((s) -> Long.valueOf(String.valueOf(s.get("num")))).sum();  //list中存储的是map集合
  • 根据list中对象的某一属性组合成新的集合
List instanceIds = instances.stream().map(ThirdCloudInstance::getInstanceId).collect(Collectors.toList());
  • 按照list对象中的某一属性进行分组
Map<String, List> collect = list.stream().collect(Collectors.groupingBy(MonitorBusyDegreeResult::getDataDesc));
  • list中存储对象,根据对象某一属性排序
List< MetricVO> metricVosBusy  = new ArrayList<>();
businessSystems.stream().sorted(Comparator.comparing(MonitorBusyDegreeResult::getBusyDegree, Comparator.reverseOrder())).collect(Collectors.toList())
        .forEach(e -> {
            metricVosBusy.add(new MetricVO().setKey(e.getDataDesc()).setValue(String.valueOf(e.getBusyDegree())));
        });
  • list中存储map,按map中的某一key对应的value进行排序,取排行
 //正序//  
 List<Map<String, Object>> listTop9 = list.stream().sorted(Comparator.comparing(e -> org.apache.commons.collections.MapUtils.getLong(e, "count"))).limit(9).collect(Collectors.toList());
//逆序
List<Map<String, Object>> listTop9 = list.stream().sorted((c1, c2) -> org.apache.commons.collections.MapUtils.getDouble(c2, "count")        .compareTo(org.apache.commons.collections.MapUtils.getDouble(c1, "count"))).limit(9).collect(Collectors.toList());

十一、RabbitMQ

1. 引入依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 配置连接信息

• 生产者配置

  rabbitmq:
    host: 192.168.64.100
    port: 5672
    username: guest
    password: guest
    virtual-host: /

• 消费者配置

  rabbitmq:
    host: 192.168.64.100
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    publisher-returns: true
    listener:
      simple:
        acknowledge-mode: manual

3. 设置消息转换器(可以省略)

目的:默认情况下 Spring 采用的序列化方式是 JDK 序列化,而 JDK 的序列化存在可读性性差、占用内存大、存在安全漏洞等问题。所以,这里我们一般便用 Jackson 的序列化代替JDK的序列化。

在生产者和清费者的启动类上加上如下代码:

4. 配置文件

@Configuration
public class RabbitConfig {
    /**
     * 延迟时间 单位毫秒
     */
    private static final long DELAY_TIME = 1000 * 30;


    //行程超时队列
    public static final String STROKE_OVER_QUEUE = "STROKE_OVER_QUEUE";
    //行程死信队列
    public static final String STROKE_DEAD_QUEUE = "STROKE_DEAD_QUEUE";

    //行程超时队列交换器
    public static final String STROKE_OVER_QUEUE_EXCHANGE = "STROKE_OVER_QUEUE_EXCHANGE";

    //行程死信队列交换器
    public static final String STROKE_DEAD_QUEUE_EXCHANGE = "STROKE_DEAD_QUEUE_EXCHANGE";
    //行程超时交换器 ROUTINGKEY
    public static final String STROKE_OVER_KEY = "STROKE_OVER_KEY";

    //行程死信交换器 ROUTINGKEY
    public static final String STROKE_DEAD_KEY = "STROKE_DEAD_KEY";



    /**
     * 声明行程超时队列
     *
     * @return
     */
    @Bean
    public Queue strokeOverQueue() {
        //【重要配置】超时队列配置,死信队列的绑定在该方法中实现
        //需要用到以下属性:
        Map<String,Object> args = new HashMap<>();

        // x-dead-letter-exchange    这里声明当前队列绑定的死信交换机
        args.put("x-dead-letter-exchange",STROKE_DEAD_QUEUE_EXCHANGE);

        // x-dead-letter-routing-key  这里声明当前队列的死信路由key
        args.put("x-dead-letter-routing-key",STROKE_DEAD_KEY);

        // x-message-ttl  声明队列的TTL
        args.put("x-message-ttl",DELAY_TIME);

        return new Queue(STROKE_OVER_QUEUE,true,false,false,args);
    }


    /**
     * 声明行程死信队列
     *
     * @return
     */
    @Bean
    public Queue strokeDeadQueue() {
        return new Queue(STROKE_DEAD_QUEUE,true);
    }

    /**
     * 创建行程超时队列交换器
     *
     * @return
     */
    @Bean
    DirectExchange strokeOverQueueExchange() {
        return new DirectExchange(STROKE_OVER_QUEUE_EXCHANGE);
    }

    /**
     * 创建行程死信队列交换器
     *
     * @return
     */
    @Bean
    DirectExchange strokeDeadQueueExchange() {
        return new DirectExchange(STROKE_DEAD_QUEUE_EXCHANGE);
    }



    /**
     * 行程超时队列绑定
     *
     * @return
     */
    @Bean
    Binding bindingStrokeOverDirect() {
        return BindingBuilder.bind(strokeOverQueue()).to(strokeOverQueueExchange()).with(STROKE_OVER_KEY);
    }

    /**
     * 行程死信队列绑定
     *
     * @return
     */
    @Bean
    Binding bindingStrokeDeadDirect() {
        return BindingBuilder.bind(strokeDeadQueue()).to(strokeDeadQueueExchange()).with(STROKE_DEAD_KEY);
    }
}

5. 生产者代码

@Component
public class MQProducer {
    private final static Logger logger = LoggerFactory.getLogger(MQProducer.class);
    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 发送延时订单MQ
     *
     * @param strokeVO
     */
    public void sendOver(StrokeVO strokeVO) {
        String mqMessage = JSON.toJSONString(strokeVO);
        logger.info("send timeout msg:{}",mqMessage);
        // TODO:任务4.2-发送邀请消息
        rabbitTemplate.convertAndSend(RabbitConfig.STROKE_OVER_QUEUE_EXCHANGE,RabbitConfig.STROKE_OVER_KEY,mqMessage);

    }
}

6. 消费者代码

@Component
public class MQConsumer{
    private final static Logger logger = LoggerFactory.getLogger(MQConsumer.class);

    @Autowired
    private StrokeHandler strokeHandler;


    /**
     * 行程超时监听
     *
     * @param massage
     * @param channel
     * @param tag
     */
    @RabbitListener(
            bindings =
                    {
                            @QueueBinding(value = @Queue(value = RabbitConfig.STROKE_DEAD_QUEUE, durable = "true"),
                                    exchange = @Exchange(value = RabbitConfig.STROKE_DEAD_QUEUE_EXCHANGE), key = RabbitConfig.STROKE_DEAD_KEY)
                    })
    @RabbitHandler
    public void processStroke(Message massage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
        // TODO:任务4.3-接收死信队列消息(以下代码已完成,用于验证配置和发送无误)
        StrokeVO strokeVO = JSON.parseObject(massage.getBody(), StrokeVO.class);
        logger.info("get dead msg:{}",massage.getBody());
        if (null == strokeVO) {
            return;
        }
        try {
            strokeHandler.timeoutHandel(strokeVO);
            //手动确认机制
            channel.basicAck(tag, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值