This commit is contained in:
2025-03-26 21:20:24 +08:00
parent 02800359a6
commit 960940fd25
161 changed files with 31248 additions and 7 deletions

Submodule admin/application/webautocode updated: 054b01bbc8...6f9caec20a

View File

@@ -3063,12 +3063,12 @@
"source": {
"type": "git",
"url": "https://gitee.com/drop_drop/general_utility_tools_php.git",
"reference": "2f9aad4bc8455b9e88f0ff731755c7725a50a54c"
"reference": "d00a9e33723912dca0ceda8e3d619844eff0b031"
},
"require": {
"ext-json": "*"
},
"time": "2024-08-31T10:06:33+00:00",
"time": "2025-01-29T15:31:25+00:00",
"default-branch": true,
"type": "library",
"installation-source": "source",

View File

@@ -3,7 +3,7 @@
'name' => 'karsonzhang/fastadmin',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '8f864cdfbb9699eb9dcf7567ea86fe702961f0cf',
'reference' => '02800359a6d9198cbb39dc723df08821a5ee7d9e',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -58,7 +58,7 @@
'karsonzhang/fastadmin' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '8f864cdfbb9699eb9dcf7567ea86fe702961f0cf',
'reference' => '02800359a6d9198cbb39dc723df08821a5ee7d9e',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -483,7 +483,7 @@
'wanghua/general-utility-tools-php' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '2f9aad4bc8455b9e88f0ff731755c7725a50a54c',
'reference' => 'd00a9e33723912dca0ceda8e3d619844eff0b031',
'type' => 'library',
'install_path' => __DIR__ . '/../wanghua/general-utility-tools-php',
'aliases' => array(

Submodule admin/vendor/wanghua/general-utility-tools-php deleted from a64ab59e2d

View File

@@ -0,0 +1,37 @@
# general_utility_tools_php
#### Description
php常用工具箱
general utility tool for php
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

View File

@@ -0,0 +1,142 @@
# general_utility_tools_php
#### 介绍
[PHP常用通用工具库]
一、常用验证类库
1. 验证手机号是否正确
2. 验证邮箱
3. 验证身份证号码
4. 是否字母或数字
5. 是否是小数格式
6. 更多功能请参考代码
二、PHP日期处理工具
1. 日期加上N年、月、日、时、分、秒得到计算后的时间
2. 两个日期相减得到天、时、分、秒(没有两个日期相加一说)
3. 更多更完善功能敬请期待......
三、更多类库参考本类库目录
1. 每个目录都有使用说明.
#### 软件架构
每一种工具独立为一个功能类库。
#### 软件要求
thinkphp5.0+PHP7.1+
#### 安装教程
composer require wanghua/general-utility-tools-php dev-master
###### 注意如果总是安装失败可切换国内或者国外源如果是找不到版本则加上dev-master尝试
###### 注意如果总是安装失败可卸载此包重新安装卸载指令composer remove wanghua/general-utility-tools-php
###### 注意:如果总是不能提交 vendor下面的一个文件夹(或文件)请参考这篇文章解决https://blog.csdn.net/qq_15941409/article/details/113184021
### 一、常用验证类库使用说明
##### 初始化
//静态方法无需实例化直接调用
##### 验证参数是否是小数格式
$str = '1.223';
var_dump(Validate::is_float_number($str));
##### 验证参数是否字母或数字
$str = 'qwer#199';
var_dump(Validate::is_letter_or_number($str));
###### 注: 更多功能请参考源码
### 二、PHP日期处理工具使用说明
##### 初始化
$date = (new Date());
$date->date_format = 'Y-m-d H:i:s';//设置格式默认Y-m-d H:i:s
##### 日期类型参数(第2个参数)可选项:
//分、时、天、周、月、年
protected $data_type = [
'm' =>'minute',//分钟
'minute' =>'minute',//分钟
'h' =>'hour',//小时
'hour' =>'hour',//小时
'd' =>'day',//天
'day' =>'day',//天
'w' =>'week',//周
'week' =>'week',//周
'M' =>'month',//月
'month' =>'month',//月
'y' =>'year',//年
'year' =>'year',//年
];
##### 在当前时间基础上加3天第三个参数不传默认使用当前时间
$res = $date->addTime(3, 'd');//支持单词和字母例如d表示天day也表示天
dump($res);
##### 在当前时间基础上加1小时第三个参数不传默认使用当前时间
$res = $date->addTime(1, 'h');//支持单词和字母例如h表示小时hour也表示小时
dump($res);
##### 在指定时间基础上加1小时
$res = $date->addTime(1, 'h', strtotime('2010-10-01 12:00:10'));
dump($res);
##### 在指定时间基础上减1个月 注意月是大写M字母分钟是小写m字母
$res = $date->reduceTime(1, 'M', strtotime('2010-10-01 12:00:10'));
dump($res);
##### 在指定时间基础上减20分钟 注意分钟是小写m字母
$res = $date->reduceTime(20, 'm', strtotime('2010-10-01 12:00:10'));
dump($res);
##### 在指定时间基础上减1年
$res = $date->reduceTime(1, 'y', strtotime('2010-10-01 12:00:10'));
dump($res);
### 日期时间相减
//时间相减返回的时间类型 秒、分、时、天 默认返回秒
protected $time_type = [
's' =>1,//秒
'second' =>1,//秒
'm' =>60,//分钟
'minute' =>60,//分钟
'h' =>3600,//小时
'hour' =>3600,//小时
'd' =>86400,//天
'day' =>86400,//天
];
$start_time = '2010-05-01 12:30:00';
$end_time = '2010-10-28 12:30:00';
//结束时间减去开始时间得到秒数 (返回类型参考上方配置)
$res = $date->timeReduceTime($end_time, $start_time);
dump($res);
//结束时间减去开始时间得到天数 (返回类型参考上方配置)
$res = $date->timeReduceTime($end_time, $start_time, 'day');
dump($res);
###### 注: 更多功能请参考源码
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

View File

@@ -0,0 +1,25 @@
{
"type": "library",
"name": "wanghua/general-utility-tools-php",
"homepage": "https://gitee.com/drop_drop/general_utility_tools_php.git",
"description": "general utility tools for thinkPHP",
"license": "Apache-2.0",
"version":"1.0.1",
"keywords": ["general-utility-tools","php tool","common tool"],
"authors": [
{
"name": "wanghua",
"email": "wanghua@qq.com",
"homepage": "https://blog.csdn.net/qq_15941409"
}
],
"minimum-stability": "stable",
"require": {
"ext-json": "*"
},
"autoload": {
"psr-4": {
"wanghua\\general_utility_tools_php\\": "src/"
}
}
}

View File

@@ -0,0 +1,396 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/10/27} {11:43}
*/
namespace wanghua\general_utility_tools_php;
/**
* 日期时间处理类
* Class Date
* @package libraries
*/
class Date
{
//时间默认返回格式
public $date_format = 'Y-m-d H:i:s';
//分、时、天、周、月、年
protected $data_type = [
'm' =>'minute',//分钟
'minute' =>'minute',//分钟
'h' =>'hour',//小时
'hour' =>'hour',//小时
'd' =>'day',//天
'day' =>'day',//天
'w' =>'week',//周
'week' =>'week',//周
'M' =>'month',//月
'month' =>'month',//月
'y' =>'year',//年
'year' =>'year',//年
];
//秒、分、时、天
protected $time_type = [
's' =>1,//秒
'second' =>1,//秒
'm' =>60,//分钟
'minute' =>60,//分钟
'h' =>3600,//小时
'hour' =>3600,//小时
'd' =>86400,//天
'day' =>86400,//天
];
/**
* desc日期时间加N
*
* 【注意】
* 请在计算月时使用$date->date_format = 'Y-m'防止跳月份
* 例如2020-03-31月基础上加一个月在默认$date->date_format='Y-m-d H:i:s'时会跳到2020-05-01
*
* authorwh
* @param int $times 相加的时间数量 整型
* @param string $date_type 要相加的时间类型 可选值m 分钟h 小时d 天w 周M 月y 年
* @param int $default_time 时间戳,默认当前时间
* @return false|string 返回$this->date_format格式可根据需要设定格式
*/
function addTime(int $times, string $date_type, int $default_time=0){
return date($this->date_format, strtotime("+{$times} {$this->data_type[$date_type]}", $default_time?$default_time:time()));
}
/**
* desc日期时间减N
*
* 【注意】
* 请在计算月时使用$date->date_format = 'Y-m'防止跳月份
* 例如2020-03-31月基础上加一个月在默认$date->date_format='Y-m-d H:i:s'时会跳到2020-05-01
*
* authorwh
* @param int $times 减去的时间数量 整型
* @param string $date_type 要相减的时间类型 可选值m 分钟h 小时d 天w 周M 月y 年
* @param int $default_time 时间戳,默认当前时间
* @return false|string 返回$this->date_format格式可根据需要设定格式
*/
function reduceTime(int $times, string $date_type, int $default_time=0){
return date($this->date_format, strtotime("-{$times} {$this->data_type[$date_type]}", $default_time?$default_time:time()));
}
/**
* desc日期时间相减通常结束时间大于开始时间
* authorwh
* @param string $end_time 结束时间
* @param string $start_time 开始时间
* @param string $return_type 日期时间相减后得到的时间类型可能是小数。可选值s 秒m 分钟h 小时d
* @return float|int 返回计算后的天、时、分、秒数
*/
function timeReduceTime(string $end_time, string $start_time, string $return_type='s'){
return (strtotime($end_time) - strtotime($start_time)) / $this->time_type[$return_type];
}
/**
* desc日期减日期, 返回月数或年数
*
* authorwh
* @param string $start_time 开始时间
* @param string $end_time 结束时间
* @param string $return_type M 返回一共有多少个月y 返回有多少个年
* @return float|int
*/
function dateCutDate(string $start_time, string $end_time, string $return_type='M'){
$e = date_create($end_time);
$s = date_create($start_time);
$diff = date_diff($e, $s);
//计算月份
if ($diff->y > 0) {
$m = $diff->y * 12 + $diff->m;
}else{
$m = $diff->m;
}
return $return_type=='M'?$m:$diff->y;
}
/**
* desc日期相减得到月数
* 注意:不是计算的时间戳,而是计算的月份差值
* authorwh
* @param string $start_time
* @param string $end_time
* @return false|float|int|string
*/
function dateCutMonth(string $start_time, string $end_time){
$start_y = date('Y', strtotime($start_time));
$end_y = date('Y', strtotime($end_time));
$start_m = date('m', strtotime($start_time));
$end_m = date('m', strtotime($end_time));
$ym = ($end_y-$start_y) * 12;
return $ym - $start_m + $end_m;
}
/**
* desc日期相减得到年数
* 注意:不是计算的时间戳,而是计算的年份差值
* authorwh
* @param string $start_time
* @param string $end_time
* @return false|float|int|string
*/
function dateCutYear(string $start_time, string $end_time){
$start_y = date('Y', strtotime($start_time));
$end_y = date('Y', strtotime($end_time));
return ($end_y-$start_y) * 12;
}
/**
* desc返回年中第几天
* authorwh
* @param string $month 指定月份
* @param string $day 指定日
* @param string $year 指定年份
* @return false|string
*/
function get_day_Year(string $month, string $day, string $year){
return date('z',mktime(0,0,0,$month,$day,$year));
}
/**
* desc今天的开始时间
* authorwh
* @return false|string
*/
function beginToday(){
return date('Y-m-d').' 00:00:00';
}
/**
* desc今天的结束时间
* authorwh
* @return false|string
*/
function endToday(){
return date('Y-m-d').' 23:59:59';
}
/**
* desc昨天的开始时间
* authorwh
* @return string
*/
function beginYesterday(){
return date('Y-m-d', strtotime('-1 day')).' 00:00:00';
}
/**
* desc昨天的结束时间
* authorwh
* @return string
*/
function endYesterday(){
return date('Y-m-d', strtotime('-1 day')).' 23:59:59';
}
/**
* desc前天的开始时间
* authorwh
* @return string
*/
function beginBeforeYesterday(){
return date('Y-m-d', strtotime('-2 day')).' 00:00:00';
}
/**
* desc前天的结束时间
* authorwh
* @return string
*/
function endBeforeYesterday(){
return date('Y-m-d', strtotime('-2 day')).' 23:59:59';
}
/**
* 本周的开始日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function beginWeek($His = true)
{
//此代码会跳周、月、年
//$timestamp = mktime(0, 0, 0, date('m'), date('d') - date('w') + 1, date('Y'));
//return $His ? date('Y-m-d H:i:s', $timestamp) : date('Y-m-d', $timestamp);
//改进
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
$first=1;
$sdefaultDate = date("Y-m-d");
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
$w=date('w',strtotime($sdefaultDate));
//是否展示时分秒
$week_start = date('Y-m-d',strtotime("$sdefaultDate -".($w ? $w - $first : 6).' days'));
//是否展示时分秒
if($His){
//$week_start
return $week_start.' 00:00:00';
}
//$week_start
return $week_start;
}
/**
* 本周的结束日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function endWeek($His = true)
{
//此代码会跳周、月、年
//$timestamp = mktime(23, 59, 59, date('m'), date('d') - date('w') + 7, date('Y'));
//return $His ? date('Y-m-d H:i:s', $timestamp) : date('Y-m-d', $timestamp);
//改进
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
$first=1;
$sdefaultDate = date("Y-m-d");
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
$w=date('w',strtotime($sdefaultDate));
//是否展示时分秒
$week_start = date('Y-m-d',strtotime("$sdefaultDate -".($w ? $w - $first : 6).' days'));
$week_end=date('Y-m-d',strtotime("$week_start +6 days"));
//是否展示时分秒
return $His?$week_end.' 23:59:59':$week_end;
}
/**
* desc上周开始
* authorwh
* @return false|string
*/
function beginBeforeWeek(){
$week = $this->beginWeek();
$this->date_format = 'Y-m-d';
return $this->reduceTime(7,'d',strtotime($week)) . ' 00:00:00';
//此代码会跳周、月、年
//return date('Y-m-d', strtotime('-1 monday', time())).' 00:00:00';//上周一,无论今天几号,-1 monday为上一个有效周未
}
/**
* desc上周结束
* authorwh
* @return false|string
*/
function endBeforeWeek(){
$week = $this->beginWeek();
$this->date_format = 'Y-m-d';
return $this->reduceTime(1,'d',strtotime($week)) . ' 23:59:59';
//此代码会跳周、月、年
//return date('Y-m-d', strtotime('-1 sunday', time())).' 23:59:59'; //上一个有效周日,同样适用于其它星期
}
/**
* 本月的开始日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function beginMonth($His = true)
{
return $His ? date('Y-m-').'01 00:00:00' : date('Y-m-').'01';
}
/**
* 本月的结束日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function endMonth($His = true)
{
$timestamp = mktime(23, 59, 59, date('m'), date('t'), date('Y'));
return $His ? date('Y-m-d H:i:s', $timestamp) : date('Y-m-d', $timestamp);
}
/**
* desc上月开始时间上月一日
* authorwh
* @return false|string
*/
function beginBeforeMonth(){
return date('Y-m-d', strtotime('-1 month', strtotime(date('Y-m', time()) . '-01 00:00:00'))).' 00:00:00'; //本月一日直接strtotime上减一个月
}
/**
* desc上月结束时间上月最后一日
* authorwh
* @return false|string
*/
function endBeforeMonth(){
return date('Y-m-d', strtotime(date('Y-m', time()) . '-01 00:00:00') - 86400).' 23:59:59'; //本月一日减一天即是上月最后一日
}
/**
* desc七天(一周)以内
* authorwh
* @return false|string
*/
function innerWeekDay(){
$time = time();
//当前时间减去30天
$second = $time - 7 * 86400;
return date('Y-m-d', $second);
}
/**
* desc一月以内
* authorwh
* @return false|string
*/
function innerMonth(){
$time = time();
//当前时间减去30天
$second = $time - 30 * 86400;
return date('Y-m-d', $second);
}
/**
* desc一年以内
* authorwh
* @return false|string
*/
function innerYear(){
$time = time();
//当前时间减去30天
$second = $time - 365 * 86400;
return date('Y-m-d', $second);
}
/**
* 几年的开始日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function beginYear($His = true)
{
$timestamp = date('Y');
return $His ? $timestamp.'-01-01 00:00:00' : $timestamp.'-01-01';
}
/**
* 几年的结束日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function endYear($His = true)
{
$timestamp = date('Y');
return $His ? $timestamp.'-12-31 23:59:59' : $timestamp.'-12-31';
}
}

View File

@@ -0,0 +1,133 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/5/1} {21:28}
*/
namespace wanghua\general_utility_tools_php;
use think\Db;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* thinkphp5模型快捷方法库
* Class Mmodel
* @package wanghua\general_utility_tools_php
*/
class Mmodel
{
/**
* desc存在则更新否则插入
* authorwh
* @param string $table 表名
* @param array $where 更新条件(只在更新时有效)
* @param array $data 更新或写入数据
* @return int 数据主键id
*/
static function existsUpdateInsert($table,$where,$data){
if(empty($where)){
return Db::table($table)->insertGetId($data);
}
$res = Db::table($table)
->where($where)
->find();
if($res){
//不修改条件数据
$data = array_diff_key($data,$where);//从data中删除where数组中的键值对
Db::table($table)
->where($where)
->data($data)
->update();
return $res['id'];
}
return Db::table($table)->insertGetId($data);
}
/**
* desc捕获式函数块
*
* 无事务处理
*
* authorwh
* @param $fn
*/
static function catch($fn){
try{
Tools::log_to_write_txt(['input'=>input()]);
$res = $fn();
Tools::log_to_write_txt(['output'=>$res]);
return $res;
}catch (\Exception $e){
Tools::error_txt_log($e);
return Tools::set_fail('操作失败.',$e->getMessage());
}
}
/**
* desc捕获式函数块
*
* 无事务处理
*
* authorwh
* @param $fn
*/
static function catchJson($fn){
try{
Tools::log_to_write_txt(['input'=>input()]);
$res = $fn();
Tools::log_to_write_txt(['output'=>$res]);
return json($res);
}catch (\Exception $e){
Tools::error_txt_log($e);
return json(Tools::set_fail('操作失败.',$e->getMessage()));
}
}
/**
* desc捕获式函数块
*
* 自动事务处理
*
* authorwh
* @param $fn
*/
static function catchTrans($fn){
Db::startTrans();
try{
Tools::log_to_write_txt(['input'=>input()]);
$res = $fn();
Tools::log_to_write_txt(['output'=>$res]);
Db::commit();
return $res;
}catch (\Exception $e){
Db::rollback();
Tools::error_txt_log($e);
return Tools::set_fail('操作失败.',$e->getMessage());
}
}
/**
* desc捕获式函数块
*
* 自动事务处理
*
* authorwh
* @param $fn
*/
static function catchTransJson($fn){
Db::startTrans();
try{
Tools::log_to_write_txt(['input'=>input()]);
$res = $fn();
Tools::log_to_write_txt(['output'=>$res]);
Db::commit();
return json($res);
}catch (\Exception $e){
Db::rollback();
Tools::error_txt_log($e);
return json(Tools::set_fail('操作失败.',$e->getMessage()));
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,50 @@
<?php
/*
* description
* authorwh
* email
* createTime{2023/12/31} {21:56}
*/
namespace wanghua\general_utility_tools_php;
use app\common\model\TabConf;
use think\Db;
/**
* 系统杂项配置
*
* 读取fa_sundry_config表中的配置
*
* Class SundryConfig
* @package wanghua\general_utility_tools_php
*/
class SundryConfig
{
private static $tablename = 'fa_zc_sundry_config';
/**
* desc获取配置的值设置配置的值
*
* 默认缓存值
*
* authorwh
* @param string $key
* @param string $val
* @return float|mixed|string
*/
static function val(string $key,string $val=''){
$tabname = self::$tablename;
if($val){
$obj = Db::table($tabname);
$obj->where('key',$key);
$obj->data(['val'=>$val]);
return $obj->update();
}
$obj = Db::table($tabname);
return $obj->where(['key'=>$key])
->cache($tabname.'_'.$key,0,$tabname)
->value('val');
}
}

View File

@@ -0,0 +1,360 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/11/1} {15:09}
*/
namespace wanghua\general_utility_tools_php;
class Validate
{
/**
* desc验证手机号是否正确
* authorwh
* @param $m
* @return bool
*/
static function is_mobile($m) {
return preg_match('/^1[3,4,5,6,7,8,9]{1}\d{9}$/', $m) ? true : false;
}
/**
* 验证邮箱
* authorwh
* @param $email
* @return bool
*/
static function is_email($email){
return preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/",$email)?true:false;
}
/**
* [is_IDCard 验证身份证号码]
* @Author
* @Date 2019-11-26
* @param [string] $string [身份证号]
* @return boolean [description]
*/
static function is_IDCard($string){
return preg_match("/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/", $string)?true:false;
}
/**
* [is_money_times 验证金额是否是100的整数倍]
* @Author
* @Date 2019-12-12
* @param [type] $money [金额]
* @return boolean [description]
*/
static function is_money_times($money){
if(!is_numeric($money) || $money <= 0){
return false;
}
return $money%100===0;
}
/**
* desc是否是金额
* authorwh
* @param $money
* @return bool
*/
static function is_money($money){
return (preg_match('/^[0-9]+.[0-9]+$/', $money) || preg_match('/^\d+$/', $money))?true:false;
}
/**
* desc是否是金额并且最多只有两位小数
*
* 严格金额格式校验
*
* authorwh
* @param $money
* @return bool
*/
static function is_money_double($money){
$r = (preg_match('/^[0-9]+.[0-9]+$/', $money) || preg_match('/^\d+$/', $money))?true:false;
$exp = explode('.',$money);
if(strpos($money,'.') && preg_match('/^[0-9]+.[0-9]+$/', $money) && strlen($exp[1])>2){
//返回false表示非严格的2位数金额
return false;
}
return $r;
}
/**
* desc是否是小数格式
* authorwh
* @param string $num
* @return bool
*/
static function is_float_number(string $num){
return preg_match('/^[0-9]+.[0-9]+$/', $num)?true:false;
}
/**
* desc是否是数字
* authorwh
* @param string $num
* @return bool
*/
static function is_number(string $num){
return preg_match('/^\d+$/', $num)?true:false;
}
/**
* desc是否是字母
* author wh
* @param string $str
* @return bool
*/
static function is_letter(string $str){
return preg_match('/^[a-zA-Z]+$/', $str) ? true : false;
}
/**
* desc是否字母或数字
* authorwh
* @param string $str
* @return bool
*/
static function is_letter_or_number(string $str){
return preg_match('/^[a-zA-Z|0-9]+$/', $str) ? true : false;
}
/**
* desc验证数组是否存在空值
* 仅针对基本数据类型
* 仅针对一维数组
* authorwh
* @param $array
* @return bool
*/
static function check_array_val_empty($array){
$is_empty = false;
foreach ($array as $value){
if(($value!==0 && $value !=='0') && empty($value)){
$is_empty = true;
break;
}
if(is_int($value) && empty(1*$value)){
$is_empty = true;
break;
}
}
return $is_empty;
}
/**
* desc是否是url
* authorwh
* @param $v
* @return bool
*/
static function is_url($v){
$pattern="#(http|https)://(.*\.)?.*\..*#i";
return preg_match($pattern,$v)?true:false;
}
/**
* [是否全部大写]
*
* @param $str
* @return bool
* @example
* @see
* @link
*/
static function is_upper($str){
return preg_match('/^[A-Z]+$/', $str)?true:false;
}
/**
* [是否全部小写]
*
* @param $str
* @return bool
* @example
* @see
* @link
*/
static function is_lower($str){
return preg_match('/^[a-z]+$/', $str)?true:false;
}
/**
* desc验证是微信内还是微信外
*
* 是否在微信内,是否在微信中
*
* authorwh
* @return bool
*/
static function is_weixin(){
return strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false;
}
/**
* desc是否包含特殊字符
* authorwh
* @param string $str
* @return bool
*/
static function is_special_character(string $str){
$res = preg_match ( '/[\Q~!@#$%^&*()+-_=.:?<>\E]/', $str );
return $res ? true : false;
}
/**
* desc验证字符串是否全部是中文
*
* 返回 true表示全部是中文false表示部分是中文或没有中文
*
* authorwh
* @param string $str
* @return bool
*/
static function is_all_chinese(string $str){
return preg_match("/^[\x{4e00}-\x{9fa5}]+$/u",$str)?true:false;
}
/**
* desc验证两个浮点数值是否相等
*
* 例如:
* $num1 = 0.1;
$num2 = 0.7;
$res = $num1 + $num2;
var_dump($res);
var_dump($res == 0.8);//false 不相等
var_dump(intval(strval($res)) == intval(strval(0.8)));//true 相等
*
* authorwh
* @param $float_num1
* @param $float_num2
* @return bool
*/
static function is_equal_num_val($float_num1, $float_num2){
return intval(strval($float_num1)) == intval(strval($float_num2));
}
/**
* desc是否包含给定的主域名
* authorwh
* @param $domain
* @param $main_domain
* @return bool
*/
static function is_main_domain($domain,$main_domain){
$exp_arr = explode('.',$domain);
$str = $exp_arr[count($exp_arr)-2] .'.'. $exp_arr[count($exp_arr)-1];
return $str==$main_domain;
}
/**
* desc国内国外IP校验校验IP来源
*
* authorwh
* @param $ip
* @return bool
*/
static function validate_ip($ip) {
$chinaStart = ip2long('1.0.1.0'); // 中国大陆起始IP
$chinaEnd = ip2long('254.254.254.254'); // 中国大陆结束IP
$hongKongStart = ip2long('8.36.0.0'); // 香港起始IP
$hongKongEnd = ip2long('8.37.255.255'); // 香港结束IP
$taiwanStart = ip2long('192.168.0.0'); // 台湾起始IP
$taiwanEnd = ip2long('192.168.255.255'); // 台湾结束IP
$ipLong = ip2long($ip);
if ($ipLong >= $chinaStart && $ipLong <= $chinaEnd ||
$ipLong >= $hongKongStart && $ipLong <= $hongKongEnd ||
$ipLong >= $taiwanStart && $ipLong <= $taiwanEnd) {
return true; // IP属于国内
} else {
return false; // IP不属于国内
}
}
/**
* 是否移动端访问访问
*
* @return bool
*/
static function is_mobile_client()
{
// 如果有HTTP_X_WAP_PROFILE则一定是移动设备
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
return true;
}
//如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
if (isset($_SERVER['HTTP_VIA'])) {
//找不到为flase,否则为true
return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
}
//判断手机发送的客户端标志
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$clientkeywords = [
'nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp',
'sie-', 'philips', 'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu',
'android', 'netfront', 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi',
'openwave', 'nexusone', 'cldc', 'midp', 'wap', 'mobile','alipay'
];
// 从HTTP_USER_AGENT中查找手机浏览器的关键字
if (preg_match("/(".implode('|', $clientkeywords).")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
return true;
}
}
//协议法,因为有可能不准确,放到最后判断
if (isset($_SERVER['HTTP_ACCEPT'])) {
// 如果只支持wml并且不支持html那一定是移动设备
// 如果支持wml和html但是wml在html之前则是移动设备
if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
return true;
}
}
return false;
}
/**
* 判断是否微信内置浏览器访问
* @return bool
*/
static function is_wx_client()
{
return strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false;
}
/**
* 判断是否支付宝内置浏览器访问
* @return bool
*/
static function is_ali_client()
{
return strpos($_SERVER['HTTP_USER_AGENT'], 'Alipay') !== false;
}
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/27} {17:36}
*/
namespace wanghua\general_utility_tools_php\algorithm;
/**
* 常见算法
* Class AlgorithmTools
* @package wanghua\general_utility_tools_php\tool
*/
class Algorithm
{
/**
* desc权重随机选择算法
*
* 使用场景:转盘抽奖等需要指定概率的场景
*
* authorwh
* @param array $weights 权重数组 eg[1, 9, 30, 70] 概率的总和是100%
* return 返回计算后的索引
*/
static function weightRandom($weights){
//$weights = [1, 9, 30, 70]; // 权重数组
$totalWeight = array_sum($weights);
$min_num = min($weights);//取出数组中最小的值
$random = mt_rand($min_num, $totalWeight); // 生成一个最小元素到总权重的随机数
$cumulativeWeight = 0; // 累积权重
$selected = 0; // 选择的索引
foreach ($weights as $index => $weight) {
$cumulativeWeight += $weight; // 累加权重
if ($random <= $cumulativeWeight) {
$selected = $index; // 根据权重选择索引
break;
}
}
//输出选择的索引
return $selected;
}
/**
* desc测试权重随机算法概率
* authorwh
* @param array $weights 权重数组 eg[1, 9, 30, 70]
*/
static function testWeightRandom(array $weights){
echo json_encode($weights)."\n";
$trials = 1000; // 试验次数
$selectionCounts = array_fill(0, count($weights), 0); // 用于存储每个索引的选择次数
for ($i = 0; $i < $trials; $i++) {
$index = self::weightRandom($weights);
$selectionCounts[$index]++; // 记录选择次数
}
// 计算每个索引的选择概率
$probabilities = [];
foreach ($selectionCounts as $index => $count) {
$probabilities[$index] = ($count / $trials) * 100; // 转换为百分比
}
// 输出结果
echo "After {$trials} trials, the probabilities of selecting each index are:\n";
foreach ($probabilities as $index => $probability) {
echo "Index {$index}: {$probability}%\n";
}
}
/**
* desc洗牌算法
*
* 对数组进行随机排序,实现洗牌算法
* authorwh
* @param array $items eg: [1, 2, 3, 4, 5]
* @return array 返回计算后的数组
*/
static function shuffle(array $items) {
//$items = [1, 2, 3, 4, 5];
shuffle($items);
return $items;
}
/**
* 随机密码生成算法
*
* 作用:创建安全的随机密码,常用于用户注册或密码重置
* 使用场景:用户账户创建、密码重置链接生成
*/
static function randomPassword($length = 8) {
$password = '';
$possibleChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for ($i = 0; $i < $length; $i++) {
$password .= substr($possibleChars, mt_rand(0, strlen($possibleChars) - 1), 1);
}
return $password;
}
/**
* 随机颜色生成算法
*
* 作用生成随机RGB颜色值可用于网页设计中动态改变元素颜色
* 使用场景:动态网页背景、随机颜色显示的元素
*/
static function randomColor() {
$red = mt_rand(0, 255);
$green = mt_rand(0, 255);
$blue = mt_rand(0, 255);
return [$red, $green, $blue];
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/27} {17:36}
*/
namespace wanghua\general_utility_tools_php\algorithm;
/**
* 算法测试
* Class AlgorithmTest
* @package wanghua\general_utility_tools_php\algorithm
*/
class AlgorithmTest
{
/**
* desc测试权重随机算法概率
* authorwh
* @param array $weights 权重数组 eg[1, 9, 30, 70]
*/
static function testWeightRandom(array $weights){
echo json_encode($weights)."\n";
$trials = 1000; // 试验次数
$selectionCounts = array_fill(0, count($weights), 0); // 用于存储每个索引的选择次数
for ($i = 0; $i < $trials; $i++) {
$index = Algorithm::weightRandom($weights);
$selectionCounts[$index]++; // 记录选择次数
}
// 计算每个索引的选择概率
$probabilities = [];
foreach ($selectionCounts as $index => $count) {
$probabilities[$index] = ($count / $trials) * 100; // 转换为百分比
}
// 输出结果
echo "After {$trials} trials, the probabilities of selecting each index are:\n";
foreach ($probabilities as $index => $probability) {
echo "Index {$index}: {$probability}%\n";
}
}
}

View File

@@ -0,0 +1 @@
## 常见实用算法

View File

@@ -0,0 +1,162 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/28} {14:36}
*/
namespace wanghua\general_utility_tools_php\alibaba;
use wanghua\general_utility_tools_php\http\Curl;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 授权
*
* 注意:
* 分销自研自用版不需要授权直接使用永久accessToken
* 请把当前账号添加为应用的日常测试账号
*
* Class Auth
* @package app\index\logic\alibaba
*/
class AlibabaAuth
{
public $base_url = '';//不同业务线,授权地址不一样
public $config = [];
public $request_body = [];
public function __construct($config)
{
$this->config = $config;
}
function trusteeshipAuth(){
//待开发
}
/**
* desc授权分为托管式授权和WEB端授权这里是WEB端授权后去code临时凭证
*
* authorwh
* @param string $appKey app注册时分配给app的唯一标识
* @param string $redirect_uri app的入口地址授权临时令牌会以queryString的形式跟在该url后返回。注意参数中回调地址的域名必须与app注册时填写的回调地址的域名匹配。
* @param string $site site参数标识当前授权的站点直接填写1688
* @param string $state 可选app自定义参数回跳到redirect_uri时会原样返回
* return 用户输入用户名密码并确认授权返回临时授权码code给app具体返回方式参照redirect_uri说明
*/
function webAppAuth($redirect_uri,$site='1688',$state=''){
$appKey = $this->config['appkey'];
$url = "https://auth.1688.com/oauth/authorize?client_id={$appKey}&site={$site}&redirect_uri={$redirect_uri}&state={$state}";
//$res = Curl::curl_post($url,[]);
return "<script>location.href='{$url}'</script>";
}
/**
* desc使用code获取令牌【code有效期2分钟
*
* 注此接口必须使用POST方法提交必须使用https
getToken接口参数说明
a) grant_type为授权类型使用authorization_code即可
b) need_refresh_token为是否需要返回refresh_token如果返回了refresh_token原来获取的refresh_token也不会失效除非超过半年有效期
c) client_id为app唯一标识即appKey
d) client_secret为app密钥
e) redirect_uri为app入口地址
f) code为授权完成后返回的一次性令牌
g) 调用getToken接口不需要签名
如果超过code有效期2分钟或者已经使用code获取了一次令牌code都将失效需要返回第二步重新获取code
* authorwh
*/
function getTokenByCode($code,$redirect_uri){
$appkey = $this->config['appkey'];
$secret = $this->config['appsecret'];
$url = "https://gw.open.1688.com/openapi/";
$urlPath = "http/1/system.oauth2/getToken/".$appkey;
$paramstr = "?grant_type=authorization_code&need_refresh_token=true&client_id={$appkey}&client_secret={$secret}&redirect_uri={$redirect_uri}&code={$code}";
$url = $url.$urlPath.$paramstr;
return Curl::curl_post($url,[]);
}
/**
* 生成签名
*
1、 构造urlPath。urlPath是url 中的一部分,从协议开始截取,到“?”为止包含了协议、namespace、apiName和apiVersion如urlPath=http/1/system/currentTime/1688
2、 拼装参数。首先将所有参数按照key+value拼装得到一个字符串数组如[b1,a2]),然后对数组进行排序([a2,b1]最后将排序后的数组合并为字符串a2b1
3、 合并。把前两步的字符串拼接http/1/system/currentTime/1688a2b1
4、 执行hmac_sha1算法。 Signature=uppercase (hex (hmac_sha1 (concatString, secretKey))假设secretKey=test123那么得到的签名为2F1E96587451DE0E171978F4AAE1A90FF9B2F94B
a) concatString为合并后的字符串
b) secretKey为签名密钥与urlPath中的appKey1000000对应
c) hmac_sha1为通用的hmac_sha1算法各编程语言一般都对应类库
d) hex为转为十六进制
e) uppercase为转为大写字符
* authorwh
* @param string $urlPath eg: param2/1/com.alibaba.trade/alibaba.trade.fastCreateOrder/7571561
* @param array $params 请求参数
* @return string
*/
public function signature($urlPath,$params)
{
$accessToken = $this->config['accessToken'];
$params['access_token'] = $accessToken;
//$url = 'http://gw.open.1688.com/openapi';//1688开放平台使用gw.open.1688.com域名
//$appKey = $this->config['appkey'];
$appSecret = $this->config['appsecret'];
//dump('$urlPath: '.$urlPath);
//$apiInfo = 'param2/1/system/currentTime/' . $appKey;//此处请用具体api进行替换
//$urlPath = $urlPath . $appKey;//此处请用具体api进行替换
//配置参数请用apiInfo对应的api参数进行替换
$aliParams = array();
foreach ($params as $key => $val) {
$aliParams[] = $key . $val;
}
sort($aliParams);
$sign_str = join('', $aliParams);
$sign_str = $urlPath . $sign_str;
$code_sign = strtoupper(bin2hex(hash_hmac("sha1", $sign_str, $appSecret, true)));
return $code_sign;
}
/**
* desc发起请求自动完成签名并返回结果
* authorwh
* @param string $urlPath eg: param2/1/com.alibaba.trade/alibaba.trade.fastCreateOrder/23222222
* @param array $request_data 接口请求数据
*/
function request($urlPath, $request_data){
set_time_limit(0);
$request_params_str = '';
foreach ($request_data as $k=>$v){
if($request_params_str == ''){
$request_params_str .= $k.'='.$v;
}else{
$request_params_str .= '&'.$k.'='.$v;
}
}
$accessToken = $this->config['accessToken'];
//dump($request_params_str);
$_aop_signature = $this->signature($urlPath,$request_data);
//dump($_aop_signature);
//$request_data['_aop_signature'] = $_aop_signature;
$url = $this->base_url.$urlPath.'?'.$request_params_str.'&access_token='.$accessToken.'&_aop_signature='.$_aop_signature;
//dump($url);
$this->request_body = $url;
$res = Curl::curl_post($url,[]);
return $res;
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/29} {12:23}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes;
class BaseStrict
{
public $base_url = 'https://gw.open.1688.com/openapi/';
//授权对象
protected $authObj = null;
public function __construct($authObj)
{
$this->authObj = $authObj;
//初始化并设置当前模块的基础url
$this->authObj->base_url = $this->base_url;
}
}

View File

@@ -0,0 +1,3 @@
## distributes 分销模块
### strict 分销中严选模块(商家自营)

View File

@@ -0,0 +1,158 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/29} {12:57}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes;
use app\common\model\TabConf;
use wanghua\general_utility_tools_php\alibaba\AlibabaAuth;
use wanghua\general_utility_tools_php\alibaba\distributes\strict\CustomerService;
use wanghua\general_utility_tools_php\alibaba\distributes\strict\StrictCategory;
use wanghua\general_utility_tools_php\alibaba\distributes\strict\StrictGoods;
use wanghua\general_utility_tools_php\alibaba\distributes\strict\StrictLogistics;
use wanghua\general_utility_tools_php\alibaba\distributes\strict\StrictOrder;
use think\Db;
use wanghua\general_utility_tools_php\alibaba\distributes\strict\StrictPay;
/**
* 测试案例
*
* Class Testexample
* @package wanghua\general_utility_tools_php\alibaba\distributes
*/
class Testexample
{
/**
* desc查询订单是否开通免密支付
*/
function queryOrderIsNoPassPay(){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictPay($authObj);
return $obj->queryOrderIsNoPassPay();
}
function getLogisticsTemplate(){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictLogistics($authObj);
return $obj->getLogisticsTemplate();
}
//物流模板详情
function getLogisticsTemplateDetail($template_id){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictLogistics($authObj);
return $obj->getLogisticsTemplateDetail($template_id);
}
function getGoodsList($where){
set_time_limit(0);
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictGoods($authObj);
return $obj->getGoodsList($where);
}
function getGoodsListsPft($where){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictGoods($authObj);
return $obj->getGoodsListsPft($where);
}
function getGoodsDetail(array $itemIds){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictGoods($authObj);
return $obj->getGoodsDetail($itemIds);
}
function getCategoryByCID($category_id){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictCategory($authObj);
return $obj->getCategoryByCID($category_id);
}
function testGetRootCategory(){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$obj = new StrictCategory($authObj);
return $obj->getRootCategory();
}
function testWebAuth($redirect_uri){
$config = config('meebo_supply_config');
$res = (new AlibabaAuth($config))->webAppAuth($redirect_uri);
return $res;
}
function getTokenByCode($code,$redirect_uri){
$config = config('meebo_supply_config');
$res = (new AlibabaAuth($config))->getTokenByCode($code,$redirect_uri);
return $res;
}
//测试创建订单
function createOrder(){
$order_info = Db::table(TabConf::$fa_order)
->where('orderid','mm70wgex1719586377768')
->find();
$address = Db::table(TabConf::$fa_useraddress)
->where('id',$order_info['useraddress_id'])
->where('users_id',$order_info['users_id'])
->find();
$goods = Db::table(TabConf::$fa_goods)
->where('id',$order_info['goods_id'])
->find();
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$order = new StrictOrder($authObj);
$order->setAddress($address);
$order->setGoods($goods,$order_info['buy_num']);
return $order->createOrder($order_info);
}
function previewOrder(){
$order_info = Db::table(TabConf::$fa_order)
->where('orderid','mm70wgex1719586377768')
->find();
$address = Db::table(TabConf::$fa_useraddress)
->where('id',$order_info['useraddress_id'])
->where('users_id',$order_info['users_id'])
->find();
$goods = Db::table(TabConf::$fa_goods)
->where('id',$order_info['goods_id'])
->find();
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$order = new StrictOrder($authObj);
$order->setAddress($address);
$order->setGoods($goods,$order_info['buy_num']);
return $order->previewOrder();
}
//查询订单可以支持的支付渠道
function queryOrderPayChannel($order_id){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$order = new StrictPay($authObj);
return $order->queryOrderPayChannel($order_id);
}
//组合收银台url获取
function getPayUrl(array $orderIds){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$order = new StrictPay($authObj);
return $order->getPayUrl($orderIds,'PC');
}
//获取唤起旺旺聊天的链接
function getCustomerServiceLink($toOpenUid){
$config = config('meebo_supply_config');
$authObj = new AlibabaAuth($config);
$order = new CustomerService($authObj);
return $order->getCustomerServiceLink($toOpenUid);
}
}

View File

@@ -0,0 +1,127 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/29} {17:31}
*/
namespace app\index\controller;
use app\common\model\TabConf;
use app\index\logic\alibaba\distributes\notify\BaseStrictNotify;
use wanghua\general_utility_tools_php\alibaba\distributes\Testexample;
use app\index\logic\alibabanotify\ProductNotify;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 阿里巴巴异步通知控制器类,是消息处理入口类
* Class Alibabanotify
* @package app\index\controller
*/
class Alibaba extends BasePublicController
{
/**
* desc通知
* index/Alibaba/notify
*
* 输出message
{
"data":{
"productIds":"107680826",
"msgSendTime":"2018-05-30 20:29:37",
"memberId":"shyxsscl",
"status":"RELATION_VIEW_PRODUCT_REPOST"
},
"gmtBorn":1720459935606,
"msgId":88837491757,
"type":"PRODUCT_RELATION_VIEW_PRODUCT_REPOST",
"userInfo":"b2b-1881150273"
}
* authorwh
* @return \think\response\Json
*/
function notify(){
return Mmodel::catchJson(function (){
Tools::log_to_write_txt(['执行消息处理']);
//根据消息类型,选择对应的处理类
$message_data = input('message');
if(empty($message_data)){
return Tools::set_fail('message为空');
}
$message_data = json_decode($message_data,true);
Tools::log_to_write_txt(['消息解码'=>$message_data]);
if(empty($message_data['type'])){
return Tools::set_fail('type为空');
}
//$key = strtolower(explode('_',$message_data['type'])[0]);
//获取消息类型
$fn = strtolower($message_data['type']);//类型即为方法名
//消息类型前缀即为类名
$class = '\\app\\index\\logic\\alibabanotify\\'.ucfirst(explode('_',$fn)[0]).'Notify';
//实例化类
return (new $class())->{$fn}($message_data);//调用方法
});
}
/**
* descali采购获取临时授权code
*
* index/alibaba/webAuthCode
*
* authorwh
* @return string
*/
function webAuthCode(){
$Testexample = new Testexample();
$res = $Testexample->testWebAuth(request()->domain().'/index/alibaba/webUserAuth');
return $res;
}
/**
* desc网页授权,code换取token
*
* index/alibaba/webUserAuth
*
* authorwh
*/
function webUserAuth(){
$code = input('code');
if(empty($code)){
return $this->error('授权失败');
}
$redirect_uri = url('test/test2');
$Testexample = new Testexample();
$res = $Testexample->getTokenByCode($code,$redirect_uri);
if($res['code'] != 200){
return $this->error('授权失败.'.$res['msg']);
}
$data = json_decode($res['data']);
$auth_data = [
'access_token'=>$data['access_token'],
'aliid'=>$data['aliId'],
'expires_in'=>$data['expires_in'],
'memberid'=>$data['memberId'],
'refresh_token'=>$data['refresh_token'],
'refresh_token_timeout'=>$data['refresh_token_timeout'],
'resource_owner'=>$data['resource_owner'],
];
//写入数据库
Mmodel::existsUpdateInsert(TabConf::$fa_alibaba_user_auth,['access_token'=>$data['access_token']],$auth_data);
return '授权成功';
}
//function getCode(){
// return Mmodel::catchJson(function (){
// //保存code
// $code = input('code');
//
// });
//}
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/25} {0:41}
*/
namespace app\index\logic\alibabanotify;
use app\index\logic\BaseLogic;
class BaseAlibabaLogic extends BaseLogic
{
//接收消息成功后只要响应http code是200,则认为消息消费成功
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/25} {1:04}
*/
namespace app\index\logic\alibabanotify;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
class LogisticsNotify extends BaseAlibabaLogic
{
/**
* 物流单状态变更(买家视角)
*/
function logistics_buyer_view_trace($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 物流单号修改消息
*/
function logistics_mail_no_change($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
}

View File

@@ -0,0 +1,137 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/25} {0:37}
*/
namespace app\index\logic\alibabanotify;
use app\index\logic\BaseLogic;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
class OrderNotify extends BaseAlibabaLogic
{
/**
* desc1688创建订单买家视角/order created (buyer view)
* authorwh
* @return array
*/
function order_buyer_view_buyer_make($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* desc1688交易成功卖家视角
* authorwh
*/
function order_buyer_view_order_success($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688订单批量支付状态同步消息
*/
function order_batch_pay($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688交易付款买家视角/1688 transaction payment (buyer view)
*/
function order_buyer_view_order_pay($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688卖家关闭订单买家视角/seller closing order (buyer view)
*/
function order_buyer_view_order_seller_close($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688修改订单价格买家视角/order price modification (buyer view)
*/
function order_buyer_view_order_price_modify($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688订单发货买家视角/1688 order delivery (buyer view)
*/
function order_buyer_view_announce_sendgoods($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688订单部分发货买家视角/Partial delivery of 1688 order (buyer view)
*/
function order_buyer_view_part_part_sendgoods($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 商家修改订单地址(买家视角)
*/
function order_buyer_view_order_seller_modify_adress($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688订单售中退款买家视角
*/
function order_buyer_view_order_buyer_refund_in_sales($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688订单售后退款买家视角
*/
function order_buyer_view_order_refund_after_sales($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688买家关闭订单买家视角/buyer closing order (buyer view)
*/
function order_buyer_view_order_buyer_close($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688订单确认收货买家视角/order receipt confirmation (buyer view)
*/
function order_buyer_view_order_comfirm_receivegoods($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/25} {1:06}
*/
namespace app\index\logic\alibabanotify;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
class ProductNotify extends BaseAlibabaLogic
{
/**
* 1688产品审核下架关系用户视角
*/
function product_relation_view_product_audit($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688产品删除关系用户视角
*/
function product_relation_view_product_delete($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688产品新增或修改关系用户视角
*/
function product_relation_view_product_new_or_modify($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688产品上架关系用户视角
*/
function product_relation_view_product_repost($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688商品库存变更消息关系用户视角
*/
function product_product_inventory_change($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 精选货源商品下架消息
*/
function product_pft_offer_quit($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 精选货源商品价格变动消息
*/
function product_pft_offer_price_modify($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
/**
* 1688产品下架关系用户视角
*/
function product_relation_view_product_expire($message_data){
return Mmodel::catch(function ()use($message_data){
Tools::log_to_write_txt([__FUNCTION__.'消息入参:'=>$message_data]);
});
}
}

View File

@@ -0,0 +1,18 @@
## 阿里巴巴开放平台消息通知模板
### 特别说明
#### Alibaba.php是控制器类复制可用是消息通道httpCallback的回调地址类文件。
##### eghttp://www.xxx.com/index/alibaba/notify
##### 回调地址配置入口https://open.1688.com/develop/app/detail?spm=a260s.develop-app-list.0.0.4d875bfaS5YzoD&appkey=7571564&appKey=7571564
进入页面后找到“日常消息测试”按钮,点击后在弹出的界面中进行配置。
### 文件说明
#### 按照消息类型前缀分类(复制可用)
1. 订单相关 OrderNotify.php
2. 产品相关 ProductNotify.php
3. 物流相关 LogisticsNotify.php
4. 售后相关
......以此类推,消息类型的小写就是类的方法名。
这里只是打个样,这几个模板可以直接复制,补充上自己的业务逻辑即可。

View File

@@ -0,0 +1,55 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/9} {13:42}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\notify;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 弃用,通知属于业务模块,直接在业务控制器中处理,这里仅作参考
* 分销严选消息通知基类
*
* 初始化该基类,按需调用通知处理类
*
* Class BaseStrictNotify
* @package app\index\logic\alibaba\distributes\notify
*/
class BaseStrictNotify
{
private $object = null;
/**
* desc执行消息处理
* authorwh
* @param $callback
* @return array
*/
function doMessage($callback=null){
return Mmodel::catch(function () use ($callback){
Tools::log_to_write_txt(['执行消息处理']);
//根据消息类型,选择对应的处理类
$message_data = input('message');
if(empty($message_data)){
return Tools::set_fail('message为空');
}
$message_data = json_decode($message_data,true);
Tools::log_to_write_txt(['消息解码'=>$message_data]);
if(empty($message_data['type'])){
return Tools::set_fail('type为空');
}
//获取消息类型
$fn = strtolower($message_data['type']);//类型即为方法名
//消息类型前缀即为类名
$class = '\\app\\index\\logic\\alibaba\\distributes\\notify\\'.ucfirst(explode('_',$fn)[0]).'Notify';
$this->object = (new $class());//实例化类
return $this->object->{$fn}($callback);//调用方法
});
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/9} {13:25}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\notify;
use app\index\logic\alibaba\distributes\Testexample;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 弃用,通知属于业务模块,直接在业务控制器中处理,这里仅作参考
* 产品 消息通知
*
* Class ProductNotify
* @package app\index\logic\alibaba\distributes\notify
*/
class ProductNotify extends BaseStrictNotify
{
/**
* @deprecated 弃用,通知属于业务模块,直接在业务控制器中处理,这里仅作参考
* desc商品上架通知
* authorwh
*
* 输出message
{"message":"{\"data\":{\"productIds\":\"107680826\",\"msgSendTime\":\"2018-05-30 20:29:37\",\"memberId\":\"shyxsscl\",\"status\":\"RELATION_VIEW_PRODUCT_REPOST\"},\"gmtBorn\":1720514151217,\"msgId\":88922343751,\"type\":\"PRODUCT_RELATION_VIEW_PRODUCT_REPOST\",\"userInfo\":\"b2b-1881150273\"}","_aop_signature":"03EB6538BB8864BC141ADB5A4923005BB0204F85"}
*/
function product_relation_view_product_repost($callback=null)
{
return Mmodel::catch(function () use ($callback){
$message_data = input('message');
if(empty($message_data)){
return Tools::set_fail('message不能为空');
}
$message_data = json_decode($message_data,true);
if(empty($message_data['data'])){
return Tools::set_fail('data不能为空');
}
$productIds = $data['productIds'];//商品ID集合至少有一个用逗号分割
$productIdsArr = explode(',',$productIds);
});
}
}

View File

@@ -0,0 +1,3 @@
#这是异步通知案例
##业务逻辑自行编写

View File

@@ -0,0 +1,27 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/9} {0:43}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\strict;
use wanghua\general_utility_tools_php\alibaba\distributes\BaseStrict;
class CustomerService extends BaseStrict
{
/**
* desc获取唤起旺旺聊天的链接
* authorwh
* @param $toOpenUid
* @return mixed
*/
function getCustomerServiceLink($toOpenUid){
$urlPath = "param2/1/com.alibaba.account/account.wangwangUrl.get/".$this->authObj->config['appkey'];
return $this->authObj->request($urlPath, ['toOpenUid'=>$toOpenUid]);
}
}

View File

@@ -0,0 +1,4 @@
## 分销严选模块
只能使用分销严选方案里的接口
DOC:
https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.product:alibaba.category.get-1&aopApiCategory=category_new

View File

@@ -0,0 +1,63 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/29} {12:12}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\strict;
use wanghua\general_utility_tools_php\alibaba\distributes\BaseStrict;
/**
* 根据类目Id查询类目
*
* 传0获取所有一级类目循环调用可以获取类目树
*
* 注意类目id,必须大于等于0 如果为0则查询所有一级类目
*
* 类目查询。如果需要获取所有1688类目信息需要从根类目开始遍历获取整个类目树。
* 即先传0获取所有一级类目ID然后在通过获取到的一级类目ID遍历获取所二级类目最后通过遍历二级类目ID获取三级类目。
* 注意1688类目仅三级三级类目即发布商品所需的叶子类目。
*
* doc: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.product:alibaba.category.get-1&aopApiCategory=category_new
* Class StrictCategory
* @package app\index\logic\alibaba\distributes\strict
*/
class StrictCategory extends BaseStrict
{
public function __construct($authObj)
{
parent::__construct($authObj);
}
/**
* desc获取严选一级分类顶级分类
* authorwh
* @return mixed
*/
function getRootCategory(){
//$base_url = "https://gw.open.1688.com/openapi/";
$urlPath = "param2/1/com.alibaba.product/alibaba.category.get/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['categoryID'] = '0';
return $this->authObj->request($urlPath,$request_data);
}
/**
* desc根据类目id获取类目信息
* authorwh
* @param $category_id
* @return mixed
*/
function getCategoryByCID($category_id){
$urlPath = "param2/1/com.alibaba.product/alibaba.category.get/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['categoryID'] = $category_id;
return $this->authObj->request($urlPath,$request_data);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,52 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/30} {1:54}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\strict;
use wanghua\general_utility_tools_php\alibaba\distributes\BaseStrict;
/**
* 物流
* Class StrictLogistics
* @package app\index\logic\alibaba\distributes\strict
*/
class StrictLogistics extends BaseStrict
{
/**
* 获取物流模板详情
*
* 根据物流模版ID获取卖家的物流模板。运费模板ID为0表示运费说明为1表示卖家承担运费
*
* doc:https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.logistics:alibaba.logistics.myFreightTemplate.list.get-1&aopApiCategory=Logistics_NEW
*/
function getLogisticsTemplateDetail($template_id){
$urlPath = "param2/1/com.alibaba.logistics/alibaba.logistics.myFreightTemplate.list.get/".$this->authObj->config['appkey'];
return $this->authObj->request($urlPath, ['templateId'=>$template_id]);
}
function getLogisticsTemplate(){
$urlPath = "param2/1/com.alibaba.logistics/alibaba.logistics.myFreightTemplate.list.get/".$this->authObj->config['appkey'];
return $this->authObj->request($urlPath, []);
}
/**
* desc 获取物流公司列表
*
* 物流公司列表-自联物流
*
* doc:https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.logistics:alibaba.logistics.OpQueryLogisticCompanyList.offline-1&aopApiCategory=Logistics_NEW
* authorwh
*/
function getLogisticsCompanyList(){
$urlPath = "param2/1/com.alibaba.logistics/alibaba.logistics.OpQueryLogisticCompanyList.offline/".$this->authObj->config['appkey'];
return $this->authObj->request($urlPath, []);
}
}

View File

@@ -0,0 +1,437 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/28} {15:11}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\strict;
use Exception;
use wanghua\general_utility_tools_php\http\Curl;
use wanghua\general_utility_tools_php\alibaba\distributes\BaseStrict;
/**
* 分销订单
* Class Order
* @package app\index\logic\alibaba\distributes
*/
class StrictOrder extends BaseStrict
{
//用户收货地址
private $address = [];
//商品信息
private $goods = [];
//发票信息
private $invoice = [];
//交易类型 由于不同的商品支持的交易方式不同没有一种交易方式是全局通用的所以当前下单可使用的交易方式必须通过下单预览接口的tradeModeNameList获取。
public $tradeType = '';
/**
* desc收货地址说明
1、使用保存的收货地址传参示例{"addressId":593861699}其中addressId是调用“买家获取保存的收货地址信息列表”接口获取
2、使用地址编码传参示例{"address":"桃浦镇 金达路888号贸易8楼","phone": "0517-88990077","mobile": "15251667788","fullName": "张三","postCode": "000000","districtCode": "310107"}其中districtCode需要调用“根据地址解析地区码”接口获取
3、直接使用文本地址示例{"address":"网商路699号","phone": "0517-88990077","mobile": "15251667788","fullName": "张三","postCode": "000000","areaText": "滨江区","townText": "","cityText": "杭州市","provinceText": "浙江省"},省市区要传文本;
4、优先级说明如果同时传入以上参数系统按从1至3的优先级获取地址满足1的条件下不会使用上示2中的参数满足2的条件下不会使用上示3中的参数
* authorwh
* @param $address
*/
function setAddress($address){
//{
//"address":"网商路699号",
//"phone":"0517-88990077",
//"mobile":"15251667788",
//"fullName":"张三",
//"postCode":"000000",
//"areaText":"滨江区",
//"townText":"",
//"cityText":"杭州市",
//"provinceText":"浙江省"
//}
//用户收货地址
$this->address = [
//'addressId'=>$address['id'],
'address'=>$address['address'],//街道地 网商路699号
'phone'=>$address['mobile'],//电话 0517-88990077
'mobile'=>$address['mobile'],//手机
'fullName'=>$address['name'],
'postCode'=>'000000',//邮编 000000
'areaText'=>$address['area'],//区文本
'townText'=>$address['town'],//镇文本
'cityText'=>$address['city'],//市文本
'provinceText'=>$address['provice'],//省份文本
//'districtCode'=>'000000',//地址编码 310107
];
}
function setGoods($goods,$buy_num){
$this->goods = [
'offerId'=>$goods['offerid'],
'quantity'=>$buy_num,//$goods['quantity'],//商品数量(计算金额用)
];
if(!empty($goods['specid'])){
$this->goods['specId'] = $goods['specid'];//商品规格id
}
}
/**
* desc设置发票信息
* authorwh
*/
function setInvoice(array $invoice){
$this->invoice = $invoice;
}
/**
* desc创建分销订单 下单
*
* 获取订单运费,请调用预览订单接口 alibaba.createOrder.preview
*
* 【优先推荐】
* 优先推荐(适用于大部分场景):如果渠道服务的用户在采购过程中,价格是核心决策点,推荐直接使用接口中的"最优价下单“能力即在订单预览、下单过程中选择flow为空1688会在所有买家可下单的流程中选择最优价进行下单;
* 如果渠道服务的对象以代发群体为主(例如淘卖)需要依赖包邮服务在分销严选商品下单时fow选择【boutiquefenxiao】下单这样即使在采购多件时依然能够保证包邮、48小时发货、7天无理由以及极速退款服务;
* 如果渠道服务对象以批发群体为主(例如跨境),对干是否包,邮的服务不是特别敏感在分销严选商品下单时fow选择[boutiquepifa】进行下单使用该接口时如果只单笔订单购买一件依然以包邮价进行下单在单笔多件时会自动切换为批发价+邮费的模式下单,
*
* @param $order
* @param string $flow 下单时必填 boutiquefenxiao(新分销严选包邮) boutiquepifa(分销批发)
* authorwh
*/
function createOrder($order,$flow){
if(empty($this->authObj)){
throw new Exception('未初始化授权对象');
}
if(empty($this->address)){
throw new Exception('未设置收货地址');
}
if(empty($this->goods)){
throw new Exception('未设置商品信息');
}
//$base_url = "https://gw.open.1688.com/openapi/";
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.fastCreateOrder/".$this->authObj->config['appkey'];
if(empty($this->tradeType)){
throw new Exception('交易类型必须');
}
//由于不同的商品支持的交易方式不同,没有一种交易方式是全局通用的,
//所以当前下单可使用的交易方式必须通过下单预览接口的tradeModeNameList获取。
$request_data = ['tradeType'=>$this->tradeType];
//boutiquefenxiao(新分销严选)
$request_data['flow'] = $flow;//'boutiquefenxiao';
$request_data['isvBizTypeStr'] = 'fenxiaoMedia';
$request_data['message'] = $order['remark'];//买家留言
//地址
$request_data['addressParam'] = json_encode($this->address,JSON_UNESCAPED_UNICODE);
//商品信息
$request_data['cargoParamList'] = json_encode($this->goods,JSON_UNESCAPED_UNICODE);
//发票信息
$request_data['invoiceParam'] = json_encode($this->invoice,JSON_UNESCAPED_UNICODE);
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 创建订单前预览数据接口
*
* 【下单的时候flow是必填的。只有在预览的时候不填写flow会返回最优的下单方式】
* 【调用预览接口,这个是最准确的。】
*
* 订单创建只允许购买同一个供应商的商品。
* 本接口返回创建订单相关的优惠等信息。
* 1、校验商品数据是否允许订购。 2、校验代销关系 3、校验库存、起批量、是否满足混批条件
*
* doc: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.createOrder.preview-1&aopApiCategory=trade_new
*/
function previewOrder($flow=''){
$urlPath = "param2/1/com.alibaba.trade/alibaba.createOrder.preview/".$this->authObj->config['appkey'];
$request_data = [];
//boutiquefenxiao(新分销严选)
$request_data['flow'] = $flow;//'boutiquefenxiao';
//$request_data['isvBizTypeStr'] = 'fenxiaoMedia';
//$request_data['message'] = $order['remark'];//买家留言
//地址
$request_data['addressParam'] = json_encode($this->address,JSON_UNESCAPED_UNICODE);
//商品信息
$request_data['cargoParamList'] = json_encode($this->goods,JSON_UNESCAPED_UNICODE);
//发票信息
$request_data['invoiceParam'] = json_encode($this->invoice,JSON_UNESCAPED_UNICODE);
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 取消交易
*
* 买家或者卖家取消交易注意只有特定状态的交易才能取消1688可用于取消未付款的订单。
*
* doc: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.cancel-1&aopApiCategory=trade_new
*
* authorwh
* @param string $order_id 订单号
* @param string $cancelReason 原因描述buyerCancel:买家取消订单;sellerGoodsLack:卖家库存不足;other:其它
*
* 返回:{
"success": true
}
*/
function cancelOrder($order_id, $cancelReason, $remark=''){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.cancel/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['tradeID'] = $order_id;
$request_data['webSite'] = 1688;
//原因描述buyerCancel:买家取消订单;sellerGoodsLack:卖家库存不足;other:其它
$request_data['cancelReason'] = $cancelReason;
if($remark){
$request_data['remark'] = $remark;
}
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 修改订单备忘
*
* 授权用户为卖家修改卖家备忘,授权用户为买家修改买家备忘 注意:该接口可重复调用,备注内容将覆盖前一次调用
*
* doc: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.order.memoAdd-1&aopApiCategory=trade_new
*
*/
function updateOrderRemark($order_id,$remark){
$urlPath = "param2/1/com.alibaba.trade/alibaba.order.memoAdd/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderId'] = $order_id;
$request_data['memo'] = $remark;
//备忘图标目前仅支持数字。1位红色图标2为蓝色图标3为绿色图标4为黄色图标
$request_data['remarkIcon'] = 2;
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 根据地址解析地区码
*
* 根据地址信息,解析地区码
*
* doc:https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.addresscode.parse-1&aopApiCategory=trade_new
*
*返回:{
"result": {
"address": "网商路699号",
"addressCode": "330108",
"isDefault": false,
"latest": false,
"postCode": "310051"
}
}
*/
function getAreaCode($address){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.addresscode.parse/".$this->authObj->config['appkey'];
$request_data = [];
//地址信息 eg: 浙江省 杭州市 滨江区网商路699号
$request_data['addressInfo'] = $address;
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 获取交易地址代码表详情
*
* 获取交易地址代码表该API会返回输入code的详情和该code的下一级地区code.
*
* doc: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.addresscode.get-1&aopApiCategory=trade_new
*
* 返回:{
"result": {
"code": "330108",
"name": "滨江区",
"parentCode": "330100",
"post": "310051",
"children": ["330108001",
"330108002",
"330108003"]
}
}
*/
function getAreaCodeDetail($areaCode){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.addresscode.get/".$this->authObj->config['appkey'];
$request_data = [];
//地址code码 eg: 330108
$request_data['areaCode'] = $areaCode;
$request_data['webSite'] = 1688;
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 订单列表查看(买家视角)
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.getBuyerOrderList-1&aopApiCategory=trade_new
*
*/
function getOrderList($queryParams=[],$page=1,$pageSize=20){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.getSellerOrderList/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['page'] = $page;
$request_data['pageSize'] = $pageSize;
//业务类型,支持: "cn"(普通订单类型), "ws"(大额批发订单类型), "yp"(普通拿样订单类型), "yf"(一分钱拿样订单类型),
// "fs"(倒批(限时折扣)订单类型), "cz"(加工定制订单类型), "ag"(协议采购订单类型), "hp"(伙拼订单类型),
// "gc"(国采订单类型), "supply"(供销订单类型), "nyg"(nyg订单类型), "factory"(淘工厂订单类型),
// "quick"(快订下单), "xiangpin"(享拼订单), "nest"(采购商城-鸟巢), "f2f"(当面付), "cyfw"(存样服务),
// "sp"(代销订单标记), "wg"(微供订单), "factorysamp"(淘工厂打样订单), "factorybig"(淘工厂大货订单)
if(isset($queryParams['bizTypes'])){
$request_data['bizTypes'] = $queryParams['bizTypes'];
}
//下单开始时间 20180802211113000+0800
if(isset($queryParams['createStartTime'])){
$request_data['createStartTime'] = $queryParams['createStartTime'];
}
//下单结束时间 20180802211113000+0800
if(isset($queryParams['createEndTime'])){
$request_data['createEndTime'] = $queryParams['createEndTime'];
}
//是否查询历史订单表,默认查询当前表即默认值为false
if(isset($queryParams['isHis'])){
$request_data['isHis'] = $queryParams['isHis'];
}
//查询修改时间结束 20180802211113000+0800
if(isset($queryParams['modifyStartTime'])){
$request_data['modifyStartTime'] = $queryParams['modifyStartTime'];
}
//查询修改时间结束 20180802211113000+0800
if(isset($queryParams['modifyEndTime'])){
$request_data['modifyEndTime'] = $queryParams['modifyEndTime'];
}
//订单状态,值有 success, cancel(交易取消,违约金等交割完毕), waitbuyerpay(等待卖家付款)
// waitsellersend(等待卖家发货),waitbuyerreceive(等待买家收货 )
if(isset($queryParams['orderStatus'])){
$request_data['orderStatus'] = $queryParams['orderStatus'];
}
//查询分页页码从1开始
//if(isset($queryParams['page'])){
// $request_data['page'] = $queryParams['page'];
//}
////查询的每页的数量 20
//if(isset($queryParams['pageSize'])){
// $request_data['pageSize'] = $queryParams['pageSize'];
//}
//退款状态,支持: "waitselleragree"(等待卖家同意), "refundsuccess"(退款成功), "refundclose"(退款关闭),
// "waitbuyermodify"(待买家修改), "waitbuyersend"(等待买家退货), "waitsellerreceive"(等待卖家确认收货)
if(isset($queryParams['refundStatus'])){
$request_data['refundStatus'] = $queryParams['refundStatus'];
}
//卖家memberId b2b-1624961198
if(isset($queryParams['sellerMemberId'])){
$request_data['sellerMemberId'] = $queryParams['sellerMemberId'];
}
//卖家loginId alitestforisv02
if(isset($queryParams['sellerLoginId'])){
$request_data['sellerLoginId'] = $queryParams['sellerLoginId'];
}
//卖家评价状态 (4:已评价,5:未评价,6;不需要评价)
if(isset($queryParams['sellerRateStatus'])){
$request_data['sellerRateStatus'] = $queryParams['sellerRateStatus'];
}
//交易类型: 担保交易(1), 预存款交易(2), ETC境外收单交易(3), 即时到帐交易(4), 保障金安全交易(5), 统一交易流程(6),
// 分阶段交易(7), 货到付款交易(8), 信用凭证支付交易(9), 账期支付交易(10), 1688交易4.0,新分阶段交易(50060),
// 当面付的交易流程(50070), 服务类的交易流程(50080)
if(isset($queryParams['tradeType'])){
$request_data['tradeType'] = $queryParams['tradeType'];
}
//商品名称
if(isset($queryParams['productName'])){
$request_data['productName'] = $queryParams['productName'];
}
//是否需要查询买家的详细地址信息和电话 false
if(isset($queryParams['needBuyerAddressAndPhone'])){
$request_data['needBuyerAddressAndPhone'] = $queryParams['needBuyerAddressAndPhone'];
}
//是否需要查询备注信息 false
if(isset($queryParams['needMemoInfo'])){
$request_data['needMemoInfo'] = $queryParams['needMemoInfo'];
}
//外部订单号,可用于控制幂等 90187872898371
if(isset($queryParams['outOrderId'])){
$request_data['outOrderId'] = $queryParams['outOrderId'];
}
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 订单详情查看(买家视角)
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.get.buyerView-1&aopApiCategory=trade_new
*/
function getBuyerView(int $orderId,$webSite='1688',$includeFields='',$attributeKeys='',$outOrderId=''){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.get.buyerView/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderId'] = $orderId;//交易的订单id 1234345
$request_data['webSite'] = $webSite;//站点信息指定调用的API是属于国际站alibaba还是1688网站1688
$request_data['includeFields'] = $includeFields;//查询结果中包含的域GuaranteesTerms保障条款NativeLogistics物流信息RateDetail评价详情OrderInvoice发票信息。默认返回GuaranteesTerms、NativeLogistics、OrderInvoice。
$request_data['attributeKeys'] = $attributeKeys;//垂直表中的attributeKeys
$request_data['outOrderId'] = $outOrderId;//外部订单id控制幂等
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 获取交易订单的物流信息(买家视角)
*
* 该接口需要获得订单买家的授权,获取买家的订单的物流详情,在采购或者分销场景中,作为买家也有获取物流详情的需求。
* 该接口能查能根据订单号查看物流详情,包括发件人,收件人,所发货物明细等。由于物流单录入的原因,
* 可能跟踪信息的API查询会有延迟。该API需要向开放平台申请权限才能访问。
*
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.logistics:alibaba.trade.getLogisticsInfos.buyerView-1&aopApiCategory=Logistics_NEW
*/
function getLogisticsInfosBuyerView(int $orderId,$webSite='1688',$fields=''){
$urlPath = "param2/1/com.alibaba.logistics/alibaba.trade.getLogisticsInfos.buyerView/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderId'] = $orderId;//交易的订单id 1234345
$request_data['webSite'] = $webSite;//站点信息指定调用的API是属于国际站是1688业务还是icbu业务 (1688或者alibaba)
$request_data['fields'] = $fields;//需要返回的字段,目前有:company.name,sender,receiver,sendgood。返回的字段要用英文逗号分隔开
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 获取交易订单的物流跟踪信息(买家视角)
*
* 该接口需要获取订单买家的授权,获取买家的订单的物流跟踪信息,在采购或者分销场景中,作为买家也有获取物流详情的需求。
* 该接口能查能根据物流单号查看物流单跟踪信息。由于物流单录入的原因可能跟踪信息的API查询会有延迟。
* 该API需要向开放平台申请权限才能访问。
*
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.logistics:alibaba.trade.getLogisticsTraceInfo.buyerView-1&aopApiCategory=Logistics_NEW
*/
function getLogisticsTraceInfoBuyerView(int $orderId,$webSite='1688',$logisticsId=''){
$urlPath = "param2/1/com.alibaba.logistics/alibaba.trade.getLogisticsTraceInfo.buyerView/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderId'] = $orderId;//交易的订单id 1234345
$request_data['webSite'] = $webSite;//站点信息指定调用的API是属于国际站是1688业务还是ic
$request_data['logisticsId'] = $logisticsId;//该订单下的物流编号 AL8234243
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 买家确认收货
*
* doc:https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:trade.receivegoods.confirm-1
*/
function receiveGoodsConfirm(int $orderId,$orderEntryIds=[]){
$urlPath = "param2/1/com.alibaba.trade/trade.receivegoods.confirm/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderId'] = $orderId;//交易的订单id 1234345
if($orderEntryIds){
$request_data['orderEntryIds'] = json_encode($orderEntryIds,JSON_UNESCAPED_UNICODE);
}
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/30} {12:14}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\strict;
use wanghua\general_utility_tools_php\alibaba\distributes\BaseStrict;
/**
* 支付相关
*
* Class StrictPay
* @package app\index\logic\alibaba\distributes\strict
*/
class StrictPay extends BaseStrict
{
/**
* desc 查询订单是否开通免密支付
* authorwh
*/
function queryOrderIsNoPassPay(){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.pay.protocolPay.isopen/".$this->authObj->config['appkey'];
$request_data = [];
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* desc查询订单可以支持的支付渠道
* authorwh
* @param string $order_id
*/
function queryOrderPayChannel(string $order_id){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.payWay.query/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderId'] = $order_id;
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* desc组合收银台url获取
* authorwh
* @param string $orderIds 订单列表 格式参考 字符串格式的数组:["123456789","123456789"]
* @param string $payPlatformType PC或WIRELESS
* @return mixed
*/
function getPayUrl(string $orderIds,string $payPlatformType){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.grouppay.url.get/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderIds'] = $orderIds;//json_encode($orderIds);
$request_data['payPlatformType'] = $payPlatformType;//PC或WIRELESS
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
}

View File

@@ -0,0 +1,257 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/7/23} {22:57}
*/
namespace wanghua\general_utility_tools_php\alibaba\distributes\strict;
use wanghua\general_utility_tools_php\alibaba\distributes\BaseStrict;
use wanghua\general_utility_tools_php\tool\Tools;
class StrictRefund extends BaseStrict
{
/**
* 创建退款退货申请
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.createRefund-1
*/
function createRefund($params){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.createRefund/".$this->authObj->config['appkey'];
$request_data = [];
//主订单
if(empty($params['orderId'])){
return Tools::set_fail('orderId不能为空');
}
$request_data['orderId'] = $params['orderId'];
//子订单 订单接口 productItems- subItemID 就是子订单ID
// 卖家拒绝退款后无法重新申请需要到1688后台退款单页面 修改退款协议
if(empty($params['orderEntryIds'])){
return Tools::set_fail('orderEntryIds不能为空');
}
$request_data['orderEntryIds'] = $params['orderEntryIds'];
//退款/退款退货。只有已收到货,才可以选择退款退货 退款:"refund"; 退款退货:"returnRefund"
if(empty($params['disputeRequest'])){
return Tools::set_fail('disputeRequest不能为空');
}
$request_data['disputeRequest'] = $params['disputeRequest'];
//退款金额(单位:分)。不大于实际付款金额;等待卖家发货时,必须为商品的实际付款金额。
if(empty($params['applyPayment'])){
return Tools::set_fail('applyPayment不能为空');
}
$request_data['applyPayment'] = $params['applyPayment'];
//退运费金额(单位:分)。
if(!isset($params['applyCarriage'])){
return Tools::set_fail('applyCarriage不能为空');
}
$request_data['applyCarriage'] = $params['applyCarriage'];
//退款原因id从API getRefundReasonList获取
if(empty($params['applyReasonId'])){
return Tools::set_fail('applyReasonId不能为空');
}
$request_data['applyReasonId'] = $params['applyReasonId'];
//退款申请理由2-150字
if(empty($params['description'])){
return Tools::set_fail('description不能为空');
}
$request_data['description'] = $params['description'];
//货物状态 售中等待卖家发货:"refundWaitSellerSend"; 售中等待买家收货:"refundWaitBuyerReceive";
// 售中已收货(未确认完成交易):"refundBuyerReceived" 售后未收货:"aftersaleBuyerNotReceived";
// 售后已收到货:"aftersaleBuyerReceived"
if(empty($params['goodsStatus'])){
return Tools::set_fail('goodsStatus不能为空');
}
$request_data['goodsStatus'] = $params['goodsStatus'];
//凭证图片URLs。1-5张必须使用API uploadRefundVoucher返回的“图片域名/相对路径”
if(isset($params['vouchers'])){
$request_data['vouchers'] = $params['vouchers'];
}
//子订单退款数量。仅在售中买家已收货(退款退货)时,可指定退货数量;默认,全部退货。[{"id":586683458996743215,"count":1}]
if(isset($params['orderEntryCountList'])){
$request_data['orderEntryCountList'] = $params['orderEntryCountList'];
}
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 查询退款退货原因(用于创建退款退货)
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.getRefundReasonList-1
*/
function getRefundReasonList(int $orderId,array $orderEntryIds,$goodsStatus){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.getRefundReasonList/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['orderId'] = $orderId;//主订单id 1234345
$request_data['orderEntryIds'] = json_encode($orderEntryIds);//子订单id Long[]
//售中等待买家发货:”refundWaitSellerSend"; 售中等待买家收货:"refundWaitBuyerReceive";
// 售中已收货(未确认完成交易):"refundBuyerReceived" 售后未收货:"aftersaleBuyerNotReceived";
// 售后已收到货:"aftersaleBuyerReceived"
$request_data['goodsStatus'] = $goodsStatus;//货物状态
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* @deprecated 【可在前端通过表单提交】
*
* 上传退款退货凭证
*
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.uploadRefundVoucher-1
*/
function uploadRefundVoucher($imageData){
//$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.uploadRefundVoucher/".$this->authObj->config['appkey'];
//$request_data = [];
//$request_data['imageData'] = $imageData;//凭证图片数据。小于1Mjpg格式。
//$res = $this->authObj->request($urlPath, $request_data);
//return $res;
}
/**
* 查询退款单列表(买家视角)
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.refund.buyer.queryOrderRefundList-1&aopApiCategory=trade_new
* 买家查看退款单列表,该接口不支持子账号查询,请使用主账号授权后查询
*/
function queryOrderRefundList($params){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.refund.buyer.queryOrderRefundList/".$this->authObj->config['appkey'];
$request_data = [];
//订单Id
if(isset($params['orderId'])){
$request_data['orderId'] = $params['orderId'];
}
//退款申请时间(起始) 20220926114526000+0800
if(isset($params['applyStartTime'])){
$request_data['applyStartTime'] = $params['applyStartTime'];
}
//退款申请时间(截止) 20220926114526000+0800
if(isset($params['applyEndTime'])){
$request_data['applyEndTime'] = $params['applyEndTime'];
}
//退款状态列表 等待卖家同意 waitselleragree;退款成功 refundsuccess;退款关闭 refundclose;
//待买家修改 waitbuyermodify;等待买家退货 waitbuyersend;等待卖家确认收货 waitsellerreceive
if(isset($params['refundStatusSet'])){
$request_data['refundStatusSet'] = $params['refundStatusSet'];
}
//卖家memberId
if(isset($params['sellerMemberId'])){
$request_data['sellerMemberId'] = $params['sellerMemberId'];
}
//当前页码
if(isset($params['currentPageNum'])){
$request_data['currentPageNum'] = $params['currentPageNum'];
}
//每页条数
if(isset($params['pageSize'])){
$request_data['pageSize'] = $params['pageSize'];
}
//退货物流单号传此字段查询时需同时传入sellerMemberId
if(isset($params['logisticsNo'])){
$request_data['logisticsNo'] = $params['logisticsNo'];
if(empty($params['sellerMemberId'])){
return Tools::set_fail('logisticsNo必须与sellerMemberId同时传入');
}
}
//退款修改时间(起始) 20220926114526000+0800
if(isset($params['modifyStartTime'])){
$request_data['modifyStartTime'] = $params['modifyStartTime'];
}
//退款修改时间(截止) 20220926114526000+0800
if(isset($params['modifyEndTime'])){
$request_data['modifyEndTime'] = $params['modifyEndTime'];
}
//1:售中退款2:售后退款0:所有退款单
if(isset($params['dipsuteType'])){
$request_data['dipsuteType'] = $params['dipsuteType'];
}
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 查询退款单详情-根据订单ID买家视角
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.refund.OpQueryBatchRefundByOrderIdAndStatus-1&aopApiCategory=trade_new
*/
function queryBatchRefundByOrderIdAndStatus(int $orderId,string $queryType){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.refund.OpQueryBatchRefundByOrderIdAndStatus/".$this->authObj->config['appkey'];
$request_data = [];
//订单Id
$request_data['orderId'] = $orderId;
//查询类型 1活动3:退款成功(只支持退款中和退款成功)
$request_data['queryType'] = $queryType;
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 退款单操作记录列表(买家视角)
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.refund.OpQueryOrderRefundOperationList-1
*/
function queryOrderRefundOperationList(string $refundId,$pageNo=1,$pageSize=100){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.refund.OpQueryOrderRefundOperationList/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['refundId'] = $refundId;
$request_data['pageNo'] = $pageNo;
$request_data['pageSize'] = $pageSize;
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 查询退款单详情-根据退款单ID买家视角
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.refund.OpQueryOrderRefund-1&aopApiCategory=trade_new
*/
function opQueryOrderRefund(string $refundId,$needTimeOutInfo=false,$needOrderRefundOperation=false){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.refund.OpQueryOrderRefund/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['refundId'] = $refundId;
if(isset($needTimeOutInfo)){
$request_data['needTimeOutInfo'] = $needTimeOutInfo;
}
if(isset($needOrderRefundOperation)){
$request_data['needOrderRefundOperation'] = $needOrderRefundOperation;
}
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 买家提交退款货信息
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.refund.returnGoods-1
*/
function buyerSubmitRefundGoodsInfo(string $refundId,string $logisticsCompanyNo,string $freightBill,string $description='',string $vouchers=''){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.refund.returnGoods/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['refundId'] = $refundId;
$request_data['logisticsCompanyNo'] = $logisticsCompanyNo;
$request_data['freightBill'] = $freightBill;
if($description){
$request_data['description'] = $description;//发货说明内容在2-200个字之间
}
//凭证图片URLs必须使用API alibaba.trade.uploadRefundVoucher返回的“图片域名/相对路径”,
//最多可上传 10 张图片 单张大小不超过1M支持jpg、gif、jpeg、png、和bmp格式。
// 请上传凭证,以便以后续赔所需(不上传将无法理赔)
if($vouchers){
$request_data['vouchers'] = $vouchers;//[https://cbu01.alicdn.com/img/ibank/2019/901/930/11848039109.jpg]
}
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
/**
* 取消退款退货申请
* https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade:alibaba.trade.cancelRefund-1
*/
function cancelRefund(string $refundId){
$urlPath = "param2/1/com.alibaba.trade/alibaba.trade.cancelRefund/".$this->authObj->config['appkey'];
$request_data = [];
$request_data['refundId'] = $refundId;
$res = $this->authObj->request($urlPath, $request_data);
return $res;
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/24} {10:20}
*/
namespace wanghua\general_utility_tools_php\alipay;
use app\index\model\AppOrderPayReqRecordModel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 调起支付宝支付
*
* Class AlipayLogic
*
* 依赖:
* composer require wanghua/general-utility-tools-php dev-master
* @deprecated
*/
class AlipayLogic
{
/**
* desc调起支付
* authorwh
* @return false|mixed|\SimpleXMLElement|string|\提交表单HTML文本|null
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
* @throws \think\exception\PDOException
*/
//function pay(string $notify_url,string $return_url=''){
///* *
// * 功能:支付宝手机网站支付接口(alipay.trade.wap.pay)接口调试入口页面
// * 版本2.0
// * 修改日期2016-11-01
// * 说明:
// * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
// 请确保项目文件有可写权限,不然打印不了日志。
// */
//
//header("Content-type: text/html; charset=utf-8");
//
//
//require_once Tools::get_root_path().'library/AlipayTradeWapPay/wappay/service/AlipayTradeService.php';
//require_once Tools::get_root_path().'library/AlipayTradeWapPay/wappay/buildermodel/AlipayTradeWapPayContentBuilder.php';
////require dirname ( __FILE__ ).DIRECTORY_SEPARATOR.'./../config.php';
//$config = config('pay_config.alipay');
//if(empty($_POST['WIDout_trade_no']) || trim($_POST['WIDout_trade_no'])=="") return null;
////商户订单号,商户网站订单系统中唯一订单号,必填
//$out_trade_no = input('WIDout_trade_no');
//if(empty($out_trade_no)){
// return '参数错误';
//}
//$order = AppOrderPayReqRecordModel::getOrder($out_trade_no);
//if(empty($order)){
// return '订单错误';
//}
////超时时间
//$timeout_express="1m";
//
//$payRequestBuilder = new \AlipayTradeWapPayContentBuilder();
//$payRequestBuilder->setBody($order['goods_name']);
//$payRequestBuilder->setSubject($order['goods_name']);
//$payRequestBuilder->setOutTradeNo($out_trade_no);
//$payRequestBuilder->setTotalAmount($order['goods_price']);
//$payRequestBuilder->setTimeExpress($timeout_express);
//
//$payResponse = new \AlipayTradeService($config);
//$result = $payResponse->wapPay($payRequestBuilder,$return_url,$notify_url);
//return $result;
//}
}

View File

@@ -0,0 +1,145 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/10/16} {14:07}
*/
namespace wanghua\general_utility_tools_php\alipay\pay;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 支付宝app支付
*
* Class AlipayAppPay
* @package wanghua\general_utility_tools_php\alipay\pay
*/
class AlipayAppPay
{
private $pay_config = [];
private $notifyUrl;
public function __construct($pay_config,$notifyUrl)
{
$this->pay_config = $pay_config;
$this->notifyUrl = $notifyUrl;
}
/**
* descapp支付
*
* [备注]
* 需要引入alipay-sdk-php-all官方SDK类库
* [使用]
* [alipay-sdk-php-all.zip]解压后放在项目中,然后在代码中引入
* [流程]
* 1、生成订单信息
* 2、调用支付接口生成orderStr返回给客户端
* 3、客户端用浏览器打开浏览器会自动跳转到支付宝页面完成支付
*
* api/alipay/apppay
* authorwh
*/
function apppay($order){
$root = Tools::get_root_path();
require_once $root.'library/alipay-sdk-php-all/aop/AopClient.php';
require_once $root.'library/alipay-sdk-php-all/aop/AopCertClient.php';
require_once $root.'library/alipay-sdk-php-all/aop/AopCertification.php';
require_once $root.'library/alipay-sdk-php-all/aop/AlipayConfig.php';
require_once $root.'library/alipay-sdk-php-all/aop/request/AlipayTradeAppPayRequest.php';
// 初始化SDK
$alipayClient = new \AopClient($this->getAlipayConfig($this->pay_config));
// 构造请求参数以调用接口
$request = new \AlipayTradeAppPayRequest();
$model = array();
// 设置商户订单号
$model['out_trade_no'] = $order['orderid'];
// 设置订单总金额
$model['total_amount'] = $order['real_amount'];//实付金额
// 设置订单标题
$model['subject'] = $order['remark'];
//// 设置产品码
//$model['product_code'] = "QUICK_MSECURITY_PAY";
//
////设置订单包含的商品列表信息
//$goodsDetail = array();
//$goodsDetail0 = array();
//$goodsDetail0['goods_name'] = "ipad";
//$goodsDetail0['alipay_goods_id'] = "20010001";
//$goodsDetail0['quantity'] = 1;
//$goodsDetail0['price'] = "2000";
//$goodsDetail0['goods_id'] = "apple-01";
//$goodsDetail0['goods_category'] = "34543238";
//$goodsDetail0['categories_tree'] = "124868003|126232002|126252004";
//$goodsDetail0['show_url'] = "http://www.alipay.com/xxx.jpg";
//$goodsDetail[] = $goodsDetail0;
//$model['goods_detail'] = $goodsDetail;
//
////设置订单绝对超时时间
//$model['time_expire'] = "2016-12-31 10:05:00";
//
////设置业务扩展参数
//$extendParams = array();
//$extendParams['sys_service_provider_id'] = "2088511833207846";
//$extendParams['hb_fq_seller_percent'] = "100";
//$extendParams['hb_fq_num'] = "3";
//$extendParams['industry_reflux_info'] = "{\"scene_code\":\"metro_tradeorder\",\"channel\":\"xxxx\",\"scene_data\":{\"asset_name\":\"ALIPAY\"}}";
//$extendParams['specified_seller_name'] = "XXX的跨境小铺";
//$extendParams['royalty_freeze'] = "true";
//$extendParams['card_type'] = "S0JP0000";
//$model['extend_params'] = $extendParams;
//
////设置公用回传参数
//$model['passback_params'] = "merchantBizType%3d3C%26merchantBizNo%3d2016010101111";
//
////设置商户的原始订单号
//$model['merchant_order_no'] = "20161008001";
//
////设置外部指定买家
//$extUserInfo = array();
//$extUserInfo['cert_type'] = "IDENTITY_CARD";
//$extUserInfo['cert_no'] = "362334768769238881";
//$extUserInfo['name'] = "李明";
//$extUserInfo['mobile'] = "16587658765";
//$extUserInfo['min_age'] = "18";
//$extUserInfo['need_check_info'] = "F";
//$extUserInfo['identity_hash'] = "27bfcd1dee4f22c8fe8a2374af9b660419d1361b1c207e9b41a754a113f38fcc";
//$model['ext_user_info'] = $extUserInfo;
//
////设置通知参数选项
//$queryOptions = array();
//$queryOptions[] = "hyb_amount";
//$queryOptions[] = "enterprise_pay_info";
//$model['query_options'] = $queryOptions;
//dump($pay_config);
$request->setBizContent(json_encode($model, JSON_UNESCAPED_UNICODE));
$request->setNotifyUrl($this->notifyUrl);
// 如果是第三方代调用模式请设置app_auth_token应用授权令牌
$authToken = $this->pay_config['app_auth_token'];//"<-- 请填写应用授权令牌 -->"
$orderStr = $alipayClient->sdkExecute($request, $authToken);
return Tools::set_ok('ok',$orderStr);
}
private function getAlipayConfig($pay_config)
{
$privateKey = $pay_config['alipay_app_private_key'];//'<-- 请填写您的应用私钥例如MIIEvQIBADANB ... ... -->';
$alipayPublicKey = $pay_config['alipay_app_public_key'];//'<-- 请填写您的支付宝公钥例如MIIBIjANBg... -->';
$alipayConfig = new \AlipayConfig();
$alipayConfig->setServerUrl('https://openapi.alipay.com/gateway.do');
$alipayConfig->setAppId($pay_config['app_id']);//'<-- 请填写您的AppId例如2019091767145019 -->'
$alipayConfig->setPrivateKey($privateKey);
$alipayConfig->setFormat('json');
$alipayConfig->setAlipayPublicKey($alipayPublicKey);
$alipayConfig->setCharset('UTF-8');
$alipayConfig->setSignType('RSA2');
return $alipayConfig;
}
}

View File

@@ -0,0 +1,195 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/10} {23:15}
*/
namespace wanghua\general_utility_tools_php\alipay\pay;
use Alipay\EasySDK\Kernel\Config;
use Alipay\EasySDK\Kernel\Factory;
use app\common\model\TabConf;
use app\common\tools\EmailTool;
use app\index\logic\AlipayBarcodePayLogic;
use app\index\logic\CheckstandLogic;
use app\index\model\OrderModel;
use app\index\model\PartnerAccountModel;
use think\Db;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 支付宝面对面扫码支付(用户出示付款码)
*
* 【扫码支付专用】
*
* 依赖:"alipaysdk/easysdk": "^2.2" || composer require alipaysdk/easysdk
*
* Class AlipayScanPay
* @package wanghua\general_utility_tools_php\alipay\transfer
*/
class AlipayScanPay
{
/**
* 支付参数配置
* @var array
*/
private array $config = [];
function __construct($config)
{
/** 设置支付参数 */
$this->config = $config;
}
/**
* desc面对面扫码支付-(扫用户的付款码)
*
* 注意:
* 免密支付是同步返回支付结果,输入密码支付是异步通知支付结果,
* 所以需要处理异步支付结果通知。
* @param array $order 订单信息
* @param string $auth_code 扫码枪扫描后的授权码
* @return array 返回支付结果(一维数组)
*/
function scanPay($order,$auth_code){
if(empty($auth_code)){
return Tools::set_res(500,'授权码错误');
}
try {
//1. 设置参数(全局只需设置一次)
Factory::setOptions($this->getOptions());
//2. 发起API调用以支付能力下的统一收单交易创建接口为例
$result = Factory::payment()->faceToFace()
->optional('scene','bar_code')
->pay($order['title'], $order['orderid'], $order['real_amount'], $auth_code);
if($result->code == 10003){
return Tools::set_res(230,'交易进行中(提示用户输入密码)'.$result->msg);
}
//必须解析json
$resp = json_decode($result->httpBody, true);
//支付宝面对面扫码支付后续流程
return Tools::set_ok('ok',$resp['alipay_trade_pay_response']);//返回支付结果(一维数组)
} catch (\Exception $e) {
$err_data = [
'error'=>'支付宝付款码支付错误'.$e->getMessage(),
'error_info'=>$e->getTraceAsString()
];
Tools::log_to_write_txt($err_data);
return Tools::set_fail('错误.'.$e->getMessage());
}
}
/**
* desc获取支付配置
* authorwh
* @return Config
* @throws Exception
*/
private function getOptions()
{
if(empty($this->config)){
throw new Exception('支付宝配置错误');
}
if(empty($this->config['notifyUrl'])){
throw new Exception('支付宝配置错误notifyUrl通知地址字段必须配置免密支付是同步返回支付结果输入密码支付是异步通知支付结果');
}
$config = $this->config;
$options = new Config();
$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipay.com';
$options->signType = $config['sign_type'];
$options->appId = $config['app_id'];//'<-- 请填写您的AppId例如2019022663440152 -->';
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
$options->merchantPrivateKey = $config['alipay_app_private_key'];//'<-- 请填写您的应用私钥例如MIIEvQIBADANB ... ... -->';
$options->alipayCertPath = Tools::get_root_path().$config['cert_path_alipayCertPublicKey_RSA2'];//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$options->alipayRootCertPath = Tools::get_root_path().$config['cert_path_alipayRootCert'];//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$options->merchantCertPath = Tools::get_root_path().$config['appCertPublicKey'];//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->';
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
// $options->alipayPublicKey = '<-- 请填写您的支付宝公钥例如MIIBIjANBg... -->';
//可设置异步通知接收服务地址(可选)
$options->notifyUrl = $this->config['notifyUrl'];//request()->domain().'/index/alipay/notify';//"<-- 请填写您的支付类接口异步通知接收服务地址例如https://www.test.com/callback -->";
return $options;
}
/**
* desc支付宝扫码异步通知
*
* 【通知结果是一维数组】
*
* {"gmt_create":"2024-06-10 11:53:37","charset":"UTF-8","seller_email":"15023017325",
* "subject":"购物消费",
* "sign":"oCR3X0ViRrydigLMeOCTrMBNx7O8bSZnEflT0qZYo0gXk2jTI2OVB9mG2UPCkGuSND3wq/pMRFN
* ljuz+2r3KMha9a6ArD/ey8w1am3yCKI+FMqUMQSv9TJqfpWdAsc8B45egtr+txBBOA1NMfN41hPnTxM3QzQ82cm
* 7VYw6pnYpxJVj/3AsqYxY+wcyMq5+v25eC4TgBo+YJ9pfb3rqaAV91cJBoHRq1Cwh0XEnOzm9zpv7b5cu3r+V0oeW
* wTCCi8S1TXW2dfE4H8o63xgASYfJO2fgGIu19YICQdUONkDhu9Tfbf3fFf9SHw3RIw8zrQRqkwAXC32NwfUv4Ru5NqQ==",
* "buyer_id":"2088612757425274","invoice_amount":"3258.60","notify_id":"2024061001222115350025271439748729",
* "fund_bill_list":"[{\"amount\":\"3258.60\",\"fundChannel\":\"ALIPAYACCOUNT\"}]",
* "notify_type":"trade_status_sync","trade_status":"TRADE_SUCCESS","receipt_amount":"3258.60",
* "app_id":"2021004136614333","buyer_pay_amount":"3258.60","sign_type":"RSA2",
* "seller_id":"2088642975857443","gmt_payment":"2024-06-10 11:53:50",
* "notify_time":"2024-06-10 12:07:40","version":"1.0","out_trade_no":"cs25obpv1717991615660",
* "total_amount":"3258.60","trade_no":"2024061022001425271426453509","auth_app_id":"2021004136614333",
* "buyer_logon_id":"146***@qq.com","point_amount":"0.00"}
*
* authorwh
* param function $fn 传入订单处理逻辑函数 要求返回success、fail
* @return string
*/
function scanPayNotify($fn){
Tools::log_to_write_txt([
'支付宝异步通知',
'input'=>input()
]);
$out_trade_no = input('out_trade_no');
if(empty($out_trade_no)){
Tools::log_to_write_txt(['error'=>'out_trade_no为空,文件:'.__FILE__.',line:'.__LINE__]);
return '';
}
//$order = Db::table(TabConf::$fa_order)
// ->where('orderid',$out_trade_no)
// ->find();
//if(empty($order)){
// Tools::log_to_write_txt(['error'=>'订单不存在,文件:'.__FILE__.',line:'.__LINE__]);
// return '';
//}
//该字段用于判断成功、失败
$trade_status = input('trade_status');
if(empty($trade_status)){
Tools::log_to_write_txt(['error'=>'trade_status为空,文件:'.__FILE__.',line:'.__LINE__]);
return '';
}
if(empty($this->config['seller_id'])){
Tools::log_to_write_txt(['error'=>'卖家seller_id为空,文件:'.__FILE__.',line:'.__LINE__]);
return '';
}
//判断来源
$seller_id = input('seller_id');
if($seller_id != $this->config['seller_id']){
Tools::log_to_write_txt(['error'=>'支付通知来源错误','seller_id'=>$seller_id,'config'=>$this->config['seller_id']]);
return 'fail';
}
//支付宝面对面扫码支付后续流程
$result = input();
//异步通知
//$res = CheckstandLogic::alipayScanSuccessFlow($result,$order);
//if($res['code'] == 200 || $res['code'] == 230){//成功或进行中
// return 'success';
//}
return $fn($result);//要求返回success、fail
}
}

View File

@@ -0,0 +1,143 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/09/24} {19:43}
*/
namespace wanghua\general_utility_tools_php\alipay\transfer;
use Alipay\EasySDK\Kernel\Config;
use Alipay\EasySDK\Kernel\Factory;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 支付宝转账(独立且区别于支付,连参数都不能用支付参数)
*
* 注:换句话说,转账是转账的参数,支付是支付的参数,不能混为一谈(以此备注长记性)。
*
* 环境要求PHP7.0+ThinkPHP5.0+
*
* 依赖:
* composer require alipaysdk/easysdk:^2.0
*
* composer require wanghua/general-utility-tools-php dev-master
*
* 重要说明:
*
* 支付宝开放平台可能有多个应用,可能存在一个默认应用(应用名称:应用2.0签约2020102692466480),转账配置就用此应用的参数,而不是其它应用的配置!
*
*/
class AlipayTransfer
{
/**
* desc转账到支付宝证书模式
* authorwh
* @param array $alipayConfig
* @param $transferConfig TransferConfig.php
* @return mixed
*
* 使用案例:
*
function transfer(array $trans_data){
$alipayConfig = \config('pay_config.alipay_transfer');//转账配置
$trans_config = new TransferConfig();
$trans_config->cert_path_alipayCertPublicKey_RSA2 = Tools::get_root_path().$alipayConfig['cert_path_alipayCertPublicKey_RSA2'];//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$trans_config->cert_path_alipayRootCert = Tools::get_root_path().$alipayConfig['cert_path_alipayRootCert'];//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$trans_config->appCertPublicKey = Tools::get_root_path().$alipayConfig['appCertPublicKey'];//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->'
$order_prefix = 'trs';
$order_id = Tools::to_create_order_no($order_prefix);
$trans_config->order_id = $order_id;//订单号 必须
$trans_config->timestamp = Tools::get_now_date();//请求时间 eg2020-01-08 10:12:50
$trans_config->trans_amount = $trans_data['trans_amount'];//转账金额 必须 且为字符串 最低0.1元 取值范围[0.1,100000000]
$trans_config->order_title = $trans_data['order_title'];//订单标题 可选
$trans_config->phone = $trans_data['phone'];//支付宝登录手机号 必须
$trans_config->name = $trans_data['name'];//支付宝真实姓名 必须
$trans_config->remark = $trans_data['remark'];//支付备注 可选
$trans = new AlipayTransfer();
Tools::log_to_write_txt(['转账到支付宝,入参:'=>input(), 'trans_config'=>$trans_config]);
$pay_res = $trans->alitransfer($alipayConfig, $trans_config);
Tools::log_to_write_txt(['转账到支付宝,出参:'=>$pay_res]);
//直接返回转账结果自主验证成功失败的status。code=10000表示操作成功status=SUCCESS表示转账成功
//由于是同步响应,所以不需要对结果验签
return $pay_res;
}
*
*/
function alitransfer(array $alipayConfig, $transferConfig){
//$transferConfig = new TransferConfig();
Factory::setOptions($this->getAlipayOptions($alipayConfig, $transferConfig));
$method = 'alipay.fund.trans.uni.transfer';
//公共参数
$textParams = [
'app_id'=>$alipayConfig['app_id'],
'charset'=>'utf-8',
'sign_type'=>'RSA2',
'timestamp'=>$transferConfig->timestamp,
'version'=>'1.0',
];
//请求参数
//订单号前缀
$biz_content = [
'out_biz_no'=>$transferConfig->order_id,//商家支付订单号
'trans_amount'=>$transferConfig->trans_amount,
'product_code'=>'TRANS_ACCOUNT_NO_PWD',
'biz_scene'=>'DIRECT_TRANSFER',
'order_title'=>$transferConfig->order_title,
'payee_info'=>[
'identity'=>$transferConfig->phone,//手机号
'identity_type'=>'ALIPAY_LOGON_ID',
'name'=>$transferConfig->name,//参与方真实姓名如果非空将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时本字段必填。
],
'remark'=>$transferConfig->remark,
];
$pay_res = Factory::util()->generic()->execute($method,$textParams,$biz_content);
return $pay_res;
}
/**
* desc证书模式参数非证书模式参数需单独获取
* authorwh
* @param array $alipayConfig 支付宝转账配置(非支付配置)
* @return Config
*/
protected function getAlipayOptions(array $alipayConfig, $transferConfig)
{
$options = new Config();
$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipay.com';
$options->signType = 'RSA2';
//'<-- 请填写您的AppId例如2019022663440152 -->'
$options->appId = $alipayConfig['app_id'];//'2021002103639985';
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
$options->merchantPrivateKey = $alipayConfig['alipay_app_private_key'];//'<-- 请填写您的应用私钥例如MIIEvQIBADANB ... ... -->';
$options->alipayCertPath = $transferConfig->cert_path_alipayCertPublicKey_RSA2;//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$options->alipayRootCertPath = $transferConfig->cert_path_alipayRootCert;//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$options->merchantCertPath = $transferConfig->appCertPublicKey;//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->';
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
// $options->alipayPublicKey = $alipayConfig['alipay_public_key'];//'<-- 请填写您的支付宝公钥例如MIIBIjANBg... -->';
//可设置异步通知接收服务地址(可选)
$options->notifyUrl = $transferConfig->notifyUrl;//"<-- 请填写您的支付类接口异步通知接收服务地址例如https://www.test.com/callback -->";
//可设置AES密钥调用AES加解密相关接口时需要可选
//$options->encryptKey = "<-- 请填写您的AES密钥例如aa4BtZ4tspm2wnXLb1ThQA== -->";
return $options;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/09/24} {20:07}
*/
namespace wanghua\general_utility_tools_php\alipay\transfer;
/**
* 调用转账之前请配置此参数
*
* 配置示例:
*
$alipayConfig = \config('pay_config.alipay_transfer');//转账配置
$trans_config = new TransferConfig();
$trans_config->cert_path_alipayCertPublicKey_RSA2 = Tools::get_root_path().$alipayConfig['cert_path_alipayCertPublicKey_RSA2'];//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$trans_config->cert_path_alipayRootCert = Tools::get_root_path().$alipayConfig['cert_path_alipayRootCert'];//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$trans_config->appCertPublicKey = Tools::get_root_path().$alipayConfig['appCertPublicKey'];//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->'
$order_prefix = 'trs';
$order_id = Tools::to_create_order_no($order_prefix);
$trans_config->order_id = $order_id;//订单号 必须
$trans_config->timestamp = Tools::get_now_date();//请求时间 eg2020-01-08 10:12:50
$trans_config->trans_amount = '0.21';//转账金额 必须 且为字符串 最低0.1元 取值范围[0.1,100000000]
$trans_config->order_title = '代理提现';//订单标题 可选
$trans_config->phone = '18290416033';//支付宝登录手机号 必须
$trans_config->name = '王华';//支付宝真实姓名 必须
$trans_config->remark = '代理提现到支付宝';//支付备注 可选
$trans = new AlipayTransfer();
$pay_res = $trans->alitransfer($alipayConfig, $trans_config);
dump($pay_res);
*
*/
class TransferConfig
{
//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
public $cert_path_alipayCertPublicKey_RSA2 = '';//必须
//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
public $cert_path_alipayRootCert = '';//必须
//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->'
public $appCertPublicKey = '';//必须
public $notifyUrl = '';//接收异步通知(可选)
//业务参数
public $order_id = '';//订单号 必须
public $timestamp = '';//请求时间 必须 eg2020-01-08 10:12:50
public $trans_amount = '';//转账金额 必须 且为字符串 最低0.1元 取值范围[0.1,100000000]
public $order_title = '';//订单标题 可选
public $phone = '';//支付宝登录手机号 必须
public $name = '';//支付宝真实姓名 必须
public $remark = '';//支付备注 可选
}

View File

@@ -0,0 +1,425 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {11:21}
*/
namespace wanghua\general_utility_tools_php\api;
use wanghua\general_utility_tools_php\mysql\lib\Table;
use wanghua\general_utility_tools_php\tool\Tools;
class Api extends BaseLibApi
{
private $apidir = '';
private $namespace = 'app\api\controller';
private $table_obj = null;
//一维数组
private $functions_arr = [];//要创建的初始方法
public function __construct()
{
//默认api目录
$this->apidir = Tools::get_root_path().'application/api/controller/';
if(!$this->table_obj)
$this->table_obj = new Table();
}
/**
* descapi目录
* authorwh
* @param string $dir api创建目录格式必须按照例子填写 eg: application/api/controller/
*/
function setapidir(string $dir){
$this->apidir = $dir;
$nsp = str_replace('application','app',substr($dir,0,strlen($dir)-2));
$this->namespace = str_replace('/','\\',$nsp);
}
/**
* desc替换表前缀
* authorwh
* @param $tablename
* @return false|string
*/
function replaceTablePrefix($tablename){
$prefix = config('database.prefix');
if($prefix){
return substr($tablename,strlen($prefix));
}
$tmp_prefix = 't_';
if(false !== strpos($tablename,$tmp_prefix)){
return substr($tablename,strlen($tmp_prefix));
}
return $tablename;
}
/**
* desc设置方法名称
*
* authorwh
* @param array $functions_arr
*/
function setApiFunctionsName(array $functions_arr){
$this->functions_arr = array_merge($functions_arr,$this->functions_arr);
}
/**
* desc动态创建方法
*
* authorwh
*/
private function syncCreateFunction(array $func_name_arr){
$code = '';
foreach ($func_name_arr as $func_name=>$func_remark){ //接口方法公共代码模块
$commonFunctionCodeModule = $this->commonFunctionCodeModule($func_remark);
$code .= <<<EOF
/**
* desc{$func_remark}
*
* author
*/
function {$func_name}(){
{$commonFunctionCodeModule}
}
EOF;
}
return $code;
}
/**
* desc创建查询接口的代码体
*
* 本接口只针对简单查询逻辑
*
* and
* like%%like%,
* >, <, >=, <=, !=
* between
* in
*
* authorwh
* @param array $select_in_params eg:
* [
* 'gameid'=>['and'],
* 'nickname'=>['like%'],
* 'reg_time'=>['between'],//时间区间查询
* 'audit_time'=>['2015-01-3','eq'],//时间区间查询
* ]
*
*/
function setQueryCodeBody(array $select_in_params){
$code = '';
return $code;
}
/**
* desc写入查询接口的代码体
*
* authorwh
* @param array $insert_params
* @return string
*/
function setInsertCodeBody(array $insert_params){
$code = '';
return $code;
}
/**
* desc快速构建api代码
*
* 本方法不验证接口名称是否重复
*
* authorwh
* @param string $tablename 数据表名称
* @param array $func_name_arr 方法名称数组 eg:['getData'=>'获取数据','function_aaa'=>'function_注释aaa']
*/
function buildApi(string $tablename, array $func_name_arr){
//deal table name
$tablename = $this->replaceTablePrefix($tablename);
//基础 start
//create base controller
$this->buildBaseApiPublicController();//开放控制器
$this->buildBaseApiAuthController();//权限控制器
$this->errController();//错误控制器
//基础 end
//接口类名
$classname = ucfirst(Tools::convertUnderLine($tablename));
//接口注释
$comment = $this->table_obj->getTableComment($tablename);
//批量构建方法代码
$build_functions_code = $this->syncCreateFunction($func_name_arr);
//PHP文件名不含物理路径
$file_name = $classname.$this->ext();
//PHP文件路径和文件名
$php_file = $this->apidir.$file_name;
if(file_exists($php_file)){
//追加代码
//读取并删除最后的“}”
$php_code_str = file_get_contents($php_file);
$sub_php_code_str = mb_substr($php_code_str,0,mb_strlen($php_code_str)-2);
//追加新的代码
$sub_php_code_str .= $build_functions_code ."\n }";
//清空代码
file_put_contents($php_file,'');
//追加
$this->createFileExists($php_file,$sub_php_code_str);
}else{
//创建代码文件
$use_tpl = $this->useTpl();
$php = <<<EOF
<?php
namespace {$this->namespace};
{$use_tpl}
/**
* {$comment}
* Class {$classname}
* @package {$this->namespace}
*/
class {$classname} extends BaseApiPublicController
{
{$build_functions_code}
}
EOF;
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
}
/**
* desc公共方法模块
*
* authorwh
* @param $comment
* @return string
*/
private function commonFunctionCodeModule($comment){
$php = <<<EOF
try {
Tools::log_to_write_txt(['title'=>'{$comment},__IN','INPUT'=>input()]);
//region 校验区 start
//endregion 校验区 end
//region 业务逻辑区 start
//endregion 业务逻辑区 end
//响应结果处理
Tools::log_to_write_txt(['title'=>'{$comment},__OUT','OUTPUT'=>[]]);
return json(Tools::set_res(200,'ok'));
}catch (\Exception \$e){
Tools::log_to_write_txt([
'error'=>'{$comment},API异常。'.\$e->getMessage(),
'入参'=>input(),
'error_info'=>\$e->getTraceAsString()
]);
return json(Tools::set_res(500,'系统繁忙'));
}
EOF;
return $php;
}
/**
* descapi基础开放控制器
*
* 如果存在则不创建
*
* authorwh
*/
private function buildBaseApiPublicController(){
$file_name = 'BaseApiPublicController';
$php = <<<EOF
<?php
namespace {$this->namespace};
use think\Controller;
/**
* descapi基础开放架构
*
* 所有开放接口继承此父类
*
* author
*/
class {$file_name} extends Controller
{
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
/**
* descapi基础权限控制器
*
* 如果存在则不创建
*
* authorwh
*/
private function buildBaseApiAuthController(){
$file_name = 'BaseApiAuthController';
$php = <<<EOF
<?php
namespace {$this->namespace};
use think\Controller;
/**
* descapi基础权限架构
*
* 所有权限接口继承此父类
*
* author
*/
class {$file_name} extends Controller
{
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
/**
* descapi底层基础控制器架构
*
* [按需创建][可单独调用]
*
* 如果存在则不创建
*
* authorwh
*/
function buildBaseApiController(){
$file_name = 'BaseApiController';
$php = <<<EOF
<?php
namespace {$this->namespace};
use think\Controller;
/**
* descapi底层基础控制器架构
*
* author
*/
class {$file_name} extends Controller
{
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
/**
* descuse模块
*
* authorwh
* @return string
*/
private function useTpl(){
$php = <<<EOF
use wanghua\general_utility_tools_php\\tool\Tools;
EOF;
return $php;
}
/**
* desc错误控制器
*
* authorwh
*/
function errController(){
$file_name = 'Err';
$php = <<<EOF
<?php
namespace {$this->namespace};
use wanghua\general_utility_tools_php\\framework\\base\PublicController;
use wanghua\general_utility_tools_php\\tool\Tools;
/**
*
* 错误(异常)中转架构
*
* Class Err
* @package app\index\controller
*/
class {$file_name} extends BaseApiPublicController
{
/**
* desc校验失败中转
*
* 场景:内网端口验证未通过时跳转;
*
* authorwh
* @return \\think\\response\\Json
*/
function checkfailed(){
return json(Tools::set_res(500,'Permission denied',input()));
}
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
}

View File

@@ -0,0 +1,309 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/5/15} {21:40}
*/
namespace wanghua\general_utility_tools_php\api;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* desc接口文档构建器
*
* 使用步骤:
* (new ApiDocument())->buildDoc();
* Class ApiDocument
*/
class ApiDocument
{
private $api_cache_arr = [];//缓存所有接口
public $api_domain = 'http://127.0.0.1:8080/';//接口域名/ip
/**
* @var string 接口控制器命名空间
*/
public $namespace = 'app\\api\\controller';
/**
* @var string 接口控制器基类精确到类名app\\common\\controller\\Api
* 注意:如果同级目录存在多个基类,则设置直接基类,如果同级目录没有基类,则设置底层基类
*/
public $extends_base_class = '';
//控制器目录物理路径
public $controllerDirectory = '';
/**
* @var string 接口文档保存路径
*/
public $api_docs_save_dir = 'public/api_docs/';
//设置过滤类
private $filterClassArr = [];
//设置过滤方法
private $filterFunctionArr = [];
public function __construct($api_domain='',$controllerDirectory='')
{
//默认如果是tp6那application就要改为app了自行传参吧
$this->controllerDirectory = Tools::get_root_path().'application/api/controller';
if($api_domain){
$this->api_domain = $api_domain;
}
if($controllerDirectory){
$this->controllerDirectory = $controllerDirectory;
}
}
/**
* desc设置过滤类类名
* 过滤场景:
* 1、基类
* 2、测试类
* 3、定时任务类
* 4、其它非必要类
* authorwh
* @param array $filterClassArr
*/
function setFilterClass(array $filterClassArr=[]){
$this->filterClassArr = $filterClassArr;
}
function setFilterFunction(array $filterArr=[]){
$this->filterFunctionArr = $filterArr;
}
/**
* desc构建接口文档支持同步到在线文档
* authorwh
*/
function buildDoc(){
if(empty($this->extends_base_class)){
throw new Exception('请设置接口控制器基类精确到类名app\\common\\controller\\Api');
}
$out_path = Tools::get_root_path().$this->api_docs_save_dir;
if(!file_exists($out_path)){
mkdir($out_path,0777,true);
}
$outputFile = $out_path.'api_list.md';
$controllerClasses = [];
// 搜索控制器目录下的所有PHP文件
foreach (glob($this->controllerDirectory . '/*.php') as $filename) {
// 获取文件中的类名
$class = basename($filename, '.php');
if(in_array($class,$this->filterClassArr)){
continue;
}
// 构建完整的命名空间类名
$fullClassName = $this->namespace . '\\' . $class;
foreach (explode(',',$this->extends_base_class) as $base_class){
// 检查类是否有效并且是think\Controller的子类
if (class_exists($fullClassName) && is_subclass_of($fullClassName, $base_class)) {
$controllerClasses[] = $fullClassName;
}
}
}
// 创建Markdown文件
$file = fopen($outputFile, 'w') or die('无法创建文件');
$head_text = <<<EOF
# API 文档
## 接口列表
###### ctrl+f 搜索)(如果更改了路由,请根据路由规则定位)
##### 请求域名:{$this->api_domain}
##### 请求方式POST默认
EOF;
// 写入Markdown文件头部
fwrite($file, $head_text);
foreach ($controllerClasses as $controllerClass) {
$reflector = new \ReflectionClass($controllerClass);
// 遍历控制器中的公共方法
$methods = $reflector->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
//过滤方法
if(in_array($method->name, $this->filterFunctionArr)){
continue;
}
$exp_class = explode('\\',$controllerClass);
//过滤类
if(in_array($exp_class[count($exp_class)-1],$this->filterClassArr)){
continue;
}
$comments = $method->getDocComment();
if ($comments) {
$this->processMethodComment($comments, $controllerClass, $method->name, $outputFile);
}
}
}
fclose($file);
//缓存所有接口
cache('api_doc_cache_arr',$this->api_cache_arr);
}
/**
* desc解析方法注释并写入Markdown文件
* authorwh
* @param $comments
* @param $className
* @param $methodName
* @param $savepath
*/
private function processMethodComment($comments, $className, $methodName, $savepath) {
if($methodName == '__construct'){
return '';
}
$api_url = "/api/{$className}/{$methodName}";
$js_api_func_name = "api_{$className}_{$methodName}";
$str = <<<EOF
***
```
EOF;
$comments_str = mb_substr($comments,0,-2);
$class_arr = explode('\\',$className);
$className = $class_arr[count($class_arr)-1];
$className = $this->camelCaseToUnderscore($className);
$api_name = "api/{$className}/{$methodName}";
$doc_txt = <<<EOF
* $api_name
*/
```
EOF;
$doc_txt= $str.$comments_str.$doc_txt." \r\n";
$this->api_cache_arr[$className][] = ['api_name'=>$api_name,'doc_txt'=>$doc_txt,'class_name'=>$className];
file_put_contents($savepath,$doc_txt, FILE_APPEND);
}
/**
* desc驼峰转下划线
* authorwh
* @param $string
* @return string
*/
private function camelCaseToUnderscore($string) {
$str = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $string));
return strpos($str,'_')===0?substr($str,1):$str;
}
function buildApiDocHtml(){
//给定一组浅色背景色码
$color_code_arr = [
'#f0f8ff', '#f0ffff', '#f5f5dc', '#ffe4c4', '#f5f5f5', '#f5fffa', '#fff5ee', '#f8f8ff', '#fffaf0', '#fffff0', '#fafad2', '#f0fff0', '#fff0f5', '#ffe4e1', '#f0ffff', '#f0f8ff', '#f8f8ff', '#faebd7', '#fff0f5', '#ffe4e1', '#ffe4b5', '#ffdead', '#dcdcdc', '#dda0dd', '#fffaf0', '#eee8aa', '#fffafa', '#f0fff0', '#f0fff0', '#f0f8ff', '#f0ffff', '#f5f5dc', '#ffe4c4', '#f5f5f5', '#f5fffa', '#fff5ee', '#f8f8ff', '#fffaf0', '#f0fff0', '#fff0f5', '#ffe4e1', '#f0ffff', '#f0f8ff', '#f8f8ff', '#faebd7', '#fff0f5', '#ffe4e1', '#ffe4b5', '#ffdead', '#dcdcdc', '#dda0dd', '#fffaf0', '#eee8aa', '#fffafa', '#f0fff0', '#f0fff0', '#f0f8ff', '#f0ffff', '#f5f5dc', '#ffe4c4', '#f5f5f5', '#f5fffa', '#fff5ee', '#f8f8ff', '#fffaf0',
];
$api_doc_cache_arr = cache('api_doc_cache_arr');
$htm_str = <<<EOF
<style>
.txt-lf{text-align: left}
</style>
<div style="width: 50%;margin: 0 auto;color: red;text-align: center;">
<div class="txt-lf">文档说明:</div>
<div class="txt-lf">1、如果没有明确说明提交请求均使用post</div>
<div class="txt-lf">2、此接口文档不包含websocket接口</div>
<div class="txt-lf">3、接口参数之间使用“/”符号隔开</div>
<div class="txt-lf">4、此文档接口测试功能只针对普通post、get接口不能测试文件上传或文件流</div>
<div class="txt-lf">5、功能模块按照颜色分组</div>
<div class="txt-lf" style="color: black;">清理缓存:
<a href='JavaScript:;' onclick="CacheObj.clearCache()">
点击清理
</a><span style="color: gray;font-size: 12px;">(由于接口数据可能会被缓存,发现数据没变化或需要时,可清理缓存)</span>
</div>
</div>
EOF;
$script_str = "";
foreach ($api_doc_cache_arr as $class_name_key=>$func_arr){
//随机取一个颜色
$color_code = $color_code_arr[array_rand($color_code_arr,1)];
//是否显示上边距
$is_show_margin_top = 'margin-top: 50px;';
foreach ($func_arr as $k => $item){
$api_name = $item['api_name'];
$doc_txt = $item['doc_txt'];
if($k > 0){
$is_show_margin_top = '';
}
$function_name = str_replace('/','_',$api_name);
$htm_str .= <<<EOF
<div id="{$function_name}" style="background-color: {$color_code};{$is_show_margin_top}">
<div class="markdown_content">{$doc_txt}</div>
<div>
按需填写其它接口参数:
<textarea name="" id="{$function_name}_textarea" cols="100" rows="3">/{$api_name}</textarea>
<a href='JavaScript:;' onclick="DocObject.{$function_name}()">测试</a>
</div>
<div class="{$function_name}_response_result"></div>
</div>
EOF;
$script_str.=<<<EOF
{$function_name}(){
let url = $('#{$function_name}_textarea').val();
$.post(url,{},function(res) {
$('.{$function_name}_response_result').html(JSON.stringify(res, null, "\\t"));
$('.{$function_name}_response_result').attr('style','color:green');
},'json');
},
EOF;
}
}
$html = <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>接口文档</title>
</head>
<body>
<div>
{$htm_str}
</div>
</body>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="/webautocodestatic/marked.min.js"></script>
<script src="/webautocodestatic/plugs/layui-v2.9.2/layui.js"></script>
<script>
$(function() {
//加载markdown
DocObject.markdown_content();
});
let CacheObj = {
clearCache: function () {
let url = '/index/test/clearCache';
$.post(url,{},function(res) {
layer.msg('清理成功')
},'json');
}
};
let DocObject = {
markdown_content(){
$('.markdown_content').each(function(k,ele) {
$(ele).html(marked.parse($(ele).html()));
});
},
$script_str
}
</script>
</html>
EOF;
return $html;
}
}

View File

@@ -0,0 +1,230 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/11} {16:14}
*/
namespace wanghua\general_utility_tools_php\api;
use wanghua\general_utility_tools_php\exception\api\ApiException;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 废弃
* descapi交互
*
* authorwh
* Class BaseApi
* @package app\apidata
*/
class BaseApi
{
protected $post_url = '';
private $prefix = 'api_';
protected $log_file_name = 'log';
protected $__sit__ = '';
function __construct(string $url)
{
$this->post_url = $url;
if(empty($this->post_url)) {
Tools::log_to_write_txt(['error'=>'API OPERATE: '.ApiException::EMPTY_URL_ERROR], $this->getLogFileName());
throw new \Exception(ApiException::EMPTY_URL_ERROR);
}
}
/**
* desc设置api请求日志文件名称
*
* 日志保存在runtime中
*
* authorwh
* @param string $log_file_name
*/
function setLogFileName(string $log_file_name=''){
if($log_file_name)$this->log_file_name = $log_file_name;
}
/**
* desc 获取记录日志的文件名
* authorwh
* @return string
*/
function getLogFileName(){
return $this->prefix.$this->log_file_name;
}
/**
* desc设置调用位置
* authorwh
* @param $__file__
* @param $__line__
*/
function setFileDir($__file__, $__line__){
$this->__sit__ = $__file__.' > '.$__line__;
}
/**
* desc错误位置
* authorwh
* @return string
*/
function getErrorSit(){
return $this->__sit__;
}
/**
* desc
*
* authorwh
* @param array $params
*/
private function mergeAuthParams(array &$params){
$params['nonce'] = Tools::rand_str(10);
$params['timestamp'] = time();
$token = config('service_framework_config.sign_token');
$params['sign'] = Tools::signature($params, $token);
}
/**
* desc执行post请求, 带权限参数
*
* 同框架、内部系统推荐
*
* 返回结果由code 错误码和msg 错误信息组成
*
* authorwh
* @param $param 以表单格式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function apiPost($param){
$this->mergeAuthParams($param);
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, $param);
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(empty($data['code'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR);
}
return $data;
}
/**
*
* desc执行post请求
*
* 返回结果由code 错误码和msg 错误信息组成
*
* authorwh
* @param $param 以json格式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function do($param){
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, json_encode($param));
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(empty($data['code'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR.$this->post_url);
}
return $data;
}
/**
*
* desc执行请求
*
* do 方法的改版返回结果由state 错误码和err 错误信息组成
*
* authorwh
* @param $param 以json格式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function apiDo($param){
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, json_encode($param));
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(is_null($data['state'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR);
}
$data['code'] = isset($data['state'])&&$data['state']==0?200:$data['state'];
$data['msg'] = isset($data['err'])?$data['err']:'api接口错误';
return $data;
}
/**
*
* desc执行请求
*
* 同框架、内部系统推荐
*
* authorwh
* @param $param 以表单方式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function doPost($param){
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, $param);
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(empty($data['code'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR);
}
return $data;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {15:01}
*/
namespace wanghua\general_utility_tools_php\api;
class BaseLibApi
{
/**
* desc创建目录
*
* authorwh
* @param $apidir
*/
function createDir($apidir){
if(!is_dir($apidir)){
mkdir($apidir,0777,true);
}
}
/**
* desc文件存在不写入
*
* authorwh
* @param $php_file
* @param $php
*/
function createFile($php_file,$php){
if(!file_exists($php_file)){
file_put_contents($php_file,$php);
}
}
/**
* desc追加文本
*
* authorwh
* @param $php_file
* @param $php
*/
function createFileExists($php_file,$php){
file_put_contents($php_file,$php,FILE_APPEND);
}
function ext(){
return '.php';
}
}

View File

@@ -0,0 +1,193 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/2/26} {10:54}
*/
namespace wanghua\general_utility_tools_php\api;
use app\common\model\TabConf;
use think\Db;
use wanghua\general_utility_tools_php\tool\Tools;
class BaseUserLogic
{
public $user_table = 'fa_users';
/**
* desc【通用】用户登录逻辑
*
* 请求端加密,服务端校验
*
* 签名规则
* 请求参数:
username //用户名
password //密码 md5(md5('a123456')) d477887b0636e5d87f79cc25c99d7dc9
timestamp //请求时间戳 2020-01-01 12:34:07
noncestr //随机字符串8位
sign //签名串,参考全站签名规则
other_params //其它接口的其它参数
* 全站sign规则
1. 对关联数组按照键升序排序
2. 拼接成字符串 keyvalkey2val2key3val3
3. 再2次md5生成32位小写串
4. 与请求参数一起发送到服务器
*
* authorwh
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
function tologin(){
if(!request()->isPost()){
return Tools::set_fail('错误');
}
Tools::log_to_write_txt(['登录入参:'=>input()]);
$input_data = input();
if(empty($input_data)){
return Tools::set_fail('请求参数错误');
}
if(empty($input_data['username'])){
return Tools::set_fail('用户名或密码错误');
}
if(empty($input_data['password'])){
return Tools::set_fail('用户名或密码错误.');
}
// start 这段代码可放在请求合法性里面校验 Tools::signature
if(empty($input_data['timestamp'])){
return Tools::set_fail('请求错误..');
}
if(empty($input_data['noncestr'])){
return Tools::set_fail('请求错误...');
}
if(empty($input_data['sign'])){
return Tools::set_fail('请求错误。');
}
$sign = $input_data['sign'];//md5后的字符串
unset($input_data['sign']);
//验签
$signstr = Tools::signature($input_data);
//Tools::log_to_write_txt([
// '签名日志:',
// $signstr,
// $sign,
// 'input'=>input(),
//]);
if($signstr != $sign){
return Tools::set_fail('非法请求');
}
//请求时间超过有效期 N分钟内有效
if(time()-5*60 > strtotime($input_data['timestamp'])){
return Tools::set_fail('请求失效');
}
// end 这段代码可放在请求合法性里面校验 Tools::signature
$username = $input_data['username'];
$user = Db::table($this->user_table)->where('username',$username)->find();
if(empty($user)){
return Tools::set_fail('账号密码错误!');
}
//校验密码
if($user['password'] != $input_data['password']){
return Tools::set_fail('账号密码错误!!');
}
$expires = time()+12*60*60;
//返回票据
$ticketstr = md5($user['username'].$user['expires']);
//保存ticket
$user[$ticketstr] = $expires;//N秒后过期
//修改有效期
Db::table($this->user_table)
->data([
'ticket'=>$ticketstr,
'expires'=>7*86400+time(),//7天
])
->where('username',$username)
->update();
session('api_user',$user);//备用(跨环境情况下session不生效)
return Tools::set_ok('登录成功',['ticket'=>$ticketstr]);
}
/**
* desc验证登录根据提交的ticket来校验是否登录适用于跨环境登录
*
* authorwh
* @return bool
*/
function isLogin(){
$ticket = input('ticket');
if(empty($ticket)){
Tools::log_to_write_txt(['title'=>'业务ticket字段不存在','input'=>input()]);
return false;
}
$user = Db::table(TabConf::$fa_users)
->where('ticket',$ticket)
->find();
if(empty($user)){
Tools::log_to_write_txt(['title'=>'用户未登录']);
return false;
}
//无效票据
//if(empty($user[$ticket])){
// Tools::log_to_write_txt(['title'=>'未获取到用户ticket',$ticket=>$user]);
//
// return false;
//}
//已过期
if(time() > $user['expires']){
Tools::log_to_write_txt(['title'=>'ticket已过期',$ticket=>$user]);
return false;
}
return true;
}
/**
* desc用户注册
*
* 此模块通用性不高,按需使用
*
* authorwh
*/
function reg($input_data){
try {
if(empty($input_data['username'])){
return Tools::set_fail('用户名错误');
}
if(empty($input_data['password'])){
return Tools::set_fail('密码错误.');
}
if(empty($input_data['password2'])){
return Tools::set_fail('密码错误!');
}
if($input_data['password'] != $input_data['password2']){
return Tools::set_fail('密码不一致!');
}
$insert_data = [
'username'=>$input_data['username'],
'password'=>$input_data['password'],
];
if(!empty($input_data['phone'])){
$insert_data['phone'] = $input_data['phone'];
}
Db::table($this->user_table)
->data($insert_data)
->insert();
return Tools::set_ok();
}catch (\Exception $e){
Tools::error_txt_log($e);
return Tools::set_fail();
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* description apk应用安装包信息读取
* authorwh
* email
* createTime{2020/7/1} {17:16}
*/
namespace wanghua\general_utility_tools_php\apk;
class Apk
{
/**
* 前提安装PHP ZIP扩展
* 扩展下载https://windows.php.net/downloads/pecl/releases/zip/
* desc读取APK
* authorwh
* @param string $path 文件地址
* @param bool $isRemote 是否远程文件
* @param string $proxy 代理
* @return array
*/
function read(string $path, $filename, bool $isRemote=false, $proxy=''){
/*解析安卓apk包中的压缩XML文件还原和读取XML内容
依赖功能需要PHP的ZIP包函数支持。*/
$appObj = new ApkParser();
$targetFile = $path;//a.apk; apk所在的路径地址
// 如果是远程文件,先下载到本地
if ($isRemote) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $path);
if ($proxy != '') {
curl_setopt($ch, CURLOPT_PROXY, $proxy);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 50);
$fileContent = curl_exec($ch);
curl_close($ch);
// 写入临时文件
$sys_tmp_path = tempnam(sys_get_temp_dir(), 'DL');
$fp = @fopen($sys_tmp_path, 'w+');
fwrite($fp, $fileContent);
$tmp_path = config('app.ROOT_PATH_PRO').'runtime/temp_file';
if(!file_exists($tmp_path)){
mkdir($tmp_path, 0777, true);
}
$targetFile = $tmp_path. '/' .time().$filename;
$res = move_uploaded_file($sys_tmp_path, $targetFile);
dump($res);die;
}
$result = [
'app_name'=>'',// 应用名称
'package'=>'',// 应用包名
'version_name'=>'',// 版本名称
'version_code'=>'',// 版本代码
];
dump($targetFile);die;
if(!is_file($targetFile)){
return $result;
}
$appObj->open($targetFile);//$res
$result = [
'app_name'=>$appObj->getAppName(),// 应用名称
'package'=>$appObj->getPackage(),// 应用包名
'version_name'=>$appObj->getVersionName(),// 版本名称
'version_code'=>$appObj->getVersionCode(),// 版本代码
];
return $result;
}
}

View File

@@ -0,0 +1,421 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {17:12}
*/
namespace wanghua\general_utility_tools_php\apk;
use Exception;
class ApkParser{
//----------------------
// 公共函数,供外部调用
//----------------------
public function open($apk_file, $xml_file='AndroidManifest.xml'){
$zip = new \ZipArchive();
if ($zip->open($apk_file) === TRUE) {
$xml = $zip->getFromName($xml_file);
$zip->close();
if ($xml){
try {
return $this->parseString($xml);
}catch (Exception $e){
}
}
}
return false;
}
public function parseString($xml){
$this->xml = $xml;
$this->length = strlen($xml);
$this->root = $this->parseBlock(self::AXML_FILE);
return true;
}
public function getXML($node=NULL, $lv=-1){
if ($lv == -1) $node = $this->root;
if (!$node) return '';
if ($node['type'] == self::END_TAG) $lv--;
$xml = @($node['line'] == 0 || $node['line'] == $this->line) ? '' : "\n".str_repeat(' ', $lv);
$xml .= $node['tag'];
$this->line = @$node['line'];
foreach ($node['child'] as $c){
$xml .= $this->getXML($c, $lv+1);
}
return $xml;
}
public function getPackage(){
return $this->getAttribute('manifest', 'package');
}
public function getVersionName(){
return $this->getAttribute('manifest', 'android:versionName');
}
public function getVersionCode(){
return $this->getAttribute('manifest', 'android:versionCode');
}
public function getAppName(){
return $this->getAttribute('manifest/application', 'android:name');
}
public function getMainActivity(){
for ($id=0; true; $id++){
$act = $this->getAttribute("manifest/application/activity[{$id}]/intent-filter/action", 'android:name');
if (!$act) break;
if ($act == 'android.intent.action.MAIN') return $this->getActivity($id);
}
return NULL;
}
public function getActivity($idx=0){
$idx = intval($idx);
return $this->getAttribute("manifest/application/activity[{$idx}]", 'android:name');
}
public function getAttribute($path, $name){
$r = $this->getElement($path);
if (is_null($r)) return NULL;
if (isset($r['attrs'])){
foreach ($r['attrs'] as $a){
if ($a['ns_name'] == $name) return $this->getAttributeValue($a);
}
}
return NULL;
}
//----------------------
// 类型常量定义
//----------------------
const AXML_FILE = 0x00080003;
const STRING_BLOCK = 0x001C0001;
const RESOURCEIDS = 0x00080180;
const START_NAMESPACE = 0x00100100;
const END_NAMESPACE = 0x00100101;
const START_TAG = 0x00100102;
const END_TAG = 0x00100103;
const TEXT = 0x00100104;
const TYPE_NULL =0;
const TYPE_REFERENCE =1;
const TYPE_ATTRIBUTE =2;
const TYPE_STRING =3;
const TYPE_FLOAT =4;
const TYPE_DIMENSION =5;
const TYPE_FRACTION =6;
const TYPE_INT_DEC =16;
const TYPE_INT_HEX =17;
const TYPE_INT_BOOLEAN =18;
const TYPE_INT_COLOR_ARGB8 =28;
const TYPE_INT_COLOR_RGB8 =29;
const TYPE_INT_COLOR_ARGB4 =30;
const TYPE_INT_COLOR_RGB4 =31;
const UNIT_MASK = 15;
private static $RADIX_MULTS = array(0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010);
private static $DIMENSION_UNITS = array("px","dip","sp","pt","in","mm","","");
private static $FRACTION_UNITS = array("%","%p","","","","","","");
private $xml='';
private $length = 0;
private $stringCount = 0;
private $styleCount = 0;
private $stringTab = array();
private $styleTab = array();
private $resourceIDs = array();
private $ns = array();
private $cur_ns = NULL;
private $root = NULL;
private $line = 0;
//----------------------
// 内部私有函数
//----------------------
private function getElement($path){
if (!$this->root) return NULL;
$ps = explode('/', $path);
$r = $this->root;
foreach ($ps as $v){
if (preg_match('/([^\[]+)\[([0-9]+)\]$/', $v, $ms)){
$v = $ms[1];
$off = $ms[2];
}else {
$off = 0;
}
foreach ($r['child'] as $c){
if ($c['type'] == self::START_TAG && $c['ns_name'] == $v){
if ($off == 0){
$r = $c; continue 2;
}else {
$off--;
}
}
}
// 没有找到节点
return NULL;
}
return $r;
}
private function parseBlock($need = 0){
$o = 0;
$type = $this->get32($o);
if ($need && $type != $need) throw new Exception('Block Type Error', 1);
$size = $this->get32($o);
if ($size < 8 || $size > $this->length) throw new Exception('Block Size Error', 2);
$left = $this->length - $size;
$props = false;
switch ($type){
case self::AXML_FILE:
$props = array(
'line' => 0,
'tag' => '<?xml version="1.0" encoding="utf-8"?>'
);
break;
case self::STRING_BLOCK:
$this->stringCount = $this->get32($o);
$this->styleCount = $this->get32($o);
$o += 4;
$strOffset = $this->get32($o);
$styOffset = $this->get32($o);
$strListOffset = $this->get32array($o, $this->stringCount);
$styListOffset = $this->get32array($o, $this->styleCount);
$this->stringTab = $this->stringCount > 0 ? $this->getStringTab($strOffset, $strListOffset) : array();
$this->styleTab = $this->styleCount > 0 ? $this->getStringTab($styOffset, $styListOffset) : array();
$o = $size;
break;
case self::RESOURCEIDS:
$count = $size / 4 - 2;
$this->resourceIDs = $this->get32array($o, $count);
break;
case self::START_NAMESPACE:
$o += 8;
$prefix = $this->get32($o);
$uri = $this->get32($o);
if (empty($this->cur_ns)){
$this->cur_ns = array();
$this->ns[] = &$this->cur_ns;
}
$this->cur_ns[$uri] = $prefix;
break;
case self::END_NAMESPACE:
$o += 8;
$prefix = $this->get32($o);
$uri = $this->get32($o);
if (empty($this->cur_ns)) break;
unset($this->cur_ns[$uri]);
break;
case self::START_TAG:
$line = $this->get32($o);
$o += 4;
$attrs = array();
$props = array(
'line' => $line,
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o)),
'flag' => $this->get32($o),
'count' => $this->get16($o),
'id' => $this->get16($o)-1,
'class' => $this->get16($o)-1,
'style' => $this->get16($o)-1,
'attrs' => &$attrs
);
$props['ns_name'] = $props['ns'].$props['name'];
for ($i=0; $i < $props['count']; $i++){
$a = array(
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o)),
'val_str' => $this->get32($o),
'val_type' => $this->get32($o),
'val_data' => $this->get32($o)
);
$a['ns_name'] = $a['ns'].$a['name'];
$a['val_type'] >>= 24;
$attrs[] = $a;
}
// 处理TAG字符串
$tag = "<{$props['ns_name']}";
foreach ($this->cur_ns as $uri => $prefix){
$uri = $this->getString($uri);
$prefix = $this->getString($prefix);
$tag .= " xmlns:{$prefix}=\"{$uri}\"";
}
foreach ($props['attrs'] as $a){
$tag .= " {$a['ns_name']}=\"".
$this->getAttributeValue($a).
'"';
}
$tag .= '>';
$props['tag'] = $tag;
unset($this->cur_ns);
$this->cur_ns = array();
$this->ns[] = &$this->cur_ns;
$left = -1;
break;
case self::END_TAG:
$line = $this->get32($o);
$o += 4;
$props = array(
'line' => $line,
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o))
);
$props['ns_name'] = $props['ns'].$props['name'];
$props['tag'] = "</{$props['ns_name']}>";
if (count($this->ns) > 1){
array_pop($this->ns);
unset($this->cur_ns);
$this->cur_ns = array_pop($this->ns);
$this->ns[] = &$this->cur_ns;
}
break;
case self::TEXT:
$o += 8;
$props = array(
'tag' => $this->getString($this->get32($o))
);
$o += 8;
break;
default:
throw new Exception('Block Type Error', 3);
break;
}
$this->skip($o);
$child = array();
while ($this->length > $left){
$c = $this->parseBlock();
if ($props && $c) $child[] = $c;
if ($left == -1 && $c['type'] == self::END_TAG){
$left = $this->length;
break;
}
}
if ($this->length != $left) throw new Exception('Block Overflow Error', 4);
if ($props){
$props['type'] = $type;
$props['size'] = $size;
$props['child'] = $child;
return $props;
}else {
return false;
}
}
private function getAttributeValue($a){
$type = &$a['val_type'];
$data = &$a['val_data'];
switch ($type){
case self::TYPE_STRING:
return $this->getString($a['val_str']);
case self::TYPE_ATTRIBUTE:
return sprintf('?%s%08X', self::_getPackage($data), $data);
case self::TYPE_REFERENCE:
return sprintf('@%s%08X', self::_getPackage($data), $data);
case self::TYPE_INT_HEX:
return sprintf('0x%08X', $data);
case self::TYPE_INT_BOOLEAN:
return ($data != 0 ? 'true' : 'false');
case self::TYPE_INT_COLOR_ARGB8:
case self::TYPE_INT_COLOR_RGB8:
case self::TYPE_INT_COLOR_ARGB4:
case self::TYPE_INT_COLOR_RGB4:
return sprintf('#%08X', $data);
case self::TYPE_DIMENSION:
return $this->_complexToFloat($data).self::$DIMENSION_UNITS[$data & self::UNIT_MASK];
case self::TYPE_FRACTION:
return $this->_complexToFloat($data).self::$FRACTION_UNITS[$data & self::UNIT_MASK];
case self::TYPE_FLOAT:
return $this->_int2float($data);
}
if ($type >=self::TYPE_INT_DEC && $type < self::TYPE_INT_COLOR_ARGB8){
return (string)$data;
}
return sprintf('<0x%X, type 0x%02X>', $data, $type);
}
private function _complexToFloat($data){
return (float)($data & 0xFFFFFF00) * self::$RADIX_MULTS[($data>>4) & 3];
}
private function _int2float($v) {
$x = ($v & ((1 << 23) - 1)) + (1 << 23) * ($v >> 31 | 1);
$exp = ($v >> 23 & 0xFF) - 127;
return $x * pow(2, $exp - 23);
}
private static function _getPackage($data){
return ($data >> 24 == 1) ? 'android:' : '';
}
private function getStringTab($base, $list){
$tab = array();
foreach ($list as $off){
$off += $base;
$len = $this->get16($off);
$mask = ($len >> 0x8) & 0xFF;
$len = $len & 0xFF;
if ($len == $mask){
if ($off + $len > $this->length) throw new Exception('String Table Overflow', 11);
$tab[] = substr($this->xml, $off, $len);
}else {
if ($off + $len * 2 > $this->length) throw new Exception('String Table Overflow', 11);
$str = substr($this->xml, $off, $len * 2);
$tab[] = mb_convert_encoding($str, 'UTF-8', 'UCS-2LE');
}
}
return $tab;
}
private function getString($id){
if ($id > -1 && $id < $this->stringCount){
return $this->stringTab[$id];
}else {
return '';
}
}
private function getNameSpace($uri){
for ($i=count($this->ns); $i > 0; ){
$ns = $this->ns[--$i];
if (isset($ns[$uri])){
$ns = $this->getString($ns[$uri]);
if (!empty($ns)) $ns .= ':';
return $ns;
}
}
return '';
}
private function get32(&$off){
$int = unpack('V', substr($this->xml, $off, 4));
$off += 4;
return array_shift($int);
}
private function get32array(&$off, $size){
if ($size <= 0) return NULL;
$arr = unpack('V*', substr($this->xml, $off, 4 * $size));
if (count($arr) != $size) throw new Exception('Array Size Error', 10);
$off += 4 * $size;
return $arr;
}
private function get16(&$off){
$int = unpack('v', substr($this->xml, $off, 2));
$off += 2;
return array_shift($int);
}
private function skip($size){
$this->xml = substr($this->xml, $size);
$this->length -= $size;
}
}

View File

@@ -0,0 +1,103 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/3/19} {13:57}
*/
namespace wanghua\general_utility_tools_php\coin;
use wanghua\general_utility_tools_php\http\Curl;
class Coin
{
public $apiKey = 'f94a2a9c-28be-47c3aaa-9c29-1e83b309d076';
public $url = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest';
/**
* desc
'limit'=>'1000',
'sort'=>'market_cap',
'sort_dir'=>'desc',
* @param array $params
* @return array|bool|int|string
*/
function getAll(array $params){
$params_str = http_build_query($params);
$url = $this->url.'?'.$params_str;
$header = [
'X-CMC_PRO_API_KEY:'.$this->apiKey,
'accept: application/json',
];
return Curl::curl_get($url,10,$header);
}
/**
* desc
*
*https://coinmarketcap.com/api/documentation/v1/#section/Introduction
* 'https://pro-api.coinmarketcap.com/v2/cryptocurrency/ohlcv/historical'
* authorwh
*/
function k(){
$url = $this->url;
$time_start = input('time_start');
$time_end = input('time_end');
$type = input('type');
$header = [
'X-CMC_PRO_API_KEY:'.$this->apiKey,
];
$arr = explode('=',$type);
$params = [
$arr[0]=>$arr[1],//币种
'time_start'=>$time_start,
'time_end'=>$time_end,
'count'=>7,
];
$url = $url.'?'.http_build_query($params);
return Curl::curl_get($url,10,$header);
}
private function curl_request($url, $method = 'GET',$data=null,$header=array(),$call_back=null)
{
set_time_limit(30);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
if($method == 'POST'){
if($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if($call_back){
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $call_back);
}
$result = curl_exec($ch);
if (curl_errno($ch)) {
return [
'status' => 'error',
'message' => 'curl 错误信息: ' . curl_error($ch)
];
}
curl_close($ch);
return $result;
}
}

View File

@@ -0,0 +1,390 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/12/17} {14:30}
*/
namespace wanghua\db\general_utility_tools_php;
use wanghua\db\general_utility_tools_php\SqlOperateLog;
use think\Container;
use think\Db;
use think\Paginator;
/**
* 环境要求:
* ThinkPHP5.1+,PHP7.0+,MySQL8.0+
*
*
* 查询缓存器、监控器
* 【使用框架链式操作时,配合此类才能发挥完美效果】
* 举例
* function getMemberInIds(string $ids){
$obj = Db::name($this->table)->whereIn("id",$ids)->where(["is_deleted"=>0]);
return DbCacheUtility::getAll($obj);
}
* Class DbCacheUtility
* @package libraries
*/
class DbCacheUtility
{
const NoCacheTime = 0;
const LowCacheTime = 60 * 1;
const NormalCacheTime = 60 * 10;
const HighCacheTime = 60 * 60;
const LongCacheTime = 60 * 60 * 24;
/**
* desc
* authorwh
* @param $object 查询对象
* @param int $cacheDuration
* @param bool $is_log
* @return mixed
*/
public static function getOne($object, $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = $object->fetchSql(true)->find();
$md5_name = md5($last_sql);
$key = 'get_one:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = $object->fetchSql(false)->find();
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'get_one', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $object 查询对象
* @param int $cacheDuration
* @param bool $is_log
* @return mixed
*/
public static function getAll($object, $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = $object->fetchSql(true)->select();
$md5_name = md5($last_sql);
$key = 'get_all:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = $object->fetchSql(false)->select();
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'get_all', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $object
* @param int $page
* @param int $listRows
* @param bool $simple
* @param array $config
* @param int $cacheDuration
* @param bool $is_log
* @return mixed
*/
static function paginates($object, $page=1, $listRows = null, $simple = false, $config = [], $cacheDuration = self::NoCacheTime, $is_log = true){
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = $object->limit(abs(1*$page-1) * $listRows, $listRows)->fetchSql(true)->select();
$md5_name = md5($last_sql);
$key = 'paginates:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = $object->fetchSql(false)->paginate($listRows, $simple, $config);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'paginates', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function queryScalar($sql, $bind = [], $field, $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = self::presetBind($sql, $bind);
$md5_name = md5($last_sql);
$key = 'query_scalar:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = Db::query($sql, $bind);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_scalar', $is_cache);
}
return $result ? $result[0][$field] : [];
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function queryOne($sql, $bind = [], $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = self::presetBind($sql, $bind);
$md5_name = md5($last_sql);
$key = 'query_one:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = Db::query($sql, $bind);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_one', $is_cache);
}
return $result ? $result[0] : [];
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function queryAll($sql, $bind = [], $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = self::presetBind($sql, $bind);
//echo $last_sql;die;
$md5_name = md5($last_sql);
$key = 'query_all:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = Db::query($sql, $bind);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_all', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return int
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function execute($sql, $bind = []){
return Db::execute($sql, $bind);
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
*/
static function presetBind($sql, $bind = []){
if($bind){
foreach ($bind as $key=>$val){
$sql = str_replace(':'.$key, $val, $sql);
}
}
return $sql;
}
static function paginate($sql, $bind, $total, $listRows = null, $simple = false, $config = [])
{
//if (is_int($simple)) {
// $total = $simple;
// $simple = false;
//}
$paginate = Container::get('config')->pull('paginate');
if (is_array($listRows)) {
$config = array_merge($paginate, $listRows);
$listRows = $config['list_rows'];
} else {
$config = array_merge($paginate, $config);
$listRows = $listRows ?: $config['list_rows'];
}
/** @var Paginator $class */
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);
$page = isset($config['page']) ? (int) $config['page'] : call_user_func([
$class,
'getCurrentPage',
], $config['var_page']);
$page = $page < 1 ? 1 : $page;
$config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']);
//if (!isset($total) && !$simple) {
//
//
// unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
//
// $bind = $this->bind;
// $total = $this->count();
// $results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
//} elseif ($simple) {
// $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
// $total = null;
//} else {
// $results = $this->page($page, $listRows)->select();
//}
$results = DbCacheUtility::queryAll($sql, $bind);
return $class::make($results, $listRows, $page, $total, $simple, $config);
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/5/22} {16:43}
*/
namespace wanghua\db\general_utility_tools_php;
use think\cache\driver\Redis as RedisDriver;
use think\Db;
use think\facade\Cache;
/**
* Redis操作工具
*
* 环境要求:
* ThinkPHP5.1+,PHP7.0+,MySQL8.0+
*
*
* Class RedisUtility
* @package libraries
*/
class RedisUtility
{
protected static $redisHandle = null;
/**
* desc初始化Redis对象
* authorwh
* @return object|null
*/
static function redisObject(){
if(empty(self::$redisHandle)){
//初始化驱动拿到句柄
self::$redisHandle = (new RedisDriver(config('cache.redis')))->handler();
}
return self::$redisHandle;
}
/**
* desc设置 0 永久有效
* authorwh
* @param $key_name 键名
* @param $value 值 json数据
* @return bool
*/
static function set($key_name, $value){
return Cache::store('redis')->set($key_name, json_encode($value, JSON_UNESCAPED_UNICODE));
}
/**
* desc重置
* authorwh
* @param $key_name
* @return bool
*/
static function reset($key_name){
return Cache::store('redis')->set($key_name, null);
}
/**
* desc设置过期时间
* authorwh
* @param $key_name 键名
* @param $value 值 json数据
* @param int $expire 过期时间 0 永久有效
* @return bool
*/
static function setExpire($key_name, $value, $expire=0){
return Cache::store('redis')->set($key_name, json_encode($value), $expire);
}
/**
* desc存储hash类型
* authorwh
* @param $key
* @param $field
* @param $value
*/
static function hSet($key, $field, $value){
return self::redisObject()->hSet($key, $field, json_encode($value, JSON_UNESCAPED_UNICODE));
}
/**
* desc删除指定的键。
* authorwh
* @param $key
* @return mixed
*/
static function delete($key){
return self::redisObject()->delete($key);
}
/**
* desc从存储在键上的哈希中移除一个值。
* 如果哈希表不存在,或者键不存在,则返回 FALSE 。
* authorwh
* @param $key
* @param $hashKey1
* @return mixed
*/
static function hDel($key, $hashKey1){
return self::redisObject()->hDel($key, $hashKey1);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/10/31} {9:25}
*/
namespace wanghua\db\general_utility_tools_php;
use think\Db;
use think\facade\Log;
/**
* 环境要求:
* ThinkPHP5.1+,PHP7.0+,MySQL8.0+
*
* 日志操作类配合DbCacheUtility.php使用。
* 使用前先在系统config.php文件中添加配置:
* 1、is_sql_slow_log:是否记录sql慢日志true or false
* 2、新建表log_sql_operate字段action,is_cache,duration_time,type,is_cache,sql,md5_name
* 3、如果什么错都没有报但是程序就是没反应则去runtime目录中找错误。
*
* Class SqlOperateLog
* @package wanghua\general_utility_tools_php
*/
class SqlOperateLog
{
static function add($sql,$end_time,$begin_time,$md5_name,$type,$is_cache){
$data = [
'action'=>request()->baseUrl(),
'duration_time'=>$end_time - $begin_time,
'type'=>$type,
'is_cache'=>$is_cache?1:0,
'sql'=>$sql,
'md5_name'=>$md5_name,
];
try{
if(config('app.is_sql_slow_log') && ($end_time - $begin_time) >= config('app.is_sql_slow_log')){
Db::table('log_sql_operate')->insert($data);
}
}catch (\Exception $e){
//DATABASE BOOM
//tp6
//Log::error('========[数据库异常:DATABASE BOOM]========'.$e->getTraceAsString());
//Log::close();
//tp5.1
Log::write('========[DATABASE ERROR:DATABASE BOOM]========【'.$e->getMessage().'】'.$e->getTraceAsString());
}
}
}

View File

@@ -0,0 +1,262 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/4/26} {9:45}
*/
namespace wanghua\general_utility_tools_php\db\es;
use wanghua\general_utility_tools_php\Date;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 【es原生操作库】
* 【仅支持框架ThinkPHP5+、 PHP7.2+】
*
* BaseElasticsearch.php类库的替代库
*
* 使用步骤:
* 初始化设置ip、索引前缀-普通索引请设置为空字符串)
* 设置索引范围例如查询本月如果索引不是按日期规律设置请使用普通索引设置方法setIndexNormal并设置前缀为空字符串
* 设置查询方法eg: _search
* 设置查询参数(查询条件)
* 执行
* 返回结果
* Class ElasticsearchUtility
* @package app\admin\logic
*/
class Elasticsearch
{
protected $es_log_file = 'es_log';
//最终拼装的请求地址
protected $post_url = ''; //eg: $url = 'http://49.4.3.4:9111/qa-item-2021.04/_search';
//查询参数
protected $query_param = [];
//查询方法
protected $query_method = '_search';
//文档索引
protected $index = '';
//服务器ip
protected $ip = '';
//索引前缀(优先设置)
protected $index_sign = '';//eg: qa-stat-
//设置查询日期格式符号
protected $doc_date_sign = '.';
//设置是哪个方法调用本es库用于日志记录
protected $exe_func = '';
/**
* ElasticsearchUtility constructor.
* @param string $ip_port 协议+服务器ip+端口号,必须
* @param string $index_sign 当es索引为有规律的日期索引时可使用前缀并配合日期索引设置方法体验更佳
*/
public function __construct(string $ip_port, string $index_sign='')
{
$this->ip = false===strpos($ip_port,'http')?'http://'.$ip_port:$ip_port;
$this->index_sign = $index_sign;
}
/**
* desc设置查询方法默认_search
* authorwh
* @param $method_name
*/
function setQueryMethod($method_name){
$this->query_method = $method_name;
}
/**
* desc根据日期索引查询文档
* 注:默认一个月
*
* eg:'qa-stat-2021.01';
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
* authorwh
*/
function setIndexDefault(){
$this->setRequestIndex(date('Y'.$this->doc_date_sign.'m'));
}
/**
* desc设置要查询的日期文档索引
*
* 1、按年检索文档[建议5年左右]
*
* eg: qa-stat-2018.*,qa-stat-2019.*,qa-stat-2020.*,qa-stat-2021.*
*
* 注意当索引过长es会抛出索引太长异常
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time 开始时间 eg2010-01-01 08:05:55
* @param string $end_time 结束时间 eg2021-12-31 12:05:00
* @return string
*/
function setIndexYearDate(string $start_time, string $end_time){
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'*', strtotime($start_time));
$sign = ',';//分隔符
$m = date('Y', strtotime($end_time)) - date('Y', strtotime($start_time));
//拼装查询索引
for ($i=0; $i<$m; $i++){
$index .= $sign.($this->index_sign.(date('Y', strtotime($start_time))+$i).$this->doc_date_sign.'*');
}
$this->setRequestIndex($index);
}
/**
* desc设置日期索引
*
* 检索全部日期索引文档
*
* 例如es设置的索引为
* qa-stat-2021.01
qa-stat-2021.02
qa-stat-2021.03
qa-stat-2021.04
*
* 实际查询自动设置为qa-stat-*
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @return string
*/
function setIndexAllDate(){
$this->setRequestIndex('*');
}
/**
* desc设置普通索引
*
* 调用此方法请在初始化时设置索引前缀index_sign为""空字符串
*
* authorwh
* @param string $index
*/
function setIndexNormal(string $index){
$this->setRequestIndex($index);
}
/**
* desc设置跨月份索引
*
* eg: qa-stat-2021.01,qa-stat-2021.02
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time eg:'2020-11'
* @param string $end_time eg:'2021-01'
* @return string eg: qa-stat-2020.11,qa-stat-2020.12,qa-stat-2021.01
*/
function setIndexDate(string $start_time, string $end_time){
$date = new Date();
//计算月份
$m = $date->dateCutMonth($start_time, $end_time);
////解决跨年且不足1个月时索引设置错误问题
//if($m == 0 && (date('Y', strtotime($end_time)) > date('Y', strtotime($start_time)))){
// $m = 1;
//}
//格式
$date->date_format = 'Y-m';
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'m', strtotime($start_time));
$tmp_time = $start_time;
$sign = ',';//分隔符
//拼装查询索引
for ($i=0; $i<$m; $i++){
$tmp_time = $date->addTime(1, 'M', strtotime($tmp_time));
$index .= $sign.$this->index_sign.(date('Y'.$this->doc_date_sign.'m', strtotime($tmp_time)));
}
$this->setRequestIndex($index);
}
/**
* desc设置查询参数
* authorwh
* @param string $query_param_json
*/
function setQueryParam(string $query_param_json){
$this->query_param = $query_param_json;
$this->post_url = $this->ip.'/'.$this->index.'/'.$this->query_method;
}
/**
* desc获取查询参数用于调试
* authorwh
* @return array
*/
function getQueryParam(){
return [
'ip'=>$this->ip,
'index_sign'=>$this->index_sign,
'index'=>$this->index,
'query_method'=>$this->query_method,
'post_url'=>$this->post_url,
'query_param'=>$this->query_param,
];
}
/**
* desc执行es查询
* authorwh
* @return array|bool|int|string
* @throws \Exception
*/
function execute(){
if(empty($this->ip)) throw new \Exception('请设置ip');
//if(empty($this->index_sign)) throw new \Exception('请设置索引前缀');
if(empty($this->index)) throw new \Exception('请设置索引');
if(empty($this->query_method)) throw new \Exception('请设置查询方法');
if(empty($this->query_param)) throw new \Exception('请设置查询参数');
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'request_url'=>$this->post_url,'IN'=>" | IN: ",'query_params'=>$this->query_param, 'input'=>input()], $this->es_log_file);
$result = Tools::curl_post($this->post_url, $this->query_param);
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'OUT'=>" | OUT: ", 'result'=>$result], $this->es_log_file);
return $result;
}
/**
* desc设置是哪个方法调用本es库用于日志记录
* authorwh
* @param string $function
*/
function setExecuteFunction(string $function){
$this->exe_func = $function;
}
/**
* desc设置请求文档索引
* authorwh
* @param string $index
*/
protected function setRequestIndex(string $index){
$this->index = $this->index_sign.$index;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
## 仅适用于tp5+, php7+
## 表管理
## 字段管理

View File

@@ -0,0 +1,304 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {10:57}
*/
namespace wanghua\general_utility_tools_php\db\mysql\lib;
use think\Db;
class Field
{
/**
* desc创建字段存在则修改字段
* authorwh
* @param array $table
*/
function createfield(array $table){
$table_name = $table['tablename'];
//新增字段
$dec_num = '';
if(in_array(strtoupper($table['type']), ['TINYINT','SMALLINT','MEDIUMINT','INT','BIGINT','FLOAT','DOUBLE','DECIMAL'])){
$table['default'] = '0';//整型默认值 浮点类型自动转为0.00
if(in_array(strtoupper($table['type']), ['FLOAT','DOUBLE','DECIMAL'])){
$dec_num = ','.$table['decimals_size'];//浮点类型小数位位数
}
}elseif ($table['type'] == 'enum'){
//枚举值不转换
}else{
$table['default'] = '""';
}
if(in_array(strtolower($table['type']), ['timestamp','datetime','date','time','text','mediumtext','longtext'])){
$table['size'] = 0;//重写 默认0
$table['default'] = 'null';//重写
}
//是否可以负数
if(in_array(strtolower($table['type']), ['int','tinyint','bigint','smallint', 'double', 'float', 'decimal']) && !empty($post['is_unsigned'])){//不是
$is_unsigned = 'unsigned';
}else{//是 表单可选值
$is_unsigned = '';
}
if (in_array($table['fields_name'], ['create_time', 'update_time'])){
if($table['fields_name'] == 'create_time'){
$table['default'] = 'CURRENT_TIMESTAMP';
}
if($table['fields_name'] == 'update_time'){
$table['default'] = 'NULL ON UPDATE CURRENT_TIMESTAMP';
}
}
//验证字段是否存在
$f = DB::table($table_name)->getTableFields();
if(in_array($table['fields_name'], $f)){
//枚举类型
if ($table['type'] == 'enum'){
$enum_str = '';
$enum_default = '';
//组合枚举值
if($table['default']){
$enum_str.='ENUM('.$table['default'].')';
$enum_default = explode(',', $table['default'])[0];
}
$sql2 = "ALTER TABLE {$table_name} MODIFY {$table['fields_name']} {$enum_str} NOT NULL DEFAULT {$enum_default} COMMENT '{$table['title']}';";
}else{
$sql2 = "ALTER TABLE {$table_name} MODIFY COLUMN {$table['fields_name']} {$table['type']}(".$table['size'].$dec_num.") {$is_unsigned} DEFAULT {$table['default']} COMMENT '{$table['title']}';";
}
}else{
if(in_array($table['type'], ['text','longtext'])){
$sql2 = "ALTER TABLE {$table_name} ADD COLUMN {$table['fields_name']} {$table['type']} COMMENT '{$table['title']}' AFTER id;";
}elseif ($table['type'] == 'enum'){//枚举类型
$enum_str = '';
$enum_default = '';
//组合枚举值
if($table['default']){
$enum_str.='ENUM('.$table['default'].')';
$enum_default = explode(',', $table['default'])[0];
}
$sql2 = "ALTER TABLE {$table_name} ADD {$table['fields_name']} {$enum_str} NOT NULL DEFAULT {$enum_default} COMMENT '{$table['title']}' AFTER id;";
}else{
$sql2 = "ALTER TABLE {$table_name} ADD COLUMN {$table['fields_name']} {$table['type']}(".$table['size'].$dec_num.") {$is_unsigned} DEFAULT {$table['default']} COMMENT '{$table['title']}' AFTER id;";
}
}
DB::execute($sql2);
}
/**
* desc删除字段
* authorwh
* @param $tablename
* @param $fieldname
*/
function dropFieldName($tablename, $fieldname){
$sql = "ALTER TABLE {$tablename} DROP COLUMN {$fieldname};";
DB::execute($sql);
}
/**
* desc改注释
* authorwh
* @param $tablename
* @param $comment
* @return bool
*/
function updateComment($tablename,$field, $comment){
$sql = "ALTER TABLE {$tablename} MODIFY COLUMN {$field} INT COMMENT '{$comment}';";
Db::execute($sql);
}
/**
* desc获取数据表中所有字段的数据类型含其它属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldsDataType(string $dbname,string $tablename){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'";
return Db::query($sql);
}
/**
* desc获取数据表中某个字段的数据类型含这个字段的其它属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldDataType(string $dbname,string $tablename,string $fieldname){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0];
}
/**
* desc获取数据表中某个字段的数据类型
*
* 不含这个字段的其它属性
*
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldDataTypeVal(string $dbname,string $tablename,string $fieldname){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0]['F_DATATYPE'];
}
/**
* desc获取数据表中某个字段的属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldAttrVal(string $dbname,string $tablename,string $fieldname,string $attr){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0][$attr];
}
}

View File

@@ -0,0 +1,162 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {10:48}
*/
namespace wanghua\general_utility_tools_php\db\mysql\lib;
use think\Db;
use wanghua\general_utility_tools_php\tool\Tools;
class Table
{
/**
* desc创建表初始化默认字段
* authorwh
* @param $data
* @return bool
*/
function createTable($data){
$tablename = $data['tablename'];
$title = $data['title'];
//创建表
$sql = "CREATE TABLE IF NOT EXISTS {$tablename}(
id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='{$title}' ;";
Db::execute($sql);
}
/**
* desc改注释
* authorwh
* @param $tablename
* @param $comment
* @return bool
*/
function updateComment($tablename, $comment){
$sql = "ALTER TABLE {$tablename} COMMENT '{$comment}';";
Db::execute($sql);
}
/**
* desc改表名
* authorwh
* @param $tablename
* @param $new_table_name
* @return bool
*/
function updateTableName($tablename, $new_table_name){
$sql = "ALTER TABLE {$tablename} RENAME TO {$new_table_name}";
Db::execute($sql);
}
/**
* desc查询当前数据库所有的表名
* authorwh
* @return mixed
*/
function getTables(){
return array_column(Db::query('SHOW TABLES;'), 'Tables_in_'.config('database.database'));
}
/**
* desc获取数据表字所有段名
* authorwh
* @param $tablename
* @return array
*/
function getTableFields($tablename){
$dbname = config('database.database');
$sql = "SELECT COLUMN_NAME column_name,COLUMN_COMMENT column_comment,DATA_TYPE data_type
FROM information_schema.columns WHERE TABLE_NAME='{$tablename}' AND table_schema='{$dbname}'";
return array_column(Db::query($sql), 'column_name');
}
/**
* desc获取表的属性
* Name:
表名称
Engine:
表的存储引擎
Version:
版本
Row_format:
行格式。对于MyISAM引擎这可能是DynamicFixed或Compressed。动态行的行长度可变例如Varchar或Blob类型字段。固定行是指行长度不变例如Char和Integer类型字段
Rows:
表中的行数。对于MyISAM和其他存储引擎这个值是精确的对于innoDB存储引擎这个值通常是估算的
Avg_row_length:
平均每行包括的字节数
Data_length:
整个表的数据量(以字节为单位)
Max_data_length:
表可以容纳的最大数据量,该值和存储引擎相关
Index_length:
索引占用磁盘的空间大小(以字节为单位)
Data_free:
对于MyISAM引擎表示已经分配但目前没有使用的空间。这部分空间包含之前被删除的行以及后续可以被insert利用到的空间
Auto_increment:
下一个Auto_increment的值
Create_time:
表的创建时间
Update_time:
表的最近更新时间
Check_time:
使用 check table 或myisamchk工具最后一次检查表的时间
Collation:
表的默认字符集和字符排序规则
Checksum:
如果启用,保存的是整个表的实时校验和
Create_options:
创建表时指定的其他选项
Comment:
包含了其他额外信息对于MyISAM引擎保存的是表在创建时带的注释。如果表使用的是innodb引擎 保存的是InnoDB表空间的剩余空间。如果是一个视图注释里面包含了VIEW字样。
*
* @param $tablename 表名(无表名则查询所有表)
*
* authorwh
* @return mixed
*/
function getTableInfo(string $tablename=''){
if($tablename){
$sql = "SHOW TABLE STATUS WHERE Name = 'fa_agent';";
return Db::query($sql);
}
$sql = 'show table status;';
return Db::query($sql);
}
/**
* desc检查某个表是否存在
*
* authorwh
* @param string $dbname
* @param string $tablename
* @return bool
*/
function isExistTable(string $dbname,string $tablename){
$sql = "SELECT TABLE_SCHEMA,TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA ='$dbname'
AND TABLE_NAME = '$tablename';";
return empty(Db::query($sql));
}
/**
* desc获取表注释
*
* authorwh
*/
function getTableComment($table){
$prefix = config('database.prefix');
$table = $prefix.$table;
$sql = 'show table status;';
$arr = Db::query($sql);
$tmp = Tools::key_val_arr($arr,'Name','Comment');
return empty($tmp[$table])?'':$tmp[$table];
}
}

View File

@@ -0,0 +1,15 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/4/5} {10:49}
*/
namespace wanghua\general_utility_tools_php\douyin;
class BaseDouYin
{
}

View File

@@ -0,0 +1,193 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/4/4} {12:36}
*/
namespace wanghua\general_utility_tools_php\douyin;
use wanghua\general_utility_tools_php\http\Curl;
use wanghua\general_utility_tools_php\tool\Tools;
class DouYinMiniGame extends BaseDouYin
{
public $appid = 'tt3c974a2aa3a6ee7c02';
public $secret = '5c2414e47a18b9655a164b14ef31ac7cb58179ca';
/**
* desc每2小时更新一次
*
* https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/server/interface-request-credential/get-access-token
*
* {"err_no":0,"err_tips":"success","data":{"access_token":"0801121846384c316e52536e5a76396769565145696f597742673d3d","expires_in":7200,"expiresAt":1712208063,"expires_at":1712208063}}
*
* authorwh
*/
function getAccessToken(){
$url = "https://minigame.zijieapi.com/mgplatform/api/apps/v2/token";
$method = 'POST';
//$Scope = 'open.ttgame.mgplatform';
$params = [
'appid'=>$this->appid,
'secret'=>$this->secret,
'grant_type'=>'client_credential',
];
$header = [
'Accept: application/json',
'content-type: application/json'
];
$res = Curl::curl_request($url,$method,json_encode($params),$header);
$json_arr = json_decode($res, true);
if($json_arr['err_no']){
return Tools::set_res($json_arr['err_no'],$json_arr['err_tips']);
}
//保存
session('ses_dou_yin_access_token',$json_arr['data']);
return Tools::set_ok('ok',$json_arr['data']);
}
/**
* desc实时获取token
* authorwh
* @return array
*/
function realTimeGetAccessToken(){
$json_arr = session('ses_dou_yin_access_token');
if(empty($json_arr)){
return $this->getAccessToken();
}
if(time() >= $json_arr['expiresAt']){
//过期
return $this->getAccessToken();
}
return Tools::set_ok('ok',$json_arr);
}
/**
* desc前端tt.login触发调用
*
* authorwh
*/
function jscode2session($code='',$anonymous_code=''){
$url = 'https://minigame.zijieapi.com/mgplatform/api/apps/jscode2session';
//$Scope = 'open.ttgame.mgplatform';
$method = 'GET';
if(empty($code) && empty($anonymous_code)){
return Tools::set_fail('tt.login 接口返回的匿名登录凭证code 和 anonymous_code 至少要有一个)');
}
$params = [
'appid'=>$this->appid,
'secret'=>$this->secret,
];
if($code){
$params['code'] = $code;
}
if($anonymous_code){
$params['anonymous_code'] = $anonymous_code;
}
$header = [
'Accept: application/json',
'content-type: application/json'
];
$url .= '?'.http_build_query($params);
//dump($url);
Tools::log_to_write_txt(['input'=>$url]);
$res = Curl::curl_request($url,$method,[],$header);
Tools::log_to_write_txt(['output'=>$res]);
//dump($res);
$json_arr = json_decode($res, true);
//dump($json_arr);die;
if($json_arr['error']){
return Tools::set_res($json_arr['errcode'],$json_arr['errmsg']);
}
//保存
session('ses_dou_yin_js_code_2_session',$json_arr);
return Tools::set_ok('ok',$json_arr);
}
/**
* desc创建二维码
* 接口说明
* 获取小程序/小游戏的二维码。该二维码可通过任意 app 扫码打开,
* 能跳转到开发者指定的对应字节系 app 内拉起小程序/小游戏,并传入开发者指定的参数。
* 通过该接口生成的二维码,永久有效,暂无数量限制。
*
* ⚠ Tip在使用该功能之前请记得先配置您的默认分享文案和图片配置方式可参考论坛。
* ⚠ Tip小程序的 path 要 encode 一次,如 pages%3fparam%3dtrue小游戏的 path 为 JSON 字符串,
* 如{"param":true},否则会导致取不到。
*
* 参数:
* code和anonymous_code二选一 必须
* appname 可选,目标打开应用名称 默认douyin
* background 可选背景色rgb格式英文逗号隔开默认透明色
* path 可选,小程序/小游戏启动参数,小程序则格式为 encode({path}?{query}),小游戏则格式为 JSON 字符串,默认为空
* width 宽度 可选,二维码宽度,单位 px最小 280px最大 1280px默认为 430px
* line_color 可选二维码线条颜色默认为黑色rgb格式英文逗号隔开默认黑色
* set_icon 可选,是否展示小程序/小游戏 icon默认不展示传yes展示no不展示默认no
*
* authorwh
*/
function createQRCode($path='',$appname='',$width='',$background='',$line_color='',$set_icon='no'){
try {
$url = 'https://minigame.zijieapi.com/mgplatform/api/apps/qrcode';
//$Scope = 'open.ttgame.mgplatform';
$method = 'POST';
$json_arr = $this->realTimeGetAccessToken();
if(empty($json_arr)){
return Tools::set_fail('请重新授权');
}
$params = [
'access_token'=>$json_arr['data']['access_token'],
];
if($appname){
$params['appname'] = $appname;
}
if($width){
$params['width'] = $width;
}
//if($path){
//小程序/小游戏启动参数,小程序则格式为 encode({path}?{query}),小游戏则格式为 JSON 字符串,默认为空
$params['path'] = $path;
//}
//英文逗号隔开r,g,b格式
if($background){
$background_rgb = explode(',',$background);
$params['r'] = $background_rgb[0];
$params['g'] = $background_rgb[1];
$params['b'] = $background_rgb[2];
}
if($line_color){
$line_color_rgb = explode(',',$line_color);
$params['r'] = $line_color_rgb[0];
$params['g'] = $line_color_rgb[1];
$params['b'] = $line_color_rgb[2];
}
$params['set_icon'] = $set_icon=='yes';
$header = [
'Accept: application/json',
'content-type: application/json'
];
Tools::log_to_write_txt(['input'=>input(),'$params'=>$params]);
$res = Curl::curl_request($url,$method,json_encode($params),$header);
Tools::log_to_write_txt(['output'=>$res]);
//流
return $res;
}catch (\Exception $e){
Tools::error_txt_log($e);
return Tools::set_fail();
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/10} {15:51}
*/
namespace wanghua\general_utility_tools_php\encrypt;
/**
* 3DES,三重数据加密算法
*
* Class TripleDES
* @package wanghua\general_utility_tools_php\encrypt
*/
class TripleDES
{
/**
* desc加密
*
* authorwh
* @param string $value
* @param $key
* @return string
*/
public function encrypt(string $value, $key)
{
$value = self::PaddingPKCS7($value);
//AES-128-ECB|不能用 AES-256-CBC|16 AES-128-CBC|16 BF-CBC|8 aes-128-gcm|需要加$tag DES-EDE3-CBC|8
$cipher = "DES-EDE3";
// if (in_array($cipher, openssl_get_cipher_methods())) {}
$result = openssl_encrypt($value, $cipher, $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, "");
return base64_encode($result);
}
/**
* @title 解密
*
* @param string $str 要传的参数
* @param $key
* @return bool|false|string
*
*/
public function decrypt(string $str, $key)
{
$decrypted = openssl_decrypt(base64_decode($str), 'DES-EDE3', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, "");
$ret = self::UnPaddingPKCS7($decrypted);
return $ret;
}
/**
* desc
* authorwh
* @param $data
* @return string
*/
private function PaddingPKCS7($data)
{
//$block_size = mcrypt_get_block_size('tripledes', 'cbc');//获取长度
//$block_size = openssl_cipher_iv_length('tripledes', 'cbc');//获取长度
$block_size = 8;
$padding_char = $block_size - (strlen($data) % $block_size);
$data .= str_repeat(chr($padding_char), $padding_char);
return $data;
}
/**
* desc
* authorwh
* @param $text
* @return false|string
*/
private function UnPaddingPKCS7($text)
{
$pad = ord($text{strlen($text) - 1});
if ($pad > strlen($text)) {
return false;
}
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
return false;
}
return substr($text, 0, -1 * $pad);
}
}

View File

@@ -0,0 +1,20 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {19:05}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class AuthError
{
const PERMISSION_DENIED = [60000, '权限拒绝'];
const ACCESS_REFUSE_ERROR = [60100, '访问被拒绝'];//请求不合法资源
const ACCESS_TIMES_ERROR = [60200, '访问次数限制'];
}

View File

@@ -0,0 +1,84 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/11/12} {21:01}
*/
namespace wanghua\general_utility_tools_php\errorcode;
/**
* 系统级错误, 业务错误代码放在业务层,不要放在此处
* Class SystemError
* @package errorcode
*/
class Code
{
//region 系统级别错误
const SUCCESS = [200, 'SUCCESS'];
const REQUEST_ERROR = [10000, '请求错误'];//一般url资源错误url错误、敏感参数错误等
const REQUEST_TYPE_ERROR = [10001, '请求类型错误'];//ajax get post
const FAILED = [10002, '操作失败'];
const SOURCE_NOT_FIND = [10003, '资源找不到'];
const SYSTEM_ERROR = [500, '系统错误'];
const PERMISSION_DENIED = [10005, '权限拒绝'];
const ACCESS_REFUSE_ERROR = [10006, '访问被拒绝'];//请求不合法资源
const ACCESS_TIMES_ERROR = [10007, '访问次数限制'];
//endregion 系统级别错误
//region 短信错误
const MESSAGE_SEND_ERROR = [13500, '短信发送失败'];
const MESSAGE_SEND_ULTRALIMIT = [13600, '短信发送超限'];
const MESSAGE_VERIFY_FAILED = [13700, '短信验证失败'];
const MESSAGE_NUM_LITTLE = [13800, '短信余额不足'];
const MESSAGE_SEND_EXCEPTION = [13900, '短信发送异常'];
//endregion
//region 文件上传
const UPLOAD_FAILED = [14000, '上传失败'];
const UPLOAD_FILE_IS_EMPTY = [14000, '无文件上传'];
const UPLOAD_FILE_SIZE_EMPTY = [14100, '文件内容为空'];
const UPLOAD_FILE_SIZE_ERROR = [14200, '文件大小超过限制'];
const UPLOAD_FILE_MIME_ERROR = [14300, '文件类型错误'];
const UPLOAD_FILE_EXT_ERROR = [14500, '文件扩展名错误'];
const FILE_FROM_ERROR = [14520, '来源错误'];
//endregion
//region 数据库错误
const DATA_IS_EMPTY = [50000, '未获取到相关数据'];
const UPDATE_ERROR = [50001, '修改异常'];
const INSERT_ERROR = [50002, '数据保存异常'];
const SELECT_ERROR = [50003, '查询异常'];
const DATA_REPEAT_ERROR = [50005, '数据重复'];
const DATA_INSERT_ERROR = [50006, '数据入库异常'];
//endregion
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:49}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class DbError
{
//region 数据库错误
const DATA_IS_EMPTY = [16000, '未获取到相关数据'];
const UPDATE_ERROR = [16100, '修改异常'];
const INSERT_ERROR = [16200, '数据保存异常'];
const SELECT_ERROR = [16300, '查询异常'];
const DATA_REPEAT_ERROR = [16400, '数据重复'];
const DATA_INSERT_ERROR = [16500, '数据入库异常'];
//endregion
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:47}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class EmailError
{
//region 邮件错误
const EMAIL_RECEIVER_IS_EMPTY = [23100, '未设置收信人'];
const EMAIL_SEND_FAILED = [23200, '邮件发送失败'];
//endregion
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:48}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class FileUploadError
{
//region 文件上传
const UPLOAD_FAILED = [24000, '上传失败'];
const UPLOAD_FILE_IS_EMPTY = [24100, '无文件上传'];
const UPLOAD_FILE_SIZE_EMPTY = [24200, '文件内容为空'];
const UPLOAD_FILE_SIZE_ERROR = [24300, '文件大小超过限制'];
const UPLOAD_FILE_MIME_ERROR = [24400, '文件类型错误'];
const UPLOAD_FILE_EXT_ERROR = [24500, '文件扩展名错误'];
const FILE_SOURCE_ERROR = [24600, '文件来源错误'];
//endregion
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:59}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class LoginError
{
const OFF_LINE_ERROR = [30100, '未登录'];
const LOGIN_SESSION_INVALID = [30200, '登录已过期,请重新登录'];
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/3} {14:14}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class RequestError
{
//region 请求错误
const SUCCESS = [200, 'SUCCESS'];
const REQUEST_TYPE_ERROR = [50601, '请求类型错误'];//ajax get post
const SOURCE_NOT_FIND = [50400, '资源找不到'];
const SYSTEM_ERROR = [50500, '系统错误'];
const REQUEST_ERROR = [50600, '请求错误'];//一般url资源错误url错误、敏感参数错误等
const FAILED = [50602, '请求失败'];
//endregion 请求错误
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:49}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class SmsError
{
//region 短信错误
const MESSAGE_SEND_ERROR = [43500, '短信发送失败'];
const MESSAGE_SEND_ULTRALIMIT = [43600, '短信发送超限'];
const MESSAGE_VERIFY_FAILED = [43700, '短信验证失败'];
const MESSAGE_NUM_LITTLE = [43800, '短信余额不足'];
const MESSAGE_SEND_EXCEPTION = [43900, '短信发送异常'];
//endregion
}

View File

@@ -0,0 +1,15 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:50}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class SystemError
{
}

View File

@@ -0,0 +1,264 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/4/26} {9:45}
*/
namespace wanghua\general_utility_tools_php\es;
use wanghua\general_utility_tools_php\Date;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 废弃,即将删除
*
* 【es原生操作库】
* 【仅支持框架ThinkPHP5+、 PHP7.2+】
*
* BaseElasticsearch.php类库的替代库
*
* 使用步骤:
* 初始化设置ip、索引前缀-普通索引请设置为空字符串)
* 设置索引范围例如查询本月如果索引不是按日期规律设置请使用普通索引设置方法setIndexNormal并设置前缀为空字符串
* 设置查询方法eg: _search
* 设置查询参数(查询条件)
* 执行
* 返回结果
* Class ElasticsearchUtility
* @package app\admin\logic
*/
class Elasticsearch
{
protected $es_log_file = 'es_log';
//最终拼装的请求地址
protected $post_url = ''; //eg: $url = 'http://49.4.3.4:9111/qa-item-2021.04/_search';
//查询参数
protected $query_param = [];
//查询方法
protected $query_method = '_search';
//文档索引
protected $index = '';
//服务器ip
protected $ip = '';
//索引前缀(优先设置)
protected $index_sign = '';//eg: qa-stat-
//设置查询日期格式符号
protected $doc_date_sign = '.';
//设置是哪个方法调用本es库用于日志记录
protected $exe_func = '';
/**
* ElasticsearchUtility constructor.
* @param string $ip_port 协议+服务器ip+端口号,必须
* @param string $index_sign 当es索引为有规律的日期索引时可使用前缀并配合日期索引设置方法体验更佳
*/
public function __construct(string $ip_port, string $index_sign='')
{
$this->ip = false===strpos($ip_port,'http')?'http://'.$ip_port:$ip_port;
$this->index_sign = $index_sign;
}
/**
* desc设置查询方法默认_search
* authorwh
* @param $method_name
*/
function setQueryMethod($method_name){
$this->query_method = $method_name;
}
/**
* desc根据日期索引查询文档
* 注:默认一个月
*
* eg:'qa-stat-2021.01';
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
* authorwh
*/
function setIndexDefault(){
$this->setRequestIndex(date('Y'.$this->doc_date_sign.'m'));
}
/**
* desc设置要查询的日期文档索引
*
* 1、按年检索文档[建议5年左右]
*
* eg: qa-stat-2018.*,qa-stat-2019.*,qa-stat-2020.*,qa-stat-2021.*
*
* 注意当索引过长es会抛出索引太长异常
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time 开始时间 eg2010-01-01 08:05:55
* @param string $end_time 结束时间 eg2021-12-31 12:05:00
* @return string
*/
function setIndexYearDate(string $start_time, string $end_time){
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'*', strtotime($start_time));
$sign = ',';//分隔符
$m = date('Y', strtotime($end_time)) - date('Y', strtotime($start_time));
//拼装查询索引
for ($i=0; $i<$m; $i++){
$index .= $sign.($this->index_sign.(date('Y', strtotime($start_time))+$i).$this->doc_date_sign.'*');
}
$this->setRequestIndex($index);
}
/**
* desc设置日期索引
*
* 检索全部日期索引文档
*
* 例如es设置的索引为
* qa-stat-2021.01
qa-stat-2021.02
qa-stat-2021.03
qa-stat-2021.04
*
* 实际查询自动设置为qa-stat-*
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @return string
*/
function setIndexAllDate(){
$this->setRequestIndex('*');
}
/**
* desc设置普通索引
*
* 调用此方法请在初始化时设置索引前缀index_sign为""空字符串
*
* authorwh
* @param string $index
*/
function setIndexNormal(string $index){
$this->setRequestIndex($index);
}
/**
* desc设置跨月份索引
*
* eg: qa-stat-2021.01,qa-stat-2021.02
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time eg:'2020-11'
* @param string $end_time eg:'2021-01'
* @return string eg: qa-stat-2020.11,qa-stat-2020.12,qa-stat-2021.01
*/
function setIndexDate(string $start_time, string $end_time){
$date = new Date();
//计算月份
$m = $date->dateCutMonth($start_time, $end_time);
////解决跨年且不足1个月时索引设置错误问题
//if($m == 0 && (date('Y', strtotime($end_time)) > date('Y', strtotime($start_time)))){
// $m = 1;
//}
//格式
$date->date_format = 'Y-m';
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'m', strtotime($start_time));
$tmp_time = $start_time;
$sign = ',';//分隔符
//拼装查询索引
for ($i=0; $i<$m; $i++){
$tmp_time = $date->addTime(1, 'M', strtotime($tmp_time));
$index .= $sign.$this->index_sign.(date('Y'.$this->doc_date_sign.'m', strtotime($tmp_time)));
}
$this->setRequestIndex($index);
}
/**
* desc设置查询参数
* authorwh
* @param string $query_param_json
*/
function setQueryParam(string $query_param_json){
$this->query_param = $query_param_json;
$this->post_url = $this->ip.'/'.$this->index.'/'.$this->query_method;
}
/**
* desc获取查询参数用于调试
* authorwh
* @return array
*/
function getQueryParam(){
return [
'ip'=>$this->ip,
'index_sign'=>$this->index_sign,
'index'=>$this->index,
'query_method'=>$this->query_method,
'post_url'=>$this->post_url,
'query_param'=>$this->query_param,
];
}
/**
* desc执行es查询
* authorwh
* @return array|bool|int|string
* @throws \Exception
*/
function execute(){
if(empty($this->ip)) throw new \Exception('请设置ip');
//if(empty($this->index_sign)) throw new \Exception('请设置索引前缀');
if(empty($this->index)) throw new \Exception('请设置索引');
if(empty($this->query_method)) throw new \Exception('请设置查询方法');
if(empty($this->query_param)) throw new \Exception('请设置查询参数');
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'request_url'=>$this->post_url,'IN'=>" | IN: ",'query_params'=>$this->query_param, 'input'=>input()], $this->es_log_file);
$result = Tools::curl_post($this->post_url, $this->query_param);
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'OUT'=>" | OUT: ", 'result'=>$result], $this->es_log_file);
return $result;
}
/**
* desc设置是哪个方法调用本es库用于日志记录
* authorwh
* @param string $function
*/
function setExecuteFunction(string $function){
$this->exe_func = $function;
}
/**
* desc设置请求文档索引
* authorwh
* @param string $index
*/
protected function setRequestIndex(string $index){
$this->index = $this->index_sign.$index;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:01}
*/
namespace wanghua\general_utility_tools_php\exception;
use think\Exception;
class BaseException extends Exception
{
}

View File

@@ -0,0 +1,27 @@
# 系统业务级异常类
1. api异常
2. 请求异常
3. api异常
4. 响应异常
5. 待扩展......
##使用说明
例1
if(empty($params)){
//参数错误,抛出空参数异常
throw new RequestException(RequestException::EMPTY_PARAM_ERROR);
}
例2
//假设返回结果格式为:['code'=>200, 'msg'=>'操作成功', 'data'=>[]]
if(is_null($result['code'])){
//无code字段抛出响应格式错误
throw new ResponseException(ResponseException::RESPONSE_FORMAT_ERROR);
}

View File

@@ -0,0 +1,60 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/03/08} {14:17}
*/
namespace wanghua\general_utility_tools_php\exception;
use think\exception\Handle;
use think\Request;
use wanghua\general_utility_tools_php\SundryConfig;
use wanghua\general_utility_tools_php\tool\EmailTool;
use wanghua\general_utility_tools_php\tool\Tools;
class SystemException extends Handle
{
public function render(\Exception $e)
{
// 参数验证错误
//if ($e instanceof ValidateException) {
// return json($e->getError(), 422);
//}
// 请求异常
//if ($e instanceof HttpException && request()->isAjax()) {
// return response($e->getMessage(), $e->getStatusCode());
//}
Tools::log_to_write_txt([
'error'=>'系统错误.'.$e->getMessage(),
'input'=>Request::instance()->input(),
'error_info'=>$e->getTraceAsString()
]);
$title = '新的【系统异常】,请立即处理';
//邮件通知管理员
$mail_content = '<p>error'.$e->getMessage().'</p>';
$mail_content .= '<p>url'.request()->url(true).'</p>';
$mail_content .= '<p>ip'.request()->ip().'</p>';
$mail_content .= '<p>详情请登录宝塔查看日志</p>';
$emails = SundryConfig::val('admin_error_log_email');
if(config('sys_env') == 'PROD'){//线上环境才发错误邮件
EmailTool::email_to_person($title,$mail_content,$emails);
}
if(request()->LogObj){
request()->LogObj->flush();
}
if(request()->ServeLogObj){
request()->ServeLogObj->flush();
}
//可以在此交由系统处理
return parent::render($e);
}
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:04}
*/
namespace wanghua\general_utility_tools_php\exception\api;
use wanghua\general_utility_tools_php\exception\BaseException;
class ApiException extends BaseException
{
const API_RESPONSE_FORMAT_ERROR='The api response result is incorrectly formatted: code is not exist.';
const EMPTY_PARAM_ERROR ='param is empty.';
const EMPTY_URL_ERROR ='url of param is empty.';
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:13}
*/
namespace wanghua\general_utility_tools_php\exception\request;
use wanghua\general_utility_tools_php\exception\BaseException;
class RequestException extends BaseException
{
const EMPTY_PARAM_ERROR ='param is empty.';
const EMPTY_URL_ERROR ='url of param is empty.';
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:48}
*/
namespace wanghua\general_utility_tools_php\exception\response;
use wanghua\general_utility_tools_php\exception\BaseException;
class ResponseException extends BaseException
{
const RESPONSE_FORMAT_ERROR='The response result is incorrectly formatted.';
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/1/16} {17:15}
*/
namespace wanghua\general_utility_tools_php\fastadmin;
use app\common\model\TabConf;
use think\Db;
/**
* 为fastadmin框架定制的功能
*
* Class FastadminTools
* @package wanghua\general_utility_tools_php\fastadmin
*/
class FastadminTools
{
/**
* desc线上需要自动隐藏的菜单
*
* 当你有这种需求:
* 我的菜单只需要本地显示,线上或非本地环境不需要显示时,就可以使用此方法。
* 为什么需要此方法?
* 因为,我每次在本地增加功能并新增菜单之后,都需要把本地的菜单同步到线上,但是命令管理菜单,或者有些只需要开发者才能
* 动的菜单,需要在线上隐藏。我又不想每次同步之后去改菜单表。
* 手动去菜单表改也是可以,但是次数多了就很麻烦,有时候会忘记。
*
* 如:命令管理菜单、开发者相关配置
*
* authorwh
* @param array $menu_arr 需要隐藏的菜单数组
* @param string $sys_env
* @return bool
*/
static function auto_hidden_menu_online(array $menu_arr, string $sys_env){
foreach ($menu_arr as $menu_name){
if($sys_env == 'LOCAL'){
//本地显示
Db::table('fa_auth_rule')->where('name',$menu_name)->data(['status'=>'normal'])->update();
}else{
Db::table('fa_auth_rule')->where('name',$menu_name)->data(['status'=>'hidden'])->update();
}
}
return true;
}
}

View File

@@ -0,0 +1,236 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/2/4} {16:59}
*/
namespace wanghua\general_utility_tools_php\file;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 文件、文件夹操作类
*
* eg读取文件夹、复制文件等
*
* Class FileOperate
* @package wanghua\general_utility_tools_php\file
*/
class File
{
public $file_ext = '*.*';//扩展名
public $unzip_path = '';//解压路径
/**
* desc读取文件和文件夹
*
* authorwh
* @param $folderPath 读取目标路径下的文件夹和文件
*
* @param $dirs 存储文件路径 eg:D:\wanghua\old_files
*
//array(3) {
// [0] => array(1) {
// ["path"] => string(25) "D:\wanghua\test_files/111"
// }
// [1] => array(1) {
// ["path"] => string(25) "D:\wanghua\test_files/222"
// }
//}
*
*
*
*
* @param $file_name_arr 存储文件路径、文件名称
*
//array(64) {
// [0] => array(3) {
// ["path"] => string(25) "D:\wanghua\test_files/111"
// ["path_file"] => string(33) "D:\wanghua\test_files/111/111.txt"
// ["filename"] => string(7) "111.txt"
// }
// [1] => array(3) {
// ["path"] => string(25) "D:\wanghua\test_files/111"
// ["path_file"] => string(37) "D:\wanghua\test_files/111/1111111.txt"
// ["filename"] => string(11) "1111111.txt"
// }
//}
*/
function read_folder($folderPath,&$dirs,&$file_name_arr) {
// 获取指定目录下所有文件和文件夹
$files = scandir($folderPath);
foreach ($files as $file) {
// 如果当前项为文件夹且不是"."或者".."
if (is_dir("$folderPath/$file") && !in_array($file, array('.', '..'))) {
$dirs[] = ['path'=>$folderPath.'/'.$file];//此时的$file是文件夹名称
// 调用自身进行递归操作
$this->read_folder("$folderPath/$file",$dirs,$file_name_arr);
} elseif (!in_array($file, array('.', '..'))){ // 如果当前项为文件而非"."或者".."
$file_name_arr[] = ['path'=>$folderPath,'path_file'=>"$folderPath/$file",'filename'=>$file];
}
}
}
/**
* desc复制文件
*
* authorwh
* @param array $sourceFileArr 源文件路径数组 path,path_file,filename
*
* @param string $destination_path 目标存放路径 D:\wanghua\test_files\new_files
*
* @param array $cp_type_arr 允许复制的文件后缀 eg:['jpg','png,'spine']
*
* @param bool $is_cover 是否覆盖 默认false不覆盖会以源文件名+微秒时间戳格式作为新的文件名
*
* @param bool $is_continue 相同文件是否跳过复制 默认 true
*
* 实战案例:
*
function testcopy(){
$source_dir = 'D:\wanghua\test_files';//源文件路径
$destination_path = 'D:\wanghua\test_files\new_files';//目标文件路径
$dirs = [];//存储文件路径
$file_name_arr = [];//含文件路径、名称
$file_obj = new File();
$file_obj->read_folder($source_dir,$dirs,$file_name_arr);
//dump($dirs);
//dump($file_name_arr);die;
$file_obj->copy($file_name_arr,$destination_path,['jpg','png,'spine']);
}
*
*/
function copy(array $sourceFileArr, string $destination_path, $cp_type_arr=['jpg','png'], $is_cover=false, $is_continue=false){
//设置不超时
set_time_limit(0);
//源文件路径 => 目标文件路径
foreach ($sourceFileArr as $key=>$file_arr){
if(!file_exists($file_arr['path_file'])){
continue;
}
//验证目标文件夹路径
if(!file_exists($destination_path)){
mkdir($destination_path,0777,true);
}
$name_arr = explode('.',$file_arr['filename']);
//验证符合条件的文件类型
if(!in_array($name_arr[1],$cp_type_arr)){
continue;
}
if(file_exists($destination_path.'/'.$file_arr['filename'])){
if($is_continue){
continue;//相同文件跳过复制
}
//覆盖
if($is_cover){
copy($file_arr['path_file'], $destination_path.'/'.$file_arr['filename']);
}else{
//不覆盖,名称区分
$microsecond = Tools::getMicrosecond();
copy($file_arr['path_file'], $destination_path.'/'.$name_arr[0].'('.$microsecond.').'.$name_arr[1]);
}
}else{
copy($file_arr['path_file'], $destination_path.'/'.$file_arr['filename']);
}
}
}
/**
* desc递归删除目录
*
* $dir 物理路径
*
* authorwh
*/
function recursive_delete($dir) {
if (!is_dir($dir)) {
return unlink($dir);
}
$handle = opendir($dir);
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $file;
if (is_dir($path)) {
$this->recursive_delete($path);
} else {
unlink($path);
}
}
closedir($handle);
rmdir($dir);
}
/**
* desc解压ZIP文件并存储在本地
* authorwh
* @param resource $file_stream 文件流
* 例如:$response = $client->request('POST', $url, [
'headers' => $headers,
'body' => $body,
]);
// 处理响应
//$file_stream = $response->getBody();
* @param string $save_path 存储路径
* @return array
* @throws \Exception
*/
function unzip($file_stream,$save_path){
$zpi_file_name = 'zip_tmp_file_'.Tools::rand_str(20).'.zip';
//这个目录将会在结束时删除
$zipTempPath = $save_path.'/tmp_zip_files/'; // 临时存放ZIP文件的路径
//压缩文件保存目录
if(!file_exists($zipTempPath)){
mkdir($zipTempPath,0777,true);
}
$zpi_file = $zipTempPath.$zpi_file_name;
// 保存ZIP文件到临时路径
file_put_contents($zpi_file, $file_stream);
$this->unzip_path = $zipTempPath.'unzip_files/';
//解压目录
if(!file_exists( $this->unzip_path)){
mkdir( $this->unzip_path,0777,true);
}
// 解压ZIP文件
$zip = new \ZipArchive();
if ($zip->open($zpi_file) === TRUE) {
$zip->extractTo( $this->unzip_path);//移动到临时解压目录
$zip->close();
//遍历所有文件(file_ext后缀自行指定),遍历部分文件,如:*.json,*.zip,*.mp4,*.mp3等
$wavFiles = $this->glob_by_ext( $this->unzip_path.$this->file_ext);
//$urls = [];
//// 遍历解压后的文件生成URL
//foreach ($wavFiles as $wavFilePath) {
// // 生成外部访问的URL
// $downloadUrl = request()->domain() . explode('public',$wavFilePath)[1];
// $urls[] = $downloadUrl;
//}
//// 返回所有WAV文件的URL
//return ['wav_urls' => $urls];
return $wavFiles;
} else {
throw new \Exception('Failed to open ZIP file.');
}
}
/**
* desc获取指定扩展类型的文件列表
*
* authorwh
* @param string $file_ext 如: wav,pm4,mp3,json,zip
* @return array|false
*/
function glob_by_ext($file_ext='*'){
return glob( $this->unzip_path.'*.'.$file_ext);
}
}

View File

@@ -0,0 +1,5 @@
## 文件处理
#### 文件夹读取,文件复制
#### 文件上传

View File

@@ -0,0 +1,767 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/4/3} {9:23}
*/
namespace wanghua\general_utility_tools_php\file\upload;
use wanghua\general_utility_tools_php\oss\alicloud\Objects;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Utils;
use wanghua\general_utility_tools_php\huawei\obs\InitObs;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\oss\huawei\obs\Config;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 上传组件
* 支持单图,多图,单文件,多文件
* Class FileUpload
* @package libraries
*/
class FileUpload
{
//文件扩展
protected $file_ext = 'ico,jpg,jpeg,png,gif,pdf,zip,rar,xls,xlsx,doc,docx';
//图片扩展
protected $img_ext = 'ico,jpg,jpeg,png,gif';
public $unique_key = '';//唯一key临时文件会存储至该目录推荐使用用户id
public $file_lists_urls = [];//要读取的文件地址列表
/**
* 文件存放目录
*
* 注意:后面不要“/”
* 注意:后面不要“/”
* 注意:后面不要“/”
* @var string
*/
private $file_dir = '/uploads';
/**
* desc默认目录/uploads
*
* eg/uploads/user/head_image 或 /files_manage/goods/images
*
* 注意:后面不要“/”
* 注意:后面不要“/”
* 注意:后面不要“/”
*
* authorwh
* @param $file_dir
*/
function setFileDir($file_dir){
$this->file_dir = $file_dir;
}
/**
* desc单图
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 大小,默认2M
* @param string $ext 允许的扩展
* @return array
*/
function image($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file($file_upload_name);
if(empty($file)){
return Tools::set_res(1, '请上传文件');
}
// 移动到框架应用根目录/uploads/ 目录下
$outer_req_url = $this->file_dir.'/image';
$physics_path = 'public'.$outer_req_url;
$root_path = explode('vendor',__DIR__)[0];
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($file)){
return Tools::set_res(1, '上传文件不存在');
}
if(empty($ext)) $ext = $this->img_ext;
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
return Tools::set_res(0, '上传成功', $result);
}else{
// 上传失败获取错误信息
return Tools::set_res(1, $file->getError());
}
}
/**
* desc多图
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 大小,默认2M
* @param string $ext 允许的扩展
* @return array
*/
function images($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
// 获取表单上传文件
$files = request()->file($file_upload_name);
if(empty($files)){
return Tools::set_res(1, '请上传文件');
}
$outer_req_url = $this->file_dir.'/image';
$physics_path = 'public'.$outer_req_url;
$root_path = explode('vendor',__DIR__)[0];
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($ext)) $ext = $this->img_ext;
$result = [];
$error = [];
foreach($files as $file){
if(!is_object($file)){
return Tools::set_res(1, '请上传正确的文件');
}
// 移动到框架应用根目录/uploads/ 目录下
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result[] = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
}else{
//忽略错误
// 上传失败获取错误信息
$error[] = $file->getError();
}
}
$msg = '上传成功';
$code = 0;
if(count($error) > 0){
$msg = '上传失败;提示:'.implode(',', $error);//顺带返回错误信息
$code = 1;
}
return Tools::set_res($code, $msg, $result);
}
/**
* desc单文件
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 大小,默认10M
* @param string $ext 允许的扩展
* @return array
*/
function file($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file($file_upload_name);
if(empty($file)){
return Tools::set_res(1, '请上传文件');
}
// 移动到框架应用根目录/uploads/ 目录下
$outer_req_url = $this->file_dir.'/file';
$physics_path = 'public'.$outer_req_url;
$root_path = explode('vendor',__DIR__)[0];
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($file)){
return Tools::set_res(1, '上传文件不存在');
}
if(empty($ext)) $ext = $this->file_ext;
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
return Tools::set_res(0, '上传成功', $result);
}else{
// 上传失败获取错误信息
return Tools::set_res(1, $file->getError());
}
}
/**
* desc多文件
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 单文件大小,默认10M
* @param string $ext 允许的扩展
* @return array
*/
function files($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
$root_path = explode('vendor',__DIR__)[0];
// 获取表单上传文件
$files = request()->file($file_upload_name);
if(empty($files)){
return Tools::set_res(1, '请上传文件');
}
$outer_req_url = $this->file_dir.'/file';
$physics_path = 'public'.$outer_req_url;
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($ext)) $ext = $this->file_ext;
$result = [];
$error = [];
foreach($files as $file){
// 移动到框架应用根目录/uploads/ 目录下
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result[] = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
}else{
//忽略错误
// 上传失败获取错误信息
$error[] = $file->getError();
}
}
$msg = '上传成功';
$code = 0;
if(count($error) > 0){
$msg = '上传失败;提示:'.implode(',', $error);//顺带返回错误信息
$code = 1;
}
return Tools::set_res($code, $msg, $result);
}
/**
* desc单文件或多文件上传至阿里云OSS服务
*
* authorwh
* @param array $config 配置
* //阿里云oss配置
'aliyun_oss_config' => [
//项目应用名称
'bucket'=>'wanlliuyinli-adm',//每创建一个bucket必须标明前缀代表这个bucket属于哪个项目
'UserPrincipalName'=>'wanghua@1113242774600735.onaliyun.com',
'Password'=>'0osE4cGo%tllnP1|uOQADPhM}Y?obR4U',
'AccessKeyId'=>'LTAI5tPqn1n7jugviVoGqFfa',
'AccessKeySecret'=>'BRoB5TdcUAFEuIR11BbN3R47Cm4Yep',
//https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints?spm=a2c4g.11186623.0.0.41ae2effA6oZar
'region_id'=>'cn-chengdu',//这里配置不能包含oss-前缀否则提示Invalid signing region in Authorization header
//西南1成都
//oss-cn-chengdu
//oss-cn-chengdu.aliyuncs.com
//oss-cn-chengdu-internal.aliyuncs.com
'endpoint'=>'oss-cn-chengdu.aliyuncs.com',//成都节点
'sts_endpoint'=>'sts.cn-chengdu.aliyuncs.com',//sts服务节点
]
* @param string $unique_id 唯一id可以是用户id可以是其它用于区分的标识默认存放在项目名称/控制器/方法/(唯一标识,可为空)/年月日/*.jpg
* @param string $file_upload_name 文件上传控件的name值必须带[]例如file_upload[]
* @param string $oss_type oss类型可选值ali_cloud 阿里云hua_wei 华为云(待扩展)
* @param int $size 单文件大小,默认10M
* @param string $ext 允许的文件扩展名
* @return array
*/
function filesUploadToAliCloudOss($config,$unique_id='',$file_upload_name = 'file_upload',$size=10, $ext=''){
return Mmodel::catch(function ()use ($config,$unique_id,$file_upload_name,$size,$ext){
$ini_upload_max_filesize = str_replace('M','',ini_get('upload_max_filesize'));
if(($size > $ini_upload_max_filesize)){
return Tools::set_res(1, '上传文件size受限不能超过upload_max_filesize='.ini_get('upload_max_filesize').'M');
}
// 获取表单上传文件
$files = request()->file($file_upload_name);
if(empty($files)){
return Tools::set_res(1, '请上传文件');
}
//扩展
if(empty($ext)) $ext = $this->file_ext;
//初始化阿里云OSS客户端
$oss_obj= new Objects($config);
//默认存储地址
$file_save_dir = $this->setFileSaveDir($unique_id);
$urls = [];
foreach($files as $file){
if(!$file->isValid()){
return Tools::set_res(1,'文件不合法');
}
$check_size = $file->checkSize($size*1024*1024);
if(!$check_size){
return Tools::set_res(1,'文件大小超限');
}
$check_res = $file->checkExt($ext);
if(!$check_res){
return Tools::set_res(1,'请上传合法文件');
}
$extension = explode('.',$file->getInfo('name'))[1];
$new_filename = 'file_'.Tools::rand_str(18).'.'.explode('.',$file->getInfo('name'))[1];
//必须加后缀
$filename = $file_save_dir.$new_filename;
// 从文件流上传
$res = $oss_obj->fileUpload($filename, $file->getInfo('tmp_name'));//上传之前的临时文件物理地址
$urls[] = [
'prefix'=>explode('.com',$res['info']['url'])[0].'.com',//访问前缀
'source_file_name'=>$file->getInfo('name'),//源文件名
'size'=>$file->getInfo('size'),//大小(b)
'extension'=>$extension,//扩展名
'file_type'=>$file->getInfo('type'),//文件后缀
'save_name'=>$filename,//保存文件名
'new_filename'=>$new_filename,//新文件名
'real_path'=>$res['info']['url'],//实际路径
'outer_req_url'=>$res['info']['url'],//外部访问地址
'outer_req_relative_url'=>$res['info']['url'],//外部访问相对地址
];
}
return Tools::set_ok('ok', $urls);
});
}
/**
* desc单文件上传至阿里云OSS
*
* authorwh
* @param array $config 配置
* //阿里云oss配置
'aliyun_oss_config' => [
//项目应用名称
'bucket'=>'wanlliuyinli-adm',//每创建一个bucket必须标明前缀代表这个bucket属于哪个项目
'UserPrincipalName'=>'wanghua@1113242774600735.onaliyun.com',
'Password'=>'0osE4cGo%tllnP1|uOQADPhM}Y?obR4U',
'AccessKeyId'=>'LTAI5tPqn1n7jugviVoGqFfa',
'AccessKeySecret'=>'BRoB5TdcUAFEuIR11BbN3R47Cm4Yep',
//https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints?spm=a2c4g.11186623.0.0.41ae2effA6oZar
'region_id'=>'cn-chengdu',//这里配置不能包含oss-前缀否则提示Invalid signing region in Authorization header
//西南1成都
//oss-cn-chengdu
//oss-cn-chengdu.aliyuncs.com
//oss-cn-chengdu-internal.aliyuncs.com
'endpoint'=>'oss-cn-chengdu.aliyuncs.com',//成都节点
'sts_endpoint'=>'sts.cn-chengdu.aliyuncs.com',//sts服务节点
]
* @param string $unique_id 唯一id可以是用户id可以是其它用于区分的标识默认存放在项目名称/控制器/方法/(唯一标识,可为空)/年月日/*.jpg
* @param string $file_upload_name 文件上传控件的name值例如file_upload
* @param string $oss_type oss类型可选值ali_cloud 阿里云hua_wei 华为云(待扩展)
* @param int $size 单文件大小,默认10M
* @param string $ext 允许的文件扩展名
* @return array
*/
function fileUploadToAliCloudOss($config,$unique_id='',$file_upload_name = 'file_upload',$size=10, $ext=''){
return Mmodel::catch(function ()use ($config,$unique_id,$file_upload_name,$size,$ext){
$ini_upload_max_filesize = str_replace('M','',ini_get('upload_max_filesize'));
if(($size > $ini_upload_max_filesize)){
return Tools::set_res(1, '上传文件size受限不能超过upload_max_filesize='.ini_get('upload_max_filesize').'M');
}
// 获取表单上传文件
$file = request()->file($file_upload_name);
if(empty($file)){
return Tools::set_res(1, '请上传文件');
}
//扩展
if(empty($ext)) $ext = $this->file_ext;
//初始化阿里云OSS客户端
$oss_obj= new Objects($config);
//默认存储地址
$file_save_dir = $this->setFileSaveDir($unique_id);
$urls = [];
if(!$file->isValid()){
return Tools::set_res(1,'文件不合法');
}
$check_size = $file->checkSize($size*1024*1024);
if(!$check_size){
return Tools::set_res(1,'文件大小超限');
}
$check_res = $file->checkExt($ext);
if(!$check_res){
return Tools::set_res(1,'请上传合法文件');
}
$extension = explode('.',$file->getInfo('name'))[1];
$new_filename = 'file_'.Tools::rand_str(18).'.'.explode('.',$file->getInfo('name'))[1];
//必须加后缀
$filename = $file_save_dir.$new_filename;
// 从文件流上传
$res = $oss_obj->fileUpload($filename, $file->getInfo('tmp_name'));//上传之前的临时文件物理地址
$urls[] = [
'prefix'=>explode('.com',$res['info']['url'])[0].'.com',//访问前缀
'source_file_name'=>$file->getInfo('name'),//源文件名
'size'=>$file->getInfo('size'),//大小(b)
'extension'=>$extension,//扩展名
'file_type'=>$file->getInfo('type'),//文件后缀
'save_name'=>$filename,//保存文件名
'new_filename'=>$new_filename,//新文件名
'real_path'=>$res['info']['url'],//实际路径
'outer_req_url'=>$res['info']['url'],//外部访问地址
'outer_req_relative_url'=>$res['info']['url'],//外部访问相对地址
];
return Tools::set_ok('ok', $urls);
});
}
/**
* desc设置文件保存目录
* authorwh
* @param string $unique_id 唯一id可以是用户id可以是其它用于区分的标识
* @return string
*/
function setFileSaveDir($unique_id=''){
if($unique_id){
$unique_id = $unique_id.'/';
}
//项目名称
$project_name = Tools::get_project_name();
//控制器方法路径
$ctl = strtolower(request()->controller().'/'.request()->action());
return $project_name.'/'.$ctl.'/'.$unique_id.date('Ymd').'/';//日期
}
/**
* desc检测size是否在ini配置范围
*
* authorwh
* @param $size
* @return bool
*/
private function uploadMaxFilesizeCheck($size){
$ini_upload_max_filesize = str_replace('M','',ini_get('upload_max_filesize'));
return $size<=$ini_upload_max_filesize;
}
/**
* desc上传base64格式图片【文件不用base64传输】
* authorwh
* @param string $file_upload_name
* @param int $size
* @param array $ext
* @return array
* @throws \Exception
*/
function uploadBase64Img($file_upload_name = 'file_upload', $size=10, $ext=''){
$source = input($file_upload_name, 'file_upload');
$file_size = strlen($source);
if($file_size > $size * 1024 * 1024){
return Tools::set_res(1, '上传失败,大小限制');
}
if(preg_match('/^(data:\s*image\/(\w+);base64,)/', $source, $result) < 1){
return Tools::set_res(1, '上传文件不合法');
}
$type = $result[2];
if(empty($ext)) $ext = $this->file_ext;
if(!in_array($type, explode($ext))){
return Tools::set_res(1, '上传失败,类型限制');
}
$result = base64_image_content($source);
if(false === $result){
return Tools::set_res(1, '上传失败');
}
return Tools::set_res(0, '上传成功', $result);
}
/**
* desc批量上传base64格式图片【文件不用base64传输】
* authorwh
* @param string $file_upload_name
* @param int $size
* @param array $ext
* @return array
* @throws \Exception
*/
function uploadBase64Imgs($file_upload_name = 'file_upload', $size=10, $ext=''){
$source_list = input($file_upload_name, 'file_upload');
if(!is_array($source_list)){
return Tools::set_res(1, '参数错误');
}
if(empty($ext)) $ext = $this->file_ext;
$result = [];
$error = [];
foreach ($source_list as $source){
$file_size = strlen($source);
if($file_size > $size * 1024 * 1024){
$error[] = '上传失败,大小限制';
continue;
}
if(preg_match('/^(data:\s*image\/(\w+);base64,)/', $source, $temp) < 1){
$error[] = '上传文件不合法';
continue;
}
$type = $temp[2];
if(!in_array($type, explode(',', $ext))){
$error[] = '类型限制';
continue;
}
$up_result = base64_image_content($source);
if(false === $up_result){
$error[] = '上传失败';
continue;
}
$result[] = $up_result;
}
$msg = '上传成功';
$code = 0;
if(count($error) > 0){
$msg = '上传失败;提示:'.implode(',', $error);//顺带返回错误信息
$code = 1;
}
return Tools::set_res($code, $msg, $result);
}
/**
* desc优化路径格式
* authorwh
*/
private function dealPath(string $path){
$str = str_replace('\\','/',$path);
return str_replace('//','/',$str);
}
/**
* desccurl上传文件php上传文件到远程服务器地址
*
* [php curl模拟文件上传]
*
* 依赖库:"guzzlehttp/guzzle": "^7.8",
*
* authorwh
* @param $url
* @param $params
* @param false[] $config
*/
function uploadCurlFiles($url,$params, $config=['verify' => false]){
if(empty($this->unique_key)){
throw new \Exception('UNIQUE_KEY 不能为空');
}
if(empty($this->file_lists_urls)){
throw new \Exception('文件地址列表不能为空');
}
// 创建 Guzzle HTTP 客户端实例
$client = new Client($config);
// 定义要上传的文件列表
$files = [
//[
// 'name' => 'file1', // 服务器接收的文件字段名
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\dc1c441d81d48565ac6817b89d0f8bef.mp4', 'r'), // 文件路径
// 'filename' => 'dc1c441d81d48565ac6817b89d0f8bef.mp4', // 文件名,可自定义
//],
//[
// 'name' => 'files',
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\f80179b23b60619fba8033bfd64f8817.mp4', 'r'),
// 'filename' => 'f80179b23b60619fba8033bfd64f8817.mp4',
//],
//['name'=>'prompt','contents'=>$prompt],
//['name'=>'tts_url','contents'=>$tts_url]
// 可以继续添加更多文件...
];
//print_r($params);
foreach ($params as $key=>$val){
$files[] = ['name'=>$key,'contents'=>$val];
}
$controller = strtolower(request()->controller());
$action = strtolower(request()->action());
$save_path = Tools::get_root_path() . "public/uploads/{$controller}/{$action}/".$this->unique_key;
if(!file_exists($save_path)){
mkdir($save_path,0777,true);
}
//文件远程可访问地址列表
foreach ($this->file_lists_urls as $name=>$video_url) {
//处理可用地址可能存储在本地或oss
$video_url = $this->get_usable_url($video_url);
$files[] = [
'name'=>"files",//服务器接收的文件字段名
//读取地址文件流(本地地址或远程地址均可,本地真实物理地址也行)
'contents'=>Utils::tryFopen($video_url, 'r'),
'filename'=>$name,//可自定义 文件名
];
}
// 构建多部分表单数据
$multipartStream = new MultipartStream($files);
$boundary = $multipartStream->getBoundary();
$headers = [
'Content-Type' => "multipart/form-data; boundary={$boundary}",
];
// 准备请求体
$body = $multipartStream->getContents();
// 发起 POST 请求
try {
$postinfo = [
'headers' => $headers,
'body' => $body,
];
//上传
$response = $client->request('POST', $url, $postinfo);
// 处理响应
//$responseBody = (string) $response->getBody();
// 检查状态码
//if ($response->getStatusCode() !== 200) {
// return false;
//}
return $response;//返回上传结果
} catch (\GuzzleHttp\Exception\RequestException $e) {
// 错误处理
//echo "Error: " . $e->getMessage();
Tools::error_txt_log($e);
return false;
}
}
/**
* desc获取视频可用地址兼容远程地址或本地地址
* authorwh
* @param $video_url
* @return string|string[]
*/
private function get_usable_url($video_url){
if(strpos($video_url,'http')!==false){
return $video_url;//远程地址
}
return Tools::get_root_path().$video_url;//本地地址
}
}

View File

@@ -0,0 +1 @@
## 文件上传类库

View File

@@ -0,0 +1,219 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/25} {15:45}
*/
namespace wanghua\general_utility_tools_php\framework;
use app\common\model\TabConf;
use think\Cache;
use think\Controller;
use think\Db;
use think\Exception;
use think\Request;
use wanghua\general_utility_tools_php\api\ApiDocument;
use wanghua\general_utility_tools_php\tool\MySqlTools;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 基础架构
*
* Class BaseController
* @package app\index\controller
*/
class BaseController extends Controller
{
public function __construct()
{
parent::__construct();
//校验系统维护状态 start
$chm = $this->checkMaintain();
if ($chm['is_maintain']) {
if ($chm['users_ids']) {
//解析openid
if (!in_array(api_user_info('id'), explode(',', $chm['users_ids']))) {
//白名单之外维护中
//Tools::log_to_write_txt([
// '维护测试'=>$chm['users_ids'],
// 'my'=>index_user_openid()
//]);
return $this->error($chm['msg']);
}
} else {
//不存在,直接维护中
return $this->error($chm['msg'] . '');
}
}
//校验系统维护状态 end
}
/**
* desc空控操作
* authorwh
*/
function _empty($info='')
{
}
/**
* desc检查路径维护状态
*
* “===”完全匹配不能使用like
*
* authorwh
*/
protected function checkMaintain(){
$configs = Db::table(TabConf::$fa_sys_maintain_config)
->where('status','1')
->cache()
->select();
//模块
$strmodule = request()->module();
foreach ($configs as $config){
if($strmodule == $config['url']){
//模块维护中
return ['is_maintain'=>true,'msg'=>$config['msg'],'users_ids'=>$config['users_ids']];
}
}
//模块/控制器
$strcontroller = strtolower(request()->module().'/'.request()->controller());
foreach ($configs as $config){
if($strcontroller == $config['url']){
//模块维护中
return ['is_maintain'=>true,'msg'=>$config['msg'],'users_ids'=>$config['users_ids']];
}
}
//模块/控制器/方法
$straction = strtolower(request()->module().'/'.request()->controller().'/'.request()->action());
foreach ($configs as $config){
if($straction == $config['url']){
//模块维护中
return ['is_maintain'=>true,'msg'=>$config['msg'],'users_ids'=>$config['users_ids']];
}
}
//未维护
return ['is_maintain'=>false,'msg'=>'服务运行中'];
}
/**
* desc清空系统缓存
*
/index/Test/clearCache
*
* authorwh
*/
function clearCache(){
Tools::clear_cache();
}
/**
* eg/index/test/buildTablesConf
*
* desc构建统一的表名配置
*
* authorwh
*/
function buildTablesConf(){
(new MySqlTools())->buildTablesConf();
}
/**
* desc创建&更新接口文档
*
* 默认存放在/public/api_docs/api_list.md
*
* authorwh
*/
function buildApiDoc()
{
$obj = new ApiDocument();
//根据自己的实际情况设置直接继承类(仅供参考)
$obj->extends_base_class = 'app\\api\\controller\\BaseHttpApi,wanghua\\general_utility_tools_php\\framework\\base\\BaseWechatAuthController';
//设置过滤的类(仅供参考)
$obj->setFilterClass([
'BaseCommonController',
'BaseHttpApi',
'BaseWssApi',
'Wsspush',
'BaseController',
'BaseAuthController',
//'BaseWechatAuthController',
'BasePublicController'
]);
$obj->setFilterFunction([
'buildApiDoc',
'buildTablesConf',
'checkfailed',
'clearCache',
'defaultAuth',
'operateLog',
'checkMaintain',
'_empty'
]);
//构建接口文档
$obj->buildDoc();
//生成html
$html = $obj->buildApiDocHtml();
$path = Tools::get_root_path().'/public/api_docs/';
if(!file_exists($path)){
mkdir($path,0777,true);
}
file_put_contents($path.'api_list.html',$html);
echo "<a href='/api_docs/api_list.html' target='_blank'>api_list.html</a>";
}
/**
* desc用户操作日志
* authorwh
*/
protected function operateLog($msg,$users_id=0,$username='',$name=''){
$tbname = 'fa_users_operate_log';
$sql = "
CREATE TABLE IF NOT EXISTS `{$tbname}` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`users_id` int(10) unsigned DEFAULT '0' COMMENT '用户ID',
`username` varchar(15) DEFAULT '' COMMENT '用户名',
`name` varchar(20) DEFAULT '' COMMENT '名称',
`url` varchar(60) DEFAULT '' COMMENT 'URL',
`msg` varchar(90) DEFAULT '' COMMENT '做了什么',
`ip` varchar(30) DEFAULT '' COMMENT '登录ip',
`input` text COMMENT '提交参数',
`create_time` timestamp NULL DEFAULT NULL COMMENT '登陆时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='用户操作日志';";
if(cache('cache_db_tables_now_project')){
$table_arr = cache('cache_db_tables_now_project');
}else{
$table_arr = Tools::get_tables();
}
//表是否存在
if(!in_array($tbname,$table_arr)){
cache('cache_db_tables_now_project',$table_arr);
Db::execute($sql);
}
$data = [
'users_id'=>$users_id,
'username'=>$username,
'name'=>$name,
'url'=>request()->url(),
'msg'=>$msg,
'ip'=>request()->ip(),
'input'=>json_encode(input(),JSON_UNESCAPED_UNICODE),
];
Db::table($tbname)->data($data)->insert();
}
}

View File

@@ -0,0 +1 @@
### this classes only to thinkphp5+

View File

@@ -0,0 +1,80 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/27} {10:35}
*/
namespace wanghua\general_utility_tools_php\framework\base;
use wanghua\general_utility_tools_php\framework\BaseController;
use think\Exception;
use think\Request;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 基础权限架构
* Class BaseAuthController
* @package library\framework\base
*/
class BaseAuthController extends BaseController
{
public $debug = false;
//权限错误重定向URL
protected $auth_err_redirect_url = '/index/err/fail';
public function __construct()
{
parent::__construct();
$err_redirect_url = config('service_framework_config.auth_err_redirect_url');
if($err_redirect_url){
$this->auth_err_redirect_url = $err_redirect_url;
}
}
/**
* desc默认鉴权
* authorwh
* @return bool
*/
function defaultAuth(){
$params = input();
if(empty($params['nonce'])){
Tools::log_to_write_txt(['服务被拒绝,鉴权参数缺失:nonce。params'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied']);
}
if(empty($params['timestamp'])){
Tools::log_to_write_txt(['服务被拒绝,鉴权参数缺失:timestamp。params'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied.']);
}
if(empty($params['sign'])){
Tools::log_to_write_txt(['服务被拒绝,鉴权参数缺失:sign。params'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied。']);
}
$sign = $params['sign'];
unset($params['sign']);
if(Tools::signature($params) != $sign){
Tools::log_to_write_txt(['签名失败,服务被拒绝.'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied!']);
}
return true;
}
protected function response($url,$params){
if(Request::instance()->isAjax() || Request::instance()->isPost()){
return json(Tools::set_res(500,$params['title'],['url'=>$url]));
}
//跳转至错误中转控制器
return $this->redirect(url($url,$params));
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/25} {15:48}
*/
namespace wanghua\general_utility_tools_php\framework\base;
use wanghua\general_utility_tools_php\framework\BaseController;
use think\Request;
/**
* common controller have no permission
*
* 外部公用架构
*
* 无权限校验的公用架构
*
* Class PublicController
* @package app\index\controller
*/
class BasePublicController extends BaseController
{
}

View File

@@ -0,0 +1,194 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/23} {0:12}
*/
namespace wanghua\general_utility_tools_php\framework\base;
use app\common\model\TabConf;
use think\Db;
use think\Request;
use wanghua\general_utility_tools_php\framework\BaseController;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 微信专用权限架构
*
* Class BaseWechatAuthController
* @package wanghua\general_utility_tools_php\framework\base
*/
class BaseWechatAuthController extends BaseController
{
protected $wechat_user_table_name = 'fa_users';
public function __construct()
{
parent::__construct();
//首页提示语
$this->assign('index_msg', cache('index_msg_alert_cache_time'));
//线上环境加载微信授权
if (config('sys_env') == 'PROD') {
$wx_user_info = session('wx_user_info');
if (empty($wx_user_info['openid'])) {
//重定向之前保存当前url, 在获取授权信息之后,回跳到授权之前的网页地址
session('redirect_before_url_session', request()->url(true));
$ver = Tools::get_thinkphp_version();
if($ver == '5.0'){
$url = url('index/Wexinauth/usrAuth','',false,true);
}else{
$url = request()->domain().url('index/Wexinauth/usrAuth');
}
//没有则重定向去授权
return $this->redirect($url);
}
$this->saveWechatUser($wx_user_info);
}
}
/**
* desc周期更新当前用户信息
* authorwh
* @param $wx_user_info
*/
private function saveWechatUser($wx_user_info)
{
try {
$wechat_user = $this->getWxUserByOpenid($wx_user_info['openid']);
if (empty($wechat_user)) {
return $this->insertInfo($wx_user_info);
}
//扩展,按周期更新,而不是不更新
if (empty($wechat_user['update_time']) || time() - strtotime($wechat_user['update_time']) > 5 * 3600) {
return $this->updateUser($wx_user_info);
}
} catch (\Exception $e) {
Tools::log_to_write_txt([
'error' => '存储异常.' . $e->getMessage(),
'wx_user_info' => $wx_user_info,
'error_info' => $e->getTraceAsString()
]);
}
}
/**
* desc新增微信用户信息
* authorwh
* @param $wx_user_info \app\index\model\微信用户
*
* {
* "openid":"or9D2vs863Ky5Py2ovkAiu9XFLO4",
* "nickname":"起源果蔬副食大华",
* "sex":0,
* "language":"",
* "city":"",
* "province":"",
* "country":"",
* "headimgurl":"https://thirdwx.qlogo.cn/mmopen/vi_32/joiaA475nx3fJiaqx0ibdnWo4A7Q3uCgu2hsribI0ATLItORjuUgCSP8mCaBkqL61ibGojib4pQYX1djUhZpF5zoqpSg/132",
* "privilege":[]
* }
*/
private function insertInfo(array $wx_user_info)
{
if (isset($wx_user_info['privilege'])) {
$wx_user_info['privilege'] = json_encode($wx_user_info['privilege']);
}
$data = [
'nickname' => $wx_user_info['nickname'],
'country' => $wx_user_info['country'],
'province' => $wx_user_info['province'],
'city' => $wx_user_info['city'],
'headimage' => $wx_user_info['headimgurl'],
'language' => $wx_user_info['language'],
'openid' => $wx_user_info['openid'],
'unionid' => isset($wx_user_info['unionid']) ? $wx_user_info['unionid'] : '',
'privilege' => $wx_user_info['privilege'],
'sex' => $wx_user_info['sex'],
//'arm_group' => '',
//'score' => 0,//积分
//'group_buy_earnings' => 0,//拼团收益
//'water_drop_balance' => 0,//水滴
];
Db::table($this->wechat_user_table_name)
->data($data)
->insert();
}
/**
* desc更新当前用户信息
*
* authorwh
*/
private function updateUser($wx_user_info)
{
$data = [
'nickname' => $wx_user_info['nickname'],
'headimage' => $wx_user_info['headimgurl'],
'unionid' => isset($wx_user_info['unionid']) ? $wx_user_info['unionid'] : '',
];
Db::table($this->wechat_user_table_name)
->data($data)
->where('openid', index_user_openid())
->update();
}
/**
* desc根据unionid获取用户信息
* authorwh
* @param string $unionid
*/
private function getWxUserByUnionid(string $unionid)
{
return Db::table($this->wechat_user_table_name)
->field('id')
->where('unionid', $unionid)
->find();
}
/**
* desc获取微信用户信息
* authorwh
* @param string $openid
*/
private function getWxUserByOpenid(string $openid)
{
return Db::table($this->wechat_user_table_name)
//->field('id')
->where('openid', $openid)
->find();
}
/**
* desc根据openid获取昵称
* authorwh
* @param string $openid
*/
private function getNicknameByOpenid(string $openid)
{
return Db::table($this->wechat_user_table_name)
->where('openid', $openid)
->value('nickname');
}
}

View File

@@ -0,0 +1,213 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/3/22} {9:42}
*/
namespace wanghua\general_utility_tools_php\ftp;
/**
* 利用ftp操作服务器文件增删改查下载
* Class Ftp
* @package wanghua\general_utility_tools_php\ftp
*/
class Ftp
{
public $off; // 返回操作状态(成功/失败)
public $conn_id; // FTP连接
/**
* FTP连接
* $host -- FTP主机
* $port -- 端口
* $user -- 用户名
* $password -- 密码
*/
function __construct(string $host,string $port='21',string $user,string $password)
{
set_time_limit(10);//防止连接超时
$this->conn_id = ftp_connect($host, $port);
if(!$this->conn_id) throw new \Exception("FTP服务器连接失败");
$login_res = ftp_login($this->conn_id, $user, $password);
if(!$login_res) throw new \Exception("FTP服务器登陆失败");
ftp_pasv($this->conn_id, false); // true打开被动模拟默认关闭如果总是失败可试试true
}
/**
* desc打开被动模拟登录后使用
* authorwh
*/
function set_ftp_pasv(){
ftp_pasv($this->conn_id, true); // true打开被动模拟
}
/**
* 上传文件
* param $path 本地文件路径[包含文件名]
* param $newPath 目标目录[包含文件名][默认存放在ftp用户被授权的根路径]
* param bool $type 若目标目录不存在则新建
*/
function up_file($path, $newPath, $type = true)
{
if ($type) $this->dir_mkdirs($newPath);
$this->off = ftp_put($this->conn_id, $newPath, $path, FTP_BINARY);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件上传失败,请检查权限及路径是否正确!'];
return ['code'=>200, 'msg'=>'文件移动成功'];
}
/**
* 移动文件
* 特别注意:
* $path 如果是当前目录,则必须以 “/”开始
* 正确:/test2.zip
* 错误test2.zip
* param $path 原路径(含文件名)
* param $newPath 新路径(含文件名)
* param bool $type 若目标目录不存在则新建
*/
function move_file($path, $newPath, $ftpbasedir, $type = true)
{
if ($type) $this->ftp_mksubdirs($ftpbasedir,$newPath);
$this->off = ftp_rename($this->conn_id, $path, $newPath);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件移动失败,请检查权限及原路径是否正确!'];
return ['code'=>200, 'msg'=>'文件移动成功'];
}
/**
* 文件改名
* param $path 原路径
* param $newPath 新路径
*/
function rename_file($path, $newPath)
{
$this->off = ftp_rename($this->conn_id, $path, $newPath);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件改名失败,请检查权限及原路径是否正确!'];
return ['code'=>200, 'msg'=>'文件改名成功'];
}
/**
* 复制文件
* 说明由于FTP无复制命令,本方法变通操作为:下载后再上传到新的路径
* param $path 原路径
* param $newPath 新路径
* param bool $type 若目标目录不存在则新建
*/
function copy_file($path, $newPath, $type = true)
{
$downPath = "c:/tmp.dat";
$this->off = ftp_get($this->conn_id, $downPath, $path, FTP_BINARY);// 下载
if (!$this->off)
return ['code'=>500, 'msg'=>'文件复制失败,请检查权限及原路径是否正确!'];
$this->up_file($downPath, $newPath, $type);
return ['code'=>200, 'msg'=>'文件复制成功'];
}
/**
* 删除文件
* param $path 路径
*/
function del_file($path)
{
$this->off = ftp_delete($this->conn_id, $path);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件删除失败,请检查权限及路径是否正确!'];
return ['code'=>200, 'msg'=>'文件删除成功'];
}
/**
* desc删除给定目录
* 注:
* 删除多个目录需重复调用
* authorwh
* param $dir
* return bool
*/
function del_dir($dir){
$children = ftp_nlist ($this->conn_id, $dir);
if(!$children) {
ftp_rmdir($this->conn_id, $dir);
return true;
}
throw new \Exception('请确保目录中无内容');
}
/**
* desc生成目录
* authorwh
* param $ftpcon
* param $ftpbasedir
* param $ftpath
*/
function ftp_mksubdirs($ftpbasedir,$ftpath){
ftp_chdir($this->conn_id, $ftpbasedir); // /var/www/uploads
$parts = explode('/',$ftpath); // 2013/06/11/username
if(strpos($parts[count($parts)-1], '.')){
unset($parts[count($parts)-1]);
}
foreach($parts as $part){
if(!ftp_chdir($this->conn_id, $part)){
ftp_mkdir($this->conn_id, $part);
ftp_chdir($this->conn_id, $part);
//ftp_chmod($this->conn_id, 0777, $part);
}
}
}
/**
* desc使用ftp连接方式下载文件
* authorwh
* param string $local_file 文件本地的路径(如果文件已经存在,则会被覆盖)。
* param string $remote_file 文件的远程路径。[默认查找ftp用户被授权的根路径]
* return array
*/
function download(string $local_file, string $remote_file){
// 进行ftp下载[默认保存在本地根目录]
if (!ftp_get($this->conn_id, $local_file, $remote_file, FTP_BINARY)) {
return ['code'=>500, 'msg'=>'ftp download fail'];
}
return ['code'=>200, 'msg'=>'ftp download success'];
}
/**
* desc判断ftp文件大小, 如果>-1, 说明文件存在;否则不存在.
* authorwh
* param $remote_file
* return int
*/
function ftp_size($remote_file){
return ftp_size($this->conn_id, $remote_file);
}
/**
* desc返回当前目录
* authorwh
* return false|string
*/
function ftp_pwd(){
return ftp_pwd($this->conn_id);
}
/**
* 关闭FTP连接
*/
function close()
{
ftp_close($this->conn_id);
}
}

View File

@@ -0,0 +1,21 @@
# PHP ftp类库
#### 实现用ftp方式上传、复制、移动、重命名、删除、下载文件
##### 案例代码:
```
$ftp = new Ftp();//初始化
//$ftp->up_file('D:\whua\test.zip','test.zip'); // 上传文件
//$ftp->rename_file('test.zip','test-rename.zip'); // 移动文件
//$ftp->move_file('/test2.zip','/testdir2/test2.zip', '/www/wwwroot/testftp/'); // 移动文件2
//$ftp->copy_file('test.zip','test-copy.zip'); // 复制文件
//$ftp->del_file('/www/aaa.txt'); // 删除具体文件
$ftp->del_dir('/www'); // 删除目录(错误示范),必须精确到具体文件夹删除
$ftp->del_dir('/ftp/test/copy-test/a-dir'); // 删除目录(正确示范),必须精确到具体文件夹删除,批量删除请多次调用
//下载文件
//$ftp->download('D:\whua\projects\zc_game_admin3.0\data\local.zip', 'test.zip');
//$ftp->close(); // 关闭FTP连接
//dump($ftp->ftp_pwd());die;
//dump($ftp->ftp_size('test2.zip'));die;
```

View File

@@ -0,0 +1,188 @@
<?php
namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\phpmailer\Exception;
/**
* 阿里云百炼多模态chat
*
* 单模态请使用ChatGPT.php
*
* Class AliCloudMultiChatGPT
* @package wanghua\general_utility_tools_php\gpt\chat
*/
class AliCloudChatGPT extends BaseChat
{
public $url = '';
public $apiKey = '';
public $model = '';
private $messages = [];
public $post_msg_body;
public function __construct()
{
//调用父级
parent::__construct();
}
/**
* desc定制个性
* authorwh
* @param array $customize
*/
function setCustomize($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
/**
* desc定制个性(多模态)
* authorwh
* @param array $customize
*/
function setCustomizeMulti($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
function setBefore($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
function setAfter($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
private function check()
{
if (empty($this->url) || empty($this->apiKey) || empty($this->model)) {
throw new Exception('PARAMS ERROR');
}
}
function chat($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
$answer .= $data;
echo $data;
ob_flush();
flush();
return strlen($data);
});
return $answer;
}
function returnAnswer($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
$answer .= $data;
return strlen($data);
});
return $answer;
}
private function curlPostChat($question = '', $config = [], $callback)
{
$url = $this->url;
$apiKey = $this->apiKey;
$model = $this->model;
$headers = ["Authorization: Bearer $apiKey", 'Accept: application/json', 'Content-Type: application/json',];
$post_msg_body = ["model" => $model, 'stream' => true, 'chatId'=>$this->chatId];
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$postData = json_encode($post_msg_body);
$this->post_msg_body = $post_msg_body;
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_exec($ch);
}
/**
* desc多模态场景chat如多图多轮对话
* authorwh
* @param $images
* @param $text
* @return mixed
* @throws \Exception
*/
function generateMultimodalContent($images, $text)
{
$url = $this->url;
$apiKey = $this->apiKey;
$model = $this->model;
$messages = [
[
'role' => 'user',
'content' => array_map(function ($image) {
return ['image' => $image];
}, $images),
],
];
if (!empty($text)) {
$messages[0]['content'][] = ['text' => $text];
}
$data = [
'model' => $model,
'input' => [
'messages' => $messages,
],
];
$options = [
'http' => [
'header' => "Authorization: Bearer $apiKey\r\n" .
"Content-Type: application/json\r\n",
'method' => 'POST',
'content' => json_encode($data),
],
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) {
throw new \Exception('Failed to fetch data from API');
}
return json_decode($result, true);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/3/26} {16:06}
*/
namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\tool\Tools;
class BaseChat
{
public $chatId = '';
public function __construct()
{
$this->chatId = Tools::getMillisecond();
}
/**
* desc将字符串里含有json_encode后的一维数组提取出来
* @param $str
* @return string
*/
function dealstr($str){
$rep_str = str_replace("\n",'',$str);
$rep_str = str_replace("\r",'',$rep_str);
$rep_str = str_replace(" ",'',$rep_str);
$start_sit = strpos($rep_str,"[{");
$end_sit = strpos($rep_str,"}]");
$last_json_str = substr($rep_str,$start_sit,$end_sit-$start_sit);
$last_json_str.="}]";
return $last_json_str;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,179 @@
<?php namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\phpmailer\Exception;
class ChatGPT extends BaseChat
{
public $url = '';
public $apiKey = '';
public $model = '';
private $messages = [];
public $post_msg_body;
public function __construct()
{
//调用父级
parent::__construct();
}
/**
* desc定制个性
* authorwh
* @param array $customize
*/
function setCustomize($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
function setBefore($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
function setAfter($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
private function check()
{
if (empty($this->url) || empty($this->apiKey) || empty($this->model)) {
throw new Exception('PARAMS ERROR');
}
}
function chat($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
$answer .= $data;
echo $data;
ob_flush();
flush();
return strlen($data);
});
return $answer;
}
function returnAnswer($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
$answer .= $data;
return strlen($data);
});
return $answer;
}
private function curlPostChat($question = '', $config = [], $callback)
{
$url = $this->url;
$apiKey = $this->apiKey;
$model = $this->model;
$headers = ["Authorization: Bearer $apiKey", 'Accept: application/json', 'Content-Type: application/json',];
$post_msg_body = ["model" => $model, 'stream' => true, 'chatId'=>$this->chatId];
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$postData = json_encode($post_msg_body);
$this->post_msg_body = $post_msg_body;
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_exec($ch);
}
function getchatgptresponse($question = '', $config = [])
{
$url = $this->url;
$apiKey = $this->apiKey;
$model = $this->model;
$headers = ["Authorization: Bearer $apiKey", "Content-Type: application/json"];
$post_msg_body = ["model" => $model, 'stream' => false];
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$post_msg_body = json_encode($post_msg_body);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_msg_body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_error($ch)) {
return ['code' => curl_errno($ch), 'msg' => curl_error($ch)];
} else {
curl_close($ch);
return ['code' => 200, 'msg' => 'cURL ok', 'data' => $response];
}
}
private function parseData($data)
{
if (@json_decode($data)->choices[0]->message->content) {
return json_decode($data)->choices[0]->message->content;
}
$data = str_replace('data: {', '{', $data);
$data = rtrim($data, "\n\n");
if (strpos($data, 'data: [DONE]') !== false) {
return 'data: [DONE]';
} else {
if (false !== strpos($data, "\n\n")) {
$exp_arr = explode("\n\n", $data);
$str = '';
try {
for ($i = 0; $i < count($exp_arr) - 1; $i++) {
$jsondecode_arr = json_decode($exp_arr[$i], true);
$str .= $jsondecode_arr['choices'][0]['delta']['content'];
}
return $str;
} catch (\Exception $e) {
return $str;
}
}
$data = @json_decode($data, true);
if (!is_array($data)) {
return '';
}
if ($data['choices'][0]['finish_reason'] == 'stop') {
return 'data: [DONE]';
} elseif ($data['choices'][0]['finish_reason'] == 'length') {
return 'data: [CONTINUE]';
}
return $data['choices'][0]['delta']['content'] ?? '';
}
}
}

View File

@@ -0,0 +1,141 @@
<?php namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
class F37A49CDED4199801B05D5FFFAF78E010 extends BaseChat
{
public $url = '';
public $apiKey = '';
public $model = '';
public $chatId = '';
public $detail = false;
private $messages = [];
public function __construct()
{
}
function s8EB5A47634F626B142D2A93E9BAF009F($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
function s3ASCD163240094B8C736D3A1A5CSD($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
function s326723D163240094B8C736D3A1A5CAD3($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
private function c2111E682402A3955B616FA5795DEDE4F()
{
if (empty($this->url) || empty($this->apiKey)) {
throw new Exception('PARAMS ERROR');
}
}
function cAA8AF3EBE14831A7CD1B6D1383A03755($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->c8ECA4C82960B4611B076D51EF8ADF979($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
echo $data;
ob_flush();
flush();
return strlen($data);
});
}
private function c8ECA4C82960B4611B076D51EF8ADF979($question = '', $config = [], $callback)
{
$this->c2111E682402A3955B616FA5795DEDE4F();
$url = $this->url;;
$apiKey = $this->apiKey;
$model = $this->model;
$chatId = $this->chatId;
$detail = $this->detail;
$headers = ["Authorization: Bearer $apiKey", 'Accept: application/json', 'Content-Type: application/json',];
$post_msg_body = ['detail' => $detail, 'stream' => true,];
if ($detail) {
$post_msg_body['detail'] = $detail;
}
if ($chatId) {
$post_msg_body['chatId'] = $chatId;
}
if ($model) {
$post_msg_body['model'] = $model;
}
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$postData = json_encode($post_msg_body);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_exec($ch);
}
private function pCD736318A127E12426EED8700FF2BF28($data)
{
if (@json_decode($data)->choices[0]->message->content) {
return json_decode($data)->choices[0]->message->content;
}
$data = str_replace('data: {', '{', $data);
$data = rtrim($data, "\n\n");
if (strpos($data, 'data: [DONE]') !== false) {
return 'data: [DONE]';
} else {
if (false !== strpos($data, "\n\n")) {
$exp_arr = explode("\n\n", $data);
$str = '';
try {
for ($i = 0; $i < count($exp_arr) - 1; $i++) {
$jsondecode_arr = json_decode($exp_arr[$i], true);
$str .= $jsondecode_arr['choices'][0]['delta']['content'];
}
return $str;
} catch (\Exception $e) {
return $str;
}
}
$data = @json_decode($data, true);
if (!is_array($data)) {
return '';
}
if ($data['choices'][0]['finish_reason'] == 'stop') {
return 'data: [DONE]';
} elseif ($data['choices'][0]['finish_reason'] == 'length') {
return 'data: [CONTINUE]';
}
return $data['choices'][0]['delta']['content'] ?? '';
}
}
}

View File

@@ -0,0 +1,148 @@
<?php namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
class FastGPT extends BaseChat
{
public $url = '';
public $apiKey = '';
public $model = '';
public $chatId = '';
public $detail = false;
private $messages = [];
public function __construct()
{
//调用父级
parent::__construct();
}
/**
* desc定制个性
* authorwh
* @param array $customize
*/
function setCustomize($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
function setBefore($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
function setAfter($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
private function check()
{
if (empty($this->url) || empty($this->apiKey)) {
throw new Exception('PARAMS ERROR');
}
}
function chat($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
echo $data;
ob_flush();
flush();
return strlen($data);
});
}
private function curlPostChat($question = '', $config = [], $callback)
{
$this->check();
$url = $this->url;;
$apiKey = $this->apiKey;
$model = $this->model;
$chatId = $this->chatId;
$detail = $this->detail;
$headers = ["Authorization: Bearer $apiKey", 'Accept: application/json', 'Content-Type: application/json',];
$post_msg_body = ['detail' => $detail, 'stream' => true, 'chatId'=>$this->chatId];
if ($detail) {
$post_msg_body['detail'] = $detail;
}
if ($chatId) {
$post_msg_body['chatId'] = $chatId;
}
if ($model) {
$post_msg_body['model'] = $model;
}
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$postData = json_encode($post_msg_body);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_exec($ch);
}
private function parseData($data)
{
if (@json_decode($data)->choices[0]->message->content) {
return json_decode($data)->choices[0]->message->content;
}
$data = str_replace('data: {', '{', $data);
$data = rtrim($data, "\n\n");
if (strpos($data, 'data: [DONE]') !== false) {
return 'data: [DONE]';
} else {
if (false !== strpos($data, "\n\n")) {
$exp_arr = explode("\n\n", $data);
$str = '';
try {
for ($i = 0; $i < count($exp_arr) - 1; $i++) {
$jsondecode_arr = json_decode($exp_arr[$i], true);
$str .= $jsondecode_arr['choices'][0]['delta']['content'];
}
return $str;
} catch (\Exception $e) {
return $str;
}
}
$data = @json_decode($data, true);
if (!is_array($data)) {
return '';
}
if ($data['choices'][0]['finish_reason'] == 'stop') {
return 'data: [DONE]';
} elseif ($data['choices'][0]['finish_reason'] == 'length') {
return 'data: [CONTINUE]';
}
return $data['choices'][0]['delta']['content'] ?? '';
}
}
}

View File

@@ -0,0 +1,224 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/4/19} {11:14}
*/
namespace wanghua\general_utility_tools_php\html;
/**
* PHP操作html元素【不包含修改】
* Class Html
* @package wanghua\general_utility_tools_php\html
*/
class Html
{
/**
* desc提取a标签
* 返回标签和标签包含的内容
* authorwh
* @param $html
* @return null
*/
function getAElement(string $html){
$a_array = null;
$reg="/<a .*?>.*?<\/a>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取div标签
* authorwh
* @param string $html
* @return null
*/
function getDiv(string $html){
$a_array = null;
$reg="/<div .*?>.*?<\/div>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取img标签
* authorwh
* @param string $html
* @return null
*/
function getImg(string $html){
$a_array = null;
$reg="/<img .*?>.*?<\/img>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取span标签
* authorwh
* @param string $html
* @return null
*/
function getSpan(string $html){
$a_array = null;
$reg="/<span .*?>.*?<\/span>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取ul标签
* authorwh
* @param string $html
* @return null
*/
function getUl(string $html){
$a_array = null;
$reg="/<ul .*?>.*?<\/ul>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取li标签
* authorwh
* @param string $html
* @return null
*/
function getLi(string $html){
$a_array = null;
$reg="/<li .*?>.*?<\/li>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取table标签
* authorwh
* @param string $html
* @return null
*/
function getTable(string $html){
$a_array = null;
$reg="/<table .*?>.*?<\/table>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取table tr标签
* authorwh
* @param string $html
* @return null
*/
function getTableTr(string $html){
$a_array = null;
$reg="/<tr .*?>.*?<\/tr>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取table td标签
* authorwh
* @param string $html
* @return null
*/
function getTableTd(string $html){
$a_array = null;
$reg="/<td .*?>.*?<\/td>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取a标签属性
* authorwh
* @param $html
* @return array
*/
function getAAttribute($html){
$aarray = null;
$reg1="/<a .*?>.*?<\/a>/";
preg_match_all($reg1,$html,$aarray);
$hrefarray=null;//这个存放的是匹配出来的href的链接地址
$acontent=null;//存放匹配出来的a标签的内容
$reg2="/href=\"([^\"]+)/";
$a_arr = [];
for($i=0;$i<count($aarray[0]);$i++){
preg_match_all($reg2,$aarray[0][$i],$hrefarray);
//这里输出的就是遍历出来的所有a标签的链接
$a_arr['url'] = $hrefarray[1][0];
//拿出《a》标签的内容
$reg3="/>(.*)<\/a>/";
preg_match_all($reg3,$aarray[0][$i],$acontent);
//这里输出的就是a标签的文字了
$a_arr['text'] = $acontent[1][0];
}
return $a_arr;
}
/**
* desc提取img标签src(提取图片链接)
* authorwh
* @param $html
* @return array
*/
function getImgAttribute($html){
$aarray = null;
$reg1="/<img .*?>.*?<\/img>/";
preg_match_all($reg1,$html,$aarray);
$hrefarray=null;//这个存放的是匹配出来的href的链接地址
$acontent=null;//存放匹配出来的a标签的内容
$reg2="/src=\"([^\"]+)/";
$a_arr = [];
for($i=0;$i<count($aarray[0]);$i++){
preg_match_all($reg2,$aarray[0][$i],$hrefarray);
//这里输出的就是遍历出来的所有a标签的链接
$a_arr['url'] = $hrefarray[1][0];
//拿出《a》标签的内容
$reg3="/>(.*)<\/img>/";
preg_match_all($reg3,$aarray[0][$i],$acontent);
//这里输出的就是a标签的文字了
$a_arr['text'] = $acontent[1][0];
}
return $a_arr;
}
/**
* desc删除字符串
* authorwh
*/
function clearHtml($html, $str){
return str_replace($str,'',$html);
}
/**
* desc正则提取html元素的内容
*
* 用法eg
* $temp = '
<div class="num">1</div>
<div class="num">2</div>
<div class="num">3</div>
<div class="num">4</div>
<div class="num1">3</div>
<div class="num2">4</div>
<div class="num">5</div>';
$result = $this->get_tag_data($html,"div","class","num");
*
* authorwh
* @param $html
* @param $tag
* @param $class
* @param $value
* @return mixed
*/
function getTagData($html,$tag,$class,$value){
//$value 为空则获取class=$class的所有内容
$regex = $value ? "/<$tag.*?$class=\"$value\".*?>(.*?)<\/$tag>/is" : "/<$tag.*?$class=\".*?$value.*?\".*?>(.*?)<\/$tag>/is";
preg_match_all($regex,$html,$matches,PREG_PATTERN_ORDER);
//返回数组 ,指定标签内容
return $matches[1];
}
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/11/8} {15:05}
*/
namespace wanghua\general_utility_tools_php\html;
use wanghua\general_utility_tools_php\tool\Tools;
class Htmlcontent
{
/**
* [curlHtml 获取页面信息]
* 使用curl获取html页面内容
* @param [type] $url [网址]
* @return [type] [description]
*/
function curlHtml($url)
{
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "{$url}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding: gzip, deflate, br",
"Accept-Language: zh-CN,zh;q=0.9",
"Cache-Control: no-cache",
"Connection: keep-alive",
"Pragma: no-cache",
"Upgrade-Insecure-Requests: 1",
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
"cache-control: no-cache"
),
));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) return false;
else return $response;
}
/**
* [get_tag_data 使用正则获取html内容中指定的内容]
* @param [type] $html [爬取的页面内容]
* @param [type] $tag [要查找的标签]
* @param [type] $attr [要查找的属性名]
* @param [type] $value [属性名对应的值]
* @return [type] [description]
*/
function get_tag_data($html, $tag, $attr, $value)
{
// var_dump($tag, $attr, $value);
$regex = "/<$tag.*?$attr=\".*?$value.*?\".*?>(.*?)<\/$tag>/is";
// var_dump($regex);
preg_match_all($regex, $html, $matches, PREG_PATTERN_ORDER);
//var_dump($matches);die;
$data = isset($matches[1]) ? $matches[1] : '';
// var_dump($data);die;
return $data;
}
/**
* [get_html_data 使用xpath对获取到的html内容进行处理]
* @param [type] $html [爬取的页面内容]
* @param [type] $path [Xpath语句]
* @param integer $tag [类型 0内容 1标签内容 自定义标签]
* @param boolean $type [单个 还是多个(默认单个时输出单个)]
* @return [type] [description]
*/
function get_html_data($html, $path, $tag = 1, $type = true)
{
$dom = new \DOMDocument();
@$dom->loadHTML("<?xml encoding='UTF-8'>" . $html); // 从一个字符串加载HTML并设置UTF8编码
$dom->normalize(); // 使该HTML规范化
$xpath = new \DOMXPath($dom); //用DOMXpath加载DOM用于查询
$contents = $xpath->query($path); // 获取所有内容
$data = [];
foreach ($contents as $value) {
if ($tag == 1) {
$data[] = $value->nodeValue; // 获取不带标签内容
} elseif ($tag == 2) {
$data[] = $dom->saveHtml($value); // 获取带标签内容
} else {
$data[] = $value->attributes->getNamedItem($tag)->nodeValue; // 获取attr内容
}
}
if (count($data) == 1) {
$data = $data[0];
}
return $data;
}
//调用
public function get_content($url,$tag,$attr='',$value='')
{
//$url = input('url') ?? '';
//$tag = g('tag') ?? '';
//$attr = g('attr') ?? '';
//$value = g('value') ?? '';
if (empty($url) || empty($tag)) {
return Tools::set_res(4, '缺少关键参数!');
}
$html = $this->curlHtml($url);
//使用正则获取
$data = $this->get_tag_data($html, $tag, $attr, $value);
//Xpath方法暂未搞明白
// $data = $this->get_html_data($html, $path, $tag = 1, $type = true);
return Tools::set_res(1, 'ok!', $data);
}
}

View File

@@ -0,0 +1,575 @@
<?php
/*
* desc
*
* authorwh
* email
* createTime{2022/03/30} {09:59}
*/
namespace wanghua\general_utility_tools_php\http;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Utils;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* desccUrl网络库【推荐】
*
* authorwh
* Class Curl
* @package wanghua\general_utility_tools_php\http
*/
class Curl
{
/**
* desc[推荐] curl请求
*
* 如果是文件流则需要用getContents()方法获取
* eg: $res->getBody()->getContents();
*
* authorwh
* @param $url
* @param string $method
* @param false[] $config
* @param array $headers
* @return mixed
*/
static function request($url,$method='POST',$config=['verify' => false],$headers=[]){
$client = new Client($config);
//$headers = [
// //'User-Agent' => 'Apifox/1.0.0 (https://apifox.com)',
// //'Accept' => '*/*',
// //'Host' => 'vits_simple.excn.top',
// //'Connection' => 'keep-alive'
//];
$request = new Request($method, $url, $headers);
$res = $client->sendAsync($request)->wait();
//$res->getBody()->getContents();//如果是文件流则需要用getContents()方法获取
return $res->getBody();
}
/**
* descphp curl远程上传文件提交到远程服务器适用于上传多个文件(文件类型不限)
*
* 应用场景:
* 前端->后端A->后端B
* 说明前端发起文件上传后端A接收到文件后将文件使用guzzlehttp/guzzle上传到后端B后端B将文件处理
*
* 当前版本依赖:"guzzlehttp/guzzle": "^7.8"
*
* authorwh
* @param $url 远程地址
* @param $params 提交参数
* @param array $config 配置项
*/
function curlRemoteUploadFiles($url,$params, $config=['verify' => false]){
// 创建 Guzzle HTTP 客户端实例
$client = new Client($config);
// 定义要上传的文件列表
$files = [
//[
// 'name' => 'file1', // 服务器接收的文件字段名
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\dc1c441d81d48565ac6817b89d0f8bef.mp4', 'r'), // 文件路径
// 'filename' => 'dc1c441d81d48565ac6817b89d0f8bef.mp4', // 文件名,可自定义
//],
//[
// 'name' => 'files',
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\f80179b23b60619fba8033bfd64f8817.mp4', 'r'),
// 'filename' => 'f80179b23b60619fba8033bfd64f8817.mp4',
//],
//['name'=>'prompt','contents'=>$prompt],
//['name'=>'tts_url','contents'=>$tts_url]
// 可以继续添加更多文件...
];
foreach ($params as $key=>$val){
$files[] = ['name'=>$key,'contents'=>$val];
}
$controller = \request()->controller();
$action = \request()->action();
$save_path = Tools::get_root_path() . "public/uploads/{$controller}/{$action}/";
$files_obj = \request()->file('files');
foreach ($files_obj as $k=>$file) {
if ($file->check()) {
$save_res = $file->move($save_path);
$fileinfo = $file->getInfo();
// 使用CURLFile包装文件路径以供上传
$filename = $save_path.date('Ymd').'/'.$save_res->getFilename();
$files[] = [
'name'=>"files",//服务器接收的文件字段名
'contents'=>Utils::tryFopen($filename, 'r'),
'filename'=>$fileinfo['name'],//可自定义
];
}
//else {
// return json(['code' => -1, 'msg' => '文件上传失败: ' . $file->getError()]);
//}
}
// 构建多部分表单数据
$multipartStream = new MultipartStream($files);
$boundary = $multipartStream->getBoundary();
$headers = [
'Content-Type' => "multipart/form-data; boundary={$boundary}",
];
// 准备请求体
$body = $multipartStream->getContents();
// 发起 POST 请求
try {
$response = $client->request('POST', $url, [
'headers' => $headers,
'body' => $body,
]);
// 处理响应
$responseBody = (string) $response->getBody();
//echo "Server response: " . $responseBody;
return Tools::set_ok('ok',$responseBody);
} catch (\GuzzleHttp\Exception\RequestException $e) {
// 错误处理
//echo "Error: " . $e->getMessage();
return Tools::set_fail("Error: " . $e->getMessage());
}
}
/**
* @deprecated 弃用
* GET
*
* authorwh
* @param string $url
* @param int $timeout
* @return bool|int|string
*/
static function curl_get(string $url, int $timeout = 10, $header=[])
{
$header = $header?:array(
'Accept: application/json',
);
$curl = curl_init();
//curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
//curl_setopt($curl, CURLOPT_SSLVERSION, 3);
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
// 超时设置,以秒为单位
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//print "Error: ".curl_errno($curl).'-' . curl_error($curl);
//返回错误码
return ['code' => curl_errno($curl), 'msg' => curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code' => 200, 'msg' => 'cURL ok', 'data' => $data];
}
}
/**
* @deprecated 弃用
* POST 表单
*
* authorwh
* @param string $url 是请求的链接
* @param $postdata 传输的数据,数组格式 (跟$header的参数相关
* @return bool|int|string
*/
static function curl_post(string $url, $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code'=>200, 'msg'=>'cURL ok', 'data'=>$data];
}
}
/**
* @deprecated 弃用
* POST 查询参数
*
* authorwh
* @param string $url 是请求的链接
* @param array $postdata 传输的数据http_build_query查询参数格式
* @return bool|int|string
*/
static function curl_post_build_query(string $url, $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($postdata));
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code'=>200, 'msg'=>'ok', 'data'=>$data];
}
}
/**
* @deprecated 弃用
* POST json
*
* authorwh
* @param string $url 是请求的链接
* @param array $postdata 传输的数据json格式
* @return 返回数组格式
*/
static function curl_post_json(string $url, $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postdata,JSON_UNESCAPED_UNICODE));
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code'=>200, 'msg'=>'cURL ok', 'data'=>$data];
}
}
/**
* @deprecated 弃用
* POST json ,成功直接返回请求结果,错误返回数组
*
* 注:此方法不是表单提交
*
* authorwh
* @param string $url 是请求的链接
* @param array $postdata 传输的数据最终会转换为json格式请求
* @return 直接返回请求数据适合请求第三方返回BUFFER数据流的接口
*/
static function curl_post_json_return_buffer(string $url, array $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postdata,JSON_UNESCAPED_UNICODE));
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return $data;
}
}
/**
* @deprecated 弃用
* POST [流]
*
* [请求Java接口]
*
* 思考1、Java端如果用文件流的方式去获取数据调用此方法
*
* @param $url
* @param $postdata
* @param int $timeout
* @return array
* @link
* @example
* @see
*/
static function java_curl_post_file_request($url, $postdata, $timeout = 10, $header=[])
{
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code' => curl_errno($curl), 'msg' => curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code' => 200, 'msg' => 'ok', 'data' => $data];
}
}
/**
* @deprecated 弃用
* 统一请求 GET/POST请求
*
* 注此设置允许header重定向适合部分请求头中携带参数的接口如请求头携带token
*
* @param String $url 接口地址
*/
static function curl_request($url, $method = 'GET',$data=null,$header=array(),$call_back=null)
{
set_time_limit(30);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
if($method == 'POST'){
if($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if($call_back){
//使用此特性前除非你清楚理解回调函数,否则不推荐
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $call_back);
}
$result = curl_exec($ch);
if (curl_errno($ch)) {
return [
'status' => 'error',
'message' => 'curl 错误信息: ' . curl_error($ch)
];
}
curl_close($ch);
return $result;
}
/**
* @deprecated 弃用
* descphp curl模拟文件上传
* post
* authorwh
* @param $url 提交地址
* @param $params 表单参数
* @param $files 上传文件
* @param $header 请求头
* @return array
*/
static function curlFileUpload($url,$params,$files,$header)
{
// 初始化cURL会话
$ch = curl_init();
$header = $header ?: array(
'Accept: application/json',
'Content-Type: multipart/form-data'
);
// 合并文件和其他字段数据
$data = array_merge($params, $files);
// 设置cURL选项
curl_setopt($ch, CURLOPT_URL, $url);
// 设置请求头
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
// 执行cURL请求
$result = curl_exec($ch);
if (curl_errno($ch)) {
return ['code' => curl_errno($ch), 'msg' => curl_error($ch)];
}
//关闭句柄
curl_close($ch);
// 返回的内容
return ['code' => 200, 'msg' => 'cURL ok', 'data' => $result];
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/1/17} {13:07}
*/
namespace wanghua\general_utility_tools_php\image;
use wanghua\general_utility_tools_php\tool\Tools;
class Image
{
/**
* desc将二进制字符串转为PNG图片
*
* 应用场景当你的图片来源是buffer二进制时
*
* 实战场景微信小程序获取小程序码调用获取微信小程序码接口时微信接口返回的是图片Buffer二进制
* 这时候需要前端转换为图片,有时候与你和做的前端技术需要你转换为图片,再把图片路径传给他直接使用
*
* authorwh
* @param $binary 图片的Buffer二进制数据
* @param $img_save_path 系统保存路径传public网站根目录下面的路径
* @param $filename 要保存的文件名称,必须带后缀名
*/
static function binaryToImage($binary,$img_save_path,$filename){
// 获取二进制数据(这里假设从文件或其他地方获得)
//$binaryData = file_get_contents($binary); // 替换成真正的路径
// 创建图像资源
$imageResource = imagecreatefromstring($binary);
$img_save_path = Tools::get_root_path().'public/'.$img_save_path;
if(!file_exists($img_save_path)){
mkdir($img_save_path,0777,true);
}
// 保存图像到指定位置
imagepng($imageResource, $img_save_path.$filename); // 替换成想要保存的路径及名称
// 释放内存并关闭图像资源
imagedestroy($imageResource);
}
/**
* desc从富文本任意文本中提取图片链接
*
* 使用场景
* 在需要的任意场景调用
*
* authorwh
*/
static function extractImageUrls(string $html){
// $html = <<<HTML
//<p>这是一张图片:<a href="http://example.com/images/photo1.jpg" target="_blank">点击查看</a></p>
//<p>还有一张:<img src="http://example.com/images/photo2.jpg" alt="Another Photo"></p>
//<p>图片链接也可以是:<img src="https://example.com/images/photo3.png"></p>
//HTML;
// 使用正则表达式匹配图片链接
$pattern = '/(https?:\/\/[^\s]+\.(?:jpg|jpeg|png|gif))/i';
$images = [];
if (preg_match_all($pattern, $html, $matches)) {
//echo "找到的图片链接如下:\n";
foreach ($matches[0] as $match) {
$images[] = $match;
}
} else {
return $images;
}
return $images;
}
}

Some files were not shown because too many files have changed in this diff Show More