2025-07-01 19:39:13 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Here is your custom functions.
|
|
|
|
|
|
*/
|
2025-08-04 14:03:57 +08:00
|
|
|
|
|
|
|
|
|
|
use Webman\RedisQueue\Redis as RedisQueueRedis;
|
|
|
|
|
|
|
2025-07-11 18:01:23 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 标准化账号参数
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $account 用户输入的账号(可能是手机号或邮箱)
|
|
|
|
|
|
* @return array 返回 ['phone' => $account] 或 ['email' => $account]
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('normalizeAccount')) {
|
|
|
|
|
|
function normalizeAccount(string $account): array
|
|
|
|
|
|
{
|
|
|
|
|
|
// 使用FILTER_VALIDATE_EMAIL验证邮箱格式
|
|
|
|
|
|
if (filter_var($account, FILTER_VALIDATE_EMAIL)) {
|
|
|
|
|
|
return ['email' => $account];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理手机号中的非数字字符
|
|
|
|
|
|
$cleanPhone = preg_replace('/[^0-9]/', '', $account);
|
|
|
|
|
|
|
|
|
|
|
|
// 验证手机号格式(简单版:11位数字)
|
|
|
|
|
|
if (preg_match('/^1[3-9]\d{9}$/', $cleanPhone)) {
|
|
|
|
|
|
return ['phone' => $cleanPhone];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果既不是邮箱也不是手机号,根据业务需求处理
|
|
|
|
|
|
// 这里默认按手机号处理(可根据实际需求调整)
|
|
|
|
|
|
return ['phone' => $cleanPhone];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 公共html页面
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('htmlpage')) {
|
|
|
|
|
|
function htmlpage()
|
|
|
|
|
|
{
|
|
|
|
|
|
static $html;
|
|
|
|
|
|
if (!$html) {
|
|
|
|
|
|
$html = file_get_contents(public_path('mzl.html'));
|
|
|
|
|
|
}
|
|
|
|
|
|
return $html;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('getConfig')) {
|
|
|
|
|
|
function getConfig($filePath = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 构建文件路径
|
|
|
|
|
|
$config_file = app_path($filePath);
|
|
|
|
|
|
// 检查文件是否存在
|
|
|
|
|
|
if (!file_exists($config_file)) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 包含文件并获取返回的数组
|
|
|
|
|
|
$config = include $config_file;
|
|
|
|
|
|
// 验证是否为数组
|
|
|
|
|
|
if (!is_array($config)) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
return $config;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-29 09:00:04 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* 询单号生成
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('orderNum')) {
|
2025-08-04 14:03:57 +08:00
|
|
|
|
function orderNum()
|
|
|
|
|
|
{
|
2025-07-29 09:00:04 +08:00
|
|
|
|
return "SN".date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 18:01:23 +08:00
|
|
|
|
/*
|
2025-08-04 14:03:57 +08:00
|
|
|
|
* 调试用(console)
|
2025-07-11 18:01:23 +08:00
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('dpr')) {
|
|
|
|
|
|
function dpr(\Throwable $e)
|
|
|
|
|
|
{
|
2025-08-04 14:03:57 +08:00
|
|
|
|
$contents = "=== === 报错打印 === ===\n";
|
|
|
|
|
|
$contents .= "文件:".$e->getFile()."\n";
|
|
|
|
|
|
$contents .= "行数:".$e->getLine()."\n";
|
|
|
|
|
|
$contents .= "详情:".$e->getMessage()."\n";
|
|
|
|
|
|
$contents .= "=== === 报错打印 === ===\n";
|
|
|
|
|
|
var_dump($contents);
|
|
|
|
|
|
return $contents;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 调试用(file)
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('flg')) {
|
|
|
|
|
|
function flg($eventData = '', $isFs = true, $fileName = 'flg', $fileCatalog = 'flg')
|
|
|
|
|
|
{
|
|
|
|
|
|
if ($eventData == '') {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (is_array($eventData) || is_object($eventData)) {
|
|
|
|
|
|
$eventData = json_encode($eventData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$fileCatalog = substr(__FILE__, 0, strpos(__FILE__, 'app/')) . "runtime/" . $fileCatalog;
|
|
|
|
|
|
if (!is_dir($fileCatalog)) {
|
|
|
|
|
|
mkdir($fileCatalog, 0777, true);
|
|
|
|
|
|
exec('chown -R nginx:nginx ' . $fileCatalog);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$fileName = $fileName . '-' . date('Ymd') . '.log';
|
|
|
|
|
|
|
|
|
|
|
|
if ($isFs) {
|
|
|
|
|
|
$data = [
|
|
|
|
|
|
'content' => $eventData,
|
2025-09-24 11:18:59 +08:00
|
|
|
|
'time' => date('Y-m-d H:i:s', time()),
|
|
|
|
|
|
'source' => getenv('ENVIRONMENT')
|
2025-08-04 14:03:57 +08:00
|
|
|
|
];
|
|
|
|
|
|
RedisQueueRedis::send("server", $data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return file_put_contents($fileCatalog . "/" . $fileName, date("Y-m-d H:i:s", time()) . "::" . $eventData . "\n", FILE_APPEND);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***
|
|
|
|
|
|
* 发送飞书文本消息
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('sendFeishuTextMessage')) {
|
|
|
|
|
|
function sendFeishuTextMessage($webhookUrl, $message) {
|
|
|
|
|
|
$data = [
|
|
|
|
|
|
'msg_type' => 'text',
|
|
|
|
|
|
'content' => [
|
|
|
|
|
|
'text' => $message
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
$ch = curl_init();
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $webhookUrl);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
|
|
|
|
|
|
|
|
$response = curl_exec($ch);
|
|
|
|
|
|
curl_close($ch);
|
|
|
|
|
|
|
|
|
|
|
|
return $response;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 替换模板
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('replaceTpl')) {
|
2025-08-13 14:18:11 +08:00
|
|
|
|
function replaceTpl(array $data = [], string $tplKey = 'email')
|
2025-08-04 14:03:57 +08:00
|
|
|
|
{
|
2025-08-13 14:18:11 +08:00
|
|
|
|
if (!$data || !$tplKey) return '';
|
|
|
|
|
|
$templatePath = tplUrl($tplKey);
|
2025-08-04 14:03:57 +08:00
|
|
|
|
$templatePath = app_path($templatePath);
|
|
|
|
|
|
$content = file_get_contents($templatePath);
|
|
|
|
|
|
foreach ($data as $key => $value) {
|
|
|
|
|
|
$content = str_replace('{{'.$key.'}}', $value, $content);
|
|
|
|
|
|
}
|
|
|
|
|
|
return $content;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-13 14:18:11 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 模板路径
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('tplUrl')) {
|
|
|
|
|
|
function tplUrl(string $tplKey = '')
|
|
|
|
|
|
{
|
|
|
|
|
|
$urlArray = [
|
|
|
|
|
|
'email' => 'common/utils/scaffold/templates/Email.stub',
|
2025-10-29 18:05:23 +08:00
|
|
|
|
'work_order' => 'common/utils/scaffold/templates/workOrder.stub',
|
|
|
|
|
|
'payment_files' => 'common/utils/scaffold/templates/paymentFiles.stub',
|
2025-08-13 14:18:11 +08:00
|
|
|
|
];
|
|
|
|
|
|
return $urlArray[$tplKey];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-04 14:03:57 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 替换模板
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('InquiryFormat')) {
|
|
|
|
|
|
function InquiryFormat(array $data = [])
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!$data) return '';
|
|
|
|
|
|
$content = '';
|
|
|
|
|
|
// foreach ($data as $key => $value) {
|
|
|
|
|
|
// $content .= $key . ':' . $value . "\r\n";
|
|
|
|
|
|
// }
|
|
|
|
|
|
$content .= '编号:'.$data['order_no'] . "\r\n";
|
|
|
|
|
|
$content .= '询单质量:'.$data['quality'] . "\r\n";
|
|
|
|
|
|
$content .= '渠道:'.$data['channel'] . "\r\n";
|
|
|
|
|
|
$content .= '邮箱:'.$data['email'] . "\r\n";
|
|
|
|
|
|
$content .= '电话:'.$data['tel'] . "\r\n";
|
|
|
|
|
|
$content .= 'WhatsApp:'.$data['whatsapp'] . "\r\n";
|
|
|
|
|
|
$content .= '国家:'.$data['country'] . "\r\n";
|
|
|
|
|
|
$content .= '联系人:'.$data['contacts'] . "\r\n";
|
|
|
|
|
|
$content .= '录单人:'.$data['enter_user_id'] . "\r\n";
|
|
|
|
|
|
$content .= '录单时间:'.$data['r_time'] . "\r\n";
|
|
|
|
|
|
$content .= '内容:'.$data['content'];
|
|
|
|
|
|
return $content;
|
2025-07-11 18:01:23 +08:00
|
|
|
|
}
|
2025-08-04 18:11:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 移除字符串中的括号及其内部内容(仅移除第一个匹配项)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $str 要处理的字符串
|
|
|
|
|
|
* @return string 处理后的字符串
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('removeParentheses')) {
|
|
|
|
|
|
function removeParentheses(string $str): string
|
|
|
|
|
|
{
|
|
|
|
|
|
// 匹配模式:半角括号或全角括号及其内部内容
|
|
|
|
|
|
$pattern = '/
|
|
|
|
|
|
(?: # 非捕获分组开始
|
|
|
|
|
|
\( # 匹配半角左括号
|
|
|
|
|
|
[^)]* # 匹配除半角右括号外的任意字符
|
|
|
|
|
|
\) # 匹配半角右括号
|
|
|
|
|
|
)|(?: # 或
|
|
|
|
|
|
\( # 匹配全角左括号
|
|
|
|
|
|
[^)]* # 匹配除全角右括号外的任意字符
|
|
|
|
|
|
\) # 匹配全角右括号
|
|
|
|
|
|
) # 非捕获分组结束
|
|
|
|
|
|
/xu'; // x: 忽略空白 u: Unicode模式
|
|
|
|
|
|
|
|
|
|
|
|
return trim(preg_replace($pattern, '', $str, 1));
|
|
|
|
|
|
}
|
2025-08-06 18:55:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 规范hmtl富文本
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('normalizeRichText')) {
|
|
|
|
|
|
function normalizeRichText(string $html): string
|
|
|
|
|
|
{
|
|
|
|
|
|
$dom = new DOMDocument();
|
|
|
|
|
|
|
|
|
|
|
|
// 处理UTF-8编码和HTML5标签
|
|
|
|
|
|
@$dom->loadHTML('<?xml encoding="UTF-8">' . $html,
|
|
|
|
|
|
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置编码并规范化文档
|
|
|
|
|
|
$dom->encoding = 'UTF-8';
|
|
|
|
|
|
$dom->normalizeDocument();
|
|
|
|
|
|
|
|
|
|
|
|
// 统一处理空白字符
|
|
|
|
|
|
$xpath = new DOMXPath($dom);
|
|
|
|
|
|
foreach ($xpath->query('//text()') as $node) {
|
|
|
|
|
|
$normalized = preg_replace('/\s+/u', ' ', trim($node->nodeValue));
|
|
|
|
|
|
$node->nodeValue = $normalized;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 标准化属性顺序(可选)
|
|
|
|
|
|
foreach ($xpath->query('//*[@*]') as $element) {
|
|
|
|
|
|
$attributes = $element->attributes;
|
|
|
|
|
|
$sorted = [];
|
|
|
|
|
|
foreach ($attributes as $attr) {
|
|
|
|
|
|
$sorted[$attr->nodeName] = $attr->nodeValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
ksort($sorted);
|
|
|
|
|
|
|
|
|
|
|
|
// 移除所有属性后按排序后顺序重新添加
|
|
|
|
|
|
while ($attributes->length) {
|
|
|
|
|
|
$element->removeAttributeNode($attributes->item(0));
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach ($sorted as $name => $value) {
|
|
|
|
|
|
$element->setAttribute($name, $value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 返回规范化后的HTML
|
|
|
|
|
|
$result = trim($dom->saveHTML());
|
|
|
|
|
|
// 替换所有空白实体为普通空格
|
|
|
|
|
|
return preg_replace('/\s+/u', ' ', $result);
|
|
|
|
|
|
}
|
2025-08-20 18:12:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成消息编号(唯一业务标识)
|
|
|
|
|
|
* 格式:年月日时分秒 + 微秒 + 随机数
|
|
|
|
|
|
* 例如:20250820175530987654321
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('generateMsgId')) {
|
|
|
|
|
|
function generateMsgId(): string
|
|
|
|
|
|
{
|
|
|
|
|
|
// 时间部分(精确到微秒)
|
|
|
|
|
|
$time = microtime(true);
|
|
|
|
|
|
$micro = sprintf("%06d", ($time - floor($time)) * 1e6);
|
|
|
|
|
|
$date = date('YmdHis', (int)$time);
|
|
|
|
|
|
|
|
|
|
|
|
// 随机部分(3位)
|
|
|
|
|
|
$rand = mt_rand(100, 999);
|
|
|
|
|
|
|
2025-09-01 10:08:51 +08:00
|
|
|
|
// 拼接:日期时间 + 微秒 + 随机数
|
|
|
|
|
|
return $date . $micro . $rand;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成sku_code
|
|
|
|
|
|
* 格式:年月日时分秒 + 微秒 + 随机数
|
|
|
|
|
|
* 例如:20250820175530987654321
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('skuCode')) {
|
|
|
|
|
|
function skuCode(): string
|
|
|
|
|
|
{
|
|
|
|
|
|
// 时间部分(精确到微秒)
|
|
|
|
|
|
$time = microtime(true);
|
|
|
|
|
|
$micro = sprintf("%06d", ($time - floor($time)) * 1e6);
|
|
|
|
|
|
$date = date('YmdHis', (int)$time);
|
|
|
|
|
|
|
|
|
|
|
|
// 随机部分(3位)
|
|
|
|
|
|
$rand = mt_rand(100, 999);
|
|
|
|
|
|
|
2025-08-20 18:12:07 +08:00
|
|
|
|
// 拼接:日期时间 + 微秒 + 随机数
|
|
|
|
|
|
return $date . $micro . $rand;
|
|
|
|
|
|
}
|
2025-09-04 10:26:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成备货单编号(预占式,每天从1开始)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('generateStockNo')) {
|
|
|
|
|
|
function generateStockNo(string $prefix = 'BH', int $serialLength = 3): string
|
|
|
|
|
|
{
|
|
|
|
|
|
$date = date('Ymd');
|
|
|
|
|
|
$key = "stock:order:serial:{$date}";
|
|
|
|
|
|
|
|
|
|
|
|
// 原子递增获取当日流水号
|
|
|
|
|
|
$serial = \support\Redis::incr($key);
|
|
|
|
|
|
|
|
|
|
|
|
// 首次创建时设置过期时间:到明天凌晨 + 1小时(容错)
|
|
|
|
|
|
if ($serial === 1) {
|
|
|
|
|
|
$tomorrow = strtotime(date('Y-m-d', strtotime('+1 day')));
|
|
|
|
|
|
$expireSeconds = $tomorrow - time() + 3600; // 多加1小时
|
|
|
|
|
|
\support\Redis::expire($key, $expireSeconds);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化流水号(补零)
|
|
|
|
|
|
$paddedSerial = str_pad($serial, $serialLength, '0', STR_PAD_LEFT);
|
|
|
|
|
|
|
|
|
|
|
|
return $prefix . $date . $paddedSerial;
|
|
|
|
|
|
}
|
2025-09-15 15:01:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 发送通知
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $title
|
|
|
|
|
|
* @param string $content
|
|
|
|
|
|
* @param string $url
|
|
|
|
|
|
* @param string $type
|
|
|
|
|
|
* @return void
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (!function_exists('noticePush')) {
|
|
|
|
|
|
function noticePush(array $notice = [])
|
|
|
|
|
|
{
|
|
|
|
|
|
/*
|
|
|
|
|
|
消息字段:
|
|
|
|
|
|
'sender_id' //发送人
|
|
|
|
|
|
'sender_type' //发送人类型
|
|
|
|
|
|
'title' //标题
|
|
|
|
|
|
'content' //内容
|
|
|
|
|
|
'scene' //场景
|
|
|
|
|
|
'recipient' //接收人
|
|
|
|
|
|
'push_method' //推送方式
|
|
|
|
|
|
// 如果需要同时 邮箱推送 增加推送邮箱字段
|
|
|
|
|
|
'email' //邮箱
|
|
|
|
|
|
*/
|
|
|
|
|
|
\Webman\RedisQueue\Redis::send('notice', $notice);
|
|
|
|
|
|
}
|
2025-07-11 18:01:23 +08:00
|
|
|
|
}
|