苍穹外卖知识点(简洁版)

目录

1.前端发送的请求,是如何请求到后端的?

2.日期格式解决

3.Swagger介绍

4.公共字段自动填充

5.图片上传和下载

6.微信登录

6.1HttpClient使用

6.2微信登录流程

7.Spring Cache

8.Spring Task

9.WebSocket

1.前端发送的请求,是如何请求到后端的?

         这是因为nginx进行了反向代理,就是将前端发送的动态请求由nginx转发到后端服务器。

 nginx反向代理的好处:

        (1)提高访问速度。在浏览器请求nginx时,会在nginx里面进行缓存,如果下次请求的接口相同,可以直接加载缓存,不用再请求后端服务器。

        (2)负载均衡。所谓负载均衡,就是将大量的请求按照我们指定的方式均衡的分配给集群中的每台服务器。

         (3)保证后端服务的安全。在实际开发中,后端服务不能暴露给互联网,不对外开放。

2.日期格式解决

 方式一:在属性上加上注解,对日期进行格式化。但是这种只能对一个属性进行操作,对于较多存在时间属性的实体类来说较为繁琐。

 方式二:在WebMvcConfiguration中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理。

protected void extendMessageConverters(List<HttpMessageConverter<?>> converters){
    log.info("开始扩展消息转换器...");
    //创建一个消息转换器对象
    MappingJackson2HttpMessageConverter cr = new MappingJackson2HttpMessageConverter();
    //设置对象转换器,可以将java对象转为json字符串
    cr.setObjectMapper(new JacksonObjectMapper());
    //将自己定义的转换器放入spring mvc 框架容器中
    converters.add(0,cr);
}

3.Swagger介绍

Swagger是一款RESTFUL接口的文档在线自动生成+功能测试功能软件。官网:API Documentation & Design Tools for Teams | Swagger

Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。使用方式如下:

(1).导入 knife4j maven坐标

<dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.2</version>
</dependency>

(2).在配置类中加入 knife4j 相关配置

    //通过knife4j生成接口文档
    @Bean
    public Docket docket1() {
        log.info("准备生成接口文档...");
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("管理端接口")
                .apiInfo(apiInfo)
                .select()                               //管理端controller所在包的路径
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }   

(3).设置静态资源映射,否则接口文档页面无法访问


    //设置静态资源映射
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始设置静态资源映射...");
        //访问地址:http://ip:port/doc.html
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

4.公共字段自动填充

由于本项目是基于mybatis的,所以不能用@TableField(mybatisplus提供的)。基于注解@TableField进行字段填充的方法:瑞吉外卖公共字段填充

(1) 自定义直接AutoFill,用于标识需要进行公共字段自动填充的方法。

/**
 * 自定义注解,用于标识方法需要进行字段自动填充处理
 */
//指定该注解只能加在方法上面
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
	//数据库操作类型:UPDATE INSERT
	OperationType value();
}

public enum OperationType {
    
    //更新操作
    UPDATE,
    
    //插入操作
    INSERT

}

(2)自定义切面类AutoFillAspect,统一拦截加入AutoFill注解的方法,通过反射为公共字段赋值

@Slf4j
@Aspect
@Component
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){
			//为四个公共字段赋值
			try {
				Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
				Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
				Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
				Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

				//通过反射为对象属性赋值
				setCreateTime.invoke(entity, now);
				setUpdateTime.invoke(entity, now);
				setCreateUser.invoke(entity, currentId);
				setUpdateUser.invoke(entity, currentId);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}else if (operationType == OperationType.UPDATE){
			//为两个公共字段赋值
			try {
				Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
				Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

				setCreateUser.invoke(entity, currentId);
				setUpdateUser.invoke(entity, currentId);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

5.图片上传和下载

        课程中的图片保存在了阿里云平台,所以要借助阿里云平台进行文件上传和下载。由于本人未注册阿里云账号,所以对于在学习中没有使用图片。如果想学习对于本地图片上传和下载的见:

6.微信登录

6.1HttpClient使用

HttpClient可以提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且支持HTTP协议最新的版本和建议。

<dependency>
    <groupId>org.apache.httpcomponents</groupId> 
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

发送请求的步骤:1.创建httpClient对象。2.创建http请求对象,get或post。3.创建httpClient的execute方法发送请求。

(1)Get请求

	@Test
	void testGet() throws IOException {
		//创建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();
	}

(2)Post请求

    @Test
	void testPost() throws IOException {
		//创建httpclient对象
		CloseableHttpClient httpClient = HttpClients.createDefault();
		//创建请求对象
		HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");

		EmployeeLoginDTO employeeLoginDTO = new EmployeeLoginDTO("admin","123456");
		String loginJson = JSON.toJSONString(employeeLoginDTO);

		StringEntity stringEntity = new StringEntity(loginJson);
		//指定请求编码方式
		stringEntity.setContentEncoding("UTF-8");
		//数据传输的数据格式
		stringEntity.setContentType("application/json");
		httpPost.setEntity(stringEntity);

		//发送请求
		CloseableHttpResponse response = httpClient.execute(httpPost);

		//解析数据
		//获取状态码
		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();
	}

6.2微信登录流程

小程序登录 | 微信开放文档 (qq.com)

    @PostMapping("/login")
	@ApiOperation("微信登录")
	public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){

		User user = userService.wxLogin(userLoginDTO);

		//为微信用户生成jwt令牌
		HashMap<String, Object> claims = new HashMap<>();
		claims.put(JwtClaimsConstant.USER_ID, user.getId());
		String token = JwtUtil.createJWT(this.jwt.getUserSecretKey(), this.jwt.getUserTtl(), claims);

		UserLoginVO userLoginVO = UserLoginVO.builder()
				.id(user.getId())
				.openid(user.getOpenid())
				.token(token)
				.build();

		return Result.success(userLoginVO);
	}

    @Override
	public User wxLogin(UserLoginDTO userLoginDTO) {
		//调用微信接口服务,获得当前微信用户的openId
		Map<String, String> map = new HashMap<>();
		map.put("appid", weChatProperties.getAppid());
		map.put("secret", weChatProperties.getSecret());
		map.put("js_code", userLoginDTO.getCode());
		map.put("grant_type","authorization_code");

		String json = HttpClientUtil.doGet(WX_LOGIN, map);

		String openid = JSON.parseObject(json).getString("openid");

		//判断openId是否为空,如果为空表示登录失败,抛出业务异常
		if (openid == null) throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
		//判断当前用户是否为新用户
		User user = userMapper.getUserByOpenId(openid);

		if (user != null) return user;
		//是新用户,自动完成注册
		user = User.builder().openid(openid).createTime(LocalDateTime.now()).build();
		userMapper.insertUser(user);
		//返回用户对象
		return user;
	}

7.Spring Cache

Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能

注解说明
@EnableCaching开启缓存注解功能,通常加在启动类上。
@Cacheable在方法执行前查询缓存中是否有数据,如果有数据,则返回缓存中的数据;如果没有缓存数据,调用方法并将返回值放到缓存中。
@CachePut将方法的返回值放到缓存中。
@CacheEvict将一条或多条数据从缓存中删除。

8.Spring Task

cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间。构成规则:分为67个域,由空格分隔开,每个域代表一个含义,分别为:秒、分钟、小时、日、月、周、年(可选)

cron表达式在线生成器:在线Cron表达式生成器

	//处理未付款的超时订单
	@Scheduled(cron = "0 * * * * ? ")//每分钟触发一次
	public void processTimeOutOrder(){
		log.info("定时处理超时订单:{}", LocalDateTime.now());

		//当前时间减去15分钟
		LocalDateTime time = LocalDateTime.now().plusMinutes(-15);

		//查询超时订单
		List<OrderVO> orderVOList = orderMapper.queryOrdersByStatusAndTime(Orders.PENDING_PAYMENT,time);

		if (orderVOList != null && orderVOList.size() > 0){
			//修改订单状态为取消,
			for (OrderVO orderVO : orderVOList) {
				orderVO.setStatus(Orders.CANCELLED);
				orderVO.setCancelReason("用户未付款,订单自动取消");
				orderVO.setCancelTime(LocalDateTime.now());
				orderMapper.updataStatusByCancel(orderVO);
			}
		}

	}

	//处理一直处于派送中的订单
	@Scheduled(cron = "0 0 1 * * ?")//每天凌晨一点触发一次
	public void processDeliveryOrder(){
		log.info("定时处理处于派送中的订单:{}", LocalDateTime.now());

		//当前时间减去1小时
		LocalDateTime time = LocalDateTime.now().plusHours(-1);

		//查询超时订单
		List<OrderVO> orderVOList = orderMapper.queryOrdersByStatusAndTime(Orders.DELIVERY_IN_PROGRESS,time);

		if (orderVOList != null && orderVOList.size() > 0){
			//修改订单状态为取消,
			for (OrderVO orderVO : orderVOList) {
				orderVO.setStatus(Orders.COMPLETED);
				orderMapper.updataStatusByComplete(orderVO);
			}
		}

	}

9.WebSocket

WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。

HTTP协议和WebSocket协议对比:①HTTP是短连接,WebSocket是长连接。②HTTP通信是单向的,基于请求响应模式。WebSocket支持双向通信。③HTTP和WebSocket底层都是TCP连接

应用场景:视频聊天、网页聊天、体育实况更新等。

       

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用,苍穹外卖mybatis-plus本是基于mybatis-plus框架实现的,但在某些复杂的地方,如多表查询,使用直接编写sql语句的方式会更简单。因此,推荐在简单的情况下使用mybatis-plus,而在复杂的情况下使用sql。 而根据引用,在mybatis的配置文件,可以通过指定entity扫描包的方式,让mybatis自动扫描到自定义的entity类。因此,在parameterType只需要写上实体类名,如Employee即可。 对于数据传输对象(DTO)和视图对象(VO),根据引用,DTO是前端传给后台的数据对象,而VO是后台发送给前端的数据对象。在这个项目,可能会使用到一个名为Result的类,其包含了编码、错误信息和数据等属性。在该类,还有一个success的静态方法,用于返回一个成功的结果对象。 综上所述,苍穹外卖mybatis-plus本使用了mybatis-plus框架,并且推荐简单的情况下使用mybatis-plus,复杂的情况下使用sql。在mybatis的配置文件,可以通过指定entity扫描包的方式让mybatis自动扫描到自定义的entity类。在数据传输方面,项目可能会使用到DTO和VO对象,以及一个名为Result的类用于封装返回结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [黑马-苍穹外卖-mybatis-plus](https://blog.csdn.net/weixin_72786602/article/details/132323327)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [苍穹外卖day01](https://blog.csdn.net/qq_61692791/article/details/130561582)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值