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

打印 上一主题 下一主题

主题 580|帖子 580|积分 1740

项目介绍:

定位:

        专门为餐饮企业(餐厅、饭店)定制的一款软件商品
分为:
        管理端:外卖商家使用
        用户端(微信小步伐):点餐用户使用。
功能架构:

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

技能选型:

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

环境搭建

整体架构:


前端:

        管理端使用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.新建一个对象映射器:
  1. /**
  2. * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
  3. * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
  4. * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
  5. */
  6. public class JacksonObjectMapper extends ObjectMapper {
  7.     public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
  8.     //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
  9.     public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
  10.     public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
  11.     public JacksonObjectMapper() {
  12.         super();
  13.         //收到未知属性时不报异常
  14.         this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
  15.         //反序列化时,属性不存在的兼容处理
  16.         this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  17.         SimpleModule simpleModule = new SimpleModule()
  18.                 .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
  19.                 .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
  20.                 .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
  21.                 .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
  22.                 .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
  23.                 .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
  24.         //注册功能模块 例如,可以添加自定义序列化器和反序列化器
  25.         this.registerModule(simpleModule);
  26.     }
  27. }
复制代码
2.在设置类中添加如下代码:
  1.     /**
  2.      * 扩展MVC框架的消息转换器
  3.      * @param converters
  4.      */
  5.     @Override
  6.     protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
  7.         log.info("开始扩展消息转换器");
  8.         //创建一个消息转换器对象
  9.         MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
  10.         //设置对象转换器,可以将java对象转为json对象
  11.         converter.setObjectMapper(new JacksonObjectMapper());
  12.         //将自己的转换器放入spring MVC框架的容器中,并放到0索引位置,排到第一个,这样一定会使用它
  13.         converters.add(0, converter);
  14.     }
复制代码
公共字段自动填充:



枚举类:

  1. /**
  2. * 数据库操作类型
  3. */
  4. public enum OperationType {
  5.     /**
  6.      * 更新操作
  7.      */
  8.     UPDATE,
  9.     /**
  10.      * 插入操作
  11.      */
  12.     INSERT
  13. }
复制代码
自界说注解:

  1. /**
  2. * 自定义注解,用于标识需要进行公共字段自动填充的方法
  3. */
  4. @Target(ElementType.METHOD)
  5. @Retention(RetentionPolicy.RUNTIME)
  6. public @interface AutoFill {
  7.     /**
  8.      * 枚举类 自定义数据库操作类型insert和update操作
  9.      * @return
  10.      */
  11.     OperationType value();
  12. }
复制代码
自界说切面类:

  1. /**
  2. * 自定义切面,实现公共字段自动填充处理逻辑
  3. */
  4. @Aspect
  5. @Component
  6. @Slf4j
  7. public class AutoFillAspect {
  8.     /**
  9.      * 切入点
  10.      */
  11.     @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
  12.     public void autoFillPointCut() {}
  13.     /**
  14.      * 前置通知,为公共字段赋值
  15.      */
  16.     @Before("autoFillPointCut()")
  17.     public void autoFill(JoinPoint joinPoint) {
  18.         log.info("开始进行公共字段填充...");
  19.         //获取到当前被拦截方法上的数据库操作类型
  20.         MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
  21.         AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解
  22.         OperationType operationType = autoFill.value();//获得数据库操作类型
  23.         //获取到当前被拦截方法的参数--实体对象
  24.         Object[] args = joinPoint.getArgs();
  25.         Object entity = args[0];
  26.         //准备赋值的数据
  27.         LocalDateTime now = LocalDateTime.now();
  28.         Long currentId = BaseContext.getCurrentId();
  29.         //根据当前不同的操作类型,为对应的属性通过反射来赋值
  30.         if(operationType == OperationType.INSERT) {
  31.             try {
  32.                 Method setCreateTime = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
  33.                 Method setCreateUser = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
  34.                 Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
  35.                 Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
  36.                 setCreateTime.invoke(entity,now);
  37.                 setCreateUser.invoke(entity,currentId);
  38.                 setUpdateTime.invoke(entity,now);
  39.                 setUpdateUser.invoke(entity,currentId);
  40.             } catch (Exception e) {
  41.                 e.printStackTrace();
  42.             }
  43.         } else if(operationType == OperationType.UPDATE) {
  44.             try {
  45.                 Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
  46.                 Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
  47.                 setUpdateTime.invoke(entity,now);
  48.                 setUpdateUser.invoke(entity,currentId);
  49.             } catch (Exception e) {
  50.                 e.printStackTrace();
  51.             }
  52.         }
  53.     }
  54. }
