Day_1

news2024/9/23 17:21:51

1. 环境搭建

技术选型

后端项目结构

sky-take-out

maven父工程,统一管理依赖版本,聚合其他子模块

sky-common

子模块,存放公共类,例如:工具类、常量类、异常类等

sky-pojo

子模块,存放实体类、VODTO

sky-server

子模块,配置文件、ControllerServiceMapper

sky-common

存放的是一些公共类,可以供其他模块使用

sky-pojo

存放的是一些 entityDTOVO

sky-server

存放的是 配置文件、配置类、拦截器、controllerservicemapper、启动类等

数据库

参考数据库设计文档

前后端联调

前端发送的请求,是如何请求到后端服务的?

前端请求地址:http://localhost/api/employee/login

后端接口地址:http://localhost:8080/admin/employee/login

nginx 反向代理

就是将前端发送的动态请求由 nginx 转发到后端服务器

nginx 反向代理的好处:

  1. 提高访问速度,nginx可以进行缓存
  2. 进行负载均衡,针对分布式系统
  3. 保证后端服务安全,不会对外公开自己的服务调用接口

配置方式

在文件 nginx.conf

反向代理的配置方式:

server{
	listen 80;
	server_name localhost;
	
	location /api/ {
            		proxy_pass   http://localhost:8080/admin/;  #反向代理
	}

}

nginx 负载均衡的配置方式:

upstream webservers{
	server 192.168.100.128:8080;
	server 192.168.100.129:8080;
}

server{
	listen 80;
	server_name localhost;
	
	location /api/ {
            		proxy_pass   http://webservers/admin/;  #负载均衡 默认为轮询
	}

}

 2. 登录功能

员工表中的密码是明文存储,安全性太低,采用 MD5 加密格式

拦截器配置

需求:在调用用户登录接口的时候不需要进行 jwtToken 认证,其他接口都需要进行认证

拦截器对动态方法进行拦截

登录Controller

对于新登录的用户,生成一个 jwt 令牌

@PostMapping("/login")
@ApiOperation(value = "员工登录")
public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
    log.info("员工登录:{}", employeeLoginDTO);

    Employee employee = employeeService.login(employeeLoginDTO);

    //登录成功后,生成jwt令牌
    Map<String, Object> claims = new HashMap<>();
    claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
    String token = JwtUtil.createJWT(
    jwtProperties.getAdminSecretKey(),
        jwtProperties.getAdminTtl(),
        claims);

    EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
        .id(employee.getId())
        .userName(employee.getUsername())
        .name(employee.getName())
        .token(token)
        .build();

    return Result.success(employeeLoginVO);
}

登录service

因为数据库里存的是进行 md5 加密后的信息,在进行密码对比的时候,需要将前端传入的明文密码转为 md5 后再进行对比

 3. Swagger

Knife4j 是为Java MVC框架集成Swagger生成Api文档的增强解决方案

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

使用方式

1. 导入 knife4j maven坐标

2. 在配置类中加入 knife4j 相关配置

WebMvcConfiguration.java

@Bean
public Docket docket() {
    log.info("准备生产接口文档");
    ApiInfo apiInfo = new ApiInfoBuilder()
            .title("苍穹外卖项目接口文档")
            .version("2.0")
            .description("苍穹外卖项目接口文档")
            .build();
    Docket docket = new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
            .paths(PathSelectors.any())
            .build();
    return docket;
}

3. 设置静态资源映射,否则接口文档页面无法访问

protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    log.info("开始设置静态资源映射");
    registry.addResourceHandler("/doc.html").
        addResourceLocations("classpath:/META-INF/resources/");
    registry.addResourceHandler("/webjars/**").
        addResourceLocations("classpath:/META-INF/resources/webjars/");
}

4. 访问

接口文档访问路径为 http://ip:port/doc.html

常用注解

注解

说明

@Api

用在类上,例如Controller,表示对类的说明

@ApiModel

用在类上,例如entityDTOVO

@ApiModelProperty

用在属性上,描述属性信息

@ApiOperation

用在方法上,例如Controller的方法,说明方法的用途、作用


4. 员工管理 

开发都是采用三层结构(MVC模式),具体查看源码

代码开发:

1. 设计接受前端传入的 DTO 

2. 在 Controller 定义执行方法

3. 在 Service,ServiceImpl 中进行实现方法逻辑

4. 在 Mapper 层进行对数据库的调用查询

5. Controller 返回前端需要的数据类型

新增员工

正常采用三层结构实现

@PostMapping
@ApiOperation("员工新增")
public Result save(@RequestBody EmployeeDTO employeeDTO) {
    log.info("新增员工:{}", employeeDTO);
    // 新增员工业务方法
    employeeService.save(employeeDTO);
    return Result.success();
}

程序存在问题

1. 在出现同样的 username 的时候,系统会报错;

该异常应被全局异常处理器处理

@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex) {
    //错误内容: Duplicate entry 'zhangsan' for key 'idx_username'
    final String message = ex.getMessage();
    if(message.contains("Duplicate entry")) {
        String[] split = message.split(" ");
        String username = split[2];
        String msg = username + MessageConstant.ALREADY_EXISTS;

        return Result.error(msg);
    }else {
        return Result.error(MessageConstant.UNKNOWN_ERROR);
    }
}

2. 当前登录用户的 id 如何存储

使用 ThreadLocal,是一个线程的局部变量,为每个线程单独提供一份存储空间,具有线程隔离效果,只有在线程内才能获取到对应的值,线程外则不能访问

在 sky-common 中已经封装为 BaseContext 类

员工分页查询

分页查询使用使用 mybatis 的分页插件 PageHelper 来简化分页代码的开发。

底层基于 mybatis 的拦截器实现,在 sql 语句后面进行拼接 limit

代码完善

日期时间在前端的显示结果不是我们想要的

解决方法:

1. 在属性上加入注解,对日期进行格式化

可以实现日期的序列化,但是只能实现这一个属性的序列化。 不推荐

2.  WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化

protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    //自己创建一个消息转换器
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    // 为消息转换器 设置一个对象转换器,可以将java对象序列化为json数据
    converter.setObjectMapper(new JacksonObjectMapper());

    //将自己的消息转换器加入到容器里, 默认是放在最后一个
    // 0 -> 就是把这个消息转换器放在前面
    converters.add(0, converter);
}

启用禁用员工账号

需要一个 update 数据库的方法

在 EmployeeMapper.xml 编写 SQL

<update id="update" parameterType="Employee">
    update employee
    <set>
        <if test="name != null and name != ''" >name = #{name},</if>
        <if test="username != null and username != ''" >username = #{username},</if>
        <if test="sex != null and sex != ''" >sex = #{sex},</if>
        <if test="password != null and password != ''" >password = #{password},</if>
        <if test="phone != null and phone != ''" >phone = #{phone},</if>
        <if test="idNumber != null and idNumber != ''" >id_number = #{idNumber},</if>
        <if test="status != null">status = #{status},</if>
        <if test="updateTime != null">update_time = #{updateTime},</if>
        <if test="updateUser != null">update_user = #{updateUser},</if>
    </set>
    where id = #{id}
</update>

编辑员工信息

需要一个查询员工信息的接口,和上一个的修改员工信息的接口


5. 分类模块功能

思路与员工管理的一致,做着基本的crud

注:

在删除分类的时候,要求其下面没有挂载任何内容才可以


6. 公共字段自动填充

针对业务表里的公共字段进行维护

序号

字段名

含义

数据类型

1

create_time

创建时间

datetime

2

create_user

创建人id

bigint

3

update_time

修改时间

datetime

4

update_user

修改人id

bigint

实现思路

技术:注解、AOP、反射

思路:

1.自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法

2.自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值

3.在 Mapper 的方法上加入 AutoFill 注解

开发

自定义注解 AutoFill

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //数据库操作类型: update insert
    OperationType value();

}

自定义切面类 AutoFillAspect

