2025-05-04 22:42:51 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
2025-05-05 09:44:50 +08:00
|
|
|
|
namespace App\Service\common;
|
2025-05-04 22:42:51 +08:00
|
|
|
|
|
|
|
|
|
|
use App\Enum\ErrorEnum;
|
|
|
|
|
|
use DateTime;
|
|
|
|
|
|
use Exception;
|
2025-05-05 09:44:50 +08:00
|
|
|
|
use Illuminate\Support\Facades\Route;
|
2025-05-04 22:42:51 +08:00
|
|
|
|
use Random\RandomException;
|
2025-05-05 08:15:50 +08:00
|
|
|
|
use ReflectionClass;
|
2025-05-04 22:42:51 +08:00
|
|
|
|
|
|
|
|
|
|
class UtilsService
|
|
|
|
|
|
{
|
|
|
|
|
|
private static mixed $_instance;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取实例
|
|
|
|
|
|
* @return null|static
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function getInstance(): null|static
|
|
|
|
|
|
{
|
|
|
|
|
|
$name = get_called_class();
|
|
|
|
|
|
if (!isset(self::$_instance[$name])) {
|
|
|
|
|
|
self::$_instance[$name] = new static();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return self::$_instance[$name];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 抛出异常
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $msg
|
|
|
|
|
|
* @param ErrorEnum $code
|
|
|
|
|
|
* @return mixed
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function errorThrow(string $msg = '请求出了问题!', ErrorEnum $code = ErrorEnum::FAIL): mixed
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($msg, $code->value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 抛出未找到异常 2000
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param $msg
|
|
|
|
|
|
* @return mixed
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function notFound($msg): mixed
|
|
|
|
|
|
{
|
|
|
|
|
|
return self::errorThrow($msg, ErrorEnum::NOT_FOUND);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 未授权 401
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param $msg
|
|
|
|
|
|
* @return mixed
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function notAuth($msg): mixed
|
|
|
|
|
|
{
|
|
|
|
|
|
exit(json_encode(['msg' => $msg, 'code' => ErrorEnum::NOT_AUTH->value]));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成密码
|
|
|
|
|
|
* @param $password
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function genPassword($password): string
|
|
|
|
|
|
{
|
|
|
|
|
|
return password_hash($password, PASSWORD_DEFAULT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 密码验证
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param $password
|
|
|
|
|
|
* @param $passwordAse
|
|
|
|
|
|
* @return bool
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function checkPassword($password, $passwordAse): bool
|
|
|
|
|
|
{
|
|
|
|
|
|
// 盐
|
|
|
|
|
|
return password_verify($password, $passwordAse);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成OpenId
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
* @throws RandomException
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function genOpenId(): string
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取当前时间戳(精确到秒)
|
|
|
|
|
|
$timestamp = dechex(time());
|
|
|
|
|
|
|
|
|
|
|
|
// 生成16字节的随机数据,并转换为32个十六进制字符组成的字符串
|
|
|
|
|
|
$randomString = bin2hex(random_bytes(16));
|
|
|
|
|
|
|
|
|
|
|
|
// 结合时间和随机字符串,确保总长度为28个字符
|
|
|
|
|
|
return substr($timestamp . $randomString, 0, 28);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 递归生成树形结构
|
|
|
|
|
|
* @param $data
|
|
|
|
|
|
* @param int $pid
|
|
|
|
|
|
* @return array
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function tree($data, int $pid = 0): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$tree = [];
|
|
|
|
|
|
foreach ($data as $v) {
|
|
|
|
|
|
if ($v['pid'] == $pid) {
|
|
|
|
|
|
$v['children'] = $this->tree($data, $v['id']);
|
|
|
|
|
|
if (empty($v['children'])) {
|
|
|
|
|
|
unset($v['children']);
|
|
|
|
|
|
}
|
|
|
|
|
|
$tree[] = $v;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return $tree;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查手机号格式
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $phone
|
|
|
|
|
|
* @return bool
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function checkPhone(string $phone): bool
|
|
|
|
|
|
{
|
|
|
|
|
|
$phone = trim($phone);
|
|
|
|
|
|
if (preg_match('/^1[3456789]\d{9}$/', $phone)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self::errorThrow('手机号格式不正确');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成订单号
|
|
|
|
|
|
* @param int $type
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function genOrderNo(int $type = 0): string
|
|
|
|
|
|
{
|
|
|
|
|
|
$len = 9999999999;
|
|
|
|
|
|
switch ($type) {
|
|
|
|
|
|
case 0:
|
2025-05-05 08:15:50 +08:00
|
|
|
|
return 'DK' . date('Ymd') . str_pad(mt_rand(1, $len), 11, '0', STR_PAD_LEFT);
|
2025-05-04 22:42:51 +08:00
|
|
|
|
case 1:
|
2025-05-05 08:15:50 +08:00
|
|
|
|
return 'DD' . date('Ymd') . str_pad(mt_rand(1, $len), 11, '0', STR_PAD_LEFT);
|
2025-05-04 22:42:51 +08:00
|
|
|
|
default:
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 是否是开发环境
|
|
|
|
|
|
* @return bool
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function isDev(): bool
|
|
|
|
|
|
{
|
|
|
|
|
|
return env('ECS') === 'localhost';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据身份证号计算年龄
|
|
|
|
|
|
* @param string $idno 身份证号(15位或18位)
|
|
|
|
|
|
* @return int|false 成功返回年龄,失败返回false
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function getAgeFromIdNo($idno = '')
|
|
|
|
|
|
{
|
|
|
|
|
|
// 校验身份证号长度
|
|
|
|
|
|
$len = strlen($idno);
|
|
|
|
|
|
if (!in_array($len, [15, 18])) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提取出生日期部分
|
|
|
|
|
|
if ($len === 18) {
|
|
|
|
|
|
$datePart = substr($idno, 6, 8);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 15位身份证补全年份为19XX
|
|
|
|
|
|
$datePart = '19' . substr($idno, 6, 6);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 分解年月日
|
|
|
|
|
|
$year = substr($datePart, 0, 4);
|
|
|
|
|
|
$month = substr($datePart, 4, 2);
|
|
|
|
|
|
$day = substr($datePart, 6, 2);
|
|
|
|
|
|
|
|
|
|
|
|
// 校验年月日是否为数字
|
|
|
|
|
|
if (!ctype_digit($year) || !ctype_digit($month) || !ctype_digit($day)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试创建日期对象
|
|
|
|
|
|
try {
|
|
|
|
|
|
$birthDate = new DateTime("$year-$month-$day");
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$currentDate = new DateTime();
|
|
|
|
|
|
|
|
|
|
|
|
// 检查出生日期是否在当前日期之前
|
|
|
|
|
|
if ($birthDate > $currentDate) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算年龄差
|
|
|
|
|
|
$ageInterval = $currentDate->diff($birthDate);
|
|
|
|
|
|
return $ageInterval->y;
|
|
|
|
|
|
}
|
2025-05-05 08:15:50 +08:00
|
|
|
|
|
|
|
|
|
|
public function autoRouteRegister($class): void
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach ($class as $key => $value) {
|
|
|
|
|
|
|
|
|
|
|
|
// 获取控制器$value的所有方法
|
|
|
|
|
|
$methods = (new ReflectionClass($value))->getMethods();
|
|
|
|
|
|
|
|
|
|
|
|
// 注册路由
|
|
|
|
|
|
foreach ($methods as $method) {
|
|
|
|
|
|
// 获取方法注释 @Method
|
|
|
|
|
|
$docComment = $method->getDocComment();
|
|
|
|
|
|
if ($method->name === '__construct' || preg_match('/@Method\s+(NO)\b/', $docComment, $matches)) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$httpMethod = 'any';
|
|
|
|
|
|
// 查询 @Method GET 或者 @Method POST
|
|
|
|
|
|
if (preg_match('/@Method\s+(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|ANY)\b/', $docComment, $matches)) {
|
|
|
|
|
|
$httpMethod = $matches[1];
|
|
|
|
|
|
}
|
|
|
|
|
|
Route::$httpMethod($key . '/' . cc_camel_case_to_dash($method->getName()), [$value, conjunction_symbol_processing($method->name)]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-04 22:42:51 +08:00
|
|
|
|
}
|