在使用php对接对外接口时,需要使用到curl,但是在这个过程中遇到了一个问题:当前curl版本是7.29,建议升级至7.40或更高版本。
错误提示是curl版本低于7.40导致失败
先到服务器查看当前的curl版本:
显示的是7.29
解决办法:升级curl版本
yum install wget gcc openssl-devel -y
wget https://curl.haxx.se/download/curl-7.67.0.tar.gz
gunzip -c curl-7.67.0.tar.gz | tar xvf -
cd curl-7.67.0
./configure --with-ssl --prefix=/usr/local/curl
make
make install
echo "export PATH=/usr/local/curl/bin:$PATH" >> /etc/profile
source /etc/profile
curl --version
原文链接:https://blog.csdn.net/shida_csdn/article/details/117786933
本文是使用htinkphp6 + 阿里大于发送短信的使用案例
在接口中使用到了”alibabacloud/sdk”: “^1.8” 的sdk,需要在此之前事先通过composer安装
/**
* 发送短信
* @param string $mobile 手机号码
* @param array $templateParam 设置短信模板参数
* @param string $templateCode 模板code
* @return bool 是否发送成功
*/
public function sendVerifySms($mobile, $templateParam, $templateCode)
{
try {
//获取配置信息
$config = [
'access_key_id' => 'xxx',
'access_key_secret' => 'xxxx',
'sign_name' => 'xx平台',
'template_code' => [
'verify' => 'xxx',
]
];
// 创建客户端
AlibabaCloud::accessKeyClient($config['access_key_id'], $config['access_key_secret'])
->regionId('cn-hangzhou')
->asDefaultClient();
//调用阿里云短信发送接口
$result = AlibabaCloud::rpc()
->product('Dysmsapi')
//可根据实际情况选择不同的服务地区
->regionId('cn-hangzhou')
->version('2017-05-25')
->action('SendSms')
->method('POST')
->host('dysmsapi.aliyuncs.com')
->options([
'query' => [
'RegionId' => 'cn-hangzhou',
'PhoneNumbers' => $mobile,
'SignName' => $config['sign_name'],
'TemplateCode' => $templateCode,
'TemplateParam' => json_encode($templateParam),
],
])
->request();
//判断短信发送状态
if ($result->toArray()['Code'] == 'OK') {
return true;
} else {
return false;
}
} catch (ClientException|ServerException $e) {
return false;
}
}
功能示例图如下
用户点击一键登录,用户点击允许后获取用户手机号码
前端代码:
<button class="confirm-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber" style="background: linear-gradient(55deg, #377ffd, #5db7fb);">
一键登录
</button>
js代码
// 手机号授权
onGetPhoneNumber(e){
// 用户已授权
if (e.detail.errMsg === 'getPhoneNumber:ok') {
const iv = e.detail.iv
const code = e.detail.code
const encryptedData = e.detail.encryptedData
// 将 encryptedData 和 iv 发送到后端解密
this.quickLogin(encryptedData, iv)
} else {
// 用户拒绝授权
uni.showToast({
title: '授权失败',
icon: 'error'
})
}
},
quickLogin(_encryptedData=null, _iv=null){
const that = this
uni.login({
provider: 'weixin',
success: function(loginRes) {
uni.getUserInfo({
success: (userInfoRes) => {
const iv = _iv ?? userInfoRes.iv
const encryptedData = _encryptedData ?? userInfoRes.encryptedData
http.post('/user/login', {
iv: iv,
register: true,
code: loginRes.code,
encryptedData: encryptedData,
}).then(res => {
if(res.code == 0){
//登录成功,设置用户信息
}else{
uni.showModal({
title: '登录失败',
content: res.msg,
showCancel: false,
confirmText: '确定',
})
}
})
},fail: (res) =>{
console.log('失败', res)
}
})
},fail: (res) =>{
console.log('失败', res)
}
})
}
后端使用的是thinkphp6,和easyWeChat的SKD4.0版本
文档:https://easywechat.com/4.x/
/**
* 一键登录
*/
public function quickLogin()
{
$param = $this->request->param();
$code = $param['code'];
$iv = $param['iv'];
$encryptedData = $param['encryptedData'];
$app = Factory::miniProgram($config);
//根据code获取session_key
$wxUserInfo = $this->app->auth->session($code);
//消息解密。比如获取电话等功能,信息是加密的,需要解密。
$data = $this->app->encryptor->decryptData($wxUserInfo['session_key'], $iv, $encryptedData);
//输出
dump($data);
// [
// "phoneNumber" => "手机号"
// "purePhoneNumber" => "手机号"
// "countryCode" => "86"
// "watermark" => array:2 [
// "timestamp" => 1721456497
// "appid" => ""
// ]
// ]
//登录逻辑...
}
在vue中,我们可能需要再created中获取到dom元素进行操作,但是呢在created中获取dom元素会获取不到,应为dom还没有渲染完,找不到dom元素导致获取不到,所以可以在 this.$nextTick()延迟加载中去获取dom元素
<div ref="audioPlayer">
我是元素
</div>
created() {
//在这里获取不到div元素,应为dom还没有渲染加载完,找不到div
//const audioPlayer = this.$refs.audioPlayer;
//延迟执行
this.$nextTick(() => {
//在这里才能获取到,这时dom渲染加载完了
const audioPlayer = this.$refs.audioPlayer;
});
},
created() {
this.$nextTick(() => {
console.log(4663)
this.playAudio()
});
},
场景:因为微信小程序的文件包大小限制,只能上传2M及以下的文件包,超过2M时就无法上传
解决方法:需要进行分包
微信文档分包教程文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html
https://developers.weixin.qq.com/community/develop/doc/00040e5a0846706e893dcc24256009
当前使用的是uniapp,一下教程是uniapp的分包教程
uniapp分包文档
https://uniapp.dcloud.net.cn/collocation/manifest.html#%E5%85%B3%E4%BA%8E%E5%88%86%E5%8C%85%E4%BC%98%E5%8C%96%E7%9A%84%E8%AF%B4%E6%98%8E
1、在对应平台的配置下添加”optimization”:{“subPackages”:true}开启分包优化
2、pages.json里进行分包
pages是默认文件路径,subPackages是需要分包的文件路径
3、分包之后在行进打包
分包成功,这里可以看到主包已经小于2M,即可上传成功
当前使用的php框架是fastadmin,nginx版本是1.20
问题描述:
在宝塔面板添加站点并设置thinkphp伪静态后,网站前台能正常打开(例如:www.xxx.com/index.php/article/info),但是后台无法打开(www.xxx.com/admin.php/index/login)
但是以为一直是伪静态问题,不支持pathinfo导致的,查询很多资料后发现是nginx的php.conf配置问题
(示例:比如我的nginx下目录目录BtSoft\nginx\conf\php)
找到nginx下的conf下的对应php.conf配置,将try_files $uri =404;注释掉(我用的php版本是php73)
保存后重启nginx即可
1、pages.json页面找到页面配置,添加 enablePullDownRefresh
{
"path": "pages/order/list-mine",
"style": {
"navigationBarTitleText": "订单列表",
"enablePullDownRefresh": true ,//开始下拉刷新
"backgroundTextStyle": "dark" // 下拉 loading 的样式,仅支持 dark/light
}
},
2、在 js 中定义 onPullDownRefresh 处理函数(和onLoad等生命周期函数同级),监听该页面用户下拉刷新事件。
//下拉刷新
onPullDownRefresh(){
//下拉请求接口
this.getOrder();
},
3、在接口执行成功后关闭下拉动画
getOrder() {
let query = {}
query = {
keyword: this.keyword
}
http.get("/order/query", query).then(res => {
if (res.code == 0) {
this.order = res.data.rows
}
//停止下拉刷新显示
uni.stopPullDownRefresh();
})
},
npm config set registry https://registry.npmmirror.com
1、第一种
传递参数
let data = [
title: '标题',
state: 1
]
uni.$emit('responseData', data); // 发送全局事件,传递数据
获取参数
onShow() {
const that = this;
uni.$on('responseData', function(data) { // 监听全局事件,并获取数据
// 使用 data 进行后续操作
that.responseData = data.data
if(that.signature_type == 1){
that.data.signature = that.responseData.url
}else{
that.data.parent_signature = that.responseData.url
}
});
}
2、第二种,设置缓存的方式
uni.setSystemInfoSync("responseData", data)
获取参数
uni.getSystemInfoSync("responseData")
3、第三种,url传递(如果是多个参数的话,或者参数不固定需要一个个拼接,有弊端)
uni.navigateTo({
url: '/pages/userCourse/detail?state=1&id=10'
})
获取参数
onLoad(param) {
this.id=param.id
this.state=param.state
},
<u-upload
:file-list="picList"
:action="url"
:before-upload="beforeUpload"
:before-remove="beforeRemove"
></u-upload>
js:
beforeUpload(index, list) {
this.picList = []
// console.log('list',list);
let i = 0;
list.map(async item => {
this.picList.push({
url: item.url
})
if(i == index){
let result = await this.uploadFilePromise(item.url);
console.log(666,result);
let data = JSON.parse(result)
//保存图片
this.order.car_check_img.push(data.data.img)
}
i++;
})
console.log('照片列表',this.picList);
},
// 上传图片
uploadFilePromise(url) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: this.apiUrl + "/upload/imageOne", // 仅为示例,非真实的接口地址
filePath: url,
name: "file",
formData: {
typeEnum: "IMAGE",
},
header: {
'X-CSRF-TOKEN': token.getToken('TOKEN') //自行定义token,根据接口要求是否需要加上
},
success: (res) => {
resolve(res.data);
},
});
});
},
//移除图片
beforeRemove(index, fileList){
// 确保索引在有效范围内
if (index >= 0 && index < this.order.car_check_img.length) {
// 使用 splice 方法移除 this.order.car_check_img 中的对应项
this.order.car_check_img.splice(index, 1);
}
// 如果需要,您还可以从 picList 中移除对应的项
// 假设 fileList 和 picList 是同步的,并且 fileList[index] 是要移除的文件
if (index >= 0 && index < this.picList.length) {
this.picList.splice(index, 1);
}
}
<u-upload
:file-list="picList" //默认显示的图片列表
:action="url"
:before-upload="beforeUpload" //为图片上传之前的回调
:before-remove="beforeRemove" //为图片删除之前的回调
></u-upload>
js方法:
beforeUpload(index, list) {
this.picList = []
// console.log('list',list);
list.map(async item => {
this.picList.push({
url: item.url
})
let result = await this.uploadFilePromise(item.url);
console.log(result);
})
console.log('照片列表',this.picList);
},
// 上传图片
uploadFilePromise(url) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: apiUrl + "/admin/upload/imageOne", // 仅为示例,非真实的接口地址
filePath: url,
name: "file",
formData: {
typeEnum: "IMAGE",
},
header: {
'X-CSRF-TOKEN': token.getToken('TOKEN') //自行定义token,根据接口要求是否需要加上
},
success: (res) => {
resolve(res.data);
console.log(res);
},
});
});
},
微信小程序用户授权获取用户位置信息
1、配置文件manifest.json,选择“源码视图”,添加配置
2、上代码
getLocationInfo(){
uni.getLocation({
type: 'gcj02',
success: function(res) {
const latitude = res.latitude; // 纬度
const longitude = res.longitude; // 经度
// 调用逆地理编码API将经纬度转换为地址
uni.request({
url: 'https://apis.map.qq.com/ws/geocoder/v1/',
data: {
location: `${latitude},${longitude}`,
key: '你的key' //腾讯地图开放平台上申请并获得了Web服务API密钥
},
success: function(response) {
const address = response.data.result.address;
// 在此处处理获取到的地址信息
console.log("在此处处理获取到的地址信息" + address);
this.form.get_car_address = address
}
})
}
})
}
3、如果没有key,去腾讯地图开放平台上申请并获得了Web服务API密钥
地址:https://lbs.qq.com/dev/console/home
4、常见问题
4.1 此key未开启WebserviceAPI功能
4.2 getLocation:fail the api need to be declared in the requiredPrivateInfos fie
这个问题查看第一步,没有配置manifest.json导致的
当前mybaits-plus版本为3.5.3
springboot启动报错Bean named ‘ddlApplicationRunner’ is expected to be of type ‘org.springframework.boot.Runner’ but was actually of type ‘org.springframework.beans.factory.support.NullBean’
解决:在启动类加上方法
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
/**
* 解决:Bean named 'ddlApplicationRunner' is expected 的报错
* @param ddlList
* @return
*/
@Bean
public DdlApplicationRunner ddlApplicationRunner(@Autowired(required = false) List ddlList) {
return new DdlApplicationRunner(ddlList);
}
1、真机调试时勾选局域网模式
2、修改api接口地址为本地局域网ip地址
3、修改接口地址
4、关闭windows防火墙(重要!重要!重要)
5、手机要和电脑使用的同一个网络wifi
自行查看手机连接的wifi,查看内容ip地址是否是同一个网络段
ok,搞定
更新composer时报错:Root composer.json requires PHP extension ext-http * but it is missing from your system. Install or enable PHP’s http extension
错误原文:
Your lock file does not contain a compatible set of packages. Please run composer update.
Problem 1
Root composer.json requires PHP extension ext-http * but it is missing from your system. Install or enable PHP’s http extension.
To enable extensions, verify that they are enabled in your .ini files:
D:xxx\php\74\php.ini
You can also run php —ini inside terminal to see which files are used by PHP in CLI mode.
将composer.json里的”ext-http”: ““ 删除
将composer.lokc里的”ext-http”: ““ 删除
导航栏结构代码
<u-tabbar v-model="current" :list="list" @change="navigatorTo"></u-tabbar>
Js代码
export default {
data() {
return {
current: 0,
list: [{
iconPath: "home",
selectedIconPath: "home-fill",
text: '首页',
customIcon: false,
pagePath: '/pages/business/home/home'
},
{
iconPath: "photo",
selectedIconPath: "photo-fill",
text: '站点',
customIcon: false,
pagePath: '/pages/business/site/list'
}
],
}
},
methods: {
navigatorTo(index) {
let url = this.list[index].pagePath
uni.navigateTo({
url: url,
})
}
},
}
uniapp页面跳转方式和区别
第一:wx.navigatorTo //新页面打开,默认会有返回按钮
第二:wx.redirectTo //当前页面打开,默认无返回按钮
第三:wx.switchTab //只能用于跳转到tabbar页面,并关闭其他非tabbar页面,tabbar之间做切换
第四:wx.reLaunch //关闭所有页面,打开应用内某个页面,默认无返回按钮】
隐藏左上角返回按钮和首页按钮
onShow() {
uni.hideHomeButton()
//wx.hideHomeButton()
},
例如从A小程序跳转到B小程序
wx.navigateToMiniProgram({
appId: 'xxxxx', //跳转到的小程序appid
path: '/page/home/index/index?wxrefid=xxxxx&tab=1&appid=xxxxx', //跳转到的小程序页面地址
//develop开发版;trial体验版;release正式版
envVersion: 'release',
success(res) {
// 打开成功
console.log("跳转小程序成功!",res);
}
})
<div class="avatar">
<image mode="" :src="data.profilePhoto || '../../static/index/1.jpg'"></image>
<text class="avatar-btn" @click="chooseImage">点击上传头像</text>
</div>
// 选择头像
chooseImage() {
uni.chooseImage({
count: 1, // 最多选择的图片数量,可以根据需求修改
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
// 选择图片成功
const tempFilePaths = res.tempFilePaths
this.uploadAvatar( tempFilePaths[0])
},
fail: (err) => {
// 选择图片失败
console.log('选择图片失败:', err.errMsg);
}
})
},
// 上传头像
uploadAvatar(avatar) {
console.log('上传文件地址: ', avatar)
uni.uploadFile({
url: this.action, //修改成自己的接口地址
filePath: avatar,
name: 'file',
header: {
'Content-Type': 'multipart/form-data',
'Authorization': 'Bearer ' + token.getToken('token') // 设置你需要的自定义请求头
},
formData: {
// 可以添加额外的表单数据
},
success: res => {
const data = JSON.parse(res.data)
this.data.profilePhoto = data.data.url
},
fail: err => {
console.log('上传失败', err)
this.user = uni.getStorageSync('user')
}
})
},
<div class="block">
<u-upload
:fileList="fileList"
@afterRead="afterRead"
@delete="deletePic"
name="3"
multiple
:maxCount="10"
:previewFullImage="true"
></u-upload>
</div>
// 删除图片
deletePic(event) {
this.fileList.splice(event.index, 1)
},
// 新增图片
async afterRead(event) {
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
let lists = [].concat(event.file)
let fileListLen = this.fileList.length
lists.map((item) => {
this.fileList.push({
...item,
status: 'uploading',
message: '上传中'
})
})
for (let i = 0; i < lists.length; i++) {
const result = await this.uploadFilePromise(lists[i].url)
let item = this.fileList[fileListLen]
this.fileList.splice(fileListLen, 1, Object.assign(item, {
status: 'success',
message: '',
url: result
}))
fileListLen++
}
},
uploadFilePromise(url) {
return new Promise((resolve, reject) => {
let a = uni.uploadFile({
url: this.action, //修改成自己的接口地址
filePath: url,
name: 'file',
formData: {
},
success: (res) => {
const data = JSON.parse(res.data)
this.fileListImg.push(data.data.url)
}
});
})
},
全局自定义顶部导航栏
在app.json的“window”里添加”navigationStyle”: “custom”
"window": {
"navigationStyle": "custom"
}
单独页面自定义顶部导航栏
在该页面的json文件里添加”navigationStyle”: “custom”
{
"usingComponents":{},
"navigationStyle": "custom"
}
js页面
获取系统信息并计算按钮位置信息
wxml页面
通过获取的数据编写搜索框
css页面
美化搜索框
app.js页面
import com.bderp.common.TrimFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.DispatcherType;
@Configuration
public class FilterConfig {
/**
* 去除参数头尾空格过滤器
*
* @return
*/
@Bean
public FilterRegistrationBean trimFilter() {
System.out.println("============过滤=================");
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new TrimFilter());
registration.addUrlPatterns("/*");
registration.setName("TrimFilter");
registration.setOrder(1);
return registration;
}
}
2. 创建过滤器类
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class TrimFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(new TrimHttpServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
}
}
3. 创建请求参数过滤类
import com.bderp.utils.JsonTrimUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class TrimHttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
private Map<String , String[]> params = new HashMap<>();
public TrimHttpServletRequestWrapper(HttpServletRequest request) {
// 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
super(request);
//将参数表,赋予给当前的Map以便于持有request中的参数
this.params.putAll(request.getParameterMap());
this.modifyParameterValues();
}
/**
* 重写getInputStream方法 post类型的请求参数必须通过流才能获取到值
*/
@Override
public ServletInputStream getInputStream() throws IOException {
System.out.println("data");
System.out.println("aaaaaaaaaaaaS");
//非json类型,直接返回
if(!super.getHeader(HttpHeaders.CONTENT_TYPE).contains(MediaType.APPLICATION_JSON_VALUE)){
return super.getInputStream();
}
//为空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
if (!StringUtils.hasLength(json)) {
return super.getInputStream();
}
ByteArrayInputStream bis = null;
try {
bis = new ByteArrayInputStream(JsonTrimUtils.jsonTrim(json).toJSONString().getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
return new MyServletInputStream(bis);
}
/**
* 将parameter的值去除空格后重写回去
*/
public void modifyParameterValues(){
Set<String> set =params.keySet();
Iterator<String> it=set.iterator();
while(it.hasNext()){
String key= it.next();
String[] values = params.get(key);
for (int i = 0; i < values.length; i++) {
values[i] = values[i].trim();
}
params.put(key, values);
}
}
/**
* 重写getParameter 参数从当前类中的map获取
*/
@Override
public String getParameter(String name) {
String[]values = params.get(name);
if(values == null || values.length == 0) {
return null;
}
return values[0];
}
/**
* 重写getParameterValues
*/
@Override
public String[] getParameterValues(String name) {//同上
return params.get(name);
}
class MyServletInputStream extends ServletInputStream{
private ByteArrayInputStream bis;
public MyServletInputStream(ByteArrayInputStream bis){
this.bis=bis;
}
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() {
return bis.read();
}
}
}
4. 创建过滤工具类
package com.bderp.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import java.util.Iterator;
import java.util.Map;
/**
@Version 1.0
/
public final class JsonTrimUtils {
/*
构造器
*/
private JsonTrimUtils() {
}
/**
System.out.println("============data");
System.out.println(jsonTrim(JSONObject.parseObject(jsonStr)));
return jsonTrim(JSONObject.parseObject(jsonStr));
}// /*
// 去除json值前后空格
// @param json jsonStr
// @return
// */
// public static JSON jsonTrim(JSON json) throws Exception {
// if (json instanceof JSONObject) {
// return jsonTrim(json);
// } else if (json instanceof JSONArray) {
// jsonTrimArray((JSONArray) json);
// return json;
// }
// throw new Exception(“非JSON参数”);
// }
/**
* 去除value的空格
*
* @param jsonObject jsonObject
* @return
*/
public static JSONObject jsonTrim(JSONObject jsonObject) {
Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> next = iterator.next();
Object value = next.getValue();
if (value != null) {
if (value instanceof String) {
//清空值前后空格
jsonObject.put(next.getKey(), ((String) value).trim());
} else if (value instanceof JSONObject) {
jsonTrim((JSONObject) value);
} else if (value instanceof JSONArray) {
jsonTrimArray((JSONArray) value);
}
}
}
return jsonObject;
}
/**
* 清空JSONArray 值前后空格
* @param array
*/
private static void jsonTrimArray(JSONArray array) {
if (array.size() > 0) {
for (int i = 0; i < array.size(); i++) {
Object object = array.get(i);
if (object != null) {
if (object instanceof String) {
array.set(i, ((String) object).trim());
} else if (object instanceof JSONObject) {
jsonTrim((JSONObject) object);
} else if (object instanceof JSONArray) {
jsonTrimArray((JSONArray) object);
}
}
}
}
}
}
```
import com.bderp.common.TrimFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.DispatcherType;
@Configuration
public class FilterConfig {
/**
* 去除参数头尾空格过滤器
*
* @return
*/
@Bean
public FilterRegistrationBean trimFilter() {
System.out.println("============过滤=================");
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new TrimFilter());
registration.addUrlPatterns("/*");
registration.setName("TrimFilter");
registration.setOrder(1);
return registration;
}
}
2. 创建过滤器类
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class TrimFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(new TrimHttpServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
}
}
3. 创建请求参数过滤类
import com.bderp.utils.JsonTrimUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class TrimHttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
private Map<String , String[]> params = new HashMap<>();
public TrimHttpServletRequestWrapper(HttpServletRequest request) {
// 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
super(request);
//将参数表,赋予给当前的Map以便于持有request中的参数
this.params.putAll(request.getParameterMap());
this.modifyParameterValues();
}
/**
* 重写getInputStream方法 post类型的请求参数必须通过流才能获取到值
*/
@Override
public ServletInputStream getInputStream() throws IOException {
System.out.println("data");
System.out.println("aaaaaaaaaaaaS");
//非json类型,直接返回
if(!super.getHeader(HttpHeaders.CONTENT_TYPE).contains(MediaType.APPLICATION_JSON_VALUE)){
return super.getInputStream();
}
//为空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
if (!StringUtils.hasLength(json)) {
return super.getInputStream();
}
ByteArrayInputStream bis = null;
try {
bis = new ByteArrayInputStream(JsonTrimUtils.jsonTrim(json).toJSONString().getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
return new MyServletInputStream(bis);
}
/**
* 将parameter的值去除空格后重写回去
*/
public void modifyParameterValues(){
Set<String> set =params.keySet();
Iterator<String> it=set.iterator();
while(it.hasNext()){
String key= it.next();
String[] values = params.get(key);
for (int i = 0; i < values.length; i++) {
values[i] = values[i].trim();
}
params.put(key, values);
}
}
/**
* 重写getParameter 参数从当前类中的map获取
*/
@Override
public String getParameter(String name) {
String[]values = params.get(name);
if(values == null || values.length == 0) {
return null;
}
return values[0];
}
/**
* 重写getParameterValues
*/
@Override
public String[] getParameterValues(String name) {//同上
return params.get(name);
}
class MyServletInputStream extends ServletInputStream{
private ByteArrayInputStream bis;
public MyServletInputStream(ByteArrayInputStream bis){
this.bis=bis;
}
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() {
return bis.read();
}
}
}
4. 创建过滤工具类
package com.bderp.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import java.util.Iterator;
import java.util.Map;
/**
@Version 1.0
/
public final class JsonTrimUtils {
/*
构造器
*/
private JsonTrimUtils() {
}
/**
System.out.println("============data");
System.out.println(jsonTrim(JSONObject.parseObject(jsonStr)));
return jsonTrim(JSONObject.parseObject(jsonStr));
}// /*
// 去除json值前后空格
// @param json jsonStr
// @return
// */
// public static JSON jsonTrim(JSON json) throws Exception {
// if (json instanceof JSONObject) {
// return jsonTrim(json);
// } else if (json instanceof JSONArray) {
// jsonTrimArray((JSONArray) json);
// return json;
// }
// throw new Exception(“非JSON参数”);
// }
/**
* 去除value的空格
*
* @param jsonObject jsonObject
* @return
*/
public static JSONObject jsonTrim(JSONObject jsonObject) {
Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> next = iterator.next();
Object value = next.getValue();
if (value != null) {
if (value instanceof String) {
//清空值前后空格
jsonObject.put(next.getKey(), ((String) value).trim());
} else if (value instanceof JSONObject) {
jsonTrim((JSONObject) value);
} else if (value instanceof JSONArray) {
jsonTrimArray((JSONArray) value);
}
}
}
return jsonObject;
}
/**
* 清空JSONArray 值前后空格
* @param array
*/
private static void jsonTrimArray(JSONArray array) {
if (array.size() > 0) {
for (int i = 0; i < array.size(); i++) {
Object object = array.get(i);
if (object != null) {
if (object instanceof String) {
array.set(i, ((String) object).trim());
} else if (object instanceof JSONObject) {
jsonTrim((JSONObject) object);
} else if (object instanceof JSONArray) {
jsonTrimArray((JSONArray) object);
}
}
}
}
}
}
```
第一次使用EasyWeChat微信支付SDK发现代码量很少,很简洁,但是在使用过程中也花了很多时间,遇到了很多问题
1、
第一次尝试时,发现下面的解释是当支付方式为APP时,才需要进行二次签名
//如trade_type = APP
//需要进行二次签名
所以第一次并没有进行二次签名,接口成功返回,但是一直报错支付验证签名失败
2、
尝试了几次后,进行二次签名验证再进行尝试,但是发现也是一直报错支付验证签名失败
3、之后就去微信支付接口签名校验工具,去验证签名
微信支付接口签名校验工具地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=20_1
发现xml的验证结果是正确的
4、去百度查看别人分享的EasyWeChat使用方法,发现使用的二次加密的方法和官网的不一样,结果真的就解决了
$result = $app->jssdk->appConfig($prepayId);
官网文档写的:
(new \EasyWeChat\Payment\Jssdk\Client($app))->appConfig($result[‘prepay_id’]);
总结:官网只是简单的告诉SDK的用法,但是呢还是要结合看看自己的业务来进行调整和使用
1、第一步,安装EasyWeChat版本,本章文档使用的是4.*版本
使用 composer:
composer require overtrue/wechat:~4.0 -vvv
2、配置
use EasyWeChat\Factory; //引入sdk
$config = [
// 必要配置
'app_id' => 'xxxxxx', //微信小程序appid
'mch_id' => 'xxxxxx', //微信支付商户号
'key' => 'xxxxxx', //微信支付API v2 密钥 (注意: 是v2密钥 是v2密钥 是v2密钥)
// 如需使用敏感接口(如退款、发送红包等)需要配置 API 证书路径(登录商户平台下载 API 证书)
'cert_path' => 'path/to/your/cert.pem', // XXX: 绝对路径!!!!
'key_path' => 'path/to/your/key', // XXX: 绝对路径!!!!
'notify_url' => $this->request->domain() . '/index/order/payCallback',// 支付回调地址
];
$app = Factory::payment($config);
3、下单 (本章文档使用的是JSAPI)
$result = $app->order->unify([
'body' => '充值积分',
'out_trade_no' => "xxx", //订单号
'total_fee' => 1.00, //支付金额
'spbill_create_ip' => '', // 可选,如不传该参数,SDK 将会自动获取相应 IP 地址
'notify_url' => $this->request->domain() . '/index/order/payCallback', // 支付回调地址
'trade_type' => 'JSAPI', // 请对应换成你的支付方式对应的值类型
'openid' => $user['openid'], //用户的openid
]);
4、获取签名,并返回给前端
if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') {
$package = "prepay_id=" . $result['prepay_id'];
//需要进行二次签名(重要!重要!重要!)
//$result = $app->jssdk->bridgeConfig($result['prepay_id'], false);
$result = $app->jssdk->appConfig($result['prepay_id']);
$time = time();
$result['timeStamp'] = "$time";
$result['signType'] = "MD5"; //默认加密方式
$result['package'] = $package;
return $this->success('成功', $result);
}
5、前端uniapp的uni.requestPayment是一个统一各平台的客户端支付API
//拉起支付
pay(){
http.post('/order/payment', {order_number:"xxxx"}).then((res) => {
let payParams = res.data
uni.requestPayment({
provider: 'wxpay',
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
success: function (res) {
console.log('支付成功');
console.log(res);
uni.showToast({
title: '支付成功',
icon: "success"
})
},
fail: function (res) {
console.log(res);
console.log('支付失败');
uni.showToast({
title: '支付失败',
icon: "error"
})
}
});
})
},
6、支付回调
//支付回调
public function payCallback()
{
try{
// $myfile = fopen(public_path()."/pay_log.txt", "w+") or die("Unable to open file!");
$xml = file_get_contents('php://input');
$data = $this->xmltoarrayx($xml);
// fwrite($myfile, $xml);
// fclose($myfile);
if ($data == null) {
$data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
}
if (empty($data) || $data == null || $data == '') {
//阻止微信接口反复回调接口 文档地址 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_7&index=7,下面这句非常重要!!!
//$str = ' ';
//echo $str;
$return_msg = 'OK';
$return_code = 'SUCCESS';
return json_encode([
"return_msg" => $return_msg,
"return_code" => $return_code
]);
exit('Notify 非法回调');
}
Log::info('支付回调数据 -> ' . json_encode($data));
$out_trade_no = isset($data['out_trade_no']) && !empty($data['out_trade_no']) ? $data['out_trade_no'] : 0;
if ($data['return_code'] == 'SUCCESS' && $data['result_code'] == 'SUCCESS') {
// 查询订单号
$order = OrderModel::where('uuid', $out_trade_no)->find();
if($order){
//更改订单支付状态
$order->pay_state = 1;
$order->save();
//更新金额等操作
}
}
//回调给微信,告知接收成功
//
//$str = ` `;
//echo $str;
$return_msg = 'OK';
$return_code = 'SUCCESS';
return json_encode([
"return_msg" => $return_msg,
"return_code" => $return_code
]);
}catch (\Exception $e){
// $myfile = fopen(public_path()."/pay_log_error.txt", "w+") or die("Unable to open file!");
// fwrite($myfile, $e->getMessage());
// fclose($myfile);
Log::error('支付回调异常 -> ' . $e->getMessage());
}
}
// xml 转数组
protected function xmlToArrayx($xml)
{
if (!$xml) {
return false;
}
libxml_disable_entity_loader(true);
$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $arr;
}
附:有什么参错上不懂的可以看官方文档
微信支付JSAPI调起支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
微信支付统一下单官方文档:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
开发接入文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
微信支付接口签名校验工具: https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=20_1
<view v-html="article.desc"></view>
使用v-html显示富文本编辑器的内容时,里面如果添加了缩略图图片,在微信小程序中显示时会发现由于图片太大,导致图片显示时变形
导致图片溢出
解决:可以通过设置css样式来处理
//防止富文本图片宽度溢出
if(this.article.desc !=null || this.article.desc != ""){
this.article.desc = this.article.desc.replace(/\<img/g, '<img style="max-width:100%;height;auto" ');
}
thinkphp6 使用jwt报错
Call to undefined function sodium_base642bin()
解决:找到php.ini文件将extension=sodium前面的;号去除
保存后重启php
1、自定义按钮方式 <button open-type="share"> 或监听系统右上角的分享按钮 onShareAppMessage 进行自定义分享内容
<button open-type='share' @click="setShareId(item)" type="primary" class="button-sure" :data-item='item'>
转发给好友
</button>
2、定义一个setShareId方法,用于接收点击的参数id
setShareId(item){
this.share_id = item.id;
},
3、分享方法
//分享
async onShareAppMessage(item){
try {
//请求后端接口,获取token
const result = await this.getItemSetId();
// 拿到token后在进行分享
return {
title: "分享订单详情",
path: '/pages/share/detail?token=' + result.token,
}
} catch (error) {
console.error('获取token失败:', error);
// 处理获取token失败的情况
// 返回默认的分享链接或者其他操作
}
},
4、请求后台接口方法
getItemSetId() {
return new Promise((resolve, reject) => {
http.get("/share/getToken", { id: this.share_id })
.then(res => {
// 将token作为结果传递给resolve方法
resolve({ token: res.data });
})
.catch(error => {
console.error('网络请求失败:', error);
reject(error); // 将错误对象作为结果传递给reject方法
});
});
},
这样分享后的链接页面/pages/share/detail打开后就会携带token参数了
1、把已提交到git仓库的文件或目录删除
使用以下命令将目录从Git仓库中移除:
git rm -r --cached <directory_name>
将<directory_name>替换为你要移除的目录名称。该命令将递归地移除目录及其下的所有文件,并将其从Git的缓存中删除。
2、提交更改:
git commit -m "Remove <directory_name> from Git"
微信小程序开发
使用订阅消息
thinkphp6
easywechat
//发送消息通知模板
public function sendMessage($openid)
{
$config = [
'corp_id' => '',
'agent_id' => '', // 如果有 agend_id 则填写
'secret' => '',
// 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名
'response_type' => 'array',
];
$data = [
'template_id' => "iGioueHodXKO8pahxSjQt3bU01Vq79JsjwTBS3YnfRU", // 所需下发的订阅模板id
'touser' => $openid, // 接收者(用户)的 openid
'page' => '', // 点击模板卡片后的跳转页面,仅限本小程序内的页面。
'data' => [ // 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }
'thing3' => [
'value' => '小明', //客户名称
],
],
];
$app = Factory::miniProgram($config);
$app->subscribe_message->send($data);
}
Request access_token fail: {"errcode":41002,"errmsg":"appid missing rid: 6263c40b-6076c953-2928c53a"}
缺少app_id参数
"errcode":41002
基本可以判断,是权限验证问题,可能的原因
1、缺少:app_id、secret
2、app_id、secret错误
1、初始化git仓库
git init
2、设置仓库接受代码提交
允许远程提交代码
git config receive.denyCurrentBranch ignore
3、服务器端编辑钩子文件
vim .git/hooks/post-update
4、 post-update文件内容,当有git提交时可自动更新代码
#!/bin/sh
unset GIT_DIR
cd ..
git checkout -f
5、设置文件权限
chmod +x .git/hooks/post-update
微信申请退款,报错证书验证失败
刚开始百度时,大部分都是在说配置错误导致的
后面在耐性仔细的查看的微信的说明文档后发现是需要去微信支付下载一个申请下载API证书
微信支付退款文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
下载后,在退款接口中,请求微信接口时需要带上证书即可
注意:
证书pkcs12格式,证书pem格式
部分开发语言和环境,不能直接使用p12文件,而需要使用pem(列如PHP使用的是pem)
今天拉取项目代码时,更新maven发现无法更新,然后查看错误发现pom.xml提示错误和警告信息
解决:根据提示,将项目所在目录添加进入系统的排除项中
进入到mysql安装目录的bin目录下,运行cmd
输入mysql_upgrade -uroot -p,输入密码
然后重启mysql即可
在小程序中,可以使用flex布局将图标和文字放在一个盒子中,并使它们居中显示。首先,将图标和文字放在一个view组件内,然后使用flex布局的相关属性实现居中对齐。可以通过设置view组件的display属性为flex,然后设置justify-content和align-items属性为center,将图标和文字水平和垂直居中显示。
示例:
<view style="display: flex; justify-content: center; align-items: center;">
<u-icon name="scan" color="#fff" size="50"></u-icon>
<text>文字内容</text>
</view>
错误写法:
background-image: url(/img/科技.jpg);
正确写法:
background-image: url('./img/科技.jpg');
解决思路:
通过设置div的大小大于背景图片的大小,使背景图片完全显示;
body {
width: 200px;
height: 200px;
ckground-image: url('./img/科技.jpg');
}
使用background-size属性将背景图片的大小设置为小于div的大小。
body {
background-image: url('./img/科技.jpg');
background-size: cover;
}
需求:前端通过一个输入框,既可以查询学号又可以查询姓名
后端:通过或条件查询
//可以根据学号和姓名模糊搜索
if(student.getStudentNumber() != null && !student.getStudentNumber().equals("")) {
lqw.lambda().or(l -> {
l.like(Student::getStudentNumber, student.getStudentNumber());
}).or(l -> {
l.like(Student::getName, student.getStudentNumber());
});
student.setStudentNumber(null);
}
问题详情链接:https://blog.csdn.net/weixin_55252589/article/details/129017650
解决问题:
情况一:windows用户:
git config --global core.autocrlf true
#提交时转换为LF,检出时转换为CRLF:
解释:CR/LF是不同操作系统上使用的换行符:
应用情况: