Java单体架构项目_云霄外卖-特殊点

项目介绍:

定位:

        专门为餐饮企业(餐厅、饭店)定制的一款软件商品

分为:

        管理端:外卖商家使用

        用户端(微信小程序):点餐用户使用。

功能架构:

(体现项目中的业务功能模块)

技术选型:

(展示项目中使用到的技术框架和中间件等)

环境搭建

整体架构:

前端:

        管理端使用nginx,使用已经打包好的前端代码,并且Nginx的配置文件中已经配置了反向代理,通过此配置可以将前端请求转发到后端服务。

        用户端之后再做介绍

后端:

后端框架:

  

1.sky-common子模块中存放的是一些工具类,可以供其他模块使用

2.sky-pojo子模块中存放的是一些entity(实体类),DTO,VO

3.sky-server子模块中存放的是配置文件、配置类、拦截器、controller、service、mapper、启动类等

版本控制

        使用Git进行版本控制

数据库:

前后端联调:

        使用nginx反向代理:

nginx反向代理:

好处:

使用方法:

接口文档:

前后端分离开发流程:

设计阶段:

Apifox:

        Apifox是设计阶段使用的工具,管理和维护接口。

开发阶段:

Swagger:

        Swagger是在开发阶段使用的框架,帮助后端开发人员做后端的接口测试

介绍:

使用方式:

1.

2.

3.

常用注解:

管理端业务功能编写:

登录功能:

        完善登录功能使用MD5对数据加密

员工管理:

添加员工:

        添加员工时通过ThreadLocal获取执行人id和创建人id

ThreadLocal介绍:

        并且客户端发起的每一次请求都是一个单独的线程,这样就满足使用ThreadLocal的条件。

ThreadLocal常用方法:

员工分页查询:

        调整LocalDataTime类型数据响应回去的格式

        推荐第二种,只需要一次编码就可以调整全部指定内容

操作过程:

1.新建一个对象映射器:

/*** 对象映射器:基于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);}
}

2.在配置类中添加如下代码:

    /*** 扩展MVC框架的消息转换器* @param converters*/@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("开始扩展消息转换器");//创建一个消息转换器对象MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();//设置对象转换器,可以将java对象转为json对象converter.setObjectMapper(new JacksonObjectMapper());//将自己的转换器放入spring MVC框架的容器中,并放到0索引位置,排到第一个,这样一定会使用它converters.add(0, converter);}

公共字段自动填充:

枚举类:

/*** 数据库操作类型*/
public enum OperationType {/*** 更新操作*/UPDATE,/*** 插入操作*/INSERT}

自定义注解:

/*** 自定义注解,用于标识需要进行公共字段自动填充的方法*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {/*** 枚举类 自定义数据库操作类型insert和update操作* @return*/OperationType value();}

自定义切面类:

/*** 自定义切面,实现公共字段自动填充处理逻辑*/
@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();Object entity = args[0];//准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的属性通过反射来赋值if(operationType == OperationType.INSERT) {try {Method setCreateTime = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getMethod(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) {try {Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {e.printStackTrace();}}}}

给mapper层的insert和update数据库操作类型添加自定义的注解:

        @AutoFill(OperationType.UPDATE)或@AutoFill(OperationType.INSERT)

注意点:

        ·在菜品分页查询部分,可能传来的参数:status状态在动态SQL里只能判断其为null,不能加上and status = '',这样会一直读取不到它,具体原因不清楚。

        ·<set>标签下的<if>标签中的内容记得加逗号

操作Redis:

        想要了解Redis的基础知识及使用方法可以参考作者另一边文章《Java_中间件——Redis》

Redis的Java客户端:

        下面我们使用Spring Data Redis来操作Redis

Spring Data Redis使用方式:

配置类RedisTemplate:

@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;}}

