$src_w && $height > $src_h) || ($height > $src_h && $width == 0) || ($width > $src_w && $height == 0)) { $proportion = 1; } if ($width > $src_w) { $dst_w = $width = $src_w; } if ($height > $src_h) { $dst_h = $height = $src_h; } if (!$width && !$height && !$proportion) { return false; } if (!$proportion) { if ($cut == 0) { if ($dst_w && $dst_h) { if ($dst_w / $src_w > $dst_h / $src_h) { $dst_w = $src_w * ($dst_h / $src_h); $x = 0 - ($dst_w - $width) / 2; } else { $dst_h = $src_h * ($dst_w / $src_w); $y = 0 - ($dst_h - $height) / 2; } } else if ($dst_w xor $dst_h) { if ($dst_w && !$dst_h) //有宽无高 { $propor = $dst_w / $src_w; $height = $dst_h = $src_h * $propor; } else if (!$dst_w && $dst_h) //有高无宽 { $propor = $dst_h / $src_h; $width = $dst_w = $src_w * $propor; } } } else { if (!$dst_h) //裁剪时无高 { $height = $dst_h = $dst_w; } if (!$dst_w) //裁剪时无宽 { $width = $dst_w = $dst_h; } $propor = min(max($dst_w / $src_w, $dst_h / $src_h), 1); $dst_w = (int)round($src_w * $propor); $dst_h = (int)round($src_h * $propor); $x = ($width - $dst_w) / 2; $y = ($height - $dst_h) / 2; } } else { $proportion = min($proportion, 1); $height = $dst_h = $src_h * $proportion; $width = $dst_w = $src_w * $proportion; } $src = $createfun($src_img); $dst = imagecreatetruecolor($width ? $width : $dst_w, $height ? $height : $dst_h); $white = imagecolorallocate($dst, 255, 255, 255); imagefill($dst, 0, 0, $white); if (function_exists('imagecopyresampled')) { imagecopyresampled($dst, $src, $x, $y, 0, 0, $dst_w, $dst_h, $src_w, $src_h); } else { imagecopyresized($dst, $src, $x, $y, 0, 0, $dst_w, $dst_h, $src_w, $src_h); } $otfunc($dst, $dst_img); imagedestroy($dst); imagedestroy($src); //return true; return ['width' => $src_w, 'height' => $src_h];//modify sofashi } /** * 输出字符并结束运行 */ public static function stop($string = '') { echo $string; exit(); } public static function end() { Yii::$app->end(); } //输出json数据并结束运行 public static function encode($data) { echo Json::encode($data); exit(); } /** * 合并图片 ---lqh 2017-05-05 * @param $background_img 背景原图 * @param $inline_img 内嵌原图 * @return mixed 合并图的路径名称 */ public static function mergeImg($background_img, $inline_img, $type = 'wx') { $app = Yii::getAlias("@app"); $saveDir = $app . '/../images/qrcode/'; $rand = stringUtil::buildOrderNo(); $random = empty($unique) ? $rand : $unique . '_' . $rand; //背景框图 $path_1 = $background_img; //二维码图片 $path_2 = $inline_img; //将框和二维码图片分别取到两个画布中。 针对png格式 $image_1 = imagecreatefrompng($path_1); $image_2 = imagecreatefrompng($path_2); //创建一个和框图片一样大小的真彩色画布(ps:只有这样才能保证后面copy二维码图片的时候不会失真) $image_3 = imageCreatetruecolor(imagesx($image_1), imagesy($image_1)); //为真彩色画布创建白色背景,再设置为透明 $color = imagecolorallocate($image_3, 255, 255, 255); imagefill($image_3, 0, 0, $color); imageColorTransparent($image_3, $color); //首先将框画布采样copy到真彩色画布中,不会失真 imagecopyresampled($image_3, $image_1, 0, 0, 0, 0, imagesx($image_1), imagesy($image_1), imagesx($image_1), imagesy($image_1)); //再将二维码图片copy到已经具有框图像的真彩色画布中,同样也不会失真 //根据微信或则支付宝合并位置有所不同 if ($type == 'wx') { imagecopymerge($image_3, $image_2, 220, 320, 0, 0, imagesx($image_2), imagesy($image_2), 100); } else { //支付宝 imagecopymerge($image_3, $image_2, 450, 820, 0, 0, imagesx($image_2), imagesy($image_2), 100); } //将画布保存到指定的png文件 imagepng($image_3, $saveDir . $random . '_merge.png'); return '/qrcode/' . $random . '_merge.png';//组合图片 } public static function mergeQrimg($backgroundImg, array $qrImg) { $app = Yii::getAlias("@app"); $saveDir = $app . '/../images/qrcode/'; $random = stringUtil::buildOrderNo(); $dest = imagecreatefromjpeg($backgroundImg); // Get new sizes list($width, $height) = getimagesize($qrImg['wifi']); list($newwidth, $newheight) = getimagesize($qrImg['zhifubao']); $newwidth = $newwidth + 35;//下载的wifi二维码size太小,放大些 $newheight = $newheight + 35; // Load $thumb = imagecreatetruecolor($newwidth, $newheight); $source = imagecreatefromjpeg($qrImg['wifi']); // Resize imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); imagejpeg($thumb, $saveDir . "temp.jpeg");//Output image to file //微信(支付宝)二维码、WiFi二维码 $zfbQrImg = imagecreatefrompng($qrImg['zhifubao']); $WXQrImg = imagecreatefrompng($qrImg['weixin']); $wifiQrImg = imagecreatefromjpeg($saveDir . "temp.jpeg"); imagealphablending($dest, false); imagesavealpha($dest, true); imagecopymerge($dest, $zfbQrImg, 240, 538, 0, 0, imagesx($zfbQrImg), imagesy($zfbQrImg), 100); imagecopymerge($dest, $WXQrImg, 985, 538, 0, 0, imagesx($WXQrImg), imagesy($WXQrImg), 100); imagecopymerge($dest, $wifiQrImg, 1708, 520, 0, 0, imagesx($wifiQrImg), imagesy($wifiQrImg), 100); imagepng($dest, $saveDir . $random . '_fullQrImg.png'); imagedestroy($dest); imagedestroy($zfbQrImg); imagedestroy($WXQrImg); imagedestroy($wifiQrImg); return '/qrcode/' . $random . '_fullQrImg.png';//组合图片 } //获取服务器IP public static function serverIp() { if (isset($_SERVER)) { if ($_SERVER['SERVER_ADDR']) { $server_ip = $_SERVER['SERVER_ADDR']; } else { $server_ip = $_SERVER['LOCAL_ADDR']; } } else { $server_ip = getenv('SERVER_ADDR'); } return $server_ip; } //获取根目录 ssh 2020.3.11 public static function getRootDir() { $basePath = Yii::$app->basePath; if (Yii::$app->id == 'app-console') { $root = $basePath . "/../"; } else { $root = $basePath . "/../../"; } return $root; } /** * 错误输出 * @param string $msg * @throws \Exception */ public static function fail($msg = '操作失败') { Yii::info($msg); //Yii::info($msg . "\n调用堆栈:\n" . (new \Exception())->getTraceAsString(), __METHOD__); $report = isset(Yii::$app->params['errorReport']) ? Yii::$app->params['errorReport'] : 0; if ($report == 1) { throw new \Exception($msg); } self::encode(['code' => 0, 'msg' => $msg, 'data' => []]); } public static function mistake($msg = '操作失败', $mask = false, $icon = 'none', $duration = 1500) { $report = isset(Yii::$app->params['errorReport']) ? Yii::$app->params['errorReport'] : 0; if ($report == 1) { throw new \Exception($msg); } self::encode(['code' => 0, 'msg' => $msg, 'data' => [], 'mask' => $mask, 'icon' => $icon, 'duration' => $duration]); } //错误输出,带自定义code public static function error($code, $msg, $data=[]) { self::encode(['code' => $code, 'msg' => $msg, 'data' => $data]); } //成功输出内容 public static function success($data, $msg = '操作成功') { self::encode(['code' => 1, 'msg' => $msg, 'data' => $data]); } //退出当前登陆 ssh 2020.3.8 public static function logout($msg = '当前登陆信息已失效,请重新登陆') { self::encode(['code' => 2, 'msg' => $msg, 'data' => []]); } //只输出执行完成状态 ssh 2020.3.11 public static function complete($msg = '') { $msg = empty($msg) ? '操作成功' : $msg; self::encode(['code' => 1, 'msg' => $msg, 'data' => []]); } public static function notLogin($msg = '您还没有登陆哦') { self::encode(['code' => -10, 'msg' => $msg, 'data' => []]); } /** * [getLock 时间锁] * @param [type] $key [string] * @param integer $expire [time] * @return [type] [bool] */ public static function getLock($key, $expire = 10) { $redis = Yii::$app->redis; return $redis->setex($key, $expire, time()); } /** * 检测锁是否存在 * @param $key * @return bool */ public static function checkLock($key) { $redis = Yii::$app->redis; if ($redis->get($key)) { return true; } else { return false; } } /** * 获取锁 * @param String $key 锁标识 * @param Int $expire 锁过期时间 * @param Int $waitTime 等待时间(超时时间) 秒 * @return Boolean */ public static function lock($key, $expire = 5, $waitTime = 5) { $redis = Yii::$app->redis; $isLock = $redis->setex($key, $expire, time() + $expire); $time = microtime(true); $exitTime = $waitTime + $time; // 不能获取锁 if (!$isLock) { // 判断锁是否过期 锁已过期 删除锁 重新获取 防止过期时间设置长,结束时没主动调unlock $lockTime = $redis->get($key); if ($lockTime && time() > $lockTime) { self::unlock($key); $isLock = $redis->setex($key, $expire, time() + $expire); } if (!$isLock) { while (true) { usleep(50); $isLock = $redis->setex($key, $expire, time() + $expire); if ($isLock) { break; } if (microtime(true) > $exitTime) { //超时了 break; } } } } return $isLock ? true : false; } /** * 释放锁 * @param String $key 锁标识 * @return Boolean */ public static function unlock($key) { $redis = Yii::$app->redis; return $redis->del($key); } //检查是否有重复提交 ssh 20211018 public static function checkRepeatCommit($adminId, $second = 10) { $ctl = Yii::$app->controller->id; $action = Yii::$app->controller->action->id; $cacheKey = "checkRepeatCommit_" . $ctl . "_" . $action . "_" . $adminId; $has = Yii::$app->redis->executeCommand('GET', [$cacheKey]); if (!empty($has)) { Yii::warning($cacheKey . ' 发生重复请求'); self::fail('请稍等几秒再试'); } Yii::$app->redis->executeCommand('SETEX', [$cacheKey, $second, 'has']); } //每天限制请求次数 public static function perDayCommitTimeLimit($uniqueId, $num = 0) { if ($num == 0) { //0表示不限制请求次数 return true; } $second = 86400; $ctl = Yii::$app->controller->id; $action = Yii::$app->controller->action->id; $cacheKey = "perDayCommitTimeLimit_" . $ctl . "_" . $action . "_" . $uniqueId; $hasNum = Yii::$app->redis->executeCommand('GET', [$cacheKey]); if (!empty($hasNum) && $hasNum > 0) { if ($hasNum > $num) { util::fail("已拉取{$num}次,请明天再试"); } $newNum = bcadd($hasNum, 1); Yii::$app->redis->executeCommand('SETEX', [$cacheKey, $second, $newNum]); } else { Yii::$app->redis->executeCommand('SETEX', [$cacheKey, $second, 1]); } return true; } public static function isDbConcurrencyException($exception) { $message = $exception->getMessage(); return strpos($message, 'Deadlock found') !== false || strpos($message, 'Serialization failure') !== false || strpos($message, 'Lock wait timeout exceeded') !== false || strpos($message, 'SQLSTATE[40001]') !== false || strpos($message, 'SQLSTATE[HY000]: General error: 1205') !== false; } public static function runWithDbConcurrencyRetry(callable $callback, $maxRetry = 3) { $attempt = 0; while (true) { try { return call_user_func($callback); } catch (\Exception $exception) { if (!self::isDbConcurrencyException($exception) || $attempt >= $maxRetry) { throw $exception; } $attempt++; Yii::warning('数据库并发冲突,准备重试第' . $attempt . '次:' . $exception->getMessage()); usleep(mt_rand(50000, 300000)); } } } /** * 用 mainId 验证操作权限 * @param array $arr 模型数据数组 * @param int $mainId * @throws \Exception */ public static function validMainId($arr, $mainId) { if (isset($arr['mainId']) == false || $arr['mainId'] != $mainId) { self::fail('您无法操作'); } } }