@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    /**
     * 指定切入点
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) &&             
    @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中进行公共字段的赋值
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("开始进行公共字段的填充");
        // 在这里实现逻辑
    }

}

实现逻辑:

1.  获取到当前被拦截到的方法的数据库操作类型

//获取到方法签名对象
MethodSignature signature = (MethodSignature)joinPoint.getSignature(); 
//获取方法上的注解对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); 
// 获取到数据库的操作类型
OperationType operationType = autoFill.value();  

2. 获取到被拦截的方法的参数(实体对象)  这里约定: 参数里面的实体对象为第一个参数

Object[] args = joinPoint.getArgs();
if(args == null || args.length == 0) {
    return;
}
Object entity = args[0];

3. 获取赋值的数据

LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();

4. 通过反射来赋值

if(operationType == OperationType.INSERT) {
    //给4个公共字段赋值
    try {
        // 获取创建时间方法
        Method setCreateTime = 
             entity.getClass()
            .getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME,LocalDateTime.class);
        // 获取创建人方法
        Method setCreateUser =     
             entity.getClass()
             .getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
        // 获取修改时间方法
        Method setUpdateTime = 
             entity.getClass()
            .getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
        // 获取修改人方法
        Method setUpdateUser = 
             entity.getClass()
             .getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

        // 赋值操作
        // 创建时间赋值
        setCreateTime.invoke(entity, now);
        // 创建人赋值
        setCreateUser.invoke(entity, currentId);
        // 修改时间赋值
        setUpdateTime.invoke(entity, now);
        // 修改人赋值
        setUpdateUser.invoke(entity, currentId);
    } catch (Exception e) {
        e.printStackTrace();
    }
}else if(operationType == OperationType.UPDATE) {
    //给2个公共字段赋值
    try {
        // 获取修改时间方法
        Method setUpdateTime =     
            entity.getClass()
            .getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
        // 获取修改人方法
        Method setUpdateUser = 
            entity.getClass()
            .getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

        // 赋值操作
        // 修改时间赋值
        setUpdateTime.invoke(entity, now);
        // 修改人赋值
        setUpdateUser.invoke(entity, currentId);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}else {
    // 既不是新增又不是修改
    throw new RuntimeException(MessageConstant.UNKNOWN_ERROR);
}

5. Mapper接口的方法上加入 AutoFill 注解

@AutoFill(OperationType.INSERT

@AutoFill(OperationType.UPDATE)

6. 去掉业务层这个重复的代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1645249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【爬虫】爬取A股数据写入数据库(一)

1. 对东方财富官网的分析 步骤&#xff1a; 通过刷新网页&#xff0c;点击等操作&#xff0c;我们发现https://datacenter-web.eastmoney.com/api/data/v1/get?请求后面带着一些参数即可以获取到相应数据。我们使用python来模拟这个请求即可。 我们以如下选择的页面为切入点…

GiantPandaCV | FasterTransformer Decoding 源码分析(三)-LayerNorm介绍

本文来源公众号“GiantPandaCV”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;FasterTransformer Decoding 源码分析(三)-LayerNorm介绍 作者丨进击的Killua 来源丨https://zhuanlan.zhihu.com/p/669440844 编辑丨GiantPandaC…

ollama + Anythingllm的安装

Ollama官网&#xff1a;https://ollama.com Anythingllm 官网下载&#xff1a;https://useanything.com/download 在Linux下如果直接运行./AnythingLLMDesktop.AppImage 报错的话&#xff0c;可以尝试以下命令&#xff1a; ./AnythingLLMDesktop.AppImage --appimage-extract …

微信公众号排名 SEO的5个策略

随着微信公众号在社交媒体领域的持续发展和普及&#xff0c;如何提升公众号的搜索排名&#xff0c;成为许多运营者关注的焦点。公众号排名SEO&#xff0c;即针对微信公众号进行搜索引擎优化&#xff0c;旨在提高公众号在搜索结果中的曝光率和点击率。下面&#xff0c;我们将深入…

什么是期货?期货的基础知识有哪些?

期货是一种标准化的远期合约&#xff0c;允许买卖双方在未来特定时间以预定价格交易货物或金融资产。也是一种金融衍生品&#xff0c;它为市场参与者提供了一种管理价格波动风险和进行投资的工具。 期货的基础知识有哪些 期货市场是一个复杂的金融环境&#xff0c;对于初学者来…

系统镜像地址

系统镜像 Linux 官网下载地址&#xff1a;Downloadhttps://www.centos.org/download/ 阿里云镜像下载地址&#xff1a;https://mirrors.aliyun.com/centos/https://mirrors.aliyun.com/centos/?spma2c6h.13651104.d-2001.6.6554320cwFqB8E 清华大学镜像下载地址&#xff1…

你对AI的所有疑虑,厚德云替你解答!

遇到难题不要怕&#xff01;厚德提问大佬答&#xff01; 厚德提问大佬答 你是否对AI绘画感兴趣却无从下手&#xff1f;是否有很多疑问却苦于没有大佬解答带你飞&#xff1f;从此刻开始这些问题都将迎刃而解&#xff01;你感兴趣的话题&#xff0c;厚德云替你问&#xff0c;你解…

原型图制作神器!6款软件推荐,助你轻松实现设计构想!

在现代设计领域&#xff0c;原型图的制作是一个至关重要的环节。它们帮助设计师将创意转化为可视化界面&#xff0c;评估用户体验并进行交互测试。本文将介绍六款备受推崇的原型图软件&#xff0c;它们以强大的功能、易用的界面和灵活的工作流程脱颖而出&#xff0c;为设计师创…

每日算法-java

题目来自蓝桥云 // 这是一个Java程序&#xff0c;用于解决最长不下降子序列问题。 // 问题描述&#xff1a;给定一个整数序列&#xff0c;找到最长的子序列&#xff0c;使得这个子序列是不下降的&#xff08;即相邻的元素不严格递减&#xff09;。 // 程序使用了动态规划的方法…

Redis 渐进式遍历 -- scan

前言 keys 可以一次性把 Redis 中的所有 key 都获取到&#xff0c;但这个操作比较危险&#xff0c;一次性获取所有的key 很容易会导致 Redis 阻塞。 而通过渐进式遍历&#xff08;不是一个命令就将所有的 key 值拿到&#xff0c;而是每执行一次命令只获取其中的一小部分&#x…

前后端功能实现——添加品牌

需求 点击新增&#xff0c;跳转到添加品牌的页面&#xff0c;从后一个页面提交品牌数据&#xff1a; 1、BrandMapper接口添加add()方法 /** * 添加品牌 */ void add(Brand brand); 2、BrandMapper.xml中添加sql方法 <insert id"add">insert into brand val…