代码演示:

    @Autowiredprivate RedisTemplate redisTemplate;@Testpublic void testRedisTemplate() {System.out.println(redisTemplate);//操作字符串ValueOperations valueOperations = redisTemplate.opsForValue();//操作HashHashOperations hashOperations = redisTemplate.opsForHash();//操作ListListOperations listOperations = redisTemplate.opsForList();//操作setSetOperations setOperations = redisTemplate.opsForSet();//操作有序集合ZSetOperations zSetOperations = redisTemplate.opsForZSet();}/*** 操作字符串类型的数据*/@Testpublic void testStirng() {// set get setex setnxredisTemplate.opsForValue().set("city","北京");String city = (String) redisTemplate.opsForValue().get("city");System.out.println(city);redisTemplate.opsForValue().set("code","6562",60, TimeUnit.SECONDS);redisTemplate.opsForValue().setIfAbsent("lock","1");redisTemplate.opsForValue().setIfAbsent("lock","2");}/*** 操作哈希类型的数据*/@Testpublic void testHash() {//hset hget hdel hkeys hvalsHashOperations hashOperations = redisTemplate.opsForHash();hashOperations.put("100","name","xiaobai");hashOperations.put("100","age","20");String name = (String) hashOperations.get("100", "name");System.out.println(name);Set keys = hashOperations.keys("100");System.out.println(keys);List values = hashOperations.values("100");System.out.println(values);hashOperations.delete("100","age");}/*** 操作列表类型的数据*/@Testpublic void testList() {// lpush lrange rpop llenListOperations listOperations = redisTemplate.opsForList();listOperations.leftPushAll("mylist","a","b","c");listOperations.leftPush("mylist","d");List mylist = listOperations.range("mylist", 0, -1);System.out.println(mylist);listOperations.rightPop("mylist");Long size = listOperations.size("mylist");System.out.println(size);}/*** 操作集合类型的数据*/@Testpublic void testSet() {// sadd smembers scard sinter sunion sremSetOperations setOperations = redisTemplate.opsForSet();setOperations.add("set1","a","b","c");setOperations.add("set2","b","c","d");Set set1 = setOperations.members("set1");System.out.println(set1);Long size = setOperations.size("set1");System.out.println(size);Set intersect = setOperations.intersect("set1", "set2");System.out.println(intersect);Set union = setOperations.union("set1", "set2");System.out.println(union);setOperations.remove("set1","a","b");}/*** 操作有序集合类型的数据*/@Testpublic void testZSet() {// zadd zrange zincrby zremZSetOperations zSetOperations = redisTemplate.opsForZSet();zSetOperations.add("zset1","a",10);zSetOperations.add("zset1","b",11);zSetOperations.add("zset1","c",9);Set zset1 = zSetOperations.range("zset1", 0, -1);System.out.println(zset1);zSetOperations.incrementScore("zset1","c",10);zSetOperations.remove("zset1","a","b");}/*** 通用命令操作*/@Testpublic void testCommon() {// keys exists type delSet keys = redisTemplate.keys("*");System.out.println(keys);Boolean name = redisTemplate.hasKey("name");Boolean set1 = redisTemplate.hasKey("set1");for (Object key : keys) {DataType type = redisTemplate.type(key);System.out.println(type.name());}redisTemplate.delete("mylist");}

店铺营业状态设置:

        基于Redis的字符串来进行存储

        

        1表示营业,0表示打样

代码演示:

管理端设置营业状态、获取营业状态

@RequestMapping("/admin/shop")
@RestController("adminShopController")
@Slf4j
@Api(tags = "店铺相关接口")
public class ShopController {public static final String KEY = "SHOP_STATUS";@Autowiredprivate RedisTemplate redisTemplate;/*** 设置店铺的营业状态* @param status* @return*/@PutMapping("/{status}")@ApiOperation("设置营业状态")public Result setStatus(@PathVariable Integer status) {redisTemplate.opsForValue().set(KEY,status);log.info("设置当前营业状态为:{}",status == 1 ? "营业中" : "打烊中");return Result.success();}@GetMapping("/status")@ApiOperation("管理端获取营业状态")public Result<Integer> getStatus() {Integer status = (Integer) redisTemplate.opsForValue().get(KEY);log.info("当前的营业状态为:{}",status == 1 ? "营业中":"打烊中");return Result.success(status);}}

用户端获取营业状态

@RequestMapping("/user/shop")
@RestController("userShopController")
@Slf4j
@Api(tags = "店铺相关接口")
public class ShopController {public static final String KEY = "SHOP_STATUS";@Autowiredprivate RedisTemplate redisTemplate;@GetMapping("/status")@ApiOperation("用户端获取营业状态")public Result<Integer> getStatus() {Integer status = (Integer) redisTemplate.opsForValue().get(KEY);log.info("当前的营业状态为:{}",status == 1 ? "营业中":"打烊中");return Result.success(status);}}

HttpClient

使用HttpCilent需要导入依赖:

(如果导入了阿里云OSS的依赖就不需要导入了,因为这个依赖中已经包含了这个依赖)

核心API:

发送请求步骤:

代码演示:

    /*** 测试通过HttpClient发送GET方式的请求*/@Testpublic void testGET() throws Exception {//创建HttpClient对象CloseableHttpClient httpClient = HttpClients.createDefault();//创建请求对象HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");//发送请求,接收响应结果CloseableHttpResponse response = httpClient.execute(httpGet);//解析结果//获取服务端返回的状态码int statusCode = response.getStatusLine().getStatusCode();System.out.println("服务端返回的状态码为:" + statusCode);//获取服务端返回数据HttpEntity entity = response.getEntity();String body = EntityUtils.toString(entity);System.out.println("服务端返回的数据为:" + body);//关闭资源response.close();httpClient.close();}/*** 测试通过HttpClient发送GET方式的请求*/@Testpublic void testPOST() throws Exception {//创建HttpClient对象CloseableHttpClient httpClient = HttpClients.createDefault();//创建请求对象HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");JSONObject jsonObject = new JSONObject();jsonObject.put("username", "admin");jsonObject.put("password", "123456");StringEntity entity = new StringEntity(jsonObject.toString());//指定请求编码方式entity.setContentEncoding("UTF-8");//指定数据格式entity.setContentType("application/json");httpPost.setEntity(entity);//发送请求CloseableHttpResponse response = httpClient.execute(httpPost);//解析返回结果int statusCode = response.getStatusLine().getStatusCode();System.out.println("响应码为:" + statusCode);HttpEntity entity1 = response.getEntity();String body = EntityUtils.toString(entity1);System.out.println("响应数据为:" + body);//关闭资源response.close();httpClient.close();}

用户端微信小程序开发:

小程序目录结构:

主体:

页面:

微信登录过程:

Redis缓存菜品、套餐

原因:

        使用数据库一直查找会导致系统响应慢、用户体验差。所以使用Redis缓存。

缓存菜品:

实现思路:

Spring Cache:

常用注解:

                                                                                                                     (cacheName::key)↑

@Cacheable注解在方法执行前会通过动态代理,代理出一个Controller对象,然后判断缓存中是否有数据,如果有,则直接返回缓存数据,不再执行方法,如果没有,就会通过反射来调用方法。

删除全部:

Spring Task

定位:

        定时任务框架

cron表达式:

        注意:日和周只能一个填数值一个填?

        使用Spring Task对超时订单和一直配送中的订单进行处理

WebSocket

介绍:

应用场景:

        ·视频弹幕

        ·网页聊天

        ·体育实况更新

        ·股票基金报价实时更新

实现步骤:

       

         使用WebSocket来完成来单提醒和客户催单功能。

设计:

Apache POI

介绍:

应用场景:

创新点:

        1.用户端根据分类id查看菜品:通过在service、mapper层添加获取在售(status=1)商品/套餐方法,替代通过创建实体类对象进行list方法查找,将查询时间由5~17ms变为4~7ms。

        2.将工作台订单数据使用redis存储,实时更新。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ESP32实现UDP连接——micropython版本

代码&#xff1a; import network import socket import timedef wifiInit(name, port):ap network.WLAN(network.AP_IF) # 创建一个热点ap.config(essidname, authmodenetwork.AUTH_OPEN) # 无需密码ap.active(True) # 激活热点ip ap.ifconfig()[0] # 获取ip地址print(…

【想起就补】整理了一些SSH中常用的命令

希望文章能给到你启发和灵感&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏 支持一下博主吧&#xff5e; 阅读指南 开篇说明一、基础环境说明1.1 硬件环境1.2 软件环境 二、常用命令类型2.1 远程登录相关2.2 文件操作命令2.3 权限和所有权操作命令2.4 文…

MT6989(天玑9300)芯片性能参数_MTK联发科5G处理器

MT6989是联发科Dimensity旗舰系列的成员&#xff0c;旨在为旗舰5G智能手机供应商提供最先进的技术和性能。MT6989也是联发科目前最具创新和强大的5G智能手机芯片&#xff0c;具有领先的功耗效率&#xff0c;无与伦比的计算架构&#xff0c;有史以来最快和最稳定的5G调制解调器&…

【操作系统期末速成】 EP04 | 学习笔记(基于五道口一只鸭)

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;☀️☀️☀️2.1 考点七&#xff1a;进程通信2.2 考点八&#xff1a;线程的概念2.3 考点九&#xff1a;处理机调度的概念及原则2.4 考点十&#xff1a;调度方式与调度算法 一、前言&#x1f680;…

邀请函 | 极限科技全新搜索引擎 INFINI Pizza 亮相 2024 可信数据库发展大会!

过去一年&#xff0c;在全球 AI 浪潮和国家数据局成立的推动下&#xff0c;数据库产业变革不断、热闹非凡。2024 年&#xff0c;站在中国数字经济产业升级和数据要素市场化建设的时代交汇点上&#xff0c;“2024 可信数据库发展大会” 将于 2024 年 7 月 16-17 日在北京悠唐皇冠…

docker仓库--centos7.9部署harbor详细过程与使用以及常见问题

文章目录 前言1.docker-compose是什么2.harbor是什么 centos7部署harbor详细过程与使用环境一、部署docker二、部署harbor1.下载docker-compose工具2.harbor安装3.拷贝样本文件&#xff0c;并修改文件4.安装harbor&#xff0c;安装完成自行启动5.查看 三、harbor的使用1.创建项…

2024第17届中国西部体育博览会

2024第17届中国西部体育博览会 主办单位&#xff1a; 中国西部教体医融合博览会组委会 承办单位&#xff1a;重庆中博展览有限公司 展会背景&#xff1a; 四川和重庆是教育大省&#xff0c;体育大省。成渝双城经济圈的出台&#xff0c;构建以国内大循环为主体、国内国际双循环…

探索人工智能和LLM对未来就业的影响

近年来&#xff0c;人工智能&#xff08;AI&#xff09;迅猛发展&#xff0c;引发了人们的兴奋&#xff0c;同时也引发了人们对就业未来的担忧。大型语言模型&#xff08;LLM&#xff09;就是最新的例子。这些强大的人工智能子集经过大量文本数据的训练&#xff0c;以理解和生成…

VMamba: Visual State Space Model论文笔记

文章目录 VMamba: Visual State Space Model摘要引言相关工作Preliminaries方法网络结构2D-Selective-Scan for Vision Data(SS2D) VMamba: Visual State Space Model 论文地址: https://arxiv.org/abs/2401.10166 代码地址: https://github.com/MzeroMiko/VMamba 摘要 卷积神…

Chrome浏览器web调试(js调试、css调试、篡改前置)

目录 1. 打开开发者工具(Dev Tool) 2. 打开命令菜单 截图 3. 面板介绍 4. CSS调试 右键检查快速到达元素处 查找DOM数 利用面板Console查找DOM节点 内置函数查找上一个选择点击的元素 5. 调试JS代码(Javascript调试) 日志调试 选择查看日志等级 眼睛观测变量 …

Vue3学习笔记<->创建第一个vue项目(2)

新建一个项目目录 找一个盘新建一个目录&#xff0c;我这里在D盘创建一个vuedemo目录作为项目存放的目录。使用idea打开目录。   单击ieda底部的按钮“Terminal”&#xff0c;打开命令行窗口&#xff0c;如果命令行窗口当前目录不是“vuedemo”&#xff0c;就切换到“vuedem…

论文阅读:Simple and Efficient Heterogeneous Graph Neural Network

Yang, Xiaocheng, Mingyu Yan, Shirui Pan, Xiaochun Ye and Dongrui Fan. “Simple and Efficient Heterogeneous Graph Neural Network.” AAAI Conference on Artificial Intelligence (2022). 论文地址&#xff1a;[PDF] Simple and Efficient Heterogeneous Graph Neural…

深度学习11-20

1.神经元的个数对结果的影响&#xff1a; &#xff08;http://cs.stanford.edu/people/karpathy/convnetjs/demo/classify2d.html&#xff09; &#xff08;1&#xff09;神经元3个的时候 &#xff08;2&#xff09;神经元是10个的时候 神经元个数越多&#xff0c;可能会产生…

uniapp运行到小程序Vue.use注册全局组件不起作用

真想吐槽一下小程序&#xff0c;uniapp运行到小程序使用Vue.use注册全局组件根本不起作用&#xff0c;也不报错&#xff0c;这只是其中一个问题&#xff0c;其他还有很多问题&#xff0c;比如vue中正常使用的没问题的语法&#xff0c;运行到小程序就不行&#xff0c;又是包太大…

uniapp,uni-fab组件拖动属性,替代方案

文章目录 1. 背景2. 替代方案2.1 方案一2.2 方案二 参考 1. 背景 最近基于uniapp开发一款设备参数调试的APP软件&#xff0c;其中有使用到悬浮按钮&#xff0c;快速开发阶段&#xff0c;为了能尽快上线&#xff0c;直接使用了uni-ui的扩展组件uni-fab&#xff0c;参考【1】&am…

小程序消息定时任务(定时触发器)发送总结

文章目录 小程序消息定时任务&#xff08;定时触发器&#xff09;发送总结1.开发思路2.实现办法3.查看定时触发器是否正常运作4.总结 小程序消息定时任务&#xff08;定时触发器&#xff09;发送总结 1.开发思路 在使用小程序的时候总是会遇到消息任务发送的情况&#xff0c;…

Feign 原理流程图练习-01

目录 作业: 老师给的参考流程图 要求 解答 知识扩展 Feign基础原理 接口定义 代理对象生成 请求调用 请求发送 响应处理 容错与熔断 总结 作业: 老师给的参考流程图 pdf版本 【金山文档 | WPS云文档】 Feign https://kdocs.cn/l/ctbagIyxN348 ​ 要求 结合上面…

Linux 安装 Redis 教程

优质博文&#xff1a;IT-BLOG-CN 一、准备工作 配置gcc&#xff1a;安装Redis前需要配置gcc&#xff1a; yum install gcc如果配置gcc出现依赖包问题&#xff0c;在安装时提示需要的依赖包版本和本地版本不一致&#xff0c;本地版本过高&#xff0c;出现如下问题&#xff1a…

Unity 数据持久化【PlayerPrefs】

1、数据持久化 文章目录 1、数据持久化PlayerPrefs基本方法1、PlayerPrefs概念2、存储相关3、读取相关4、删除数据思考 信息的存储和读取 PlayerPrefs存储位置1、PlayerPrefs存储的数据在哪个位置2、PlayerPrefs 数据唯一性思考 排行榜功能 2、Playerprefs实践1、必备知识点-反…

springcloud第4季 分布式事务seata实现AT模式案例2【经典案例】

一 seata案例 1.1 背景说明 本案例使用seata的at模式&#xff0c;模拟分布式事务场景&#xff1a;【下订单&#xff0c;减库存&#xff0c;扣余额&#xff0c;改状态】 AT模式原理&#xff1a;是2pc方案的演变&#xff0c; 一阶段&#xff1a;业务数据和回滚日志记录在同一…