网站建设价格山东济南兴田德润什么活动,wordpress主题无法安装目录,网络推广联系方式,家装报价单明细表电子版哈喽#xff01;大家好#xff0c;我是旷世奇才李先生 文章持续更新#xff0c;可以微信搜索【小奇JAVA面试】第一时间阅读#xff0c;回复【资料】更有我为大家准备的福利哟#xff0c;回复【项目】获取我为大家准备的项目 最近打算把我手里之前做的项目分享给大家#… 哈喽大家好我是旷世奇才李先生 文章持续更新可以微信搜索【小奇JAVA面试】第一时间阅读回复【资料】更有我为大家准备的福利哟回复【项目】获取我为大家准备的项目 最近打算把我手里之前做的项目分享给大家这个苍穹外卖系统是跟着B站上的一个视频做的 大家可以根据视频自己学习一下或者直接拿我做好的项目直接去用也可以。 文章目录 一、后台管理系统前端页面1、登录密码加密2、Swagger引入3、Swagger常用注解4、对象属性拷贝5、SQL异常处理新增相同用户名员工异常捕获6、ThreadLocal用来存储用户的信息用于新增用户时将添加人属性赋值7、分页查询员工8、日期格式化处理9、公共字段代码填充通过AOP和反射将公共字段进行填充10、使用阿里云来存储图片11、使用redis来做店铺状态功能12、使用Sping-Cache来缓存套餐数据12、使用Sping-Task来执行定时任务13、使用WebSocket实现下单提醒与催单提醒 二、微信小程序用户端页面展示三、商家后端页面展示四、总结 一、后台管理系统前端页面
1、登录密码加密
使用spring自带的加密工具类进行md5加密
password DigestUtils.md5DigestAsHex(password.getBytes());2、Swagger引入
首先引入依赖
dependencygroupIdcom.github.xiaoymin/groupIdartifactIdknife4j-spring-boot-starter/artifactIdversion3.0.2/version
/dependency然后配置基础信息以及要扫描的controller包位置
package com.sky.config;import com.sky.interceptor.JwtTokenAdminInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;/*** 配置类注册web层相关组件*/
Configuration
Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {Autowiredprivate JwtTokenAdminInterceptor jwtTokenAdminInterceptor;/*** 注册自定义拦截器** param registry*/protected void addInterceptors(InterceptorRegistry registry) {log.info(开始注册自定义拦截器...);registry.addInterceptor(jwtTokenAdminInterceptor).addPathPatterns(/admin/**).excludePathPatterns(/admin/employee/login);}/*** 通过knife4j生成接口文档* return*/Beanpublic 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;}/*** 设置静态资源映射* param registry*/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/);}
}
然后访问页面
http://localhost:8080/doc.html 3、Swagger常用注解 4、对象属性拷贝
//对象属性拷贝DTO的属性名和实体类的属性名相同BeanUtils.copyProperties(employeeDTO,employee);5、SQL异常处理新增相同用户名员工异常捕获 /*** Description: 处理SQL异常* Author: KSQC*/ExceptionHandlerpublic Result exceptionHandler(SQLIntegrityConstraintViolationException ex){//Duplicate entry zhangsan for key idx_usernameString message ex.getMessage();if(message.contains(Duplicate entry)){String[] split message.split( );String username split[2];String msg username 已存在;return Result.error(msg);}else {return Result.error(未知错误);}}6、ThreadLocal用来存储用户的信息用于新增用户时将添加人属性赋值
首先创建一个工具类
public class BaseContext {public static ThreadLocalLong threadLocal new ThreadLocal();public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}}
登录的时候将用户信息放入ThreadLocal中
//将用户id放入线程中去BaseContext.setCurrentId(empId);新增用户的时候将添加人信息赋值 //设置当前记录创建人id和修改人idemployee.setCreateUser(BaseContext.getCurrentId());employee.setUpdateUser(BaseContext.getCurrentId());7、分页查询员工
首先引入依赖pagehelper这是mybatis提供的分页插件 dependencygroupIdcom.github.pagehelper/groupIdartifactIdpagehelper-spring-boot-starter/artifactIdversion${pagehelper}/version/dependency然后进行分页查询代码开发 /*** Description: 分页查询* Author: KSQC*/public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {//开始分页查询,传入参数为第几页和每页展示多少行。PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());//普通正常查询因为PageHelper会将分页信息存入当前线程会在正常查询时进行拼接。PageEmployee page employeeMapper.pageQuery(employeePageQueryDTO);//将PageResult需要的属性获取出来进行返回。long total page.getTotal();ListEmployee records page.getResult();return new PageResult(total,records);}8、日期格式化处理
第一种方法直接在属性上加注解 //JsonFormat(pattern yyyy-MM-dd HH:mm:ss)private LocalDateTime createTime;第二种方法配置全局消息转换器 /*** Description: 扩展SpringMVC框架的消息转化器* Author: KSQC*/protected void extendMessageConverters(ListHttpMessageConverter? converters) {log.info(扩展消息转换器...);//创建一个消息转换器对象MappingJackson2HttpMessageConverter converter new MappingJackson2HttpMessageConverter();//需要为消息转换器设置一个对象转换器对象转换器可以将Java对象序列化json数据converter.setObjectMapper(new JacksonObjectMapper());//将自己的消息转化器加入容器中converters.add(0,converter);}转换器实体类如下
package com.sky.json;import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;/*** 对象映射器:基于jackson将Java对象转为json或者将json转为Java对象* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]*/
public class JacksonObjectMapper extends ObjectMapper {public static final String DEFAULT_DATE_FORMAT yyyy-MM-dd;//public static final String DEFAULT_DATE_TIME_FORMAT yyyy-MM-dd HH:mm:ss;public static final String DEFAULT_DATE_TIME_FORMAT yyyy-MM-dd HH:mm;public static final String DEFAULT_TIME_FORMAT HH:mm:ss;public JacksonObjectMapper() {super();//收到未知属性时不报异常this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);//反序列化时属性不存在的兼容处理this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);SimpleModule simpleModule new SimpleModule().addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));//注册功能模块 例如可以添加自定义序列化器和反序列化器this.registerModule(simpleModule);}
}
9、公共字段代码填充通过AOP和反射将公共字段进行填充
package com.sky.aspect;/*** Author: KSQC* Description: ${description}* Date: 2023/8/8 21:08*/import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.time.LocalDateTime;/*** Description 自定义切面实现公共字段自动填充处理逻辑* Author LiShiQi* Date 2023/8/8 21:08* Version 1.0*/
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(开始进行公共字段自动填充...);//获取到当前被拦截的方法上的数据库操作类型MethodSignature signature (MethodSignature) joinPoint.getSignature();//方法签名对象AutoFill autoFill signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象OperationType operationType autoFill.value();//获取到当前被拦截的方法的参数--实体对象Object[] args joinPoint.getArgs();if(args null || args.length 0){return;}Object entity args[0];//准备赋值的数据LocalDateTime now LocalDateTime.now();Long currentId BaseContext.getCurrentId();//根据当前不同的操作类型为对应的属性通过反射来赋值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) {e.printStackTrace();}}}}
10、使用阿里云来存储图片
配置阿里云地址等相关参数 alioss:endpoint: oss-cn-beijing.aliyuncs.comaccess-key-id: LTAI5tPeFLzsPPT8gG3LPW64access-key-secret: U6k1brOZ8gaOIXv3nXbulGTUzy6Pd7bucket-name: sky-itcast新建一个阿里云配置类
package com.sky.config;import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** Author: KSQC* Description: 配置类用于创建AliOssUtil对象* Date: 2023/8/9 9:40*/
Configuration
Slf4j
public class OssConfiguration {BeanConditionalOnMissingBeanpublic AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){log.info(开始创建阿里云文件上传工具类对象{},aliOssProperties);return new AliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());}
}
调用工具类上传图片到阿里云返回图片地址用于前端展示
//文件的请求路径String filePath aliOssUtil.upload(file.getBytes(),objectName);11、使用redis来做店铺状态功能
在spring下配置redis # redis 配置redis:# 地址host: localhost# 端口默认为6379port: 6379# 数据库索引database: 0# 密码password: 123456# 连接超时时间timeout: 10slettuce:pool:# 连接池中的最小空闲连接min-idle: 0# 连接池中的最大空闲连接max-idle: 8# 连接池的最大数据库连接数max-active: 8# #连接池最大阻塞等待时间使用负值表示没有限制max-wait: -1ms创建一个RedisConfiguration类
package com.sky.config;/*** Author: KSQC* Description: ${description}* Date: 2023/8/9 15:21*/import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Description* Author LiShiQi* Date 2023/8/9 15:21* Version 1.0*/
Configuration
Slf4j
public class RedisConfiguration {Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){log.info(开始创建redis模板对象...);RedisTemplate redisTemplate new RedisTemplate();//设置redis的连接工厂对象redisTemplate.setConnectionFactory(redisConnectionFactory);//设置redis key的序列化器redisTemplate.setKeySerializer(new StringRedisSerializer());return redisTemplate;}
}
设置店铺状态 /*** Description: 设置店铺的营业状态* Author: KSQC*/PutMapping(/{status})ApiOperation(设置店铺的营业状态)public Result setStatus(PathVariable Integer status){log.info(设置店铺的营业状态为{},status 1 ? 营业中 : 打烊中);redisTemplate.opsForValue().set(KEY,status);return Result.success();}12、使用Sping-Cache来缓存套餐数据
首先引入依赖除了引入spring-cache依赖还需要引入redis依赖因为spring-cache底层是需要使用到一个缓存中间件的可支持多种中间件目前我们使用redis作为缓存中间件。 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-cache/artifactId/dependency常用的注解当我们引入完依赖后就可以使用注解的方式来进行缓存操作。 首先启动类开启注解功能 然后找到我们需要缓存的查询方法加上注解这里会将“setmealCache”作为前缀然后加上我们的分类id一起作为key然后将查询出来的数据作为value放入redis中 当我们第二次查询的时候这个时候redis中有数据直接就会返回我们redis中的数据就不再去查询数据库大大减轻了我们数据库的压力。 新增套餐的时候清除缓存因为新增加了套餐页面上要展示出来但是缓存中是旧数据所以我们要清理缓存然后再重新查询数据库重新缓存。这里我们根据分类id进行清理在哪个分类下新增套餐就将哪个分类缓存清理。 批量删除套餐的时候我们进行批量清理将所有前缀为“setmealCache”的全部清理。 12、使用Sping-Task来执行定时任务
这个task的依赖在context包里包含了所以我们不需要额外引入依赖了。
我们需要先在启动类上添加注解EnableScheduling 然后在需要定时执行的方法上添加注解并写上执行时间这里使用cron语法不会的可以百度查询一下。 13、使用WebSocket实现下单提醒与催单提醒
首先引入依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-websocket/artifactId
/dependency新建一个WebSocketConfiguration配置类
package com.sky.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket配置类用于注册WebSocket的Bean*/
Configuration
public class WebSocketConfiguration {Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}
然后建立一个WebSocketServer功能类
package com.sky.websocket;import org.springframework.stereotype.Component;import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;/*** WebSocket服务*/
Component
ServerEndpoint(/ws/{sid})
public class WebSocketServer {//存放会话对象private static MapString, Session sessionMap new HashMap();/*** 连接建立成功调用的方法*/OnOpenpublic void onOpen(Session session, PathParam(sid) String sid) {System.out.println(客户端 sid 建立连接);sessionMap.put(sid, session);}/*** 收到客户端消息后调用的方法** param message 客户端发送过来的消息*/OnMessagepublic void onMessage(String message, PathParam(sid) String sid) {System.out.println(收到来自客户端 sid 的信息: message);}/*** 连接关闭调用的方法** param sid*/OnClosepublic void onClose(PathParam(sid) String sid) {System.out.println(连接断开: sid);sessionMap.remove(sid);}/*** 群发** param message*/public void sendToAllClient(String message) {CollectionSession sessions sessionMap.values();for (Session session : sessions) {try {//服务器向客户端发送消息session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}
然后我们在具体业务代码中去调用方法向前端发送消息例如下单成功后发送消息。 //通过websocket向客户端浏览器推送消息 type orderId contentMap map new HashMap();map.put(type,1); // 1表示来单提醒 2表示客户催单map.put(orderId,ordersDB.getId());map.put(content,订单号 outTradeNo);String json JSON.toJSONString(map);webSocketServer.sendToAllClient(json);下面是测试用例前端代码前端需要启动后就与后端建立WebSocket连接这样才能进行后续的通信。
!DOCTYPE HTML
html
headmeta charsetUTF-8titleWebSocket Demo/title
/head
bodyinput idtext typetext /button onclicksend()发送消息/buttonbutton onclickcloseWebSocket()关闭连接/buttondiv idmessage/div
/body
script typetext/javascriptvar websocket null;var clientId Math.random().toString(36).substr(2);//判断当前浏览器是否支持WebSocketif(WebSocket in window){//连接WebSocket节点websocket new WebSocket(ws://localhost:8080/ws/clientId);}else{alert(Not support websocket)}//连接发生错误的回调方法websocket.onerror function(){setMessageInnerHTML(error);};//连接成功建立的回调方法websocket.onopen function(){setMessageInnerHTML(连接成功);}//接收到消息的回调方法websocket.onmessage function(event){setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose function(){setMessageInnerHTML(close);}//监听窗口关闭事件当窗口关闭时主动去关闭websocket连接防止连接还没断开就关闭窗口server端会抛异常。window.onbeforeunload function(){websocket.close();}//将消息显示在网页上function setMessageInnerHTML(innerHTML){document.getElementById(message).innerHTML innerHTML br/;}//发送消息function send(){var message document.getElementById(text).value;websocket.send(message);}//关闭连接function closeWebSocket() {websocket.close();}
/script
/html
二、微信小程序用户端页面展示
待授权页面
确认授权页面 首页 选择口味页 购物车页面 提交订单页面 点击头像页面 历史订单页面 地址管理页面 三、商家后端页面展示
工作台展示页面 数据统计页面 订单管理页面 菜品管理页面 分类管理页面 员工管理页面 店铺状态修改页面 四、总结
项目涉及的功能还是比较全面的建议大家跟着视频做一遍。可以关注公众号回复【项目】领取项目如果有用就点赞支持一下吧。
文章持续更新可以微信搜索【小奇JAVA面试】第一时间阅读回复【项目】获取我为大家准备的项目