如何提升通信芯片一次性投片成功率

通信芯片设计是一个非常复杂的系统工程&#xff0c;整体流程设计包括产品定义&#xff0c;算法开发&#xff0c;架构设计&#xff0c;电路设计和验证&#xff0c;后端版图设计&#xff0c;晶圆生产到封装测试等多个环节。在每个环节中&#xff0c;都需要严格遵循设计规则和流程…

2024年 Java 面试八股文——SpringCloud篇

目录 1.Spring Cloud Alibaba 中的 Nacos 是如何进行服务注册和发现的&#xff1f; 2.Spring Cloud Alibaba Sentinel 的流量控制规则有哪些&#xff1f; 3.Spring Cloud Alibaba 中如何实现分布式配置管理&#xff1f; 4.Spring Cloud Alibaba RocketMQ 的主要特点有哪些&…

自编码器网络

1.自编码器网络 自动编码器是一种无监督的数据维度压缩和数据特征表达方法。 无监督 在海量数据的场景下&#xff0c;使用无监督的学习方法比有监督的学习方法更省力。 维度上的压缩 自编码网络可以根据输入的数据&#xff0c;对其进行表征学习。输入数据转换到隐藏层co…

java中如何判断一个数是不是素数(质数)

相关概念 质数就是大于1的自然数字中&#xff0c;只能被1和它自己整除的数。 题目 求101~200之间的质素的个数 代码实现 判断一个数是不是质数 for (int j 2; j < i; j) {if(i % j 0){flag false;break;}}if(flag){System.out.println("当前数字是质数");…

文件删了,回收站清空了怎么恢复?文件恢复软件一览

在日常生活和工作中&#xff0c;我们常常会遇到误删除文件的情况&#xff0c;有时甚至会因为清空了回收站而无法找回这些文件。这些文件可能包含重要的工作数据、个人照片或其他珍贵的回忆。那么&#xff0c;在这种情况下&#xff0c;我们该如何恢复这些被删除且清空回收站的文…

ubuntu配置多版本cuda+cudnn环境,及版本切换方法

ubuntu配置多版本cudacudnn环境&#xff0c;及版本切换方法 环境如下&#xff1a; ubuntu 22.04cuda v11.8cudnn v8.9.7 文章目录 ubuntu配置多版本cudacudnn环境&#xff0c;及版本切换方法1.安装Nvidia显卡驱动1.1卸载默认的驱动nouveau1.2安装nvidia驱动 2.安装cuda3.安装…

《从Paxos到Zookeeper》——第五、六章:经典应用场景

目录 第五章 使用Zookeeper 5.1 服务端部署与运行 5.2 客户端相关 5.2.1 客户端运行 5.2.2 客户端命令 5.3 Java客户端API 5.4 开源客户端 第六章 经典应用场景 6.1 典型应用场景及实现 6.1.1 数据发布/订阅&#xff08;全局配置中心&#xff09; 6.1.2 负载均衡&#xff08;Lo…

谷歌推广和seo留痕具体怎么操作?

留痕跟谷歌推广其实是一回事&#xff0c;你能在谷歌上留痕&#xff0c;其实就是推广了自己的信息&#xff0c;本质上留痕就是在各大网站留下自己的记录&#xff0c;这个记录可以是品牌信息&#xff0c;联系方式&#xff0c;看你想留下什么 如果要问自己怎么操作&#xff0c;正常…

Python 网络编程实践:从基础到进阶

目录 网络编程 一.IP地址简介 1. IP 地址的概念 1.1. IP 地址的表现形式 1.2. IP 地址的作用 2. 查看 IP 地址 3. 检查网络是否正常 4. 小技巧 二.端口和端口号 1. 什么是端口 2. 什么是端口号 3. 端口和端口号的关系 4. 端口号的分类 4.1. 知名端口号 4.2. 动…