当前位置: 首页 > news >正文

springboot第34集:ES 搜索,nginx

#用search after解决深分页性能问题
#第一页
GET /bank/_search
{"size": 10,"sort": [{"account_number": {"order": "asc"}}]
}#第二页
GET /bank/_search
{"size": 10,"sort": [{"account_number": {"order": "asc"}}],"search_after" : [13]
}
# true\false 指定是否返回_source
GET /bank/_search?size=5
{"query": {"match_all": {}},"_source": true
}#明确指定要返回的字段
GET /bank/_search
{"_source": ["address", "balance"]
}#将两个字段排除在外
GET /bank/_search
{"_source": {"excludes": ["address", "balance"]}
}

全文检索完全匹配,必须包含分词后的所有词

GET /songs_v1/_search
{"query": {"match": {"lyrics": {"query": "All I need is someone who makes me wanna sing","operator": "and"}}}
}
41a8200c113239bfdf791fb380f7ea15.png
image.png
014ac62a0e0fa930db3e7482a0e9faa9.png
image.png
647982e9df460a96f37d692310d67b37.png
image.png
f867676d1addd5a1706b67419b62aff7.png
image.png
7473ed768065c2547c2cc55b21db42b4.png
image.png

https://www.elastic.co/cn/downloads/elasticsearch

c251b34a26a7abf9ed6302e92ae3a5f0.png
image.png

1) 前台启动:bin/elasticsearch

2) 后台启动:bin/elasticsearch -d

1) 前台停止:ctrl+c

(2) 后台停止:kill -9 elasticsearch的进程号

  • 创建用户命令

adduser elasticsearch
  • 修改用户密码

passwd elasticsearch
  • 将es解压目录的所有者授予此用户

chown -R elasticsearch elasticsearch-7.8.0
  • 切换至elasticsearch用户

su elasticsearch
  • 进入es安装目录,运行启动脚本

/usr/local/elasticsearch-7.8.0/bin/elasticsearch -d

On this node:

⁃ Create an enrollment token with bin/elasticsearch-create-enrollment-token -s node.

⁃ Uncomment the transport.host setting at the end of config/elasticsearch.yml.

⁃ Restart Elasticsearch.

• On other nodes:

⁃ Start Elasticsearch with bin/elasticsearch --enrollment-token <token>, using the enrollment token that you generated.

https://www.elastic.co/cn/kibana

288fd92c3d3e32ead17f7069d1cb29ed.png
image.png
a71cc360bec62baec4d9b0ebd1c3856b.png
image.png
import http from '@/utils/request'//axios 拦截器
// 导出
export function exportData(params) {return http({url: '/customs/reimburse/export',method: 'post',params,responseType: 'arraybuffer', // 一定要加这一配置,否则导出文件打不开})
}
let res = await exportData({ ids: 5 })let href = window.URL.createObjectURL(new Blob([res],{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }))const downloadElement = document.createElement('a')downloadElement.href = hrefdownloadElement.download = '导出报表.xlsx' //下载后文件名document.body.appendChild(downloadElement)downloadElement.click() //点击下载document.body.removeChild(downloadElement) //下载完成移除元素window.URL.revokeObjectURL(href) //释放掉blob对象
import { saveAs } from 'file-saver';axios({method: 'post',url: '接口地址',responseType: 'blob' // 必须,此内容会告知后端返回数据为blob,否则会得到string内容
}).then(response => {saveAs(new Blob([response.data], { type: 'application/octet-stream' }),`download.xlsx`)
}).catch((error) => {});
download() {const data = {  }// 要发送到后台的数据axios({ method: 'get',url: '/downFile/file', // 请求地址data: data, // 参数responseType: 'blob' //  表明返回服务器返回的数据类型  这里注意    要加上responseType}).then((res) => { // 处理返回的文件流// 注意 返回的res 无需做任何处理,有时在用框架封装的请求之后会出    现返回response.text() 等情况导致文件流下载失败。const blob = new Blob([content])const fileName = 'down.xls'const alink = document.createElement('a')alink.download = fileNamealink.style.display = 'none'alink.href = URL.createObjectURL(blob)   // 这里是将文件流    转化为一个文件地址document.body.appendChild(alink)alink.click()URL.revokeObjectURL(alink.href) // 释放URL 对象document.body.removeChild(alink)})}

首先需要导入的插件有:

npm install element-plus[1] --save,//elementplus的组件库(这个可用可不用,我用的是组件的按钮所以需要使用到他)
npm install xlsx --save//xlsx的插件

当一张表的数据达到几千万时,查询一次所花的时间会变长。业界公认MySQL单表容量在 1千万 以下是最佳状态,因为这时它的BTREE索引树高在3~5之间。

数据切分可以分为:垂直切分水平切分

一、垂直切分

垂直切分又可以分为: 垂直分库垂直分表

1、垂直分库

概念 就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。做法与大系统拆分为多个小系统类似,按业务分类进行独立划分。与"微服务治理"的做法相似,

每个微服务使用单独的一个数据库。

如图:

b238f32dc7325fa0afea4912eaccce43.jpeg

说明

一开始我们是单体服务,所以只有一个数据库,所有的表都在这个库里。

后来因为业务需求,单体服务变成微服务治理。所以将之前的一个商品库,拆分成多个数据库。每个微服务对于一个数据库。

2、垂直分表

概念 把一个表的多个字段分别拆成多个表,一般按字段的冷热拆分,热字段一个表,冷字段一个表。从而提升了数据库性能。

如图:

fd145e8e44aef61a674b6e63c1e8e779.jpeg

说明

一开始商品表中包含商品的所有字段,但是我们发现:

1.商品详情和商品属性字段较长2.商品列表的时候我们是不需要显示商品详情和商品属性信息,只有在点进商品商品的时候才会展示商品详情信息

所以可以考虑把商品详情和商品属性单独切分一张表,提高查询效率。

3、垂直切分优缺点

优点

- 解决业务系统层面的耦合,业务清晰
- 与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等
- 高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈

缺点

- 分库后无法Join,只能通过接口聚合方式解决,提升了开发的复杂度
- 分库后分布式事务处理复杂
- 依然存在单表数据量过大的问题(需要水平切分)

二、水平切分

当一个应用难以再细粒度的垂直切分或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平切分了。

水平切分也可以分为:水平分库水平分表

1、水平分库

水平分库的原因

上面虽然已经把商品库分成3个库,但是随着业务的增加一个订单库也出现QPS过高,数据库响应速度来不及,一般mysql单机也就1000左右的QPS,如果超过1000就要考虑分库。

如图

271c8484284c4589e80d6d68c88d7ffb.jpeg

2、水平分表

概念 一般我们一张表的数据不要超过1千万,如果表数据超过1千万,并且还在不断增加数据,那就可以考虑分表。

如图

3e7625d063e81bb68e991d44b0536607.jpeg

3、水平切分优缺点

优点

- 不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力
- 应用端改造较小,不需要拆分业务模块

缺点

- 跨分片的事务一致性难以保证
- 跨库的Join关联查询性能较差
- 数据多次扩展难度和维护量极大

三、数据分片规则

我们我们考虑去水平切分表,将一张表水平切分成多张表,这就涉及到数据分片的规则,比较常见的有:Hash取模分表数值Range分表一致性Hash算法分表

1、Hash取模分表

概念 一般采用Hash取模的切分方式,例如:假设按goods_id分4张表。(goods_id%4 取整确定表)

530a7519de627405006f552bd45e696b.jpeg

优点

- 数据分片相对比较均匀,不容易出现热点和并发访问的瓶颈。

缺点

- 后期分片集群扩容时,需要迁移旧的数据很难。
- 容易面临跨分片查询的复杂问题。比如上例中,如果频繁用到的查询条件中不带goods_id时,将会导致无法定位数据库,从而需要同时向4个库发起查询,
再在内存中合并数据,取最小集返回给应用,分库反而成为拖累。

2、数值Range分表

概念 按照时间区间或ID区间来切分。例如:将goods_id为11000的记录分到第一个表,10012000的分到第二个表,以此类推。

如图

4cb692a72a9d49d2aa5057255ea54f5c.jpeg

优点

- 单表大小可控
- 天然便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移
- 使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题。

缺点

- 热点数据成为性能瓶颈。
例如按时间字段分片,有些分片存储最近时间段内的数据,可能会被频繁的读写,而有些分片存储的历史数据,则很少被查询

3、一致性Hash算法

一致性Hash算法能很好的解决因为Hash取模而产生的分片集群扩容时,需要迁移旧的数据的难题

1、分布式事务问题

使用分布式事务中间件解决,具体是通过最终一致性还是强一致性分布式事务,看业务需求

2、跨节点关联查询 Join 问题

切分之前,我们可以通过Join来完成。而切分之后,数据可能分布在不同的节点上,此时Join带来的问题就比较麻烦了,考虑到性能,尽量避免使用Join查询。

表,会引入新的的问题

1、分布式事务问题

使用分布式事务中间件解决,具体是通过最终一致性还是强一致性分布式事务,看业务需求,这里就不多说。

2、跨节点关联查询 Join 问题

切分之前,我们可以通过Join来完成。而切分之后,数据可能分布在不同的节点上,此时Join带来的问题就比较麻烦了,考虑到性能,尽量避免使用Join查询。

解决这个问题的一些方法:

全局表

全局表,也可看做是 "数据字典表",就是系统中所有模块都可能依赖的一些表,为了避免跨库Join查询,可以将 这类表在每个数据库中都保存一份。这些数据通常

很少会进行修改,所以也不担心一致性的问题。

字段冗余

利用空间换时间,为了性能而避免join查询。例:订单表保存userId时候,也将userName冗余保存一份,这样查询订单详情时就不需要再去查询"买家user表"了。

数据组装

在系统层面,分两次查询。第一次查询的结果集中找出关联数据id,然后根据id发起第二次请求得到关联数据。最后将获得到的数据进行字段拼装。

3、跨节点分页、排序、函数问题

跨节点多库进行查询时,会出现Limit分页、Order by排序等问题。分页需要按照指定字段进行排序,当排序字段就是分片字段时,通过分片规则就比较容易定位到指定的分片;

当排序字段非分片字段时,就变得比较复杂了。需要先在不同的分片节点中将数据进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序,最终返回给用户。

4、全局主键避重问题

如果都用主键自增肯定不合理,如果用UUID那么无法做到根据主键排序,所以我们可以考虑通过雪花ID来作为数据库的主键

微信扫一扫功能开发前期准备

# 1、微信开放平台介绍(申请里面的网站应用需要企业资料)微信开放平台网站:https://open.weixin.qq.com/# 2、什么是appid、appsecret、授权码codeappid和appsecret是 资源所有者向申请人分配的一个id和秘钥code是授权凭证,A->B 发起授权,想获取授权用户信息,那a必须携带授权码,才可以向B获取授权信息(你要从我这里拿东西出去,就必须带身份证)#   3、先仔细阅读下微信开放平台 官方给出的微信登录开发指南:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
ea6246d2a9f4d58a94d3d6af47692da6.png
image.png
48f95aaa007c6f7a68023b9ba480db36.png
image.png

微信开放平台注册并登录:

由于创建网站应用需要企业认证,而且进行微信验证 需要 交300块钱给腾讯,对于个人开发者来说成本过高,所以只能采用别人的或者自己花钱申请。

f24c2a07a5dedd61a166e772a2ee8924.png
image.png
92bf02d8db622261eb3fdc2ee4d4aac5.png
image.png

微信授权一键登录,授权URL获取

简介:获取微信开放平台扫码链接url地址

#  增加结果工具类,JsonData;  增加application.properties配置
#  微信开放平台配置wxopen.appid=wxopen.appsecret=#重定向urlwxopen.redirect_url=http://test/pub/api/v1/wechat/user/callback1

application.properties

# 微信相关配置:
# 公众号
wxpay.appid=wx5beXXXXX7cdd40c
wxpay.appsecret=55480123XXXXXXXXb382fe548215e9
# 微信开放平台配置
wxopen.appid=wx025XXXXX9a2d5b
wxopen.appsecret=f5b6730c59XXXXXXX5aeb8948a9f3
# 重定向url 重定向到首页,并根据code拿到token,从而获取微信扫码用户的登录信息
# 这个域名是别人认证过的,只能拿来做个参考,不能自己回调
wxopen.redirect_url=http://XXXX.cn/XXXX/wechat/user/callback
public class WeChatConfig {// 微信开放平台二维码连接// 待填充参数:appid=%s    redirect_uri=%s     state=%sprivate final static String OPEN_QRCODE_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect";// 微信开放平台获取access_token地址// 待填充参数:appid=%s    secret=%s     code=%sprivate final static String OPEN_ACCESS_TOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";// 获取用户信息// 待填充参数:access_token=%s    openid=%sprivate final static String OPEN_USER_INFO_URL ="https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";@Value("${wxpay.appid}")private String appid;// 微信appid@Value("${wxpay.appsecret}")private String appsecret;// 微信秘钥@Value("${wxopen.appid}")private String openAppid;// 开放平台appid@Value("${wxopen.appsecret}")private String openAppsecret;// 开放平台秘钥@Value("${wxopen.redirect_url}")private String openRedirectUrl;// 开放平台回调地址public static String getOpenUserInfoUrl() {return OPEN_USER_INFO_URL;}public static String getOpenAccessTokenUrl() {return OPEN_ACCESS_TOKEN_URL;}public static String getOpenQrcodeUrl() {return OPEN_QRCODE_URL;}
@ResponseBody@GetMapping("/login_url")@CrossOriginpublic JsonData weChatloginUrl(@RequestParam(value = "state", required = true) String state) throws UnsupportedEncodingException {/*** state :* 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防* 止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随* 机数加session进行校验,例如:state=3d6be0a4035d839573b04816624a415e*/// 获取开放平台重定向地址String redirectUrl = weChatConfig.getOpenRedirectUrl();// 微信开放平台文档规定,需要先对回调的url使用urlEncode对链接进行编码处理String callbackUrl = URLEncoder.encode(redirectUrl, "GBK");// 为扫码链接qrcodeUrl填充参数 appid=%s redirect_uri=%s state=%s 到 OPEN_QRCODE_URLString qrcodeUrl = String.format(weChatConfig.getOpenQrcodeUrl(), weChatConfig.getOpenAppid(), callbackUrl, state);// 构建json对象返回return JsonData.buildSuccess(qrcodeUrl);}

UserMapper.java

/*** @Auther: csp1999* @Date: 2020/08/28/14:31* @Description: User Mapper*/
@Repository
public interface UserMapper {// 保存微信登录用户基本信息Integer saveUser(@Param("user") User user);// 根据openid 查询User findByUserOpenid(String openid);// 根据主键id 查询User findByUserId(Integer id);// 更新微信用户基本信息void updateUser(@Param("user") User user);
}
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate WeChatConfig weChatConfig;@Autowiredprivate UserMapper userMapper;/*** 通过code并附带appId appSecret 向微信方索取access_token* 并通过 access_token 获得用户基本信息(昵称,地址,头像等) 保存数据到数据库* @param code* @return*/@Overridepublic User saveWeChatUser(String code) {// 通过 code 获取 access_tokenURLString accessTokenUrl = String.format(WeChatConfig.getOpenAccessTokenUrl(),weChatConfig.getOpenAppid(),weChatConfig.getOpenAppsecret(),code);// 通过 access_tokenURL 向微信开放平台发送请求, 获取access_tokenMap<String, Object> baseMap = HTTPUtils.doGet(accessTokenUrl);if (baseMap == null || baseMap.isEmpty()) {return null;}// 拿到 accessTokenString accessToken = (String) baseMap.get("access_token");String openId = (String) baseMap.get("openid");// 通过accessToken 得到向微信开放平台发送 用于获取用户基本信息的请求的urlString userInfoUrl = String.format(WeChatConfig.getOpenUserInfoUrl(), accessToken, openId);// 获取access_tokenMap<String, Object> baseUserMap = HTTPUtils.doGet(userInfoUrl);if (baseUserMap == null || baseUserMap.isEmpty()) {return null;}// 拿到用户基本信息String nickname = (String) baseUserMap.get("nickname");// 微信用户名System.out.println(baseUserMap.get("sex"));Double sexTemp = (Double) baseUserMap.get("sex");// 微信用户性别System.out.println(sexTemp);int sex = sexTemp.intValue();// Double => IntegerString province = (String) baseUserMap.get("province");// 微信用户所在省String city = (String) baseUserMap.get("city");// 微信用户所在市String country = (String) baseUserMap.get("country");// 微信用户所在国家String headimgurl = (String) baseUserMap.get("headimgurl");// 微信用户头像StringBuilder builder = new StringBuilder(country).append("||").append(province).append("||").append(city);String finalAddress = builder.toString();try {//解决中文乱码nickname = new String(nickname.getBytes("ISO-8859-1"), "UTF-8");finalAddress = new String(finalAddress.getBytes("ISO-8859-1"), "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}User user = new User();user.setName(nickname).setHeadImg(headimgurl).setCity(finalAddress).setOpenid(openId).setSex(sex).setCreateTime(new Date());User findUser = userMapper.findByUserOpenid(openId);if (findUser != null) { //如果数据库中已经有该微信用户信息,更新微信用户最新基本信息,并直接返回即可userMapper.updateUser(user);return user;}// 否则继续往下执行userMapper.saveUser(user);// 保存用户信息return user;}
}

通过扫码登录跳转页面携带的参数code而获取封装有user信息的token

@GetMapping("/user/callback")public String weChatUserCallback(@RequestParam(value = "code", required = true) String code,String state, // 根据实际情况而定可用作保存当前页面地址RedirectAttributes redirectAttributes){User user = userService.saveWeChatUser(code);System.out.println("user:"+user);redirectAttributes.addFlashAttribute("user",user);String token = null;if (user != null){// jwt 生成 tokentoken = JWTUtils.createJsonWebToken(user);redirectAttributes.addFlashAttribute("token",token);redirectAttributes.addFlashAttribute("state",token);return "redirect:/test/test03?token="+token;// 将token 拼接于url ,便于拦截器过滤}else{return "redirect:/error/error";}}

微信网页扫码支付简介

#   1、扫码支付文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=2_2#   2、名称理解appid:公众号唯一标识appsecret:公众号的秘钥mch_id:商户号,申请微信支付的时候分配的key:支付交易过程生成签名的秘钥,设置路径 微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全-->密钥设置#   3、和微信支付交互方式1、post方式提交2、xml格式的协议3、签名算法MD54、交互业务规则 先判断协议字段返回,再判断业务返回,最后判断交易状态5、接口交易单位为 分6、交易类型:JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付

微信支付业务流程说明:

(1)商户后台系统根据用户选购的商品生成订单。

(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;

(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。

(4)商户后台系统根据返回的code_url生成二维码。

(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。

(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。

(8)微信支付系统根据用户授权完成支付交易。

(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。

(12)商户确认订单已支付后给用户发货。

微信扫码支付之统一下单接口开发之订单增删改查

统一下单微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

c1f76cebb86a2c4db99d5933b60d7d16.png
image.png
DROP TABLE IF EXISTS `video_order`;CREATE TABLE `video_order` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`openid` varchar(32) DEFAULT NULL COMMENT '用户标示',`out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',`state` int(11) DEFAULT NULL COMMENT '0表示未支付,1表示已支付',`create_time` datetime DEFAULT NULL COMMENT '订单生成时间',`notify_time` datetime DEFAULT NULL COMMENT '支付回调时间',`total_fee` int(11) DEFAULT NULL COMMENT '支付金额,单位分',`nickname` varchar(32) DEFAULT NULL COMMENT '微信昵称',`head_img` varchar(128) DEFAULT NULL COMMENT '微信头像',`video_id` int(11) DEFAULT NULL COMMENT '视频主键',`video_title` varchar(128) DEFAULT NULL COMMENT '视频名称',`video_img` varchar(256) DEFAULT NULL COMMENT '视频图片',`user_id` int(11) DEFAULT NULL COMMENT '用户id',`ip` varchar(64) DEFAULT NULL COMMENT '用户ip地址',`del` int(5) DEFAULT '0' COMMENT '0表示未删除,1表示已经删除',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;LOCK TABLES `video_order` WRITE;
public class CommonUtils {// 生成 uuid, 即用来标识一笔单,也用做 nonce_strpublic static String generateUUID() {return UUID.randomUUID().toString().replaceAll("-", "")// 去掉默认自带的 - 分隔符.substring(0, 32);// 截取 32 位}// MD5 加密工具类public static String getMD5String(String data) {try {// 获取MD5 加密实例MessageDigest md = MessageDigest.getInstance("MD5");// 获得数组对象byte[] array = md.digest(data.getBytes("UTF-8"));// 拼接加密字符串StringBuilder builder = new StringBuilder();for (byte item : array) {builder.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return builder.toString().toUpperCase();// 所有字母大写} catch (Exception exception) {System.out.println("MD5加密算法出现异常...");}return null;}
}
# 1、统一下单参数需要微信签名,签名规则如下
-   文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
-   签名生成的通用步骤如下:第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置<---  参数:SortedMap<String, String> params = new TreeMap<>();  params.put("appid", wxPayConfig.getAppId());  //公众账号ID  params.put("mch_id", wxPayConfig.getMchId());  //商户号  params.put("nonce_str", CommonUtil.generateNonceStr());  //随机字符串  params.put("body", videoOrder.getVideoTitle());  // 商品描述  //商户订单号,商户系统内部订单号,要求 32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一params.put("out_trade_no", videoOrder.getOutTradeNo()); params.put("total_fee", videoOrder.getTotalFee().toString());  //标价金额 分params.put("spbill_create_ip", videoOrder.getIp());  //通知地址    params.put("notify_url", wxPayConfig.getDomain()+wxPayConfig.getCallbackUrl()); //交易类型 JSAPI 公众号支付 NATIVE 扫码支付 APP APP支付params.put("trade_type", "NATIVE");  //生成签名String sign = WXPayUtil.createSign(params, wxPayConfig.getKey());params.put("sign", sign); //参数转xmlString requestXMl = WXPayUtil.mapToXml(params);生成签名后,通过工具去校验https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=20_1
--->
#    2、测试地址:localhost:8081/api/v1/order/add?video_id=2#    3、课程测试签名结果:sign: 85118C91DFCB052FB02AC183BF3D57D2
#微信相关配置:
#公众号
wxpay.appid=wx252XXXXX1xs9h
wxpay.appsecret=qm4i2u43oXXXXXXXX7055s8c99a8#微信开放平台配置
wxopen.appid=wx025XXXXXXa2d5b
wxopen.appsecret=f5b6730c59XXXXXXXXeb8948a9f3
#重定向url 重定向到首页,并根据code拿到token,从而获取微信扫码用户的登录信息
#这个域名是别人认证过的,只能拿来做个参考,不能自己回调
wxopen.redirect_url=http://XXXXXXXXXXXXXX.cn/xdclass/wechat/user/callback#微信商户平台 商户id 订单秘钥 回调地址
wxpay.mer_id=8XXXXXX068
wxpay.key=MbZL0DiXXXXXXXXX5S51MK2
wxpay.callback=http://XXXXXXXXXXXXXXX.cn/xdclass/
public String unifiedOrder(VideoOrder videoOrder) throws Exception {WXPay wxPay = new WXPay();// 使用 map 封装 订单参数以及微信支付相关参数SortedMap<String, String> data = new TreeMap<>();data.put("appid", weChatConfig.getAppid());// 公众账号ID: 微信支付分配的公众账号ID(企业号corpid即为此appId)data.put("mch_id", weChatConfig.getMchId());// 商户号: 微信支付分配的商户号data.put("nonce_str", CommonUtils.getUUID());// 随机字符串: 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"data.put("body", videoOrder.getVideoTitle());// 商品描述data.put("out_trade_no", videoOrder.getOutTradeNo());// 商户订单号: 要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一。data.put("total_fee", videoOrder.getTotalFee().toString());// 标价金额: 单位为分data.put("spbill_create_ip", videoOrder.getIp());// 下单用户的客户端IPdata.put("notify_url", weChatConfig.getPayCallbackUrl());// 通知地址: 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。data.put("trade_type", "NATIVE");// 交易类型: 此处指定为扫码支付// 生成 sign 签名String sign = WXPayUtil.generateSignature(data, weChatConfig.getKey());data.put("sign", sign);// 签名: 微信返回的签名值System.out.println("---------------------- xml 数据如下:----------------------");// map 转 xmlString payXmlData = WXPayUtil.mapToXml(data);System.out.println(payXmlData);// 统一下单,发送POST请求微信后台统一下单接口:https://api.mch.weixin.qq.com/pay/unifiedorder 获取返回xml格式的字符串 orderStrString orderStr = HTTPUtils.doPost(WeChatConfig.getUnifiedOrderUrl(), payXmlData, 4000);System.out.println("---------------------- 请求统一下单接口返回的 orderStr 数据如下:----------------------");System.out.println(orderStr);if (null == orderStr) {return null;}// 将统一下单接口返回的xml格式的字符串 orderStr 转成 mapMap<String, String> unifiedOrderMap = WXPayUtil.xmlToMap(orderStr);System.out.println("---------------------- 转换成 map 的 orderStr 数据如下:----------------------");// 这样做的目的是解决打印出的对象中文乱码问题,无法阅读错误提示信息String string = new String(unifiedOrderMap.toString().getBytes("ISO-8859-1"), "UTF-8");System.out.println(string);if (unifiedOrderMap != null) {System.out.println("支付二维码url:" + unifiedOrderMap.get("code_url"));return unifiedOrderMap.get("code_url");// 获取统一下单接口返回的 code_url(支付二维码图片的url) 数据}// 否则返回nullreturn null;}
932a72c74a1743a2dae962296ccb6711.png
image.png

Nginx文件的默认安装位置如下。

目录说明
/usr/sbin/nginx存放主程序
/etc/nginx存放配置文件
/usr/share/nginx存放静态文件
/var/log/nginx存放日志

执行以下命令打开Nginx配置文件nginx.conf。

sudo vi /etc/nginx/nginx.conf
server {listen       80 default_server;listen       [::]:80 default_server;server_name  _;root         /usr/share/nginx/html;# Load configuration files for the default server block.include /etc/nginx/default.d/*.conf;location / {proxy_pass https://bucketname.oss-cn-beijing-internal.aliyuncs.com;proxy_set_header Host $host;}
}
sudo cd /usr/sbin/
sudo ./nginx

https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html?spm=a2c6h.12873639.article-detail.6.7d3c4912zCqPuf

https://open.weixin.qq.com/cgi-bin/showdocument?spm=a2c6h.12873639.article-detail.5.7d3c4912zCqPuf&action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN

加群联系作者vx:xiaoda0423

仓库地址:https://github.com/webVueBlog/JavaGuideInterview

参考资料

[1]

element-plus: https://so.csdn.net/so/search?q=element-plus&spm=1001.2101.3001.7020

相关文章:

springboot第34集:ES 搜索,nginx

#用search after解决深分页性能问题 #第一页 GET /bank/_search {"size": 10,"sort": [{"account_number": {"order": "asc"}}] }#第二页 GET /bank/_search {"size": 10,"sort": [{"account_numb…...

微信小程序中的分包使用介绍

一、分包的好处 可以优化小程序首次启动的下载时间 在多团队共同开发时可以更好的解耦协作 主包&#xff1a;放置默认启动页面/TabBar 页面&#xff0c;公共资源/JS 脚本 分包&#xff1a;根据开发者的配置进行划分 限制&#xff1a;所有分包大小不超过 20M&#xff0c;单…...

【云原生】K8S二进制搭建二:部署CNI网络组件

目录 一、K8S提供三大接口1.1容器运行时接口CRI1.2云原生网络接口CNI1.3云原生存储接口CSI 二、Flannel网络插件2.1K8S中Pod网络通信2.2Overlay Network2.3VXLAN2.4Flannel 三、Flannel udp 模式的工作原理3.1ETCD 之 Flannel 提供说明 四、vxlan 模式4.1Flannel vxlan 模式的工…...

【iOS】—— 离屏渲染

文章目录 离屏渲染UIView和CALayer关系GPU屏幕渲染有两种方式:产生离屏渲染的原因&#xff1a;既然离屏渲染这么耗性能,为什么有这套机制呢?什么情况会离屏渲染&#xff1f;既然离屏渲染这么不好&#xff0c;为什么我们还要强制开启呢&#xff1f;如何避免离屏渲染&#xff1f…...

基于人工智能的中医图像分类系统设计与实现

华佗AI 《支持中医,永远传承古老文化》 本存储库包含一个针对中药的人工智能图像分类系统。该项目的目标是通过输入图像准确识别和分类各种中草药和成分。 个人授权许可证 版权所有 2023至2050特此授予任何获得华佗AI应用程序(以下简称“软件”)副本的人免费许可,可根据以…...

spring security + oauth2 使用RedisTokenStore 以json格式存储

1.项目架构 2.自己对 TokenStore 的 redis实现 package com.enterprise.auth.config;import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis…...

css position: sticky;实现上下粘性布局,中间区域滚动

sticky主要解决的问题 1、使用absolute和fixed中间区域需要定义高度2、使用absolute和fixed底部需要写padding-bottom 避免列表被遮挡住一部分&#xff08;底部是浮窗的时候&#xff0c;需要动态的现实隐藏&#xff09; <!DOCTYPE html> <html lang"en"&…...

解密HTTP代理爬虫中的IP代理选择与管理策略

在当今数据驱动的世界中&#xff0c;HTTP代理爬虫作为一项重要的数据采集工具&#xff0c;其成功与否往往取决于IP代理的选择与管理策略。作为一家专业的HTTP代理产品供应商&#xff0c;我们深知IP代理在数据采集中的重要性。在本文中&#xff0c;我们将分享一些关于HTTP代理爬…...

pytorch入门

详细安装教程和环境配置可以看&#xff1a;Python深度学习&#xff1a;安装Anaconda、PyTorch&#xff08;GPU版&#xff09;库与PyCharm_哔哩哔哩_bilibili 跟学课程&#xff1a;B站我是土堆 pytorch中两个实用函数&#xff1a; dir()&#xff1a;打开 help():说明书…...

Redis | 主从模式

Redis | 主从模式 1. 简介 Redis主从模式&#xff08;Replication&#xff09;是Redis提供的一种数据备份和高可用性解决方案。通过主从复制&#xff0c;可以将一个Redis服务器的数据复制到其他多个从服务器&#xff0c;从而实现数据的备份和读写分离&#xff0c;提高系统的性…...

C# Blazor 学习笔记(8):row/col布局开发

文章目录 前言相关文章代码row和col组件B_rowB_col结构 使用 前言 可能是我用的element ui和 uView这种第三方组件用的太多了。我上来就希望能使用这些组件。但是目前Blazor目前的生态其实并不完善&#xff0c;所以很多组件要我们自己写。 我们对组件的要求是 我们在组件化一共…...

金融供应链智能合约 -- 智能合约实例

前提 Ownable:监管者合约,有一个函数能转让监管者。 SupplyChainFin:供应链金融合约,银行、公司信息上链&#xff0c;公司和银行之间的转账。 发票&#xff1a;记录者交易双方和交易金额等的一种记录数据。如:我在超市买了一瓶水,超市给我开了一张发票。 Ownable // SPDX-…...

论文《Contrastive Meta Learning with Behavior Multiplicity for Recommendation》阅读

论文《Contrastive Meta Learning with Behavior Multiplicity for Recommendation》阅读 论文概况论文主要贡献Background & Motivation方法论单行为图神经网络&#xff08;Behavior-aware GNN&#xff09;多行为对比学习元对比编码模型训练 实验部分论文总结 论文概况 今…...

K8S 部署 RocketMQ

文章目录 添加模板部署本地访问 集群使用 kubesphere 作为工具 添加模板 添加 helm 模板 helm repo add rocketmq-repo https://helm-charts.itboon.top/rocketmq helm repo update rocketmq-repo编写 value.yaml 文件 配置主从节点的个数&#xff0c;例子为单节点 broker:…...

[Docker]入门之docker-compose

一&#xff0c;Docker-compose简介 1&#xff0c;Docker-compose简介 Docker-Compose项目是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排。 Docker-Compose将所管理的容器分为三层&#xff0c;分别是工程&#xff08;project&#xff09;&#xff0c…...

SAP ABAP中使用函数ALSM_EXCEL_TO_INTERNAL_TABLE读取EXCEL中不同的SHEET数据

SAP提供了标准的读取EXCEL的函数&#xff08;ALSM_EXCEL_TO_INTERNAL_TABLE&#xff09;&#xff0c;但是此标准函数无法满足对同一EXCEL 进行不同SHEET的数据读取&#xff0c;一下方法就是教你如何通过修改程序来实现ALSM_EXCEL_TO_INTERNAL_TABLE读取多个SHEET&#xff1b; …...

Rust 编程小技巧摘选(6)

目录 Rust 编程小技巧(6) 1. 打印字符串 2. 重复打印字串 3. 自定义函数 4. 遍历动态数组 5. 遍历二维数组 6. 同时遍历索引和值 7. 迭代器方法的区别 8. for_each() 用法 9. 分离奇数和偶数 10. 判断素数&#xff08;质数&#xff09; Rust 编程小技巧(6) 1. 打印…...

如何保证Redis缓存和数据库的一致性问题

熟练掌握Redis缓存技术&#xff1f; 那么请问Redis缓存中有几种读写策略&#xff0c;又是如何保证与数据库的一致性问题 今天来聊一聊常用的三种缓存读写策略 Cache Aside Pattern Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式&#xff0c;比较适合读请求比…...

【数据分析入门】人工智能、数据分析和深度学习是什么关系?如何快速入门 Python Pandas?

目录 一、前言二、数据分析和深度学习的区别三、人工智能四、深度学习五、Pandas六、Pandas数据结构6.1 Series - 序列6.2 DataFrame - 数据框 七、输入、输出7.1 读取/写入CSV7.2 读取/写入Excel7.3 读取和写入 SQL 查询及数据库表 八、调用帮助九、选择(这里可以参考上一篇文…...

JavaScript 里三个点 ... 的用法

// table表头数据let tableHeadData deepClone(data);let tableCacheData [];//表格缓存对比if (!parent && isCacheHeadData) {// 缓存数据keylet tableCacheKey ${window.location.pathname}-${$self.attr(id)}if (localStorage.getItem(tableCacheKey)) {//根据缓…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...