苍穹外卖项目解读(四) 微信小程序支付、定时任务、WebSocket

news2024/11/26 21:53:06

前言

HM新出springboot入门项目《苍穹外卖》,笔者打算写一个系列学习笔记,“苍穹外卖项目解读”,内容主要从HM课程,自己实践,以及踩坑填坑出发,以技术,经验为主,记录学习,也希望能给在学想学的小伙伴一个参考。

注:本文章是直接拿到项目的最终代码,然后从代码出发,快速逆向学习技术经验! 可能需要一些前置知识

觉得文章有用可以关注点赞收藏期待更新^^,期待您的评论留言

苍穹外卖项目解读(一) 完整代码本地部署运行
苍穹外卖项目解读(二) 管理端JWT令牌、AOP注解开发、分页
苍穹外卖项目解读(三) redis、cache缓存解读
苍穹外卖项目解读(四) 微信小程序支付、定时任务、WebSocket

在这里插入图片描述

微信小程序支付

微信小程序开发

1、微信小程序开发需要到微信小程序服务平台注册,分为个人、企业、媒体、政府等,提供需要的注册文件即可。不同的注册主体所获得的开发权限有所不同:个人版就无法使用微信支付功能

2、注册完成之后,对于后端开发来说,我们需要在开发管理中获取所注册小程序ID:AppID、小程序秘钥:AppSecret。对应Java配置文件中:

wechat:
    appid: wxffb3637a228223b8
    secret: 84311df9199ecacdf4f12d27b6b9522d

3、微信开发者工具,前端交互开发工具,请大家自行探索^^

4、小程序发布上线,在微信开发者工具中,上传按钮到微信服务器(此时为开发版本),在小程序网页管理端,找到上传的版本小程序,提交审核(审核版本),审核痛过之后,即可发布(线上版本)

微信支付

1、已经进行了企业小程序的注册,在微信支付的商户平台接入微信支付,提交资料、签署协议、绑定场景(小程序支付,网页扫码支付,app调用支付等)。(一般开发人员不接触,都是企业注册完成之后拿到后续开发所需要的信息)

微信小程序的支付流程笔者在这里结合日常举例(现金)

我去水果店买东西,挑了一个大西瓜,给老板称重算价格(下单,图例123)。我告诉管着钱的女友买的啥,在哪买的,多大的西瓜,总共花了多少钱并向她要钱(申请微信下单接口,图例456)。女友看了看西瓜跟老板议论这瓜保熟吗xxx(密文,图例78)。我说买了吧,大夏天好热吃个瓜爽歪歪(确定支付 图例9)。女友给老板钱,老板给我西瓜我提着(支付结果,图例10 11 12)。女友走时跟老板说“这瓜保熟,瓜甜的话还来买”(回调,图例13 14)
其中我就是用户,老板水果店是商户,管钱的女友是微信后台
在程序中需要注意的点:
1、向她要钱(申请微信下单接口):准备好参数,去主动请求微信后台(生成预支付交易单)
2、跟老板议论这瓜保熟吗xxx(密文):告诉用户加密好了一些内容,供用户去确定支付
3、吃个瓜爽歪歪(确定支付):真正给钱,微信后台支付
4、回调:指明回调地址,得到结果信息

在这里插入图片描述
5调用微信下单接口 请求图
在这里插入图片描述
9用户确定支付请求图(参数来自7、8封装)
在这里插入图片描述
更多开发细节可关注文档中心

------配置项解析------
  wechat:
    appid: wxffb3637a228223b8  小程序id
    secret: 84311df9199ecacdf4f12d27b6b9522d  小程序秘钥
    mchid : 1561414331  商户号
    mchSerialNo: 4B3B3DC35414AD50B1B755BAF8DE9CC7CF407606  构造请求客户端build使用 WechatPayHttpClientBuilder
    privateKeyFilePath: D:\pay\apiclient_key.pem  商户私钥文件
    apiV3Key: CZBK51236435wxpay435434323FFDuv3  解密回调内容的key
    weChatPayCertFilePath: D:\pay\wechatpay_166D96F876F45C7D07CE98952A96EC980368ACFC.pem  平台证书文件
    notifyUrl: https://58869fb.r2.cpolar.top/notify/paySuccess  回调地址公网ip
    refundNotifyUrl: https://58869fb.r2.cpolar.top/notify/refundSuccess  回调地址

定时任务

spring对定时调度的开发又很友好的开发方式,即启动类上@EnableScheduling,在定时任务上使用@Scheduled,并搭配cron表达式。下面从源码解析spring是如何进行定时调度的。

@EnableScheduling

1、ScheduledAnnotationBeanPostProcessor

