马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
项目介绍:
定位:
专门为餐饮企业(餐厅、饭店)定制的一款软件商品
分为:
管理端:外卖商家使用
用户端(微信小步伐):点餐用户使用。
功能架构:
(体现项目中的业务功能模块)
技能选型:
(展示项目中使用到的技能框架和中间件等)
环境搭建
整体架构:
前端:
管理端使用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
- */
- @Override
- protected 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 {
- @Bean
- public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
- log.info("开始创建redis模板对象...");
- RedisTemplate redisTemplate = new RedisTemplate();
- //设置redis连接工厂对象
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- //设置redis key的序列化器
- redisTemplate.setKeySerializer(new StringRedisSerializer());
- return redisTemplate;
- }
- }
复制代码 代码演示:
- @Autowired
- private RedisTemplate redisTemplate;
- @Test
- public void testRedisTemplate() {
- System.out.println(redisTemplate);
- //操作字符串
- ValueOperations valueOperations = redisTemplate.opsForValue();
- //操作Hash
- HashOperations hashOperations = redisTemplate.opsForHash();
- //操作List
- ListOperations listOperations = redisTemplate.opsForList();
- //操作set
- SetOperations setOperations = redisTemplate.opsForSet();
- //操作有序集合
- ZSetOperations zSetOperations = redisTemplate.opsForZSet();
- }
- /**
- * 操作字符串类型的数据
- */
- @Test
- public void testStirng() {
- // set get setex setnx
- redisTemplate.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");
- }
- /**
- * 操作哈希类型的数据
- */
- @Test
- public void testHash() {
- //hset hget hdel hkeys hvals
- HashOperations 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");
- }
- /**
- * 操作列表类型的数据
- */
- @Test
- public void testList() {
- // lpush lrange rpop llen
- ListOperations 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);
- }
- /**
- * 操作集合类型的数据
- */
- @Test
- public void testSet() {
- // sadd smembers scard sinter sunion srem
- SetOperations 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");
- }
- /**
- * 操作有序集合类型的数据
- */
- @Test
- public void testZSet() {
- // zadd zrange zincrby zrem
- ZSetOperations 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");
- }
- /**
- * 通用命令操作
- */
- @Test
- public void testCommon() {
- // keys exists type del
- Set 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";
- @Autowired
- private 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";
- @Autowired
- private 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方式的请求
- */
- @Test
- public 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方式的请求
- */
- @Test
- public 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存储,实时更新。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |