对于数据库的理解:
员工数据库:结构为
员工登陆的实现,登陆后生成一个token,用户携带这个token就能访问登陆之后的界面
定义了一个Result用作返回的数据类型
由于和前端页面联调后端的Employee类中字段名过多,而前端需要回显的字段名少,所以又创建了EmployeeDTO类用于封装数据传输对象(将数据库中的数据转换为前端需要的格式,方便前后端之间的数据交互)以及EmployeeVO类用于封装值对象(方便前端页面的显示和交互)。
Controller层:
@PostMapping("/login") @ApiOperation(value = "员工登录") public Resultlogin(@RequestBody EmployeeLoginDTO employeeLoginDTO) { //传进来一个json对象 //返回数据是什么类型,泛型就是什么类型 log.info("员工登录:{}", employeeLoginDTO); //日志级别:等级由低到高:debug 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层:
public Employee login(EmployeeLoginDTO employeeLoginDTO) { String username = employeeLoginDTO.getUsername(); String password = employeeLoginDTO.getPassword(); //1、根据用户名查询数据库中的数据 Employee employee = employeeMapper.getByUsername(username); //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定) if (employee == null) { //账号不存在 throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND); } //密码比对 //对前端传过来的明文密码进行md5加密处理 password = DigestUtils.md5DigestAsHex(password.getBytes()); if (!password.equals(employee.getPassword())) { //密码错误 throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR); } if (employee.getStatus() == StatusConstant.DISABLE) { //账号被锁定 throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED); } //3、返回实体对象 return employee; }
public static String createJWT(String secretKey, long ttlMillis, Mapclaims) { // 指定签名的时候使用的签名算法,也就是header那部分 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 生成JWT的时间 long expMillis = System.currentTimeMillis() + ttlMillis; Date exp = new Date(expMillis); // 设置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 .setClaims(claims) // 设置签名使用的签名算法和签名使用的秘钥 .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8)) // 设置过期时间 .setExpiration(exp); return builder.compact(); } /** * Token解密 * * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个 * @param token 加密后的token * @return */ public static Claims parseJWT(String secretKey, String token) { // 得到DefaultJwtParser Claims claims = Jwts.parser() // 设置签名的秘钥 .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) // 设置需要解析的jwt .parseClaimsJws(token).getBody(); return claims; }
进行md5加密为了密码不在前端页面明文显示以及设置数字签名。
生成jwt令牌是为了避免多次查询数据库,通过设置拦截器可以更方便的让用户访问管理端允许访问的资源。(token包含了所需要的信息)即用户在登录后得到一个token,之后用户每次访问其他资源时都要带上这个token验证身份,所以需要检查token是否存在。
controller:
@PostMapping @ApiOperation("新增员工") public Result save(@RequestBody EmployeeDTO employeeDTO){ log.info("新增员工:{}",employeeDTO); employeeService.save(employeeDTO); return Result.success(); }
分页查询运用了分页查询PageHelper
新增员工或修改员工数据的时候,会设置修改时间和修改人的数据,故设置一个公共字段自动填充的功能方便自动填充
在这之前科普一下AOP
Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。 Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。 Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。 Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。 Target(目标对象):织入 Advice 的目标对象.。 Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
首先要创建一个AutoFill的annotation注解,并且添加相应的Target注解和Retention注解来说明这个自定义的注解用在什么地方以及在什么时候生效。
//自定义注解,用于标识某个方法需要进行功能字段自动填充处理 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { //数据库操作类型:UPDATE INSERT OperationType value(); }
然后再创建一个自定义Aspect,
//自定义切面,实现公共字段自动填充处理逻辑 @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("开始进行公共字段自动填充..."); } }
切入点:对哪些类的哪些方法进行拦截。@Pointcut里面写的是对哪些方法进行拦截,要满足2点:①必须是mapper下的所有类的方法,②还要有AutoFill这个注解。
HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口(基于Http协议的),提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入。
由于菜品和套餐存储在数据库中,而mysql数据库在短期内有大量的人查询会导致数据库压力过大,对于数据的回显响应太慢,是用户体验不佳,所以使用redis技术将数据存入到redis中,因为redis的数据是存放在内存中的,内存操作的性能比磁盘IO性能更高。而且redis的性能极高。
运用了springCache,springboot的启动类上加上EnableCaching注解来开启springboot的启动,用Cacheable来根据请求的参数对其进行缓存,指定缓存的类型和对象。
@Cacheable
注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。
在启动类加上注解@EnableScheduling开启定时任务功能
运用@Scheduled自定义定时任务(cron表达式有在线生成网站)
实现实时接收用户信息的功能--点单退单等操作。
WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:
WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。