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

101
extend/apiRes/Api.php Normal file
View File

@@ -0,0 +1,101 @@
<?php
namespace apiRes;
class Api
{
private static $response;
private $state = [];
private $default_states = [
'success' => [
'errno' => 0,
'errmsg' => '操作成功!',
'data' => []
],
'error' => [
'errno' => 2001,
'errmsg' => '操作错误!',
'data' => []
],
'NoData' => [
'errno' => 2002,
'errmsg' => '没有数据!',
'data' => []
],
'NotLoggedIn' => [
'errno' => 2003,
'errmsg' => '未登录',
'data' => []
]
];
private function __construct($erro)
{
isset($erro) || die('请确认返回类型!');
$this->state = $this->default_states[$erro];
return $this;
}
// 单例
public static function result($erro)
{
if (!self::$response) {
self::$response = new self($erro);
}
return self::$response;
}
// 返回错误
public static function error($msg='', $data=[], $to_array=false)
{
$ins = self::result('error');
if ($msg) {
$ins = $ins->message($msg);
}
return $ins->response($data, $to_array);
}
// 返回成功
public static function success($msg='', $data=[], $to_array=false)
{
$ins = self::result('success');
if ($msg) {
$ins = $ins->message($msg);
}
return $ins->response($data, $to_array);
}
// 写入返回信息
public function message($message='')
{
$message ? $this->state['errmsg'] = $message : '';
return $this;
}
// 返回结果
public function response($data=[], $to_array=false)
{
if (!empty($data)) {
$this->state['data'] = $data;
}
if (request()->isJson() || $to_array) {
$output = $this->state;
} else {
$output = json_encode($this->state, JSON_UNESCAPED_UNICODE);
}
return $output;
}
/**
* 转成数组
*/
public function toArray()
{
if (is_array($this->state)) {
return $this->state;
}
return json_decode($this->state, true);
}
}

292
extend/encryption/Rsa.php Normal file
View File

@@ -0,0 +1,292 @@
<?php
namespace encryption;
class Rsa
{
const SIGN_CERT_PWD = '123456'; // 签名证书密码
private $public_key; // 公钥
private $private_key; // 私钥
private $cert_save_path = ''; // 证书文件保存目录
private $openssl_cnf_path = '/etc/ssl/openssl.cnf';
protected static $rsa;
/**
* 设私有构造方法防止对象被new
*/
private function __construct($config)
{
if ($config['public'] && $config['private']) {
if (is_file($config['public'])) {
$this->public_key = $this->getPublicKey($config['public']);
} else {
$this->public_key = openssl_pkey_get_public($config['public']);
if (!$this->public_key) {
die('请检查公钥是否正确');
}
}
if (is_file($config['private'])) {
$this->private_key = $this->getPrivateKey($config['private']);
} else {
$this->private_key = openssl_pkey_get_private($config['private']);
if (!$this->private_key) {
die('请检查私钥是否正确');
}
}
}
if ($config['cert_save_path']) $this->cert_save_path = $config['cert_save_path'];
}
/**
* 单例获取对象实例
*/
public static function instance($config=array())
{
if (!self::$rsa) {
if (empty($config)) {
$config = [
'public' => '',
'private' => '',
'cert_save_path' => ''
];
}
self::$rsa = new self($config);
}
return self::$rsa;
}
/**
* 读取文件
*/
private function fileGet($file)
{
if (!file_exists($file)) {
die($file . "不是文件");
}
return file_get_contents($file);
}
/**
* 获取公钥
*/
private function getPublicKey($path)
{
$pubKey = $this->fileGet($path);
if (!$pubKey) {
die('请检查公钥路径是否正确');
}
return openssl_pkey_get_public($pubKey);
}
/**
* 获取私钥
*/
private function getPrivateKey($path)
{
$priKey = $this->fileGet($path);
if (!$priKey) {
die('请检查私钥路径是否正确');
}
return openssl_pkey_get_private($priKey);
}
/**
* 使用私钥生成签名
* @param string 用于生成签名的字符串
* @return string 签名后的值
*/
public function signPrivate($data)
{
$signature = false;
if (openssl_sign($data, $signature, $this->private_key)) {
$signature = base64_encode($signature);
}
return $signature;
}
/**
* 使用公钥验签
* @param $data = array('data'='','signature'='')
* @return bool
*/
public function verifyPublic($data)
{
$bool = false;
if (!$data['data'] || !$data['signature']) {
die('数据源或签名值为出错!');
}
$result = base64_decode($data['signature']);
switch (openssl_verify($data['data'], $result, $this->public_key)) {
case 1:
$bool = true;
break;
case 0:
case -1:
$bool = false;
break;
default:
$bool = false;
}
return $bool;
}
/**
* 使用公钥加密
*/
public function encryptPublic($data)
{
$crypted = false;
if (is_array($data)) {
$data = json_encode($data);
}
$cryp = strrev($data); // 将数据前后反转再加密
if (openssl_public_encrypt($cryp, $crypted, $this->public_key, OPENSSL_PKCS1_PADDING)) {
$crypted = base64_encode($crypted);
}
return $crypted;
}
/**
* 使用公钥分块加密
*/
public function chunkEncryptPublic($data, $limit = 117)
{
if (is_array($data)) {
$data = json_encode($data);
}
$datas = str_split($data, $limit);
$crypteds = [];
foreach ($datas as $key => $val) {
$crypteds[$key] = $this->encryptPublic($val);
}
return $crypteds;
}
/**
* 使用私钥解密
*/
public function decryptPrivate($encrypted)
{
$decrypted = '';
$data = base64_decode($encrypted);
if (openssl_private_decrypt($data, $decrypted, $this->private_key, OPENSSL_PKCS1_PADDING)) {
$decrypted = rtrim(strrev($decrypted), '\0');
}
return $decrypted;
}
/**
* 使用私钥分块解密
*/
public function chunkDecryptPrivate($encrypteds)
{
$decrypted = '';
$encrypteds = htmlspecialchars_decode($encrypteds);
if (!is_array($encrypteds)) {
$arr = explode(',', $encrypteds);
if ($arr) {
$encrypteds = $arr;
} else {
return json_decode($this->decryptPrivate($encrypteds));
}
}
foreach ($encrypteds as $key => $val) {
$decrypted .= $this->decryptPrivate($val);
}
$res = json_decode($decrypted, true);
if (is_array($res)) return $res;
return json_decode($res, true);
}
/**
* 生成公私钥
*/
public function createRsa($writeFile=false)
{
$config = array("config" => $this->openssl_cnf_path);
// 生成公私钥
$privkey = openssl_pkey_new($config);
openssl_pkey_export($privkey, $private_key, null, $config); // 导出私钥$private_key
$public_key = openssl_pkey_get_details($privkey); // 导出公钥$public_key
if (!$writeFile) {
return array('private_key' => $private_key, 'public_key' => $public_key['key']);
}
$publicPath = $this->cert_save_path . "rsa_public_key.pem"; // 生成公钥路径
$privatePath = $this->cert_save_path . "rsa_private_key.pem"; // 私钥文件路径
// 生成公钥文件
if (!is_writeable($publicPath)) {
chmod($publicPath, 0755);
}
$fp = fopen($publicPath, "w");
fwrite($fp, $public_key['key']);
fclose($fp);
// 生成私钥文件
if (!is_writeable($privatePath)) {
chmod($privatePath, 0755);
}
$fp = fopen($privatePath, "w");
fwrite($fp, $private_key);
fclose($fp);
}
/**
* 生成证书和密钥
*/
public function createCsr($writeFile=false)
{
$dn = array(
"countryName" => 'CN', // 所在国家名称
"stateOrProvinceName" => 'Hunan', // 所在省份名称
"localityName" => 'Changsha', // 所在城市名称
"organizationName" => 'opd', // 注册人姓名
"organizationalUnitName" => 'orico_opd', // 组织名称
"commonName" => 'orico_opd', // 公共名称
"emailAddress" => '735273025@qq.com' // 邮箱
);
$privkeypass = self::SIGN_CERT_PWD; //私钥密码
$config = array(
"config" => $this->openssl_cnf_path,
"digest_alg" => "sha1",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_DSA,
"encrypt_key" => false
);
$privkey = openssl_pkey_new($config);
$csr = openssl_csr_new($dn, $privkey, $config);
$sscert = openssl_csr_sign($csr, null, $privkey, 365, $config);
openssl_x509_export($sscert, $csrkey); // 导出证书$csrkey 
openssl_pkcs12_export($sscert, $private_key, $privkey, $privkeypass); // 导出密钥$private_key 
if (!$writeFile) {
return array('csrkey' => $csrkey, 'private_key_pfx' => $private_key);
}
$cerpath = $this->cert_save_path . "csrkey.cer"; // 生成证书路径
$pfxpath = $this->cert_save_path . "private_key.pfx"; // 密钥文件路径
// 生成证书文件
if (!is_writeable($cerpath)) {
chmod($cerpath, 0755);
}
$fp = fopen($cerpath, "w");
fwrite($fp, $csrkey);
fclose($fp);
// 生成密钥文件
if (!is_writeable($pfxpath)) {
chmod($pfxpath, 0755);
}
$fp = fopen($pfxpath, "w");
fwrite($fp, $private_key);
fclose($fp);
}
}

View File

View File

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCb4X1wN9ret271gWrsXlSymFinlheMUz2kIsyKREtlfE7ajH6Bm5bEFxe/HnCDxh37ekUJq4JMeU1awJgYA4mlvaQ67DSf4/AZWk2Dg8TLQaE7VeBJoPadDyciIwuFhcWB2lfD62KRlbXqgjfji7IjZE4ysVB99DofpJ9c9xd0NtzzuBNjgxh+f6BsegsIkqLIpKGIONePrY9cCj+vOMaRdUgWczQOftWXtJXiRz5kUxwV6CSQQbbPFEySStlW2WMBF15FyeOTXERvpgOoP4O+j/0GMW3R+4dKUxFY+SPrmMMB6HmJgySgCIVuwlUJyLlmhZuaIumryyJ19Yc+jB0ZAgMBAAECggEAA82hoN5MAckZ9PV+ZVv71wuuhhA8bNkPFPUgZnM6H7TDu8NXxtPD/u7NToVwm1Z3Hs6h5pV/rVv8Ti2KyHM1pYyV+DYRJqGKFyRD5eSOJaNpSqhSu5koyzY2bzfJrS1RX1SzdX1Fv2WWta0NrgiXhROKcOQwoZIW5PTASqVgth4iztEmseuU9pgh/dnMOXCLGmGB26Z2HTnuV0tHC4/goMmsi0eGOy40VEBxPL0JJegWM+teRSbSWGzOfTu95wnpIr66FT0aOVdHVuhMTYYHUfbEh/lnCZExfqjNfEVuSLB+jHBeLfFXdg3TY/svVizXJ4SvzI1EzM6Y145zMTdk8QKBgQDPKT1BQHWIPyXs3IzqDpmP9koBOm03rZZ46CneGLFj9vRAeK9awAto6aqH/WHLb48fSK5LQteWf+rvRiqoPjansoFvzh1dgk+0FON1jUNk4LlwOVEL38WhD98cJ/SwOVdwsYfYySb2pwcZ3K4gdTmax264XFRNGU8Ar4l17NHVtQKBgQDAoVcyWHtpJ6Pk4CuOSWt1S5y1km1dAudJfRAyYlk+9G6Qq1zSoScio/1T/TAS58KHcoSV0SmGu9heF2p02ov4MzsFr//cnBh2Y3aiTnvnyxehp5V0M9CLy524gHDANHO3BxCjlV6tMFqvUuhJfurN36KarYvLJjGU3MoWLPiIVQKBgQCfuWozP2HfdODm6X30kOExsHGK1OOUxFB6VHbDeUJ8ktHsS6DYVa32bANB6slWM2OkeUN5X8Ydke8wIWeOZLl7PxSxgbf+exS9AkYGQq6tvt77ehXVE+mqrzQU33G31d5nU1oseZuDHXFKLW5MNFX/0ri5iOaus07Ido+Dzt5S6QKBgQCef+MEnQOmyaQ0vjN4l3bhA79FM9/i6XGv6UT7gCsY/v0+8pGlCk7uVOssQadd1u8cjiEEOR+nKkdlXHUPA4iqSFUnX+Ly/kmumDbUZCnV1ylxrTfvbncJoBf96UldDYutLRYG0/AcJy5nFpvlZZYEoh/kkCn3lUy0xVtPFd/GJQKBgQCDfPkSHDxyeSZQ2xlj+4xqIGfGLbK/ijO9AuC7rSnWE2bYD1cseItVK6O0nZ0Gw4vmmvHDF9Z6yxCobIzyf6fUExvVRm3oMwjSrjVWfycTKaUUb7CHAU6JH8zQ1DPsTQ+F+W+q1RZZCj1qUBO9cHe9xIPWoK6PN829/UmZAzJeyg==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm+F9cDfa3rdu9YFq7F5UsphYp5YXjFM9pCLMikRLZXxO2ox+gZuWxBcXvx5wg8Yd+3pFCauCTHlNWsCYGAOJpb2kOuw0n+PwGVpNg4PEy0GhO1XgSaD2nQ8nIiMLhYXFgdpXw+tikZW16oI344uyI2ROMrFQffQ6H6SfXPcXdDbc87gTY4MYfn+gbHoLCJKiyKShiDjXj62PXAo/rzjGkXVIFnM0Dn7Vl7SV4kc+ZFMcFegkkEG2zxRMkkrZVtljARdeRcnjk1xEb6YDqD+Dvo/9BjFt0fuHSlMRWPkj65jDAeh5iYMkoAiFbsJVCci5ZoWbmiLpq8sidfWHPowdGQIDAQAB
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,89 @@
<?php
/*
Sitemap Generator by Slava Knyazev. Further acknowledgements in the README.md file.
Website: https://www.knyz.org/
I also live on GitHub: https://github.com/knyzorg
Contact me: Slava@KNYZ.org
*/
//Make sure to use the latest revision by downloading from github: https://github.com/knyzorg/Sitemap-Generator-Crawler
/* Usage
Usage is pretty strait forward:
- Configure the crawler by editing this file.
- Select the file to which the sitemap will be saved
- Select URL to crawl
- Configure blacklists, accepts the use of wildcards (example: http://example.com/private/* and *.jpg)
- Generate sitemap
- Either send a GET request to this script or run it from the command line (refer to README file)
- Submit to Google
- Setup a CRON Job execute this script every so often
It is recommended you don't remove the above for future reference.
*/
// Default site to crawl
$site = "https://www.knyz.org/";
// Default sitemap filename
$file = "sitemap.xml";
$permissions = 0644;
// Depth of the crawl, 0 is unlimited
$max_depth = 0;
// Show changefreq
$enable_frequency = false;
// Show priority
$enable_priority = false;
// Default values for changefreq and priority
$freq = "daily";
$priority = "1";
// Add lastmod based on server response. Unreliable and disabled by default.
$enable_modified = false;
// Disable this for misconfigured, but tolerable SSL server.
$curl_validate_certificate = true;
// The pages will be excluded from crawl and sitemap.
// Use for exluding non-html files to increase performance and save bandwidth.
$blacklist = array(
"*.jpg",
"*/secrets/*",
"https://www.knyz.org/supersecret"
);
// Enable this if your site do requires GET arguments to function
$ignore_arguments = false;
// Not yet implemented. See issue #19 for more information.
$index_img = false;
//Index PDFs
$index_pdf = true;
// Set the user agent for crawler
$crawler_user_agent = "Mozilla/5.0 (compatible; Sitemap Generator Crawler; +https://github.com/knyzorg/Sitemap-Generator-Crawler)";
// Header of the sitemap.xml
$xmlheader ='<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">';
// Optionally configure debug options
$debug = array(
"add" => true,
"reject" => false,
"warn" => false
);
//Modify only if configuration version is broken
$version_config = 2;