复制代码
给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:

  1. @Configuration
  2. @Slf4j
  3. public class RedisConfiguration {
  4.     @Bean
  5.     public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  6.         log.info("开始创建redis模板对象...");
  7.         RedisTemplate redisTemplate = new RedisTemplate();
  8.         //设置redis连接工厂对象
  9.         redisTemplate.setConnectionFactory(redisConnectionFactory);
  10.         //设置redis key的序列化器
  11.         redisTemplate.setKeySerializer(new StringRedisSerializer());
  12.         return redisTemplate;
  13.     }
  14. }
复制代码
代码演示:

  1.     @Autowired
  2.     private RedisTemplate redisTemplate;
  3.     @Test
  4.     public void testRedisTemplate() {
  5.         System.out.println(redisTemplate);
  6.         //操作字符串
  7.         ValueOperations valueOperations = redisTemplate.opsForValue();
  8.         //操作Hash
  9.         HashOperations hashOperations = redisTemplate.opsForHash();
  10.         //操作List
  11.         ListOperations listOperations = redisTemplate.opsForList();
  12.         //操作set
  13.         SetOperations setOperations = redisTemplate.opsForSet();
  14.         //操作有序集合
  15.         ZSetOperations zSetOperations = redisTemplate.opsForZSet();
  16.     }
  17.     /**
  18.      * 操作字符串类型的数据
  19.      */
  20.     @Test
  21.     public void testStirng() {
  22.         // set get setex setnx
  23.         redisTemplate.opsForValue().set("city","北京");
  24.         String city = (String) redisTemplate.opsForValue().get("city");
  25.         System.out.println(city);
  26.         redisTemplate.opsForValue().set("code","6562",60, TimeUnit.SECONDS);
  27.         redisTemplate.opsForValue().setIfAbsent("lock","1");
  28.         redisTemplate.opsForValue().setIfAbsent("lock","2");
  29.     }
  30.     /**
  31.      * 操作哈希类型的数据
  32.      */
  33.     @Test
  34.     public void testHash() {
  35.         //hset hget hdel hkeys hvals
  36.         HashOperations hashOperations = redisTemplate.opsForHash();
  37.         hashOperations.put("100","name","xiaobai");
  38.         hashOperations.put("100","age","20");
  39.         String name = (String) hashOperations.get("100", "name");
  40.         System.out.println(name);
  41.         Set keys = hashOperations.keys("100");
  42.         System.out.println(keys);
  43.         List values = hashOperations.values("100");
  44.         System.out.println(values);
  45.         hashOperations.delete("100","age");
  46.     }
  47.     /**
  48.      * 操作列表类型的数据
  49.      */
  50.     @Test
  51.     public void testList() {
  52.         // lpush lrange rpop llen
  53.         ListOperations listOperations = redisTemplate.opsForList();
  54.         listOperations.leftPushAll("mylist","a","b","c");
  55.         listOperations.leftPush("mylist","d");
  56.         List mylist = listOperations.range("mylist", 0, -1);
  57.         System.out.println(mylist);
  58.         listOperations.rightPop("mylist");
  59.         Long size = listOperations.size("mylist");
  60.         System.out.println(size);
  61.     }
  62.     /**
  63.      * 操作集合类型的数据
  64.      */
  65.     @Test
  66.     public void testSet() {
  67.         // sadd smembers scard sinter sunion srem
  68.         SetOperations setOperations = redisTemplate.opsForSet();
  69.         setOperations.add("set1","a","b","c");
  70.         setOperations.add("set2","b","c","d");
  71.         Set set1 = setOperations.members("set1");
  72.         System.out.println(set1);
  73.         Long size = setOperations.size("set1");
  74.         System.out.println(size);
  75.         Set intersect = setOperations.intersect("set1", "set2");
  76.         System.out.println(intersect);
  77.         Set union = setOperations.union("set1", "set2");
  78.         System.out.println(union);
  79.         setOperations.remove("set1","a","b");
  80.     }
  81.     /**
  82.      * 操作有序集合类型的数据
  83.      */
  84.     @Test
  85.     public void testZSet() {
  86.         // zadd zrange zincrby zrem
  87.         ZSetOperations zSetOperations = redisTemplate.opsForZSet();
  88.         zSetOperations.add("zset1","a",10);
  89.         zSetOperations.add("zset1","b",11);
  90.         zSetOperations.add("zset1","c",9);
  91.         Set zset1 = zSetOperations.range("zset1", 0, -1);
  92.         System.out.println(zset1);
  93.         zSetOperations.incrementScore("zset1","c",10);
  94.         zSetOperations.remove("zset1","a","b");
  95.     }
  96.     /**
  97.      * 通用命令操作
  98.      */
  99.     @Test
  100.     public void testCommon() {
  101.         // keys exists type del
  102.         Set keys = redisTemplate.keys("*");
  103.         System.out.println(keys);
  104.         Boolean name = redisTemplate.hasKey("name");
  105.         Boolean set1 = redisTemplate.hasKey("set1");
  106.         for (Object key : keys) {
  107.             DataType type = redisTemplate.type(key);
  108.             System.out.println(type.name());
  109.         }
  110.         redisTemplate.delete("mylist");
  111.     }
