init: 项目初始代码

This commit is contained in:
2026-10-14 14:00:29 +08:00
commit cc434cf89f
464 changed files with 66984 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
<?php
class BusinessDaysCalculator {
const MONDAY = 1;
const TUESDAY = 2;
const WEDNESDAY = 3;
const THURSDAY = 4;
const FRIDAY = 5;
const SATURDAY = 6;
const SUNDAY = 7;
private $date = '';
/**
* @param DateTime $startDate Date to start calculations from
* @param DateTime[] $holidays Array of holidays, holidays are no conisdered business days.
* @param DateTime[] $nonBusinessDays Array of days of the week which are not business days.
* @param DateTime[] $specialBusinessDay Array is the special work day.
*/
public function __construct(DateTime $startDate, array $holidays, array $nonBusinessDays ,array $specialBusinessDay) {
$this->date = $startDate;
$this->holidays=[];
foreach($holidays as $holiday){
array_push($this->holidays,new DateTime($holiday));
}
$this->nonBusinessDays = $nonBusinessDays;
$this->specialBusinessDay = $specialBusinessDay;
}
/**
* 指几个工作日后
*/
public function addBusinessDays($howManyDays) {
$i = 0;
while ($i < $howManyDays) {
$this->date->modify("+1 day");
if ($this->isBusinessDay($this->date)) {
$i++;
}
}
}
public function getDate() {
return $this->date->format('Y-m-d');
}
private function isBusinessDay(DateTime $date) {
if(in_array($date->format('Y-m-d') , $this->specialBusinessDay)) return true; // 判断当前日期是否是因法定节假日调休而上班的周末,这种情况也算工作日
if (in_array((int)$date->format('N'), $this->nonBusinessDays)) {
return false; // 当前日期是周末
}
foreach ($this->holidays as $day) {
if ($date->format('Y-m-d') == $day->format('Y-m-d')) {
return false; // 当前日期是法定节假日
}
}
return true;
}
}
$holidays=["2017-01-27","2017-01-28","2017-01-29","2017-01-30","2017-01-31","2017-02-01","2017-02-02"]; // 从聚合数据接口获得
$specialBusinessDay=["2017-01-22"]; // 因法定节假日调休而上班的周末,这种情况也算工作日.因为这种情况少,可以通过手动配置
$calculator = new BusinessDaysCalculator(
new DateTime(), // 当前时间
$holidays,
[BusinessDaysCalculator::SATURDAY, BusinessDaysCalculator::SUNDAY],
$specialBusinessDay
);
$calculator->addBusinessDays(2); // 2个工作日后的时间
$afterBusinessDay=$calculator->getDate();
echo $afterBusinessDay;

48
extend/utils/FileLog.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
class FileLog
{
private $filepath;
private $filename;
private $handle = null;
private $time;
public function __construct($filepath, $filename)
{
$this->time = date('Y-m-d H:i:s');
$this->filepath = $filepath;
$this->filename = $filename;
$this->init();
}
public function init()
{
if (!is_dir($this->filepath))
{
mkdir($this->filepath, 0777, true);
}
}
public function write($content, $type='err')
{
$str = $type . '=====';
$str .= '当前时间为: ' . $this->time;
$str .= '=====';
$str .= $content . PHP_EOL;
if (substr($this->filepath, -1) == '/')
{
$file = $this->filepath . $this->filename;
}
else
{
$file = $this->filepath . '/' . $this->filename;
}
return file_put_contents($file, $str, FILE_APPEND);
}
}

168
extend/utils/Jwt.php Normal file
View File

@@ -0,0 +1,168 @@
<?php
namespace utils;
class Jwt
{
private $typ = 'JWT';
#key用户生成secret
private $key = 'jSCq5heSuhDiuhSsad441wq6541SDA5415W6QE564dsda';
private $secret;
#加密算法
private $alg = 'HS256';
#签发者-payload组成部分
private $iss;
#过期时间-payload组成部分
private $exp;
#签发时间-payload组成部分
private $iat;
#用户基础信息-payload组成部分
private $user_info;
#过期多久之前有效
private $timeout = 7200;
public function __construct($config)
{
$this->config = $config;
$this->init();
}
private function init()
{
$this->iss = $this->config['iss'];
$this->exp = $this->config['exp'];
$this->iat = time();
}
/**
* 获取token
* @param array $user_info
* @return string
*/
public function getToken(array $user_info):string
{
//头信息
$header = [
'typ' => $this->typ,
'alg' => $this->alg
];
$base64_header = base64_encode(json_encode($header));
//有效载荷
$payload = [
'iss' => $this->iss,
'exp' => $this->exp,
'iat' => $this->iat,
'user_info' => $user_info,
];
$base64_payload = base64_encode(json_encode($payload));
//签名信息
$this->secret = $this->getSecret($user_info);
$signature = $base64_header . $base64_payload . $this->secret;
$base64_signature = $this->enc($this->alg, $signature);
$jwt_token = $base64_header . '.' . $base64_payload . '.' . $base64_signature;
return $jwt_token;
}
/**
* 校验token
* @param $jwt_token
* @return int
*/
public function checkToken($jwt_token)
{
$arr = explode('.', $jwt_token);
if (count($arr) < 3) {
return -1;
}
$base64_header = $arr[0];
$base64_payload = $arr[1];
$base64_signature = $arr[2];
$payload = json_decode(base64_decode($base64_payload), true);
if (!is_array($payload) || !isset($payload['exp']) || !isset($payload['iat']) || !isset($payload['user_info'])) {
return -2;
}
//生成签名,比较
$this->secret = $this->getSecret($payload['user_info']);
$tmp_signature = $base64_header . $base64_payload . $this->secret;
$tmp_base64_signature = $this->enc($this->alg, $tmp_signature);
if ($base64_signature !== $tmp_base64_signature) {
return -3;
}
// 是否过期
if ($payload['exp'] + $payload['iat'] - time() < 0) {
// token过期了
return -4;
} else {
// token没有过期
if ($payload['exp'] + $payload['iat'] - time() - $this->timeout < 0) {
return 201;
}
return 200;
}
}
/**
* 获取用户信息
* @param $jwt_token
* @return array|mixed
*/
public function getUserInfo($jwt_token)
{
$arr = explode('.', $jwt_token);
if (count($arr) < 3) {
return [];
}
$base64_payload = $arr[1];
$payload = json_decode(base64_decode($base64_payload), true);
if (!is_array($payload) || !isset($payload['user_info'])) {
return [];
}
return $payload['user_info'];
}
/**
* 获取secret,保证每个用户的secret都不同
* @param $user_info
* @return string
*/
private function getSecret($user_info)
{
return md5($this->key . json_encode($user_info));
}
/**
* 加密
* @param $enc_type
* @param $content
* @return string
*/
private function enc($enc_type, $content)
{
if ($enc_type == 'HS256')
{
return hash('sha256', $content);
}
}
}

87
extend/utils/Mailer.php Normal file
View File

@@ -0,0 +1,87 @@
<?php
namespace utils;
require_once root_path() . '/vendor/phpmailer/class.phpmailer.php';
require_once root_path() . '/vendor/phpmailer/class.smtp.php';
use PHPMailer;
class Mailer
{
const CHARTSET = 'UTF-8';
const PORT = 456;
const HOST = 'smtp.qq.com';
private $username = '735273025@qq.com';
private $password = 'gtxknrzhmctlbbfb';
private $fromAccount = '735273025@qq.com';
private $fromName = 'Jsasg';
private $toAccount = '735273025@qq.com';
private $toName = 'Jsasg';
private $subject = '';
private $body = '';
private $altBody = '';
/**
* 设置主题
* @param string $subject
*/
public function setSubject($subject)
{
$this->subject = $subject;
return $this;
}
/**
* 设置内容
* @param string $body
*/
public function setBody($body)
{
$this->body = $body;
return $this;
}
/**
* 设置不支持html时的内容
* @param string $string 内容
*/
public function setAltBody($string)
{
$this->altBody = $string;
return $this;
}
/**
* 发送
*/
public function send()
{
$success = false;
$mail = new PHPMailer(true);
try {
$mail->CharSet = self::CHARTSET; //设定邮件编码
$mail->SMTPDebug = 0; // 调试模式输出
$mail->isSMTP(); // 使用SMTP
$mail->Host = self::HOST; // SMTP服务器
$mail->SMTPAuth = true; // 允许 SMTP 认证
$mail->Username = $this->username; // SMTP 用户名 即邮箱的用户名
$mail->Password = $this->password; // SMTP 密码 部分邮箱是授权码(例如163邮箱)
$mail->SMTPSecure = 'ssl'; // 允许 TLS 或者ssl协议
$mail->Port = 465; // 服务器端口 25 或者 465 具体要看邮箱服务器支持
$mail->setFrom($this->fromAccount, $this->fromName); //发件人
$mail->addAddress($this->toAccount, $this->toName); // 收件人
$mail->isHTML(true);
$mail->Subject = $this->subject;
$mail->Body = $this->body;
$mail->AltBody = $this->altBody;
$success = $mail->send();
} catch (\Exception $error) {
exit($mail->ErrorInfo);
}
return $success;
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace utils;
use think\cache\driver\Redis;
use think\facade\Config;
class OrederStatusSync4Redis
{
private $client;
private $key;
private $group;
private $consumer;
public function __construct($config=[])
{
$this->key = Config::get('redis.erp.order_sync_stream.key');
if (empty($this->key)) {
throw new \Exception('未配置默认STREAM标识key');
}
$this->group = Config::get('redis.erp.order_sync_stream.group');
if (empty($this->group)) {
throw new \Exception('未配置默认STREAM消费者组');
}
$this->consumer = Config::get('redis.erp.order_sync_stream.consumer');
if (empty($this->consumer)) {
throw new \Exception('未配置默认STREAM消费者');
}
if (empty($config)) {
$config = Config::get('redis.erp.conf');
}
$this->client = new Redis($config);
}
/**
* @param string $key 设置stream标识key
*/
public function streamKey($key)
{
if (empty($key)) {
return false;
}
$this->key = $key;
}
/**
* @param string $group 设置消费者组
*/
public function streamGroup($group)
{
if (empty($group)) {
return false;
}
$this->group = $group;
}
/**
* @param string $consumer 设置消费者
*/
public function streamConsumer($consumer)
{
if (empty($consumer)) {
return false;
}
$this->consumer = $consumer;
}
/**
* 检查是否存在消息组
* @param string $streamGroup 消费者组
* @return bool
*/
public function hasGroup($streamGroup='')
{
$groups = $this->client->xInfo('GROUPS', $this->key);
if (empty($groups)) {
return false;
}
if (empty($streamGroup)) {
$streamGroup = $this->group; // 使用配置的消费组
}
if (!in_array($streamGroup, array_column($groups, 'name'))) {
return false;
}
return true;
}
/**
* 创建消息组
* @param string $streamGroup 消费者组
* @param string $msgID 0为查看所有消息,$为查看最新消息
*/
public function createGroup($streamGroup='', $msgID='0')
{
if (empty($streamGroup)) {
$streamGroup = $this->group; // 使用配置的消费组
}
$this->client->xGroup('CREATE', $this->key, $streamGroup, $msgID);
}
/**
* 获取新消息
* @param int $count 获取条数
* @param int $block 是否阻塞:0为永久阻塞,null为不阻塞,1000为阻塞1秒
* @return array
*/
public function fetchMessage($count=null, $block=null)
{
$keys[$this->key] = '>'; // 获取新消息
$messages = $this->client->xReadGroup($this->group, $this->consumer, $keys, $count, $block);
return !empty($messages) ? $messages[$this->key] : [];
}
/**
* 获取未处理消费
* @param int $count 获取条数
* @param int $block 是否阻塞:0为永久阻塞,null为不阻塞,1000为阻塞1秒
* @return array
*/
public function fetchMessage4UnAck($count=null, $block=null)
{
$keys[$this->key] = '0'; // 获取未处理的
$messages = $this->client->xReadGroup($this->group, $this->consumer, $keys, $count, $block);
return !empty($messages) ? $messages[$this->key] : [];
}
/**
* 获取处理PENDING中消息
* @param int $count 获取条数
* @return array
*/
public function fetchMessage4Pending($count = 10)
{
$messages = $this->client->xPending($this->key, $this->group, '-', '+', $count, $this->consumer);
return !empty($messages) ? $messages : [];
}
/**
* 根据消息ID设为消息已处理
* @param array|string 消息ID
* @param int 处理系数
*/
public function ack($messagesID)
{
if (!is_array($messagesID)) {
$messagesID = [$messagesID];
}
return $this->client->xAck($this->key, $this->group, $messagesID);
}
}

View File

@@ -0,0 +1,200 @@
<?php
namespace utils;
use think\facade\Env;
use Qiniu\Auth;
use Qiniu\Storage\UploadManager;
/**
* 七牛云上传
* Class QiniuUploader
* @package utils
*/
class QiniuUploader
{
protected $bucket = 'orico-cms';
protected $accessKey = 'dOsTum4a5qvhPTBbZRPX0pIOU7PZWRX7htKjztms';
protected $secretKey = 'KFxsGbnErkALFfeGdMa8QWTdodJbamMX0iznLe-q';
protected $rule = [
'fileSize' => 1024 * 1024 * 5, // 默认最大上传5M
'fileExt' => 'jpeg,jpg,png', // 默认上传文件后缀
'fileMime' => 'image/jpeg,image/png,image/gif' // 默认上传文件mime
];
protected $dir = true;
protected $originalName = false;
protected $pathPrefix = '';
protected $fileNamePrefix = 'cms_';
protected $domain = '';
public function __construct($conf = [])
{
$this->domain = Env::get("QINIU.cdn");
if (!empty($conf['bucket'])) {
$this->bucket = $conf['bucket'];
}
if (!empty($conf['accessKey'])) {
$this->accessKey = $conf['accessKey'];
}
if (!empty($conf['secretKey'])) {
$this->secretKey = $conf['secretKey'];
}
if (!empty($conf['pathPrefix'])) {
$this->pathPrefix = trim($conf['pathPrefix'], '/');
}
}
/**
* 生成随机字符串
*/
private function random($length, $type="string", $convert="0")
{
$conf = [
'number' => '0123456789',
'string' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'all' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789='
];
$string = $conf[$type];
if(!$string) {
$string = $conf['string'];
}
$strlen = strlen($string) - 1;
$char = '';
for($i=0; $i<$length; $i++) {
$char .= $string[mt_rand(0, $strlen)];
}
if($convert > 0) {
$res = strtoupper($char);
} elseif ($convert == 0) {
$res = $char;
} elseif ($convert < 0) {
$res = strtolower($char);
}
return $res;
}
/**
* 组装文件名
*/
private function buildFileName()
{
return $this->fileNamePrefix . time().substr(time(),-5).substr(microtime(),2,3) . $this->random(8);
}
/**
* 上传验证规则
*/
public function validate($rule)
{
$this->rule = $rule;
}
/**
* 上传文件到七牛云
* @param $name
* @param bool $multi 多文件模式
* @param int $num 多文件模式文件数量
* @return array
* @throws \Exception
*/
public function uploadFile($name,$multi=false,$num = 1)
{
// 构建鉴权对象
$auth = new Auth($this->accessKey, $this->secretKey);
// 生成上传 Token
$token = $auth->uploadToken($this->bucket);
// 初始化 UploadManager 对象并进行文件的上传。
$uploadMgr = new UploadManager();
$data = [];
if (false === $multi) {
$file = request()->file($name);
$data = $this->doFileToQn($file, $uploadMgr, $token);
} else {
for ($i = 0; $i < $num; $i++) {
$file = request()->file($name)[$i];
$data[] = $this->doFileToQn($file, $uploadMgr, $token);
}
}
return $data;
}
/**
* 七牛云上传本地文件,并且本地相对路由和七牛云路径完全一致
* @param string $fullName 文件名称带全路径。/www/wwwroot/test.yulinzhihou.com/public/ueditor/php/upload/file/20210424/1619232876970230.png
* @param string $relativePath 相对路径,注意:前面不能以斜杠开头 如:ueditor/php/upload/file/20210424/1619232876970230.png
* @return array
* @throws \Exception
*/
public function uploadLocalFile($fullName,$relativePath)
{
// 构建鉴权对象
$auth = new Auth($this->accessKey, $this->secretKey);
// 生成上传 Token
$token = $auth->uploadToken($this->bucket);
// 初始化 UploadManager 对象并进行文件的上传。
$uploadMgr = new UploadManager();
$file = ltrim($relativePath,'\/');
list($ret, $err) = $uploadMgr->putFile($token,$file,$fullName);
if ($err !== null) {
throw new \Exception($err);
} else {
return ['hash'=>$ret['hash'], 'filename'=>$ret['key'], 'remote_url'=>$this->domain . $ret['key']];
}
}
/**
* @param $file
* @param UploadManager $uploadMgr
* @param string $token
* @return array
* @throws \Exception
*/
private function doFileToQn($file, UploadManager $uploadMgr, string $token): array
{
$aspectRatio = [];
if (!empty($this->rule['aspectRatio'])) {
$aspectRatio = $this->rule['aspectRatio'];
unset($this->rule['aspectRatio']);
}
$fileName = $file->getOriginalName(); // 文件原名
if (!$this->originalName) {
$fileName = $this->buildFileName() . '.' . $file->extension();
if (!$this->dir && !empty($this->pathPrefix)) {
$fileName = $this->pathPrefix . '/' . $fileName;
}
}
if ($this->dir) {
$fileName = date('Y') . '/' . date('m') . '/' . date('d') . '/' . $fileName;
if (!empty($this->pathPrefix)) {
$fileName = $this->pathPrefix . '/' . $fileName;
}
}
$filePath = $file->getPathname(); // 临时路径
if (!empty($aspectRatio)) { // 验证图片宽高
list($width, $height, $type, $attr) = getimagesize($file);
if ($width != $aspectRatio['width'] || $height != $aspectRatio['height']) {
throw new \Exception('图片宽高不符合');
}
}
$fileType = $file->getOriginalMime();
list($ret, $err) = $uploadMgr->putFile($token, $fileName, $filePath, null, $fileType, false);
if ($err !== null) {
throw new \Exception($err);
} else {
return ['hash' => $ret['hash'], 'filename' => $ret['key'], 'remote_url' => $this->domain . $ret['key']];
}
}
}

371
extend/utils/UEUploader.php Normal file
View File

@@ -0,0 +1,371 @@
<?php
namespace utils;
/**
* Created by JetBrains PhpStorm.
* User: taoqili
* Date: 12-7-18
* Time: 上午11: 32
* UEditor编辑器通用上传类
*/
class UEUploader
{
private $fileField; //文件域名
private $file; //文件上传对象
private $base64; //文件上传对象
private $config; //配置信息
private $oriName; //原始文件名
private $fileName; //新文件名
private $fullName; //完整文件名,即从当前配置目录开始的URL
private $filePath; //完整文件名,即从当前配置目录开始的URL
private $fileSize; //文件大小
private $fileType; //文件类型
private $stateInfo; //上传状态信息,
private $stateMap = array( //上传状态映射表,国际化用户需考虑此处数据的国际化
"SUCCESS", //上传成功标记,在UEditor中内不可改变,否则flash判断会出错
"文件大小超出 upload_max_filesize 限制",
"文件大小超出 MAX_FILE_SIZE 限制",
"文件未被完整上传",
"没有文件被上传",
"上传文件为空",
"ERROR_TMP_FILE" => "临时文件错误",
"ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件",
"ERROR_SIZE_EXCEED" => "文件大小超出网站限制",
"ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许",
"ERROR_CREATE_DIR" => "目录创建失败",
"ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限",
"ERROR_FILE_MOVE" => "文件保存时出错",
"ERROR_FILE_NOT_FOUND" => "找不到上传文件",
"ERROR_WRITE_CONTENT" => "写入文件内容错误",
"ERROR_UNKNOWN" => "未知错误",
"ERROR_DEAD_LINK" => "链接不可用",
"ERROR_HTTP_LINK" => "链接不是http链接",
"ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确",
"INVALID_URL" => "非法 URL",
"INVALID_IP" => "非法 IP"
);
/**
* 构造函数
* @param string $fileField 表单名称
* @param array $config 配置项
* @param bool $base64 是否解析base64编码,可省略。若开启,则$fileField代表的是base64编码的字符串表单名
*/
public function __construct($fileField, $config, $type = "upload")
{
$this->fileField = $fileField;
$this->config = $config;
$this->type = $type;
if ($type == "remote") {
$this->saveRemote();
} else if($type == "base64") {
$this->upBase64();
} else {
$this->upFile();
}
}
/**
* 上传文件的主处理方法
* @return mixed
*/
private function upFile()
{
$file = $this->file = $_FILES[$this->fileField];
if (!$file) {
$this->stateInfo = $this->getStateInfo("ERROR_FILE_NOT_FOUND");
return;
}
if ($this->file['error']) {
$this->stateInfo = $this->getStateInfo($file['error']);
return;
} else if (!file_exists($file['tmp_name'])) {
$this->stateInfo = $this->getStateInfo("ERROR_TMP_FILE_NOT_FOUND");
return;
} else if (!is_uploaded_file($file['tmp_name'])) {
$this->stateInfo = $this->getStateInfo("ERROR_TMPFILE");
return;
}
$this->oriName = $file['name'];
$this->fileSize = $file['size'];
$this->fileType = $this->getFileExt();
$this->fullName = $this->getFullName();
$this->filePath = $this->getFilePath();
$this->fileName = $this->getFileName();
$dirname = dirname($this->filePath);
//检查文件大小是否超出限制
if (!$this->checkSize()) {
$this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
return;
}
//检查是否不允许的文件格式
if (!$this->checkType()) {
$this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
return;
}
//创建目录失败
if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
$this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
return;
} else if (!is_writeable($dirname)) {
$this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
return;
}
//移动文件
if (!(move_uploaded_file($file["tmp_name"], $this->filePath) && file_exists($this->filePath))) {
//移动失败
$this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE");
} else {
//移动成功
$this->stateInfo = $this->stateMap[0];
}
}
/**
* 处理base64编码的图片上传
* @return mixed
*/
private function upBase64()
{
$base64Data = $_POST[$this->fileField];
$img = base64_decode($base64Data);
$this->oriName = $this->config['oriName'];
$this->fileSize = strlen($img);
$this->fileType = $this->getFileExt();
$this->fullName = $this->getFullName();
$this->filePath = $this->getFilePath();
$this->fileName = $this->getFileName();
$dirname = dirname($this->filePath);
//检查文件大小是否超出限制
if (!$this->checkSize()) {
$this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
return;
}
//创建目录失败
if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
$this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
return;
} else if (!is_writeable($dirname)) {
$this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
return;
}
//移动文件
if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
$this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
} else { //移动成功
$this->stateInfo = $this->stateMap[0];
}
}
/**
* 拉取远程图片
* @return mixed
*/
private function saveRemote()
{
$imgUrl = htmlspecialchars($this->fileField);
$imgUrl = str_replace("&amp;", "&", $imgUrl);
//http开头验证
if (strpos($imgUrl, "http") !== 0) {
$this->stateInfo = $this->getStateInfo("ERROR_HTTP_LINK");
return;
}
preg_match('/(^https*:\/\/[^:\/]+)/', $imgUrl, $matches);
$host_with_protocol = count($matches) > 1 ? $matches[1] : '';
// 判断是否是合法 url
if (!filter_var($host_with_protocol, FILTER_VALIDATE_URL)) {
$this->stateInfo = $this->getStateInfo("INVALID_URL");
return;
}
preg_match('/^https*:\/\/(.+)/', $host_with_protocol, $matches);
$host_without_protocol = count($matches) > 1 ? $matches[1] : '';
// 此时提取出来的可能是 ip 也有可能是域名,先获取 ip
$ip = gethostbyname($host_without_protocol);
// 判断是否是私有 ip
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
$this->stateInfo = $this->getStateInfo("INVALID_IP");
return;
}
//获取请求头并检测死链
$heads = get_headers($imgUrl, 1);
if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
$this->stateInfo = $this->getStateInfo("ERROR_DEAD_LINK");
return;
}
//格式验证(扩展名验证和Content-Type验证)
$fileType = strtolower(strrchr($imgUrl, '.'));
if (!in_array($fileType, $this->config['allowFiles']) || !isset($heads['Content-Type']) || !stristr($heads['Content-Type'], "image")) {
$this->stateInfo = $this->getStateInfo("ERROR_HTTP_CONTENTTYPE");
return;
}
//打开输出缓冲区并获取远程图片
ob_start();
$context = stream_context_create(
array('http' => array(
'follow_location' => false // don't follow redirects
))
);
readfile($imgUrl, false, $context);
$img = ob_get_contents();
ob_end_clean();
preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $imgUrl, $m);
$this->oriName = $m ? $m[1]:"";
$this->fileSize = strlen($img);
$this->fileType = $this->getFileExt();
$this->fullName = $this->getFullName();
$this->filePath = $this->getFilePath();
$this->fileName = $this->getFileName();
$dirname = dirname($this->filePath);
//检查文件大小是否超出限制
if (!$this->checkSize()) {
$this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
return;
}
//创建目录失败
if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
$this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
return;
} else if (!is_writeable($dirname)) {
$this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
return;
}
//移动文件
if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
$this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
} else { //移动成功
$this->stateInfo = $this->stateMap[0];
}
}
/**
* 上传错误检查
* @param $errCode
* @return string
*/
private function getStateInfo($errCode)
{
return !$this->stateMap[$errCode] ? $this->stateMap["ERROR_UNKNOWN"] : $this->stateMap[$errCode];
}
/**
* 获取文件扩展名
* @return string
*/
private function getFileExt()
{
return strtolower(strrchr($this->oriName, '.'));
}
/**
* 重命名文件
* @return string
*/
private function getFullName()
{
//替换日期事件
$t = time();
$d = explode('-', date("Y-y-m-d-H-i-s"));
$format = $this->config["pathFormat"];
$format = str_replace("{yyyy}", $d[0], $format);
$format = str_replace("{yy}", $d[1], $format);
$format = str_replace("{mm}", $d[2], $format);
$format = str_replace("{dd}", $d[3], $format);
$format = str_replace("{hh}", $d[4], $format);
$format = str_replace("{ii}", $d[5], $format);
$format = str_replace("{ss}", $d[6], $format);
$format = str_replace("{time}", $t, $format);
//过滤文件名的非法自负,并替换文件名
$oriName = substr($this->oriName, 0, strrpos($this->oriName, '.'));
$oriName = preg_replace("/[\|\?\"\<\>\/\*\\\\]+/", '', $oriName);
$format = str_replace("{filename}", $oriName, $format);
//替换随机字符串
$randNum = rand(1, 10000000000) . rand(1, 10000000000);
if (preg_match("/\{rand\:([\d]*)\}/i", $format, $matches)) {
$format = preg_replace("/\{rand\:[\d]*\}/i", substr($randNum, 0, $matches[1]), $format);
}
$ext = $this->getFileExt();
return $format . $ext;
}
/**
* 获取文件名
* @return string
*/
private function getFileName () {
return substr($this->filePath, strrpos($this->filePath, '/') + 1);
}
/**
* 获取文件完整路径
* @return string
*/
private function getFilePath()
{
$fullname = $this->fullName;
$rootPath = $_SERVER['DOCUMENT_ROOT'];
if (substr($fullname, 0, 1) != '/') {
$fullname = '/' . $fullname;
}
return $rootPath . $fullname;
}
/**
* 文件类型检测
* @return bool
*/
private function checkType()
{
return in_array($this->getFileExt(), $this->config["allowFiles"]);
}
/**
* 文件大小检测
* @return bool
*/
private function checkSize()
{
return $this->fileSize <= ($this->config["maxSize"]);
}
/**
* 获取当前上传成功文件的各项信息
* @return array
*/
public function getFileInfo()
{
return array(
"state" => $this->stateInfo,
"url" => $this->fullName,
"title" => $this->fileName,
"original" => $this->oriName,
"type" => $this->fileType,
"size" => $this->fileSize
);
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* unionpay在线网关支付B2C业务
*/
class UnionpayB2C
{
protected $pay_type = 'unionpay_b2c';
#版本号
protected $version;
#编码方式
protected $encoding = 'UTF-8';
#交易币种列表
protected $currencyCode_list = [
'CNY' => '156'
];
protected $currencyCode;
#交易子类型列表,支付接口专用
protected $txnSubType_list = [
'zizhu' => '01',
'fenqi' => '03',
];
protected $txnSubType;
#接入类型
protected $accessType = '0'; //商户直连
#签名算法
protected $signMethod;
#渠道类型列表
protected $channelType_list = [
'pc' => '07',
'mobile' => '08'
];
protected $channelType;
#商户号
// protected $merId = '777290058181532'; //test
protected $merId = '802310057320534'; //线上
#接口url列表
protected $dev_url_list = [
'pay' => 'https://gateway.test.95516.com/gateway/api/frontTransReq.do',
'query' => 'https://gateway.test.95516.com/gateway/api/queryTrans.do',
];
protected $prod_url_list = [
'pay' => 'https://gateway.95516.com/gateway/api/frontTransReq.do',
'query' => 'https://gateway.95516.com/gateway/api/queryTrans.do',
];
protected $url_list;
private $logger;
public function __construct()
{
parent::__construct();
$this->init();
}
/**
* 初始化部分参数
*/
private function init()
{
require_once app()->getRootPath() . '/extend/payment/unionpay/sdk/AcpService.php';
$this->version = com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->version;
$this->front_url = com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->frontUrl;
$this->back_url = com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->backUrl;
$this->signMethod = com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->signMethod;
// $this->url_list = $this->dev_url_list;
$this->url_list = $this->prod_url_list;
$date = explode('-', date('Y-m-d'));
$filepath = APP_PATH . 'Lib/pay/log/' . $date[0] . '-' . $date[1] . '/' . $date[2];
$filename = $this->pay_type . '.log';
import('ORG.Util.FileLog');
$this->logger = new FileLog($filepath, $filename);
}
/**
* 查询支付状态
* @param $pay_log === pay_log表记录
*/
public function query($pay_log)
{
$out_trade_no = $pay_log['out_trade_no'];
$txnTime = date('YmdHis', strtotime($pay_log['create_time']));
$this->bizType = $this->bizType_list['query'][$pay_log['pay_type']];
$this->channelType = $this->channelType_list[$pay_log['source']];
$params = [
'version' => $this->version, //版本号
'encoding' => $this->encoding, //编码方式
'signMethod' => $this->signMethod, //签名方法
'txnType' => '00', //交易类型
'txnSubType' => '00', //交易子类
'bizType' => '000000', //业务类型
'accessType' => $this->accessType, //接入类型
'channelType' => $this->channelType, //渠道类型
'orderId' => $out_trade_no, //请修改被查询的交易的订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数
'merId' => $this->merId, //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数
'txnTime' => $txnTime, //请修改被查询的交易的订单发送时间,格式为YYYYMMDDhhmmss,此处默认取demo演示页面传递的参数
];
com\unionpay\acp\sdk\AcpService::sign ( $params ); // 签名
$url = $this->url_list['query'];
$this->logger->write('查询请求参数: ' . json_encode($params) . '=====url: ' . $url, 'suc');
$result_arr = com\unionpay\acp\sdk\AcpService::post ( $params, $url);
if (count($result_arr) > 0 &&
com\unionpay\acp\sdk\AcpService::validate ($result_arr) &&
$result_arr["respCode"] == "00" &&
$result_arr["origRespCode"] == "00"
)
{
$this->logger->write('查询完成,交易成功,交易单号: ' . $out_trade_no, 'suc');
return $result_arr;
}
$this->logger->write('查询完成,交易失败,交易单号: ' . $out_trade_no);
return false;
}
private function http_success()
{
header("HTTP/1.1 200 OK");
exit;
}
private function http_fail()
{
header("HTTP/1.1 500 ERROR");
exit;
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace utils;
use think\facade\Db;
/**
* 获取工作日
*/
class WorkingDay
{
private $time = 0;
private $holidays = [];
public function __construct($time=0)
{
if (empty($time)) $this->time = time();
}
/**
* 获取当前年度的节假日
*/
private function getHolidays()
{
$data = Db::name('holiday')->where('year', '=', date('Y'))->column('data');
if (empty($data)) {
$this->holidays = [];
}
$this->holidays = $data;
}
/**
* 获取工作日
*/
public function getDates($adays)
{
// 获取节假日
$this->getHolidays();
// 组装工作日
$ctime = $this->time;
$days = [];
$i = 1;
while($adays>0) {
$day = strtotime("+ $i day", $ctime);
$week = date('w', $day);
// 剔除周六周日与假日
if (0 != $week && 6 != $week && !in_array(date('Y-m-d', $day), $this->holidays)) {
$adays-=1;
$days[] = date('Y-m-d H:i:s', $day);
}
$i+=1;
}
return $days;
}
/**
* 获取几个工作日后日期
*/
public function getDate($adays, $ctime = 0)
{
if (!empty($ctime)) {
$this->time = $ctime;
}
$days = $this->getDates($adays);
rsort($days);
return $days[0];
}
}

View File

@@ -0,0 +1,94 @@
/* 前后端通信相关的配置,注释只允许使用多行方式 */
{
/* 上传图片配置项 */
"imageActionName": "uploadimage", /* 执行上传图片的action名称 */
"imageFieldName": "upfile", /* 提交的图片表单名称 */
"imageMaxSize": 20480000, /* 上传大小限制,单位B */
"imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
"imageCompressEnable": true, /* 是否压缩图片,默认是true */
"imageCompressBorder": 1600, /* 图片压缩最长边限制 */
"imageInsertAlign": "none", /* 插入的图片浮动方式 */
"imageUrlPrefix": "", /* 图片访问路径前缀 */
"imagePathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
/* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
/* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
/* {time} 会替换成时间戳 */
/* {yyyy} 会替换成四位年份 */
/* {yy} 会替换成两位年份 */
/* {mm} 会替换成两位月份 */
/* {dd} 会替换成两位日期 */
/* {hh} 会替换成两位小时 */
/* {ii} 会替换成两位分钟 */
/* {ss} 会替换成两位秒 */
/* 非法字符 \ : * ? " < > | */
/* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */
/* 涂鸦图片上传配置项 */
"scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */
"scrawlFieldName": "upfile", /* 提交的图片表单名称 */
"scrawlPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"scrawlMaxSize": 20480000, /* 上传大小限制,单位B */
"scrawlUrlPrefix": "", /* 图片访问路径前缀 */
"scrawlInsertAlign": "none",
/* 截图工具上传 */
"snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */
"snapscreenPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"snapscreenUrlPrefix": "", /* 图片访问路径前缀 */
"snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */
/* 抓取远程图片配置 */
"catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
"catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */
"catcherFieldName": "source", /* 提交的图片列表表单名称 */
"catcherPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"catcherUrlPrefix": "", /* 图片访问路径前缀 */
"catcherMaxSize": 20480000, /* 上传大小限制,单位B */
"catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */
/* 上传视频配置 */
"videoActionName": "uploadvideo", /* 执行上传视频的action名称 */
"videoFieldName": "upfile", /* 提交的视频表单名称 */
"videoPathFormat": "/ueditor/php/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"videoUrlPrefix": "", /* 视频访问路径前缀 */
"videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */
"videoAllowFiles": [
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */
/* 上传文件配置 */
"fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */
"fileFieldName": "upfile", /* 提交的文件表单名称 */
"filePathFormat": "/ueditor/php/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"fileUrlPrefix": "", /* 文件访问路径前缀 */
"fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */
"fileAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
], /* 上传文件格式显示 */
/* 列出指定目录下的图片 */
"imageManagerActionName": "listimage", /* 执行图片管理的action名称 */
"imageManagerListPath": "/ueditor/php/upload/image/", /* 指定要列出图片的目录 */
"imageManagerListSize": 20, /* 每次列出文件数量 */
"imageManagerUrlPrefix": "", /* 图片访问路径前缀 */
"imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */
"imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */
/* 列出指定目录下的文件 */
"fileManagerActionName": "listfile", /* 执行文件管理的action名称 */
"fileManagerListPath": "/ueditor/php/upload/file/", /* 指定要列出文件的目录 */
"fileManagerUrlPrefix": "", /* 文件访问路径前缀 */
"fileManagerListSize": 20, /* 每次列出文件数量 */
"fileManagerAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
] /* 列出的文件类型 */
}