其中postProcessAfterInitialization方法中,主要对标注@Scheduled和聚合注解@Schedules的类成员方法进行处理,主要分为2步:
1)识别标注@Scheduled和聚合注解@Schedules的方法;
2)对注解方法调用processScheduled方法进行处理;

2、processScheduled处理过程如下

1)将调用目标方法的过程包装为ScheduledMethodRunnable类
2)构造CronTask并进行调度
3)构造FixedDelayTask并进行调度
4)构造FixedRateTask并进行调度

3、调度框架支持的Task类型
在这里插入图片描述

Spring调度框架中重要支持3种调度任务类型(继承结构如上图),具体说明如下:
1)CronTask:cron表达式调度的任务
2)FixedDelayTask:固定延迟时间执行的任务
3)FixedRateTask:固定速率执行的任务

4、3种的调度执行实现近似,以常用的cron为例

	@Nullable
	public ScheduledTask scheduleCronTask(CronTask task) {
		ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
		boolean newTask = false;
		if (scheduledTask == null) {
			scheduledTask = new ScheduledTask(task);
			newTask = true;
		}
		if (this.taskScheduler != null) {
			scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
		}
		else {
			addCronTask(task);
			this.unresolvedTasks.put(task, scheduledTask);
		}
		return (newTask ? scheduledTask : null);
	}

1)将调度任务包装为ScheduledTask类型,其中封装了执行结果ScheduledFuture
2)存在任务调度器(taskScheduler)时,直接进行调度执行.
3)不存在任务调度器(taskScheduler)时,将任务暂存到addCronTask中,待调用afterPropertiesSet方法时再进行调度执行

5、任务调度器支持自定义,当无自定义调度器时,调度框架提供了默认的任务调度器;
自定义任务调度器的处理逻辑在方法finishRegistration中

上述获取任务调度器的优先级顺序为:
1)当Bean后处理器中定义了任务调度器时,优先取Bean后处理器的任务调度器
2)在BeanFactory中获取Bean类型为SchedulingConfigurer的实例,在其方法configureTasks中可以自定义任务调度器
3)获取BeanFactory中TaskScheduler类型的bean(如有)
4)获取BeanFactory中ScheduledExecutorService类型的bean(如有)
5)当上述方式获取的任务调度器都不存在时,会使用框架中默认的任务调度器,如下:

		if (this.taskScheduler == null) {
			this.localExecutor = Executors.newSingleThreadScheduledExecutor();
			this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
		}

6、框架内提供的任务调度器

框架内提供的任务调度器主要包括:

1)ConcurrentTaskScheduler

2)ThreadPoolTaskScheduler
继承结构如下:
在这里插入图片描述
以上述框架默认的ConcurrentTaskScheduler进行说明,在调用调度器方法scheduleWithFixedDelay执行时,具体执行逻辑为:

public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
		try {
			if (this.enterpriseConcurrentScheduler) {//默认false
				return new EnterpriseConcurrentTriggerScheduler().schedule(decorateTask(task, true), trigger);
			}
			else {
				ErrorHandler errorHandler =
						(this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true));
				return new ReschedulingRunnable(task, trigger, this.clock, this.scheduledExecutor, errorHandler).schedule();
			}
		}
		catch (RejectedExecutionException ex) {
			throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
		}
	}

这里主要包含2部分:
1)首先把task任务包装为DelegatingErrorHandlingRunnable类型(支持嵌入错误处理器逻辑),具体是在方法decorateTask中实现
2)调用线程池方法ReschedulingRunnable().schedule()进行调度执行this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);

@Scheduled

fixedDealy

在上一次调用结束和下一次调用开始之间的固定时间内执行注释方法。时间单位默认为毫秒,但可以通过 timeUnit 重载。

fixedRate

以固定的调用间隔执行注释方法。时间单位默认为毫秒,但可以通过 timeUnit.Me 方法重载。

cron表达式

包括秒、分、时、月、月日和星期的触发器。
例如,"0 * * * MON-FRI “表示在工作日每分钟触发一次(在分钟的顶部,即第 0 秒)。
从左到右读取的字段解释如下。
秒 分钟 小时 日 月 星期
特殊值”-"表示禁用 cron 触发器,主要用于由 ${…} 占位符解析的外部指定值。

WebSocket

WebSocket 是一种支持双向通讯网络通信协议。
意思就是服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息
属于服务器推送技术的一种.

特点:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据(blob对象或Arraybuffer对象)

(5)收到的数据类型 可以使用binaryType 指定, 显式指定收到的二进制数据类型

(6)没有同源限制,客户端可以与任意服务器通信。

(7)协议标识符是ws(握手http)(如果加密,则为wss(tcp +TLS)),服务器网址就是 URL。

WebSocket对象
WebSocket对象提供了用于创建和管理WebSocket 连接,以及可以通过该连接发送和接收数据的 API。

使用 WebSocket() 构造函数来构造一个 WebSocket 。

前后端都需要实现websocket的open close message方法

@Component
@ServerEndpoint("/ws/{sid}") //此处类似controller的方式,由前端访问到
public class WebSocketServer {

    //存放会话对象 建立websocket连接的对象,此处的Session为websocke包下
    private static Map<String, Session> sessionMap = new HashMap();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        System.out.println("客户端:" + sid + "建立连接");
        sessionMap.put(sid, session);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam("sid") String sid) {
        System.out.println("收到来自客户端:" + sid + "的信息:" + message);
    }

    /**
     * 连接关闭调用的方法
     *
     * @param sid
     */
    @OnClose
    public void onClose(@PathParam("sid") String sid) {
        System.out.println("连接断开:" + sid);
        sessionMap.remove(sid);
    }

    /**
     * 群发
     *
     * @param message
     */
    public void sendToAllClient(String message) {
        Collection<Session> sessions = sessionMap.values();
        for (Session session : sessions) {
            try {
                //服务器向客户端发送消息
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
<script type="text/javascript">
    var websocket = null;
    var clientId = Math.random().toString(36).substr(2);

    //判断当前浏览器是否支持WebSocket
    if('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>

reference:
https://blog.csdn.net/supzhili/article/details/131324690
https://baijiahao.baidu.com/s?id=1706643919026404240&wfr=spider&for=pc

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爬虫018_urllib库_cookie反爬_post请求百度翻译获取百分翻译内容_以及详细翻译内容---python工作笔记037

然后我们来看如何用urllib发送post请求,这里我们 用百度翻译为例 我们翻译一个spider,然后我们看请求,可以看到有很多 找到sug这个 可以看到这里的form data,就是post请求体中的内容 然后我们点击preview其实就是 返回的实际内容 然后请求方式用的post 然后我们把上面的信息…

Untiy Json和Xml的序列化和反序列化

Json的序列化和反序列化 1.定义数据类 [Serializable] public class ZoomPoint {// 点名称, 将作为Key被字典存储public string name;// 轴心X坐标public Vector2 pivot Vector2.one / 2;// 放大倍率&#xff0c;小于1是为缩小倍率&#xff0c;小于0是取绝对值&#xff0c;不…

BIGEMAP双端buff助力AEC行业无压力进行AutoCAD作图

工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP AEC行业&#xff0c;即建筑(Architecture)、工程(Engineering)、施工(Construction)&#xff0c;热衷于引入信息通信技术&#xff0c;不仅活跃于国际舞台&#xff0c;还…

GoFastDFS单节点部署

&#x1f388; 作者&#xff1a;互联网-小啊宇 &#x1f388; 简介&#xff1a; CSDN 运维领域创作者、阿里云专家博主。目前从事 Kubernetes运维相关工作&#xff0c;擅长Linux系统运维、开源监控软件维护、Kubernetes容器技术、CI/CD持续集成、自动化运维、开源软件部署维护…

Oracle 开发篇+Java通过HiKariCP访问Oracle数据库

标签&#xff1a;HikariCP、数据库连接池、JDBC连接池、释义&#xff1a;HikariCP 是一个高性能的 JDBC 连接池组件&#xff0c;号称性能最好的后起之秀&#xff0c;是一个基于BoneCP做了不少的改进和优化的高性能JDBC连接池。 ★ Java代码 import java.sql.Connection; impor…

【数学建模】--主成分分析

本讲将介绍主成分分析&#xff08;Principal Component Analysis&#xff0c;PCA&#xff09;&#xff0c;主成分分析是一种降维算法&#xff0c;它能将多个指标转换为少数几个主成分&#xff0c;这些主成分是原始变量的线性组合&#xff0c;且彼此之间互不相关&#xff0c;其能…

企业这样给员工谋福利!看完直呼绝了!

当我们谈论新零售模式时&#xff0c;自动售货机无疑是一个引人注目的亮点。在这个数字化和智能化的时代&#xff0c;自动售货机作为新零售模式的重要代表&#xff0c;正在改变着我们的购物方式和消费体验。 通过自动售货机&#xff0c;消费者可以在24小时内购买所需商品&#x…

linux配置上网 linux adsl拨号上网设置

Linux里面配置ADSL上网是件很麻烦的事。但配置完成之后就能开机自动拨号上网&#xff0c;可谓十分的方便。支持的系统有Redhat,CentOS,SuSE,FreeBSD,Ubuntu等常见的Linux。 工具/原料 ADSL网络&#xff0c;电信&#xff0c;网通&#xff0c;移动等常见宽带。 Linux系统的安装光…

【逗老师的PMP学习笔记】9、项目资源管理

目录 一、规划资源管理1、【关键工具】责任分配矩阵RACI矩阵2、【关键工具】组织理论2.1、马斯洛需求层次理论2.2、麦格雷戈-X-Y理论2.3、赫兹伯格双因素理论 3、【关键输出】资源管理计划4、【关键输出】团队章程 二、估算活动资源1、【关键输入】资源日历 三、获取资源1、【关…

中电金信:国际结算系统的“王冠”,为什么十年都戴在“它”的头上

2001年&#xff0c;中国正式加入WTO&#xff0c;在此后的二十多年里&#xff0c;中国出口席卷全球&#xff0c;迅速成长为一个贸易大国。 作为跨国贸易不可或缺的一环&#xff0c;无论是商品交易、服务提供、资金分配还是国际借贷等&#xff0c;都需要通过银行办理的外汇收支业…

什么是自定义表单和工作流?看完这篇文章就懂了

在很多中大型企业中&#xff0c;低代码技术平台的应用价值是较高的&#xff0c;也深得广大用户朋友的青睐和喜爱。其中的自定义表单和工作流是该平台的主要功能&#xff0c;可以解决当前工作效率低下、解放程序员时间和精力等各种现实问题&#xff0c;可以说是实现办公流程化、…

UnityWebGL移动端兼容性说明

官方文档说明 依据Unity官方最新版本文档&#xff08;2021.3LTS&#xff09;&#xff0c;关于WebGL的兼容性说明为"Unity WebGL不支持移动设备。它可能适用于高端设备&#xff0c;但当前的设备通常不够强大&#xff0c;并且没有足够的内存来支持Unity WebGL内容。为了让最…

(leecode)设计循环队列

&#xff08;温馨提示&#xff1a;这是博主最最喜欢的歌曲哦&#xff0c;没有之一&#xff09; 题目&#xff1a; 题解&#xff1a; 思路&#xff1a; 方法一(数组)&#xff1a; 方法二(链表)&#xff1a; 题目&#xff1a; 设计你的循环队列实现。 循环队列是一种线性数…

使用chatGPT-4 畅聊量子物理学

与chatGPT深入研究起源、基本概念&#xff0c;以及海森堡、德布罗意、薛定谔、玻尔、爱因斯坦和狄拉克如何得出他们的想法和方程。 1965 年&#xff0c;费曼&#xff08;左&#xff09;与朱利安施温格&#xff08;未显示&#xff09;和朝永信一郎&#xff08;右&#xff09;分享…

bootloader跳转APP注意事项

在gd32f427 时跳转异常 参考文章&#xff1a; https://club.rt-thread.org/ask/question/425321.html%20https:/club.rt-thread.org/ask/question/eab19452583b5959.html https://club.rt-thread.org/ask/question/eab19452583b5959.html 关闭全部中断&#xff0c;并且清除中…

每天一道leetcode:剑指 Offer 27. 二叉树的镜像(适合初学者递归树)

今日份题目&#xff1a; 请完成一个函数&#xff0c;输入一个二叉树&#xff0c;该函数输出它的镜像。 例如输入&#xff1a; 4 / \ 2 7 / \ / \ 1 3 6 9 镜像输出&#xff1a; 4 / \ 7 2 / \ / \ 9 6 3 1 示例 输入&#xff1a;root [4,2,7…

小白到运维工程师自学之路 第七十一集 (kubernetes网络设置)

一、概述 Master 节点NotReady 的原因就是因为没有使用任何的网络插件&#xff0c;此时Node 和Master的连接还不正常。目前最流行的Kubernetes 网络插件有Flannel、Calico、Canal、Weave 这里选择使用flannel。 二、安装flannel 1、master下载kube-flannel.yml&#xff0c;所…

Leetcode-每日一题【剑指 Offer 19. 正则表达式匹配】

题目 请实现一个函数用来匹配包含. 和*的正则表达式。模式中的字符.表示任意一个字符&#xff0c;而*表示它前面的字符可以出现任意次&#xff08;含0次&#xff09;。在本题中&#xff0c;匹配是指字符串的所有字符匹配整个模式。例如&#xff0c;字符串"aaa"与模式…

OSI参考模型及TCP/IP协议栈

一、网络概述 1.1、什么是网络&#xff1f; 1、网络的本质就是实现资源共享 2、将各个系统联系到一起&#xff0c;形成信息传递、接收、共享的信息交互平台 1.2、典型的园区网拓扑 1.3、网络历史发展&#xff0c;ARPA和ARPANET 1、1969年&#xff0c;美国国防部高级研究计…

Python下载的11种方法,一种比一种高级

概要 今天我们一起学习如何使用不同的Python模块从web下载文件。此外&#xff0c;你将下载到常规文件、web页面、Amazon S3和其他资源。 最后&#xff0c;你将学习到如何克服可能遇到的各种挑战&#xff0c;例如下载重定向的文件、下载大型文件、完成一个多线程下载以及其他策…