复制代码

店铺营业状态设置:

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

        1表现营业,0表现打样
代码演示:

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

  1. @RequestMapping("/admin/shop")
  2. @RestController("adminShopController")
  3. @Slf4j
  4. @Api(tags = "店铺相关接口")
  5. public class ShopController {
  6.     public static final String KEY = "SHOP_STATUS";
  7.     @Autowired
  8.     private RedisTemplate redisTemplate;
  9.     /**
  10.      * 设置店铺的营业状态
  11.      * @param status
  12.      * @return
  13.      */
  14.     @PutMapping("/{status}")
  15.     @ApiOperation("设置营业状态")
  16.     public Result setStatus(@PathVariable Integer status) {
  17.         redisTemplate.opsForValue().set(KEY,status);
  18.         log.info("设置当前营业状态为:{}",status == 1 ? "营业中" : "打烊中");
  19.         return Result.success();
  20.     }
  21.     @GetMapping("/status")
  22.     @ApiOperation("管理端获取营业状态")
  23.     public Result<Integer> getStatus() {
  24.         Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
  25.         log.info("当前的营业状态为:{}",status == 1 ? "营业中":"打烊中");
  26.         return Result.success(status);
  27.     }
  28. }
复制代码
用户端获取营业状态

  1. @RequestMapping("/user/shop")
  2. @RestController("userShopController")
  3. @Slf4j
  4. @Api(tags = "店铺相关接口")
  5. public class ShopController {
  6.     public static final String KEY = "SHOP_STATUS";
  7.     @Autowired
  8.     private RedisTemplate redisTemplate;
  9.     @GetMapping("/status")
  10.     @ApiOperation("用户端获取营业状态")
  11.     public Result<Integer> getStatus() {
  12.         Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
  13.         log.info("当前的营业状态为:{}",status == 1 ? "营业中":"打烊中");
  14.         return Result.success(status);
  15.     }
  16. }
复制代码
HttpClient


使用HttpCilent必要导入依赖:
(如果导入了阿里云OSS的依赖就不必要导入了,由于这个依赖中已经包罗了这个依赖)

核心API:


发送哀求步调:


代码演示:

  1.     /**
  2.      * 测试通过HttpClient发送GET方式的请求
  3.      */
  4.     @Test
  5.     public void testGET() throws Exception {
  6.        //创建HttpClient对象
  7.         CloseableHttpClient httpClient = HttpClients.createDefault();
  8.         //创建请求对象
  9.         HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
  10.         //发送请求,接收响应结果
  11.         CloseableHttpResponse response = httpClient.execute(httpGet);
  12.         //解析结果
  13.         //获取服务端返回的状态码
  14.         int statusCode = response.getStatusLine().getStatusCode();
  15.         System.out.println("服务端返回的状态码为:" + statusCode);
  16.         //获取服务端返回数据
  17.         HttpEntity entity = response.getEntity();
  18.         String body = EntityUtils.toString(entity);
  19.         System.out.println("服务端返回的数据为:" + body);
  20.         //关闭资源
  21.         response.close();
  22.         httpClient.close();
  23.     }
  24.     /**
  25.      * 测试通过HttpClient发送GET方式的请求
  26.      */
  27.     @Test
  28.     public void testPOST() throws Exception {
  29.         //创建HttpClient对象
  30.         CloseableHttpClient httpClient = HttpClients.createDefault();
  31.         //创建请求对象
  32.         HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
  33.         JSONObject jsonObject = new JSONObject();
  34.         jsonObject.put("username", "admin");
  35.         jsonObject.put("password", "123456");
  36.         StringEntity entity = new StringEntity(jsonObject.toString());
  37.         //指定请求编码方式
  38.         entity.setContentEncoding("UTF-8");
  39.         //指定数据格式
  40.         entity.setContentType("application/json");
  41.         httpPost.setEntity(entity);
  42.         //发送请求
  43.         CloseableHttpResponse response = httpClient.execute(httpPost);
  44.         //解析返回结果
  45.         int statusCode = response.getStatusLine().getStatusCode();
  46.         System.out.println("响应码为:" + statusCode);
  47.         HttpEntity entity1 = response.getEntity();
  48.         String body = EntityUtils.toString(entity1);
  49.         System.out.println("响应数据为:" + body);
  50.         //关闭资源
  51.         response.close();
  52.         httpClient.close();
  53.     }
复制代码
用户端微信小步伐开辟:

小步伐目次结构:

主体:


页面:


微信登录过程:



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存储,实时更新。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

数据人与超自然意识

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表