View File

@@ -0,0 +1,426 @@
<?php
// Abstracted function to output formatted logging
function logger($message, $type)
{
global $debug, $color;
if ($color) {
switch ($type) {
case 0:
//add
echo $debug["add"] ? "\033[0;32m [+] $message \033[0m\n" : "";
break;
case 1:
//reject
echo $debug["reject"] ? "\033[0;31m [-] $message \033[0m\n" : "";
break;
case 2:
//manipulate
echo $debug["warn"] ? "\033[1;33m [!] $message \033[0m\n" : "";
break;
case 3:
//critical
echo "\033[1;33m [!] $message \033[0m\n";
break;
}
return;
}
switch ($type) {
case 0:
//add
echo $debug["add"] ? "[+] $message\n" : "";
break;
case 1:
//reject
echo $debug["reject"] ? "31m [-] $message\n" : "";
break;
case 2:
//manipulate
echo $debug["warn"] ? "[!] $message\n" : "";
break;
case 3:
//critical
echo "[!] $message\n";
break;
}
}
function flatten_url($url)
{
global $real_site;
$path = explode($real_site, $url)[1];
return $real_site . remove_dot_seg($path);
}
/**
* Remove dot segments from a URI path according to RFC3986 Section 5.2.4
*
* @param $path
* @return string
* @link http://www.ietf.org/rfc/rfc3986.txt
*/
function remove_dot_seg($path)
{
if (strpos($path, '.') === false) {
return $path;
}
$inputBuffer = $path;
$outputStack = [];
/**
* 2. While the input buffer is not empty, loop as follows:
*/
while ($inputBuffer != '') {
/**
* A. If the input buffer begins with a prefix of "../" or "./",
* then remove that prefix from the input buffer; otherwise,
*/
if (strpos($inputBuffer, "./") === 0) {
$inputBuffer = substr($inputBuffer, 2);
continue;
}
if (strpos($inputBuffer, "../") === 0) {
$inputBuffer = substr($inputBuffer, 3);
continue;
}
/**
* B. if the input buffer begins with a prefix of "/./" or "/.",
* where "." is a complete path segment, then replace that
* prefix with "/" in the input buffer; otherwise,
*/
if ($inputBuffer === "/.") {
$outputStack[] = '/';
break;
}
if (substr($inputBuffer, 0, 3) === "/./") {
$inputBuffer = substr($inputBuffer, 2);
continue;
}
/**
* C. if the input buffer begins with a prefix of "/../" or "/..",
* where ".." is a complete path segment, then replace that
* prefix with "/" in the input buffer and remove the last
* segment and its preceding "/" (if any) from the output
* buffer; otherwise,
*/
if ($inputBuffer === "/..") {
array_pop($outputStack);
$outputStack[] = '/';
break;
}
if (substr($inputBuffer, 0, 4) === "/../") {
array_pop($outputStack);
$inputBuffer = substr($inputBuffer, 3);
continue;
}
/**
* D. if the input buffer consists only of "." or "..", then remove
* that from the input buffer; otherwise,
*/
if ($inputBuffer === '.' || $inputBuffer === '..') {
break;
}
/**
* E. move the first path segment in the input buffer to the end of
* the output buffer, including the initial "/" character (if
* any) and any subsequent characters up to, but not including,
* the next "/" character or the end of the input buffer.
*/
if (($slashPos = stripos($inputBuffer, '/', 1)) === false) {
$outputStack[] = $inputBuffer;
break;
} else {
$outputStack[] = substr($inputBuffer, 0, $slashPos);
$inputBuffer = substr($inputBuffer, $slashPos);
}
}
return ltrim(implode($outputStack), "/");
}
// Check if a URL has already been scanned
function is_scanned($url)
{
global $scanned;
if (isset($scanned[$url])) {
return true;
}
//Check if in array as dir and non-dir
$url = ends_with($url, "/") ? substr($url, 0, -1) : $url . "/";
if (isset($scanned[$url])) {
return true;
}
return false;
}
function ends_with($haystack, $needle)
{
$length = strlen($needle);
if ($length == 0) {
return true;
}
return (substr($haystack, -$length) === $needle);
}
// Gets path for a relative linl
// https://somewebsite.com/directory/file => https://somewebsite.com/directory/
// https://somewebsite.com/directory/subdir/ => https://somewebsite.com/directory/subdir/
function get_path($path)
{
$path_depth = explode("/", $path);
$len = strlen($path_depth[count($path_depth) - 1]);
return (substr($path, 0, strlen($path) - $len));
}
//Get the root of the domain
function domain_root($href)
{
$url_parts = explode('/', $href);
return $url_parts[0] . '//' . $url_parts[2] . '/';
}
//The curl client is create outside of the function to avoid re-creating it for performance reasons
$curl_client = curl_init();
function get_data($url)
{
global $curl_validate_certificate, $curl_client, $index_pdf, $crawler_user_agent, $enable_modified;
//Set URL
curl_setopt($curl_client, CURLOPT_URL, $url);
//Follow redirects and get new url
curl_setopt($curl_client, CURLOPT_RETURNTRANSFER, 1);
//Get headers
curl_setopt($curl_client, CURLOPT_HEADER, 1);
//Optionally avoid validating SSL
curl_setopt($curl_client, CURLOPT_SSL_VERIFYPEER, $curl_validate_certificate);
//Set user agent
curl_setopt($curl_client, CURLOPT_USERAGENT, $crawler_user_agent);
//Get data
$data = curl_exec($curl_client);
$content_type = curl_getinfo($curl_client, CURLINFO_CONTENT_TYPE);
$http_code = curl_getinfo($curl_client, CURLINFO_HTTP_CODE);
$redirect_url = curl_getinfo($curl_client, CURLINFO_REDIRECT_URL);
//Scan new url, if redirect
if ($redirect_url) {
logger("URL is a redirect.", 1);
if (strpos($redirect_url, '?') !== false) {
$redirect_url = explode($redirect_url, "?")[0];
}
unset($url, $data);
if (!check_blacklist($redirect_url)) {
echo logger("Redirected URL is in blacklist", 1);
} else {
scan_url($redirect_url);
}
}
//If content acceptable, return it. If not, `false`
$html = ($http_code != 200 || (!stripos($content_type, "html"))) ? false : $data;
//Additional data
if ($enable_modified){
curl_setopt($curl_client, CURLOPT_FILETIME, true);
$timestamp = curl_getinfo($curl_client, CURLINFO_FILETIME);
$modified = ($timestamp != -1) ? date('c', $timestamp) : null;
}
else $modified = null;
if (stripos($content_type, "application/pdf") !== false && $index_pdf) {
$html = "This is a PDF";
}
//Return it as an array
return array($html, $modified, (stripos($content_type, "image/") && $index_img));
}
//Try to match string against blacklist
function check_blacklist($string)
{
global $blacklist;
if (is_array($blacklist)) {
foreach ($blacklist as $illegal) {
if (fnmatch($illegal, $string)) {
return false;
}
}
}
return true;
}
//Extract array of URLs from html document inside of `href`s
function get_links($html, $parent_url, $regexp)
{
if (preg_match_all("/$regexp/siU", $html, $matches)) {
if ($matches[2]) {
$found = array_map(function ($href) use (&$parent_url) {
global $real_site, $ignore_arguments;
logger("Checking $href", 2);
if (strpos($href, "#") !== false) {
logger("Dropping pound.", 2);
$href = preg_replace('/\#.*/', '', $href);
}
//Seperate $href from $query_string
$query_string = '';
if (strpos($href, '?') !== false) {
list($href, $query_string) = explode('?', $href);
//Parse &amp to not break curl client. See issue #23
$query_string = str_replace('&amp;', '&', $query_string);
}
if ($ignore_arguments) {
$query_string = '';
}
if (strpos($href, '?') !== false) {
echo "EFEASDEFSED";
}
if ((substr($href, 0, 7) != "http://") && (substr($href, 0, 8) != "https://")) {
// Link does not call (potentially) external page
if (strpos($href, ":")) {
logger("URL is an invalid protocol", 1);
return false;
}
if ($href == '/') {
logger("$href is domain root", 2);
$href = $real_site;
} elseif (substr($href, 0, 1) == '/') {
logger("$href is relative to root, convert to absolute", 2);
$href = domain_root($real_site) . substr($href, 1);
} else {
logger("$href is relative, convert to absolute", 2);
$href = get_path($parent_url) . $href;
}
}
logger("Result: $href", 2);
if (!filter_var($href, FILTER_VALIDATE_URL)) {
logger("URL is not valid. Rejecting.", 1);
return false;
}
if (substr($href, 0, strlen($real_site)) != $real_site) {
logger("URL is not part of the target domain. Rejecting.", 1);
return false;
}
if (is_scanned($href . ($query_string ? '?' . $query_string : ''))) {
//logger("URL has already been scanned. Rejecting.", 1);
return false;
}
if (!check_blacklist($href)) {
logger("URL is blacklisted. Rejecting.", 1);
return false;
}
return flatten_url($href . ($query_string ? '?' . $query_string : ''));
}, $matches[2]);
return $found;
}
}
logger("Found nothing", 2);
return array();
}
function scan_url($url)
{
global $scanned, $deferredLinks, $file_stream, $freq, $priority, $enable_priority, $enable_frequency, $max_depth, $depth, $real_site, $indexed;
$depth++;
logger("Scanning $url", 2);
if (is_scanned($url)) {
logger("URL has already been scanned. Rejecting.", 1);
return $depth--;
}
if (substr($url, 0, strlen($real_site)) != $real_site) {
logger("URL is not part of the target domain. Rejecting.", 1);
return $depth--;
}
if (!($depth <= $max_depth || $max_depth == 0)) {
logger("Maximum depth exceeded. Rejecting.", 1);
return $depth--;
}
//Note that URL has been scanned
$scanned[$url] = 1;
//Send cURL request
list($html, $modified, $is_image) = get_data($url);
if ($is_image) {
//Url is an image
}
if (!$html) {
logger("Invalid Document. Rejecting.", 1);
return $depth--;
}
if (strpos($url, "&") && strpos($url, ";") === false) {
$url = str_replace("&", "&amp;", $url);
}
$map_row = "<url>\n";
$map_row .= "<loc>$url</loc>\n";
if ($enable_frequency) {
$map_row .= "<changefreq>$freq</changefreq>\n";
}
if ($enable_priority) {
$map_row .= "<priority>$priority</priority>\n";
}
if ($modified) {
$map_row .= " <lastmod>$modified</lastmod>\n";
}
$map_row .= "</url>\n";
fwrite($file_stream, $map_row);
$indexed++;
logger("Added: " . $url . (($modified) ? " [Modified: " . $modified . "]" : ''), 0);
unset($is_image, $map_row);
// Extract urls from <a href="??"></a>
$ahrefs = get_links($html, $url, "<a\s[^>]*href=(\"|'??)([^\" >]*?)\\1[^>]*>(.*)<\/a>");
// Extract urls from <frame src="??">
$framesrc = get_links($html, $url, "<frame\s[^>]*src=(\"|'??)([^\" >]*?)\\1[^>]*>");
$links = array_filter(array_merge($ahrefs, $framesrc), function ($item) use (&$deferredLinks) {
return $item && !isset($deferredLinks[$item]);
});
unset($html, $url, $ahrefs, $framesrc);
logger("Found urls: " . join(", ", $links), 2);
//Note that URL has been deferred
foreach ($links as $href) {
if ($href) {
$deferredLinks[$href] = 1;
}
}
foreach ($links as $href) {
if ($href) {
scan_url($href);
}
}
$depth--;
}
// fnmatch() filler for non-POSIX systems
if (!function_exists('fnmatch')) {
function fnmatch($pattern, $string)
{
return preg_match("#^" . strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.')) . "$#i", $string);
} // end
} // end if
$version_functions = 2;

128
extend/sitemap/sitemap.php Normal file
View File

@@ -0,0 +1,128 @@
<?php
/***************************\
|***DO NOT EDIT THIS FILE***|
|**EDIT sitemap.config.php**|
\***************************/
error_reporting(E_ALL);
//Read global variables from config file
require_once( 'sitemap.config.php' );
// Include all functions
require_once('sitemap.functions.php');
//Default html header makes browsers ignore \n
header("Content-Type: text/plain");
$color = false;
$version_script = 2;
if ($version_script != $version_functions || $version_functions != $version_config){
logger("Script versions mismatch!",3);
logger("Update necessary",3);
logger("Version of sitemap.functions.php " .$version_functions ,3);
logger("Version of sitemap.config.php " .$version_config ,3);
logger("Version of sitemap.php " .$version_script ,3);
logger("Download new files here: https://www.github.com/knyzorg/sitemap-generator-crawler" ,3);
die("Stopped.");
}
// Add PHP CLI support
if (php_sapi_name() === 'cli' && PHP_OS != 'WINNT') {
parse_str(implode('&', array_slice($argv, 1)), $args);
$color = true;
}
//Allow variable overloading with CLI
if (isset($args['file'])) {
$file = $args['file'];
}
if (isset($args['site'])) {
$site = $args['site'];
}
if (isset($args['max_depth'])) {
$max_depth = $args['max_depth'];
}
if (isset($args['enable_frequency'])) {
$enable_frequency = $args['enable_frequency'];
}
if (isset($args['enable_priority'])) {
$enable_priority = $args['enable_priority'];
}
if (isset($args['enable_modified'])) {
$enable_modified = $args['enable_modified'];
}
if (isset($args['freq'])) {
$freq = $args['freq'];
}
if (isset($args['priority'])) {
$priority = $args['priority'];
}
if (isset($args['blacklist'])) {
$blacklist = $args['blacklist'];
}
if (isset($args['debug'])) {
$debug = $args['debug'];
}
if (isset($args['ignore_arguments'])) {
$ignore_arguments = !!$args['ignore_arguments'];
}
if (isset($args['pdf_index'])) {
$pdf_index = $args['pdf_index'];
}
//Begin stopwatch for statistics
$start = microtime(true);
//Setup file stream
$tempfile = tempnam(sys_get_temp_dir(), 'sitemap.xml.');
$file_stream = fopen($tempfile, "w") or die("Error: Could not create temporary file $tempfile" . "\n");
fwrite($file_stream, $xmlheader);
// Global variable, non-user defined
$depth = 0;
$indexed = 0;
$scanned = array();
$deferredLinks = array();
// Reduce domain to root in case of monkey
$real_site = domain_root($site);
if ($real_site != $site){
logger("Reformatted site from $site to $real_site", 2);
}
// Begin by crawling the original url
scan_url($real_site);
// Finalize sitemap
fwrite($file_stream, "</urlset>\n");
fclose($file_stream);
// Pretty-print sitemap
if ((PHP_OS == 'WINNT') ? `where xmllint` : `which xmllint`) {
logger("Found xmllint, pretty-printing sitemap", 0);
$responsevalue = exec('xmllint --format ' . $tempfile . ' -o ' . $tempfile . ' 2>&1', $discardedoutputvalue, $returnvalue);
if ($returnvalue) {
die("Error: " . $responsevalue . "\n");
}
}
// Generate and print out statistics
$time_elapsed_secs = round(microtime(true) - $start, 2);
logger("Sitemap has been generated in " . $time_elapsed_secs . " second" . (($time_elapsed_secs >= 1 ? 's' : '') . "and saved to $file"), 0);
$size = sizeof($scanned);
logger("Scanned a total of $size pages and indexed $indexed pages.", 0);
// Rename partial file to the real file name. `rename()` overwrites any existing files
rename($tempfile, $file);
// Apply permissions
chmod($file, $permissions);
// Declare that the script has finished executing and exit
logger("Operation Completed", 0);

115
extend/sso/SSOClient.php Normal file
View File

@@ -0,0 +1,115 @@
<?php
namespace sso;
use apiRes\Api;
use think\facade\Session;
class SSOClient
{
private $sso_url = '';
private $app_id = '';
private $app_secret = '';
private $public_ssl_key = '';
private $private_ssl_key = '';
public function __construct($data)
{
// $this->sso_url = config('sso.sso_url');
// if (config('sso.app_id')) $this->app_id = config('sso.app_id');
// if (config('sso.app_secret')) $this->app_secret = config('sso.app_secret');
// if (config('sso.public_key')) $this->public_ssl_key = config('sso.public_key');
// if (config('sso.private_key')) $this->private_ssl_key = config('sso.private_key');
$this->app_id = $data['app_id'];
$this->sso_url = $data['sso_url'];
$this->app_secret = $data['app_secret'];
$this->public_ssl_key = $data['public_key'];
$this->private_ssl_key = $data['private_key'];
}
/**
* 校验code,返回access_token
*/
public function getAccessToken($code)
{
if (empty($code)) {
return Api::error('参数code不能为空', [], true);
}
$data = [
'app_id' => $this->app_id,
'app_secret' => $this->app_secret,
'code' => $code
];
return $this->http('authorize/access_token', $data, true, ['cookie'=>'PHPSESSID=' . Session::getId()]);
}
/**
* 刷新access_token
*/
public function refreshToken($refresh_token, $sessid='')
{
if (empty($refresh_token)) {
return Api::error('参数refresh_token不能为空', [], true);
}
$data = [
'app_id' => $this->app_id,
'app_secret' => $this->app_secret,
'refresh_token' => $refresh_token
];
return $this->http('authorize/refresh_token', $data, true, ['cookie'=>'PHPSESSID=' . $sessid]);
}
/**
* 通知sso退出登录
*/
public function notifySSOServerSigninOut($access_token, $sessid='')
{
if (empty($access_token)) {
return Api::error('参数access_token不能为空', [], true);
}
$data = [
'app_id' => $this->app_id,
'app_secret' => $this->app_secret,
'access_token' => $access_token,
];
if (empty($sessid)) {
$sessid = Session::getId();
}
return $this->http('authorize/signin_out', $data, true, ['cookie'=>'PHPSESSID=' . $sessid]);
}
/**
* 请求数据
*/
private function http($url, $data, $is_post=false, $options=[])
{
$data = json_encode($data);
$headerArray = [
'Content-type: application/json;charset=utf-8',
'Accept: application/json'
];
$url = $this->sso_url . $url;
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,FALSE);
if ($is_post) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl,CURLOPT_HTTPHEADER,$headerArray);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
if (!empty($options['cookie'])) {
curl_setopt($curl, CURLOPT_COOKIE, $options['cookie']);
}
$output = curl_exec($curl);
$http_status = curl_errno($curl);
$http_msg = curl_error($curl);
curl_close($curl);
if ($http_status == 0) {
return json_decode($output, true);
} else {
return ['errno' => $http_status, 'errmsg' => $http_msg, 'data' => []];
}
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace storage;
use FceskyUpload\contract\FileInfo;
use FceskyUpload\UploadManager;
use FceskyUpload\drivers\LocalDriver;
class LocalUploader
{
private $config = [];
private $domain = "";
private $_error = "";
public function __construct($conf = [])
{
$this->config = [
'disks' => [
'local' => [
'driver' => LocalDriver::class, //上传类
'save_dir' => app()->getRootPath() . 'public/attachment', //上传保存路径
'file_path' => '/attachment', //上传保存路径
'tmp_dir' => root_path('runtime' . DIRECTORY_SEPARATOR . 'uploadtmp'),//临时目录
'slice_first' => 0, //从第几片开始
],
//继续添加其它仓库
],
'default' => [
'disk' => 'local', //默认磁盘
'extensions' => 'jpg,png,mp4', //后缀
'mimeTypes' => 'image/*,video/*', //类型
'middlewares' => [], //中间件数组
'maxSize' => 1 * 1024 * 1024, //单包最大上传大小 2M
]
];
}
/**文件上传
* @param $data
* @param $file
* @return array
*/
public function uploadFile($data, $file)
{
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit; // finish preflight CORS requests here
}
try {
//处理部分数据
$data['size'] = $file->getSize();
$data['name'] = $file->getOriginalName();
$data['ext'] = $file->extension();
//检测文件大小
if (!$this->checkSize($data['size'])) {
$this->_error = "超出文件上传大小限制";
return ['code' => -1, 'data' => []];
}
$config = $this->config;
$uploadManager = new UploadManager($config);
$fileInfo = new FileInfo();
$fileInfo->identifier = $data['hash'];
$fileInfo->filename = $data['name'];
$fileInfo->chunkNumber = $data['chunk'] ?? 0;;
$fileInfo->chunkNumber = intval($fileInfo->chunkNumber ?? 0);
//未设置分片数量 则默认总数为1 当前为0
$fileInfo->totalChunks = $data['chunks'] ?? 1;
$fileInfo->totalChunks = intval($fileInfo->totalChunks ?? 1);
$fileInfo->totalChunks = ($fileInfo->totalChunks > 0) ? $fileInfo->totalChunks : 1;
$fileInfo->totalSize = $data['size'];
//获取后缀
$fileInfo->ext = $data['ext'];
if (!$this->checkFileType($fileInfo->ext)) {
$this->_error = "禁止上传的文件类型";
return ['code' => -1, 'data' => []];
}
$upload = $uploadManager->uploadFile($fileInfo);
//如果文件较小 直接上传完成
if (isset($upload['savepath']) && $upload['code'] == 0) {
$upload = $this->buildFileUrl($upload);
return ['code' => 1, 'data' => $upload, 'msg' => '成功'];
}
$check = $uploadManager->checkFile($fileInfo);
//如果上传完成
if (isset($check['savepath']) && $check['code'] == 0) {
$check = $this->buildFileUrl($check);
return ['code' => 1, 'data' => $check];
} else {
$this->_error = "文件包不完整";
return ['code' => 0, 'data' => $check];
}
} catch (\Exception $e) {
$this->_error = $e->getMessage();
return ['code' => -1, 'data' => $e->getMessage()];
}
}
/**
* 处理文件
* 扩展包存在问题 ,考虑后续更新 尽量不更改扩展包,在这里做文件的处理
*/
public function buildFileUrl($data)
{
if (isset($data['savepath']) && $data['savepath'] != '') {
$conf = $this->config;
//获取除域名外的文件路径
$save_dir = $conf['disks'][$conf['default']['disk']]['save_dir'];
$file_path = str_replace($save_dir, '', $data['savepath']);
//拼接域名
$data['save_path'] = $conf['disks'][$conf['default']['disk']]['file_path'] . $file_path;
$data['file_path'] = $this->domain . $data['save_path'];
//删除多余的信息
unset($data['savepath']);
unset($data['code']);
}
return $data;
}
//检测文件上传大小
public function checkSize($file_size)
{
if ($file_size > $this->config['default']['maxSize']) {
return false;
}
return true;
}
/**检测文件类型
* @param $file_ext
* @return array|bool
*/
public function checkFileType($file_ext)
{ //获取配置信息
$extensions = $this->config['default']['extensions'];
//为空或者为 * 表示不过滤
if ($extensions == "" || $extensions == '*') {
return true;
}
//字符转换成数组
$exts = explode(',', $extensions);
if (in_array($file_ext, $exts)) {
$this->_error = "禁止上传该类型文件";
return ['code' => 0, 'data' => ''];
}
return true;
}
/**
* 检测文件分片情况
*/
public function checkFile($hash,$count){
$filePath = $this->config['disks'][$this->config['default']['disk']]['tmp_dir'] . $hash; //临时分片文件路径
// dump($filePath);
//检查分片是否存在
$chunkExists = [];
for ($index = 0; $index < $count; $index++) {
if (file_exists("{$filePath}_{$index}")) {
array_push($chunkExists, $index);
}
}
//分片有缺失,返回已存在的分片
$res['uploaded'] = $chunkExists;
return $res;
}
/**
* 删除文件
* @param $path
*/
public function deleteFile($path){
$path = app()->getRootPath() . 'public'.$path;
if (file_exists($path)){
@unlink($path);
}
return true;
}
/**
* 返回报错
* @return string
*/
public function getError()
{
return $this->_error;
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace storage;
use Qiniu\Auth;
use Qiniu\Storage\UploadManager;
use Qiniu\Storage\BucketManager;
class QiniuUploader
{
private $bucket = 'orico-opd';
private $accessKey = 'dOsTum4a5qvhPTBbZRPX0pIOU7PZWRX7htKjztms';
private $secretKey = 'KFxsGbnErkALFfeGdMa8QWTdodJbamMX0iznLe-q';
private $rule = [
'fileSize' => 1024 * 1024 * 5, // 默认最大上传5M
'fileExt' => 'jpeg,jpg,png', // 默认上传文件后缀
'fileMime' => 'image/jpeg,image/png,image/gif' // 默认上传文件mime
];
private $dir = true;
private $originalName = false;
private $pathPrefix = '';
private $fileNamePrefix = 'orico';
static public $domain = 'http://opdfile.f2b211.com/';
public function __construct($conf = [])
{
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;
}
/**
* 上传文件到七牛云
*/
public function uploadFile($name)
{
// 构建鉴权对象
$auth = new Auth($this->accessKey, $this->secretKey);
// 生成上传 Token
$token = $auth->uploadToken($this->bucket);
// 初始化 UploadManager 对象并进行文件的上传。
$uploadMgr = new UploadManager();
$file = request()->file($name);
$aspectRatio = [];
if (!empty($this->rule['aspectRatio'])) {
$aspectRatio = $this->rule['aspectRatio'];
unset($this->rule['aspectRatio']);
}
$validate = validate([$name => $this->rule]);
if (!$validate->check([$name => $file])) {
throw new \Exception($validate->getError());
}
$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' => self::$domain . $ret['key']];
}
}
/**
* 上传文件到七牛云
*/
public function deleteFile($name)
{
// 构建鉴权对象
$auth = new Auth($this->accessKey, $this->secretKey);
// 初始化 BucketManager 对象并进行文件的删除。
$bucketManager = new BucketManager($auth);
$ret = $bucketManager->delete($this->bucket, $name);
return $ret;
}
}

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"
] /* 列出的文件类型 */
}