Commit 0343a4e1 authored by 王源's avatar 王源 🎧

分割rpc文件

parent bb7cfa5a
# 导出Excel csv工具使用说明
1、导出Excel,不使用模板的情况
```$xslt
$exporter = new Exporter(Exporter::EXPORTER_TYPE_XLSX,'导出产品信息');#不用模板的情况
//填充表头
$title = ['产品','颜色','尺码','sku']
$list = Product::with(["color","size"])->get()->toArray();
$exporter->append($title)
->append($list,['name','color.name','size.name|未设定尺码','sku'])//填充数据
->setUnprotectRange("A1:B".$exporter->getCurrentRowIndex())//设置保护
->download(Exporter::DOWNLOAD_TYPE_STREAM,"产品数据");//下载数据
```
2、导出Excel,使用模板的情况
```$xslt
//根据模板导出器
$exporter = new Exporter(Exporter::EXPORTER_TYPE_XLSX, 'template/xxx.xlsx', '导出产品信息');#使用模板来生成
//新增sheet并使用它
$exporter->addSheet('颜色参考表', true);
//填充表头
$title = ['颜色id','中文颜色名','英文颜色名']
$exporter->append($title)
->append(Color::get(["id", "cn_name", "en_name"]))
->setUnprotectRange();//全表保护
//设置保护
//使用别的sheet表
$exporter->setSheetByIndex(0)->setBeginRowIndex(2)->setBeginColumnChar("C");//从第三列开始填充数据
//填充数据
$list = Product::with(["color","size"])->get()->toArray();
$exporter->append($list,['name','color.name','size.name|未设定尺码','sku']);
$file= $exporter->download(Exporter::DOWNLOAD_TYPE_RETURN_FILE_PATH,"产品数据");
echo $file;
```
<?php
declare(strict_types=1);
namespace Meibuyu\Micro\Amqp\Producer;
use Exception;
use Hyperf\Amqp\Annotation\Producer;
use Hyperf\Amqp\Message\ProducerMessage;
use Meibuyu\Micro\Exceptions\HttpResponseException;
use Meibuyu\Micro\Model\Auth;
/**
* @Producer(exchange="micro", routingKey="exception-log")
*/
class ExceptionLogProducer extends ProducerMessage
{
public function __construct($data)
{
try {
$data['operator'] = Auth::user()['name'];
} catch (HttpResponseException $e) {
put_log('获取操作人失败; ' . $e->getMessage(), 'ExceptionLogProducer.log');
}
// 获取trace中真实文件和行数
if (!empty($data['trace'])) {
preg_match('/(\/var\/www\/app\/.+?\.php)(?=\()/', $data['trace'], $matchFiles);
if (!empty($matchFiles[0])) {
$data['file'] = $matchFiles[0];
$file = str_replace('/', '\/', $matchFiles[0]);
$file = str_replace('.', '\.', $file);
$pattern = '/(?<=' . $file . '\()[0-9]+(?=\))/';
preg_match($pattern, $data['trace'], $matchLines);
if (!empty($matchLines[0])) {
$data['line'] = $matchLines[0];
}
}
}
if (!empty($data['file'])) {
// 只对项目app文件夹下的错误获取编码人
preg_match('/(\/var\/www\/app\/)/', $data['file'], $matchPaths);
if (!empty($matchPaths[0])) {
try {
exec("cd {$matchPaths[0]} && git blame -L {$data['line']},{$data['line']} {$data['file']}", $output);
if (!empty($output[0]) && is_string($output[0])) {
preg_match('/(?<=\()[^ ]+/', $output[0], $matchCoders);
if (!empty($matchCoders[0])) {
$data['coder'] = $matchCoders[0];
}
}
} catch (Exception $e) {
put_log('获取编码人失败; ' . $e->getMessage(), 'ExceptionLogProducer.log');
}
}
}
$this->payload = $data;
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/4/9
* Time: 14:59
*/
namespace Meibuyu\Micro\Annotation;
use Hyperf\Di\Annotation\AbstractAnnotation;
/**
* @Annotation
* @Target({"CLASS"})
*/
class AutoPerm extends AbstractAnnotation
{
/**
* @var string
*/
public $prefix = '';
/**
* @var array
*/
public $exclude = [];
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/4/8
* Time: 13:59
*/
namespace Meibuyu\Micro\Annotation;
use Hyperf\Di\Annotation\AbstractAnnotation;
/**
* @Annotation
* @Target({"METHOD"})
*/
class Perm extends AbstractAnnotation
{
/**
* @var string
*/
public $name = '';
public function __construct($value = null)
{
$this->bindMainProperty('name', $value);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/4/8
* Time: 14:48
*/
namespace Meibuyu\Micro\Aspect;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\Utils\Str;
use Meibuyu\Micro\Annotation\AutoPerm;
use Meibuyu\Micro\Annotation\Perm;
use Meibuyu\Micro\Exceptions\HttpResponseException;
use Meibuyu\Micro\Handler\PermHandler;
/**
* @Aspect()
*/
class PermAnnotationAspect extends AbstractAspect
{
/**
* @Inject()
* @var PermHandler
*/
private $permHandler;
/**
* @Inject
* @var ConfigInterface
*/
protected $config;
public $annotations = [
Perm::class,
AutoPerm::class,
];
/**
* @param ProceedingJoinPoint $proceedingJoinPoint
* @return mixed
* @throws HttpResponseException
* @throws \Hyperf\Di\Exception\Exception
*/
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
$perm = $this->genPermName($proceedingJoinPoint);
if ($perm) {
if ($this->permHandler->check($perm)) {
return $proceedingJoinPoint->process();
} else {
throw new HttpResponseException('当前用户没有此操作权限');
}
}
return $proceedingJoinPoint->process();
}
// 生成权限名
public function genPermName(ProceedingJoinPoint $proceedingJoinPoint)
{
/** @var AutoPerm $autoPerm */
/** @var Perm $perm */
[$autoPerm, $perm] = $this->getAnnotations($proceedingJoinPoint);
$className = $proceedingJoinPoint->className;
$methodName = $proceedingJoinPoint->methodName;
if ($autoPerm && in_array($methodName, $autoPerm->exclude)) {
return false; // 跳过不需要鉴权的方法
}
$prefix = $autoPerm && $autoPerm->prefix ? $autoPerm->prefix : $this->genPrefix($className);
$name = $perm && $perm->name ? $perm->name : $this->genName($methodName);
return $this->parsePermName($prefix, $name);
}
// 拼接权限名
protected function parsePermName($prefix, $name)
{
// 注意每个应用的app_name的唯一性
$appName = trim($this->config->get('app_name'), '_');
$prefix = trim($prefix, '_');
$name = trim($name, '_');
return $appName . '_' . $prefix . '_' . $name;
}
// 生成前缀
protected function genPrefix(string $className): string
{
$handledNamespace = Str::replaceFirst('Controller', '', Str::after($className, '\\Controller\\'));
$namespaceLength = strrpos($handledNamespace, '\\');
$prefix = $namespaceLength ? substr($handledNamespace, $namespaceLength + 1) : $handledNamespace;
$prefix = Str::snake($prefix);
$prefix = str_replace('__', '_', $prefix);
return $prefix;
}
// 生成名称
protected function genName(string $methodName): string
{
$methodName = Str::snake($methodName);
$methodName = str_replace('__', '_', $methodName);
return $methodName;
}
// 获取注解
public function getAnnotations(ProceedingJoinPoint $proceedingJoinPoint)
{
$metadata = $proceedingJoinPoint->getAnnotationMetadata();
return [
$metadata->class[AutoPerm::class] ?? null,
$metadata->method[Perm::class] ?? null
];
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace Meibuyu\Micro\Command;
use Hyperf\Command\Annotation\Command;
use Hyperf\Command\Command as HyperfCommand;
use Hyperf\Contract\ContainerInterface;
use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Database\Schema\MySqlBuilder;
use Hyperf\DbConnection\Db;
use Hyperf\Utils\Str;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
/**
* @Command
*/
class MakeModelCommand extends HyperfCommand
{
/**
* @var ContainerInterface
*/
protected $container;
private $path;
private $appPath;
private $builder;
private $table = "";
private $tableIndex = 0;
private $tables = [];
private $allTableStructures = [];
private $currentTableStructure = [];
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->path = __DIR__ . "/stubs/";
$this->appPath = BASE_PATH . '/app/';
$this->builder = $this->getSchemaBuilder();
parent::__construct('mm');
}
/**
* 获取数据库创建者
* @return MySqlBuilder
*/
protected function getSchemaBuilder(): MySqlBuilder
{
$resolver = $this->container->get(ConnectionResolverInterface::class);
$connection = $resolver->connection();
return $connection->getSchemaBuilder();
}
public function handle()
{
$this->initTableStructures();
/*$this->info(print_r($this->allTableStructures, true));
return false;*/
$tables = [];
$isAll = $this->input->getOption('database');
$table = $this->input->getArgument('name');
if (!$table && !$isAll) {
$this->error("请输入表名进行单表生成 或者 -d 参数 进行全库生成,或使用 --help查看帮助");
return false;
}
if ($this->input->getOption('database')) {
$tables = $this->tables;
} else {
$tables[] = $this->getTable();
}
foreach ($tables as $k => $v) {
if ($v == 'migrations') {
continue;
}
if (!$this->builder->hasTable($v)) {
$this->info("表" . $v . "不存在!");
continue;
}
$this->tableIndex = $k;
$this->input->setArgument("name", $v);
$this->table = $v;
$this->currentTableStructure = $this->allTableStructures[$v];
$this->info("开始生成:" . $v);
if (!Str::contains($v, "_to_") && ($this->input->getOption('model') || $this->input->getOption('all'))) {
$this->makeModel();
}
if (!Str::contains($v, "_to_") && ($this->input->getOption('controller') || $this->input->getOption('all'))) {
$this->makeRepositoryInterface();
$this->makeRepository();
$this->makeController();
}
if (!Str::contains($v, "_to_") && ($this->input->getOption('validator') || $this->input->getOption('all'))) {
$this->makeValidator();
}
if (!Str::contains($v, "_to_") && $this->input->getOption('seeder')) {
$this->makeSeeder();
}
if ($this->input->getOption('migrate') || $this->input->getOption('all')) {
$this->makeMigrate();
}
}
}
/**
*获取表结构
*/
private function initTableStructures()
{
//tables
$tables = Db::select("select table_name,`engine`,table_comment from information_schema.tables
where table_schema=database()");
$tables = array_map('get_object_vars', $tables);
$tables = collect($tables)->keyBy("table_name")->toArray();
//fields
$fields = Db::select("select table_name,column_name,column_default,is_nullable,data_type,collation_name,column_type,column_key,extra,column_comment
from information_schema.columns where table_schema=database()");
$fieldList = array_map('get_object_vars', $fields);
//对字段数据进行处理
foreach ($fieldList as $fk => $fv) {
$fieldList[$fk]['length'] = trim(str_replace([$fv['data_type'], "(", ")"], "", $fv['column_type']));
//针对层级处理
if ($fv['column_name'] == 'parent_id' || $fv['column_name'] == 'pid') {
$relation = [];
$relation['function'] = "parent";
$relation['relation_model_name'] = Str::studly(Str::singular($fv['table_name']));
$relation['relation_table'] = $fv['table_name'];
$relation['relation_table_key'] = "id";
$relation['local_table'] = $fv['table_name'];
$relation['local_table_key'] = $fv['column_name'];
$tables[$relation['local_table']]['relations']['belongsTo'][] = $relation;
$reverseRelation = [];
$reverseRelation['function'] = 'children';
$reverseRelation['relation_model_name'] = $relation['relation_model_name'];
$reverseRelation['relation_table'] = $relation['local_table'];
$reverseRelation['relation_table_key'] = $relation['local_table_key'];
$reverseRelation['local_table'] = $relation['relation_table'];
$reverseRelation['local_table_key'] = $relation['relation_table_key'];
$tables[$reverseRelation['relation_table']]['relations']['hasMany'][] = $reverseRelation;
} else if (Str::endsWith($fv['column_name'], '_id')) {
//关联表处理
if (Str::contains($fv['table_name'], "_to_")) {
$ts = explode("_to_", $fv['table_name']);
$ts = collect($ts)->map(function ($item) {
return Str::plural($item);
})->all();
//$this->info(print_r($ts, true));
$relation = [];
if (Str::singular($ts[0]) . "_id" == $fv['column_name']) {
$relation['constraint_table'] = $ts[1];
$relation['constraint_table_key'] = Str::singular($ts[1]) . "_id";
$relation['local_table'] = $ts[0];
} else {
$relation['constraint_table'] = $ts[0];
$relation['constraint_table_key'] = Str::singular($ts[0]) . "_id";
$relation['local_table'] = $ts[1];
}
$relation['local_table_key'] = $fv['column_name'];
$relation['relation_table'] = $fv['table_name'];
$relation['function'] = Str::snake($relation['constraint_table']);
$relation['relation_model_name'] = Str::studly(Str::singular($relation['constraint_table']));
$tables[$relation['local_table']]['relations']['belongsToMany'][] = $relation;
} else {
$relation = [];
$relation['relation_table'] = Str::plural(Str::replaceLast("_id", "", $fv['column_name']));
if (!isset($tables[$relation['relation_table']])) {
continue;
}
$relation['function'] = Str::snake(Str::singular($relation['relation_table']));
$relation['relation_model_name'] = Str::studly(Str::singular($relation['relation_table']));
$relation['relation_table_key'] = "id";
$relation['local_table'] = $fv['table_name'];
$relation['local_table_key'] = $fv['column_name'];
$tables[$relation['local_table']]['relations']['belongsTo'][] = $relation;
$reverseRelation = [];
$reverseRelation['relation_table'] = $relation['local_table'];
$reverseRelation['relation_table_key'] = $relation['local_table_key'];
$reverseRelation['local_table'] = $relation['relation_table'];
$reverseRelation['local_table_key'] = $relation['relation_table_key'];
$reverseRelation['function'] = Str::snake($reverseRelation['relation_table']);
$reverseRelation['relation_model_name'] = Str::studly(Str::singular($reverseRelation['relation_table']));
$tables[$reverseRelation['local_table']]['relations']['hasMany'][] = $reverseRelation;
}
}
}
$fields = collect($fieldList)->groupBy("table_name")->toArray();
//constraints
$constraints = Db::select("select a.constraint_name,a.table_name,b.column_name,a.referenced_table_name,
b.referenced_column_name,a.update_rule,a.delete_rule
from information_schema.referential_constraints a,information_schema.key_column_usage b
where a.constraint_schema=database() and a.constraint_schema=b.constraint_schema
and a.table_name=b.table_name and a.constraint_name=b.constraint_name");
$constraints = array_map('get_object_vars', $constraints);
$constraints = collect($constraints)->groupBy("table_name")->toArray();
//$indexes
$indexes = Db::select("select table_name,non_unique,index_name,column_name
from information_schema.statistics where table_schema=database()");
$indexes = array_map('get_object_vars', $indexes);
$indexes = collect($indexes)->groupBy("table_name")->toArray();
foreach ($tables as $k => $v) {
$v['fields'] = $fields[$k] ?? [];
$v['constraints'] = $constraints[$k] ?? [];
if (isset($indexes[$k])) {
$v['indexes'] = collect($indexes[$k])->groupBy("index_name")->toArray();
} else {
$v['indexes'] = [];
}
$tables[$k] = $v;
}
$this->resortTable($tables);
$this->allTableStructures = $tables;
}
private function resortTable($tables)
{
$done = false;
while (!$done) {
foreach ($tables as $k => $v) {
if (!$v['constraints']) {
$this->tables[] = $k;
unset($tables[$k]);
} else {
$p = true;
foreach ($v['constraints'] as $cs) {
if (!in_array($cs['referenced_table_name'], $this->tables) && $cs['referenced_table_name'] != $k) {
$p = false;
break;
}
}
if ($p) {
$this->tables[] = $k;
unset($tables[$k]);
}
}
}
if (empty($tables)) {
$done = true;
}
}
}
/**
* 获取当前数据库表名
* @return string
*/
private function getTable(): string
{
return Str::lower(trim($this->input->getArgument('name')));
}
private function makeModel()
{
$stubFile = $this->path . 'model.stub';
$folder = $this->appPath . 'Model';
$this->makeFolder($folder);
$table = $this->table;
$modelName = Str::studly(Str::singular($table));
$file = $folder . "/" . $modelName . ".php";
$content = file_get_contents($stubFile);
$info = $this->currentTableStructure;
$filterFields = ["id", "created_at", "updated_at", "deleted_at"];
$fillAble = '';
$properties = '';
$timestamps = 0;
$softDelete = false;
$list = $info['fields'];
foreach ($list as $v) {
$name = $v['column_name'];
$pc = [
'bigint' => 'integer',
'int' => 'integer',
'tinyint' => 'integer',
'smallint' => 'integer',
'mediumint' => 'integer',
'integer' => 'integer',
'numeric' => 'integer',
'float' => 'float',
'real' => 'double',
'double' => 'double',
'decimal' => 'double',
'bool' => 'bool',
'boolean' => 'bool',
'char' => 'string',
'tinytext' => 'string',
'text' => 'string',
'mediumtext' => 'string',
'longtext' => 'string',
'year' => 'string',
'varchar' => 'string',
'string' => 'string',
'enum' => 'array',
'set' => 'array',
'date' => 'string',
'datetime' => 'string',
'custom_datetime' => 'string',
'timestamp' => 'string',
'collection' => 'collection',
'array' => 'array',
'json' => 'string',
];
$properties .= " * @property " . ($pc[$v['data_type']] ?? "string") . " $" . $name . ($v['column_comment'] ? " " . $v['column_comment'] : "") . "\n";
if ($name == 'created_at' || $name == 'updated_at') {
$timestamps++;
}
if ($name == 'deleted_at') {
$softDelete = true;
}
if (in_array($name, $filterFields)) {
continue;
}
$fillAble .= "\t\t'" . $name . "'," . "\n";
}
$relation = '';
if (isset($info['relations']) && $info['relations']) {
$relation .= "\n";
if (isset($info['relations']['belongsTo'])) {
foreach ($info['relations']['belongsTo'] as $v) {
$relation .= "\n\t/**\n\t* 属于" . $v['relation_model_name'] . "的关联\n\t*/";
$relation .= "\n\tpublic function " . $v['function'] . "()";
$relation .= "\n\t{";
$relation .= "\n\t\t" . 'return $this->belongsTo(' . $v['relation_model_name'] . "::class);";
$relation .= "\n\t}";
$properties .= " * @property " . $v['relation_model_name'] . " $" . $v['function'] . "\n";
}
}
if (isset($info['relations']['belongsToMany'])) {
foreach ($info['relations']['belongsToMany'] as $v) {
$relation .= "\n\t/**\n\t* 属于很多" . $v['relation_model_name'] . "的关联";
$relation .= "\n\t* @return \Hyperf\Database\Model\Relations\BelongsToMany";
$relation .= "\n\t**/";
$relation .= "\n\tpublic function " . $v['function'] . "()";
$relation .= "\n\t{";
$relation .= "\n\t\t" . 'return $this->belongsToMany(' . $v['relation_model_name'] . "::class,'"
. $v['relation_table'] . "');";
$relation .= "\n\t}";
$properties .= " * @property " . $v['relation_model_name'] . "[] $" . $v['function'] . "\n";
}
}
if (isset($info['relations']['hasMany'])) {
foreach ($info['relations']['hasMany'] as $v) {
$relation .= "\n\t/**\n\t* 有很多" . $v['relation_model_name'] . "的关联";
$relation .= "\n\t* @return \Hyperf\Database\Model\Relations\HasMany";
$relation .= "\n\t**/";
$relation .= "\n\tpublic function " . $v['function'] . "()";
$relation .= "\n\t{";
$relation .= "\n\t\t" . 'return $this->hasMany(' . $v['relation_model_name'] . "::class);";
$relation .= "\n\t}";
$properties .= " * @property " . $v['relation_model_name'] . "[] $" . $v['function'] . "\n";
}
}
}
$sd = '';
$sdn = '';
if ($softDelete) {
$sd = "use SoftDeletes;\n";
$sdn = "use Hyperf\Database\Model\SoftDeletes;\n";
}
$patterns = ['%namespace%', "%ClassName%", "%fillAble%", '%relations%', '%timestamps%', '%properties%', '%SoftDelete%'];
$replacements = [$sdn, $modelName, $fillAble, $relation, ($timestamps == 2 ? 'true' : 'false'), $properties, $sd];
$content = $this->buildField($patterns, $replacements, $content);
$this->writeToFile($file, $content);
}
/**
* 创建目录
* @param $folder
*/
private function makeFolder($folder)
{
if (!file_exists($folder)) {
@mkdir($folder, 0777, true);
}
}
/**
* 替换文件内容
* @param array $patterns 被替换的字符数组
* @param array $replacements 替换的字符数组
* @param string $content 文件原始内容
* @return string
*/
private function buildField(array $patterns, array $replacements, string $content): string
{
$author = $this->input->getOption("author");
$patterns = array_merge($patterns, ['%user%', '%date%', '%time%']);
$replacements = array_merge($replacements, [$author ?: "Auto generated.", date("Y-m-d"), date("h:i:s")]);
return str_replace($patterns, $replacements, $content);
}
/**
* 把内容写入文件
* @param string $file 文件路径和文件名
* @param string $content 文件内容
* @return bool 创建是否成功
*/
private function writeToFile(string $file, string $content): bool
{
$force = $this->input->getOption("force");
if (!$force && file_exists($file)) {
return false;
}
file_put_contents($file, $content);
$file = pathinfo($file, PATHINFO_FILENAME);
$this->info("<info>[INFO] Created File:</info> $file");
return true;
}
private function makeRepositoryInterface()
{
$stubFile = $this->path . 'repositoryInterface.stub';
$folder = $this->appPath . '/Repository/Interfaces';
$this->makeFolder($folder);
$table = $this->table;
$className = Str::studly(Str::singular($table)) . "Repository";
$file = $folder . "/" . $className . ".php";
$content = file_get_contents($stubFile);
$patterns = ["%ClassName%"];
$replacements = [$className];
$content = $this->buildField($patterns, $replacements, $content);
$this->writeToFile($file, $content);
}
private function makeRepository()
{
$stubFile = $this->path . 'repository.stub';
$folder = $this->appPath . '/Repository/Eloquent';
$this->makeFolder($folder);
$table = $this->table;
$modelClass = Str::studly(Str::singular($table));
$className = $modelClass . "RepositoryEloquent";
$file = $folder . "/" . $className . ".php";
$content = file_get_contents($stubFile);
$info = $this->currentTableStructure;
//列表
$list = "\$conditions = \$this->request->all();\n";
$list .= "\t\t\$list = \$this->model->where(function (\$q) use (\$conditions) {\n";
foreach ($info['fields'] as $v) {
if (Str::endsWith($v['column_name'], "_id")) {
$list .= "\t\t\tif(isset(\$conditions['" . $v['column_name'] . "']) && \$conditions['" . $v['column_name'] . "'] !== '') {\n";
$list .= "\t\t\t\t\$q->where('" . $v['column_name'] . "', \$conditions['" . $v['column_name'] . "']);\n";
$list .= "\t\t\t}\n";
} else if ($v['column_name'] == 'name' || Str::contains($v['column_name'], "_name")) {
$list .= "\t\t\tif(isset(\$conditions['" . $v['column_name'] . "']) && \$conditions['keyword'] !== '') {\n";
$list .= "\t\t\t\t\$q->where('" . $v['column_name'] . "', \$conditions['keyword']);\n";
$list .= "\t\t\t}\n";
}
}
$list .= "\t\t})";
//显示
$show = "\$info = \$this->model\n";
//新增
$create = "/** @var $modelClass \$model */\n \t\t\t\$model = parent::create(\$attributes);\n";
//新增
$update = "/** @var $modelClass \$model */\n \t\t\t\$model = parent::update(\$attributes, \$id);\n";
//删除
$delete = '';
//关联查询
$rs = "";
if (isset($info['relations']) && $info['relations']) {
if (isset($info['relations']) && $info['relations']) {
if (isset($info['relations']['belongsTo'])) {
$list .= "\n\t\t->with([";
$show .= "\n\t\t->with([";
$t = [];
foreach ($info['relations']['belongsTo'] as $v) {
$x = "\n\t\t\t'" . $v['function'] . "' => function (\$q) {\n";
$fields = $this->listColumns($v['relation_table']);
$fields = collect($fields['fields'])->keyBy('column_name')
->forget(['created_at', 'updated_at', 'deleted_at'])->pluck('column_name')->toArray();
$fields = join("','", $fields);
$x .= "\t\t\t\t\$q->select(['" . $fields . "']);\n";
$x .= "\t\t\t}";
$t[] = $x;
}
$t = join(",", $t) . "])";
$list .= $t;
$show .= $t;
}
if (isset($info['relations']['belongsToMany'])) {
$list .= "\n\t\t->with([";
$show .= "\n\t\t->with([";
$t = [];
foreach ($info['relations']['belongsToMany'] as $v) {
$x = "\n\t\t\t'" . $v['function'] . "' => function (\$q) {\n";
$fields = $this->listColumns($v['constraint_table']);
$fields = collect($fields['fields'])->keyBy('column_name')
->forget(['created_at', 'updated_at', 'deleted_at'])->pluck('column_name')->toArray();
$fields = join("','", $fields);
$x .= "\t\t\t\t\$q->select(['" . $fields . "'])->orderByDesc('id')->limit(20);\n";
$x .= "\t\t\t}";
$t[] = $x;
$create .= "\t\t\tisset(\$attributes['" . $v['constraint_table_key'] . "s']) && \$model->{$v['function']}()->sync(\$attributes['" . $v['constraint_table_key'] . "s']);\n";
$update .= "\t\t\tisset(\$attributes['" . $v['constraint_table_key'] . "s']) && \$model->{$v['function']}()->sync(\$attributes['" . $v['constraint_table_key'] . "s']);\n";
}
$t = join(",", $t) . "])";
$list .= $t;
$show .= $t;
}
if (isset($info['relations']['hasMany'])) {
foreach ($info['relations']['hasMany'] as $v) {
$f = Str::camel($v['function']);
$rs .= "\n\tpublic function $f(\$id): array\n";
$rs .= "\t{\n";
$rs .= "\t\t\$pageSize = (int)\$this->request->input('page_size', DEFAULT_PAGE_SIZE);\n";
$rs .= "\t\treturn \$this->find(\$id)->{$v['function']}()->orderByDesc('id')->paginate(\$pageSize)->toArray();\n";
$rs .= "\t}\n";
}
}
}
}
$list .= "\n\t\t->paginate(\$pageSize)\n";
$list .= "\t\t->toArray();\n";
$list .= "\t\treturn \$list;";
$show .= "\n\t\t->find(\$id)\n\t\t->toArray();\n";
$show .= "\t\treturn \$info;";
$patterns = ["%ModelClass%", "%list%", "%show%", "%create%", "%update%", "%delete%", "%rs%"];
$replacements = [$modelClass, $list, $show, $create, $update, $delete, $rs];
$content = $this->buildField($patterns, $replacements, $content);
$this->writeToFile($file, $content);
$this->addDepends($modelClass);
}
private function listColumns($table)
{
$table = trim($table);
return $this->allTableStructures[$table] ?? [];
}
/**
* 添加类到依赖注入配置文件
* @param $modelClass
*/
private function addDepends($modelClass)
{
$file = BASE_PATH . '/config/autoload/dependencies.php';
if (file_exists($file)) {
$content = file_get_contents($file);
if (strpos($content, "\App\Repository\Interfaces\\" . $modelClass . "Repository::class") !== false) {
return;
}
$content = str_replace("]", " \\App\\Repository\\Interfaces\\" . $modelClass . "Repository::class => \\App\\Repository\\Eloquent\\" . $modelClass . "RepositoryEloquent::class,\n]", $content);
$this->writeToFile($file, $content);
}
}
private function makeController()
{
$stubFile = $this->path . 'controller.stub';
$folder = $this->appPath . '/Controller';
$this->makeFolder($folder);
$table = $this->table;
$modelClass = Str::studly(Str::singular($table));
$className = $modelClass . "Controller";
$file = $folder . "/" . $className . ".php";
$content = file_get_contents($stubFile);
$info = $this->currentTableStructure;
//关联查询
$rs = "";
$routes = [];
if (isset($info['relations']) && $info['relations']) {
if (isset($info['relations']) && $info['relations']) {
if (isset($info['relations']['hasMany'])) {
foreach ($info['relations']['hasMany'] as $v) {
$f = Str::camel($v['function']);
$rs .= "\n\t/**";
$rs .= "\n\t * 获取{$v['relation_model_name']}关联列表数据";
$rs .= "\n\t * @Perm(\"index\")";
$rs .= "\n\t * @param; \$id id编号";
$rs .= "\n\t * @return mixed";
$rs .= "\n\t */";
$rs .= "\n\tpublic function $f(\$id)\n";
$rs .= "\t{\n";
$rs .= "\t\t\$data = \$this->repository->$f(\$id);\n";
$rs .= "\t\treturn success('获取成功', \$data);\n";
$rs .= "\t}\n";
$routes[] = $v['function'];
}
}
}
}
$patterns = ["%ModelClass%", "%rs%"];
$replacements = [$modelClass, $rs];
$content = $this->buildField($patterns, $replacements, $content);
$this->writeToFile($file, $content);
$this->addRoutes($modelClass, $routes);
}
/**
* 添加Controller到路由类
* @param $modelClass
* @param array $routes
*/
private function addRoutes($modelClass, $routes = [])
{
$file = BASE_PATH . '/config/routes.php';
if (file_exists($file)) {
$table = $this->table;
$content = file_get_contents($file);
$group = str_replace("_", "/", Str::snake($table));
if (strpos($content, "Router::addGroup('" . $group . "', function () {") !== false) {
return;
}
$info = $this->currentTableStructure;
$tableComment = (isset($info['table_comment']) && $info['table_comment']) ? $info['table_comment'] : $table;
$content .= "\n\t// " . $tableComment;
$content .= "\n\tRouter::addGroup('" . $group . "', function () {";
$content .= "\n\t\tRouter::get('', 'App\Controller\\" . $modelClass . "Controller@index');";
$content .= "\n\t\tRouter::get('/{id:\d+}', 'App\Controller\\" . $modelClass . "Controller@show');";
$content .= "\n\t\tRouter::post('', 'App\Controller\\" . $modelClass . "Controller@create');";
$content .= "\n\t\tRouter::patch('/{id:\d+}', 'App\Controller\\" . $modelClass . "Controller@update');";
$content .= "\n\t\tRouter::delete('/{id:\d+}', 'App\Controller\\" . $modelClass . "Controller@delete');";
if ($routes) {
foreach ($routes as $v) {
$content .= "\n\t\tRouter::get('/$v/{id:\d+}', 'App\Controller\\" . $modelClass . "Controller@$v');";
}
}
$content .= "\n\t});";
$this->writeToFile($file, $content);
}
}
/**
* 创建验证文件
*/
private function makeValidator()
{
$stubFile = $this->path . 'validator.stub';
$folder = $this->appPath . '/Validators';
$this->makeFolder($folder);
$table = $this->table;
$modelClass = Str::studly(Str::singular($table));
$className = $modelClass . "Validator";
$file = $folder . "/" . $className . ".php";
$content = file_get_contents($stubFile);
$info = $this->currentTableStructure;
$filterFields = ["id", "created_at", "updated_at", "deleted_at"];
$rules = '';
$attributes = '';
$list = $info['fields'];
foreach ($list as $v) {
$name = $v['column_name'];
$default = $v['column_default'];
$type = $v['data_type'];
$key = $v['column_key'];
$null = $v['is_nullable'];
$comment = $v['column_comment'];
$length = $v['length'];
if (in_array($name, $filterFields)) {
continue;
}
$rs = [];
$required = "nullable";
if ($null !== 'YES') {
if ($default !== '' && $default !== '0' && !$default) {
$required = "required";
}
}
$rs[] = $required;
switch ($type) {
case "bigint":
case "smallint":
case "tinyint":
case "mediumint":
case "int":
case "integer":
$rs[] = 'integer';
break;
case "decimal":
case "double":
case "float":
case "numeric":
case "real":
$rs[] = 'numeric';
break;
case "char":
case "varchar":
case "tinytext":
case "mediumtext":
case "longtext":
case "text":
$rs[] = 'string';
if ($length) {
$rs[] = 'max:' . $length;
}
break;
case "date":
case "datetime":
case "time":
case "timestamp":
case "year":
$rs[] = 'date';
break;
case "enum":
case "set":
$rs[] = 'in:[' . $length . "]";
break;
default:
if (Str::contains($name, "email") || Str::contains($name, "e-mail") || Str::contains($name, "e_mail")) {
$rs[] = 'email';
} elseif ($name == 'url'
|| Str::contains($name, "_url")
|| Str::contains($name, "url_")) {
$rs[] = 'url';
} elseif ($name == 'date'
|| Str::contains($name, "_date")
|| Str::contains($name, "date_")) {
$rs[] = 'date';
}
break;
}
if ($key == 'uni') {
$rs[] = "unique:$table," . $name;
}
if ($comment) {
$attributes .= "\t\t'" . $name . "' => '" . $comment . "'," . "\n";
}
$rules .= "\t\t\t'" . $name . "' => '" . implode("|", $rs) . "'," . ($comment ? "// " . $comment . "-" . $type : "//" . $type) . "\n";
}
$patterns = ["%ModelClass%", '%createRules%', '%updateRules%', '%attributes%'];
$createRules = $rules;
$updateRules = str_replace("nullable", "sometimes|nullable", $rules);
$updateRules = str_replace("required", "sometimes|required", $updateRules);
$replacements = [$modelClass, $createRules, $updateRules, $attributes];
$content = $this->buildField($patterns, $replacements, $content);
$this->writeToFile($file, $content);
}
private function makeSeeder()
{
$stubFile = $this->path . 'seeder.stub';
$folder = BASE_PATH . '/seeders/seeders/';
$this->makeFolder($folder);
$table = $this->table;
$modelClass = Str::studly(Str::singular($table));
$className = Str::studly($table) . "TableSeeder";
$file = $folder . "/" . $table . "_table_seeder.php";
$content = file_get_contents($stubFile);
$info = $this->currentTableStructure;
$filterFields = ["id"];
$fields = [];
$otherModel = [];
$generateCount = $this->input->getOption("seeder");
$generateCount = $generateCount ?: 30;
$otherProcess = "";
$list = $info['fields'];
$maxNumber = 4;
foreach ($list as $v) {
$name = $v['column_name'];
$type = $v['data_type'];
$length = explode(" ", $v['length']);
if (in_array($name, $filterFields)) {
continue;
}
switch ($type) {
case "bigint":
case "smallint":
case "tinyint":
case "mediumint":
case "int":
case "integer":
if ($name == "sex") {
$fields[] = "\t\t\t\t'$name' => \$faker->randomElement([0,1]),";
} else if (Str::contains($name, "status")) {
$fields[] = "\t\t\t\t'$name' => \$faker->randomDigit,";
} else if (Str::endsWith($name, "_id")) {
$o = str_replace("_id", "", $name);
$os = Str::plural($o);
if (in_array($os, $this->tables)) {
$o = Str::studly($o);
$otherModel[] = "\nuse App\Model\\" . $o . ";";
$fields[] = "\t\t\t\t'$name' => $o::orderBy(Db::raw('rand()'))->first()->id,";
} else {
$n = ((isset($length[0]) && $length[0] && $length[0] < $maxNumber) ? $length[0] : $maxNumber);
$n = rand(1, $n);
$fields[] = "\t\t\t\t'$name' => \$faker->randomNumber($n),";
}
} else {
$n = ((isset($length[0]) && $length[0] < $maxNumber) ? $length[0] : $maxNumber);
$n = rand(1, $n);
$fields[] = "\t\t\t\t'$name' => \$faker->randomNumber($n),";
}
break;
case "decimal":
case "double":
case "float":
case "numeric":
case "real":
$n = ((isset($length[0]) && $length[0] && $length[0] < $maxNumber) ? $length[0] : $maxNumber);
$n = rand(1, $n);
$n2 = ((isset($length[1]) && $length[1] < $maxNumber) ? $length[1] : 2);
$n3 = $n2 + 20;
$fields[] = "\t\t\t\t'$name' => \$faker->randomFloat($n,$n2,$n3),";
break;
case "char":
case "varchar":
$n = ((isset($length[0]) && $length[0]) ? $length[0] : 255);
if ($name == "ip" || $name == "ip_address" || $name == "ip_addr") {
$fields[] = "\t\t\t\t'$name' => \$faker->ipv4,";
} else if (Str::contains($name, "email")) {
$fields[] = "\t\t\t\t'$name' => \$faker->email,";
} else if ($name == "userName" || $name == "user_name" || $name == "uname") {
$fields[] = "\t\t\t\t'$name' => \$faker->userName,";
} else if ($name == "url" || $name == "domain" || Str::endsWith($name, "_url") || Str::startsWith($name, "url_")) {
$fields[] = "\t\t\t\t'$name' => \$faker->url,";
} else if ($name == "company" || $name == "company_name") {
$fields[] = "\t\t\t\t'$name' => \$faker->company,";
} else if ($name == "gender") {
$fields[] = "\t\t\t\t'$name' => \$faker->title(),";
} else if ($name == "name") {
$fields[] = "\t\t\t\t'$name' => \$faker->name(),";
} else if (Str::contains($name, "city")) {
$fields[] = "\t\t\t\t'$name' => \$faker->city,";
} else if (Str::contains($name, "street") || Str::contains($name, "address")) {
$fields[] = "\t\t\t\t'$name' => \$faker->streetAddress,";
} else if (Str::contains($name, "postcode")) {
$fields[] = "\t\t\t\t'$name' => \$faker->postcode,";
} else if (Str::contains($name, "country")) {
$fields[] = "\t\t\t\t'$name' => \$faker->country,";
} else if (Str::contains($name, "phoneNumber") || $name == "tel" || $name == "mobile" || Str::contains($name, "phone")) {
$fields[] = "\t\t\t\t'$name' => \$faker->phoneNumber,";
} else if (Str::contains($name, "color")) {
$fields[] = "\t\t\t\t'$name' => \$faker->colorName,";
} else if (Str::contains($name, "image") || Str::contains($name, "path")) {
$fields[] = "\t\t\t\t'$name' => \$faker->imageUrl(640, 480),";
} else if ($name == "ean" || $name == "bar_code") {
$fields[] = "\t\t\t\t'$name' => \$faker->ean13,";
} else if ($n < 10) {
$fields[] = "\t\t\t\t'$name' => \$faker->word,";
} elseif ($n < 100) {
$fields[] = "\t\t\t\t'$name' => \$faker->sentence(6),";
} else {
$n = rand(2, 5);
$fields[] = "\t\t\t\t'$name' => \$faker->paragraph($n, true),";
}
break;
case "tinytext":
case "mediumtext":
case "longtext":
case "text":
$fields[] = "\t\t\t\t'$name' => \$faker->text,";
break;
case "date":
$fields[] = "\t\t\t\t'$name' => \$faker->date('Y-m-d'),";
break;
case "datetime":
$fields[] = "\t\t\t\t'$name' => \$faker->date('Y-m-d').' '.\$faker->time('H:i:s'),";
break;
case "time":
$fields[] = "\t\t\t\t'$name' => \$faker->time('H:i:s'),";
break;
case "timestamp":
if ($name == 'created_at' || $name == 'updated_at' || $name == 'deleted_at') {
$fields[] = "\t\t\t\t'$name' => \$faker->date('Y-m-d').' '.\$faker->time('H:i:s'),";
} else {
$fields[] = "\t\t\t\t'$name' => \$faker->unixTime(),";
}
break;
case "year":
$fields[] = "\t\t\t\t'$name' => \$faker->year(),";
break;
case "enum":
case "set":
$n = implode(",", $length);
$fields[] = "\t\t\t\t'$name' => \$faker->randomElement([$n]),";
break;
default:
$fields[] = "\t\t\t\t'$name' => \$faker->word,";
break;
}
}
$fields = join("\n", $fields);
$otherModel = join("", $otherModel);
$patterns = ["%modelClass%", '%className%', '%otherModel%', '%generateCount%', '%fields%', '%otherProcess%'];
$replacements = [$modelClass, $className, $otherModel, $generateCount, $fields, $otherProcess];
$content = $this->buildField($patterns, $replacements, $content);
$this->writeToFile($file, $content);
$this->addSeeder();
}
private function addSeeder()
{
$stubFile = $this->path . 'databaseSeeder.stub';
$folder = BASE_PATH . '/seeders/';
$file = $folder . "/DatabaseSeeder.php";
if (!file_exists($file)) {
$content = file_get_contents($stubFile);
$this->writeToFile($file, $content);
}
$content = file_get_contents($file);
if (strpos($content, Str::studly($this->table) . "TableSeeder::class") !== false) {
return;
}
$content = str_replace("];", "\t\t\t" . Str::studly($this->table) . "TableSeeder::class,\n\t\t];", $content);
$this->writeToFile($file, $content);
}
private function makeMigrate()
{
$stubFile = $this->path . 'migration.stub';
$folder = BASE_PATH . '/migrations';
$this->makeFolder($folder);
$table = $this->table;
$className = "Create" . Str::studly($table) . "Table";
$file = $folder . "/" . $this->getDatePrefix() . "_create_" . $table . "_table.php";
$content = file_get_contents($stubFile);
$info = $this->currentTableStructure;
$attributes = [];
$timestamps = 0;
//$b = new Blueprint('test');
$softDelete = false;
$pri = false;
//生成字段
foreach ($info['fields'] as $v) {
$name = $v['column_name'];
$default = $v['column_default'];
$type = $v['data_type'];
$collation = $v['collation_name'];
$null = $v['is_nullable'];
$extra = $v['extra'];
$comment = $v['column_comment'];
$length = $v['length'];
if ($name == 'updated_at' || $name == 'created_at') {
$timestamps++;
} elseif ($name == 'deleted_at') {
$softDelete = true;
} else {
$t = "\t\t\t\$table->";
switch ($type) {
case "bigint":
case "smallint":
case "tinyint":
case "mediumint":
case "int":
case "integer":
if ($type == 'int' || $type == 'integer') {
$t .= "integer('" . $name . "'";
} else {
$t .= str_replace("int", "", $type) . "Integer('" . $name . "'";
}
if ($extra == 'auto_increment') {
$pri = true;
$t .= ", true";
} else {
$t .= ", false";
}
if ($length && strpos($length, "unsigned") !== false) {
$t .= ", true";
}
$t .= ")";
break;
case "decimal":
case "double":
case "float":
case "numeric":
case "real":
$tc = [
'numeric' => 'decimal',
'real' => 'double',
];
$t .= ($tc[$type] ?? $type) . "('" . $name . "'";
if ($length) {
$length = explode(" ", $length);
$length = explode(",", $length[0]);
$t .= ", " . $length[0] . ", " . $length[1];
}
$t .= ")";
break;
case "char":
case "varchar":
case "tinytext":
case "mediumtext":
case "longtext":
case "desc":
case "bit":
case "boolean":
case "text":
$tc = [
'char' => 'char',
'varchar' => 'string',
'desc' => 'text',
'tinytext' => 'text',
'text' => 'text',
'bit' => 'boolean',
'boolean' => 'boolean',
'longtext' => 'longText',
'mediumtext' => 'mediumText',
];
$t .= $tc[$type] . "('" . $name . "'";
if ($length) {
$t .= ", " . $length;
}
$t .= ")";
break;
case "date":
case "datetime":
case "time":
case "timestamp":
case "year":
$tc = [
'datetime' => 'dateTime',
];
$t .= ($tc[$type] ?? $type) . "('" . $name . "')";
break;
case "binary":
case "varbinary":
case "longblob":
case "blob":
case "mediumblob":
case "tinyblob":
$t .= "binary('" . $name . "')";
break;
case "enum":
case "set":
$tc = [
'set' => 'enum',
];
$t .= ($tc[$type] ?? $type) . "('" . $name . "'";
$t .= ", [{
$length}])";
break;
case "geometry":
case "geometrycollection":
case "json":
case "jsonb":
case "point":
case "polygon":
case "linestring":
case "multipoint":
case "multipolygon":
case "multilinestring":
$tc = [
'geometrycollection' => 'geometryCollection',
'linestring' => 'lineString',
'multipoint' => 'multiPoint',
'multipolygon' => 'multiPolygon',
'multilinestring' => 'multiLineString',
];
$t .= ($tc[$type] ?? $type) . "('" . $name . "'";
if ($length) {
$t .= ", " . $length;
}
$t .= ")";
break;
default:
$t = '';
break;
}
if ($t) {
if ($null == 'YES') {
$t .= "->nullable()";
}
if (($default && $default !== '') || $default === '0') {
if ($type !== 'timestamp') {
$t .= "->default('$default')";
}
}
if ($collation) {
$t .= "->collation('$collation')";
}
if ($comment) {
$t .= "->comment('$comment')";
}
$t .= ";";
$attributes[] = $t;
}
}
}
if ($timestamps == 2) {
$attributes[] = "\t\t\t\$table->timestamps();";
}
if ($softDelete) {
$attributes[] = "\t\t\t\$table->softDeletes();";
}
//主键及索引
if ($info['indexes']) {
foreach ($info['indexes'] as $k => $v) {
$fields = collect($v)->pluck("column_name")->all();
$fields = implode("','", $fields);
if ($k == 'PRIMARY') {
if (!$pri) {
$attributes[] = "\t\t\t\$table->primary(['$fields']);";
}
} else {
if ($v[0]['non_unique'] === 0) {
$attributes[] = "\t\t\t\$table->unique(['$fields'], '$k');";
} else {
$attributes[] = "\t\t\t\$table->index(['$fields'], '$k');";
}
}
}
}
//外键
if ($info['constraints']) {
foreach ($info['constraints'] as $v) {
$t = "\t\t\t\$table->foreign('{$v['column_name']}','{$v['constraint_name']}')->references('{$v['referenced_column_name']}')->on('{$v['referenced_table_name']}')";
if ($v['delete_rule']) {
$t .= "->onDelete('{$v['delete_rule']}')";
}
/*if ($v['update_rule']) {
$t .= "->onUpdate('{$v['update_rule']}')";
}*/
$attributes[] = $t . ";";
}
}
$attributes = implode("\n", $attributes);
$tableComment = "";
if (isset($info['table_comment']) && $info['table_comment']) {
$tableComment = 'Db::statement("alter table `' . $table . '` comment \'' . $info['table_comment'] . '\'");';
}
$patterns = ["%ClassName%", '%tableName%', '%attributes%', '%tableComment%'];
$replacements = [$className, $table, $attributes, $tableComment];
$content = $this->buildField($patterns, $replacements, $content);
$this->writeToFile($file, $content);
}
/**生成迁移文件的日期格式
* @return string
*/
private function getDatePrefix(): string
{
return date('Y_m_d_His');
}
public function configure()
{
parent::configure();
$this->addOption('all', 'a', InputOption::VALUE_NONE, '生成所有文件');
$this->addOption('model', 'm', InputOption::VALUE_NONE, '生成model文件');
$this->addOption('controller', 'c', InputOption::VALUE_NONE, '生成controller文件');
$this->addOption('migrate', 'i', InputOption::VALUE_NONE, '生成迁移文件');
$this->addOption('validator', 'l', InputOption::VALUE_NONE, '生成验证文件');
$this->addOption('author', 'r', InputOption::VALUE_OPTIONAL, '文件作者,后面可跟空格名字,表示生成的文件作者');
$this->addOption('seeder', 's', InputOption::VALUE_OPTIONAL, '生成数据填充文件,后面可跟空格数字,表示生成的数据量');
$this->addOption('force', 'f', InputOption::VALUE_NONE, '文件存在是否覆盖');
$this->addOption('database', 'd', InputOption::VALUE_NONE, '全数据库索引自动生成全站文件');
$this->setDescription('根据数据表生成model文件和迁移文件和控制器');
}
/**
* 配置文件内容
* @return array
*/
protected function getArguments()
{
return [
['name', InputArgument::OPTIONAL, '数据库表名'],
];
}
}
<?php
/**
* Created by PhpStorm.
* User: %user%
* Date: %date%
* Time: %time%
* Description:
*/
namespace App\Controller;
use App\Repository\Interfaces\%ModelClass%Repository;
use Hyperf\Di\Annotation\Inject;
use Meibuyu\Micro\Annotation\AutoPerm;
use Meibuyu\Micro\Annotation\Perm;
/**
* @AutoPerm()
* Class %ModelClass%Controller
* @package App\Controller
*/
class %ModelClass%Controller extends AbstractController
{
/**
* @Inject()
* @var %ModelClass%Repository
*/
private $repository;
/**
* 获取列表数据
* @return mixed
*/
public function index()
{
$list = $this->repository->list();
return success('获取成功', $list);
}
/**
* 获取列表数据
* @Perm("index")
* @param int $id id编号
* @return mixed
*/
public function show($id)
{
$data = $this->repository->show($id);
return success('获取成功', $data);
}
/**
* 添加记录
* @return mixed
*/
public function create()
{
$data = $this->repository->create($this->request->all());
return success('创建成功', $data);
}
/**
* 更新数据
* @param int $id id编号
* @return mixed
*/
public function update($id)
{
$data = $this->repository->update($this->request->all(), $id);
return success('更新成功', $data);
}
/**
* 删除单条数据
* @param int $id id编号
* @return mixed
*/
public function delete($id)
{
$deleted = $this->repository->delete($id);
return success('删除成功', $deleted);
}
%rs%
}
<?php
declare(strict_types=1);
use Hyperf\Database\Seeders\Seeder;
use Hyperf\DbConnection\Db;
class DataBaseSeeder extends Seeder
{
/**
* Run the database seeds.
* @return void
*/
public function run()
{
/**
* var $seeders 可执行的seeder填充类
**/
$seeders = [
];
Db::statement('SET FOREIGN_KEY_CHECKS = 0');
foreach($seeders as $item){
require BASE_PATH . "/seeders/seeders/" . \Hyperf\Utils\Str::snake(str_replace("::class", "", $item)) . ".php";
$instance = new $item;
$instance->run();
}
Db::statement('SET FOREIGN_KEY_CHECKS = 1');
}
}
<?php
/**
* Created by PhpStorm.
* User: %user%
* Date: %date%
* Time: %time%
* Description:
*/
use Hyperf\Database\Migrations\Migration;
use Hyperf\Database\Schema\Blueprint;
use Hyperf\Database\Schema\Schema;
use Hyperf\DbConnection\Db;
class %ClassName% extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::disableForeignKeyConstraints();
Schema::create('%tableName%', function (Blueprint $table) {
%attributes%
});
%tableComment%
Schema::enableForeignKeyConstraints();
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::disableForeignKeyConstraints();
Schema::dropIfExists('%tableName%');
Schema::enableForeignKeyConstraints();
}
}
<?php
/**
* Created by PhpStorm.
* User: %user%
* Date: %date%
* Time: %time%
* Description:
*/
declare (strict_types=1);
namespace App\Model;
%namespace%
/**
* 模型类 %ClassName%
* @package App\Model
%properties%
*/
class %ClassName% extends Model
{
%SoftDelete%
/**
* 是否使用时间戳管理
* @var bool
*/
public $timestamps = %timestamps%;
/**
* 可写入数据的字段.
* @var array
*/
protected $fillable = [
%fillAble%
];
%relations%
}
<?php
/**
* Created by PhpStorm.
* User: %user%
* Date: %date%
* Time: %time%
* Description:
*/
declare(strict_types=1);
namespace App\Repository\Eloquent;
use App\Model\%ModelClass%;
use App\Repository\Interfaces\%ModelClass%Repository;
use App\Validators\%ModelClass%Validator;
use Hyperf\DbConnection\Db;
use Meibuyu\Micro\Exceptions\HttpResponseException;
use Meibuyu\Micro\Repository\Eloquent\BaseRepository;
/**
* Class %ModelClass%RepositoryEloquent
* @package App\Repository\Eloquent;
*/
class %ModelClass%RepositoryEloquent extends BaseRepository implements %ModelClass%Repository
{
public function model()
{
return %ModelClass%::class;
}
public function validator()
{
return %ModelClass%Validator::class;
}
/**
* 获取数据列表
* @return array
*/
public function list()
{
$pageSize = (int)$this->request->input('page_size', DEFAULT_PAGE_SIZE);
%list%
}
/**
* 获取单条数据
* @param $id
* @return array
*/
public function show($id)
{
%show%
}
/**
* 添加记录
* @param array $attributes
* @return bool
*/
public function create(array $attributes)
{
Db::transaction(function () use ($attributes) {
%create%
});
return true;
}
/**
* 更新数据
* @param array $attributes
* @param $id
* @return bool
*/
public function update(array $attributes, $id)
{
Db::transaction(function () use ($attributes, $id) {
%update%
});
return true;
}
/**
* 删除单条数据
* @param $id
* @return bool
* @throws HttpResponseException
*/
public function delete($id)
{
return parent::delete($id); // TODO: Change the autogenerated stub
}
%rs%
}
<?php
/**
* Created by PhpStorm.
* User: %user%
* Date: %date%
* Time: %time%
* Description:
*/
declare(strict_types=1);
namespace App\Repository\Interfaces;
use Meibuyu\Micro\Repository\Contracts\RepositoryInterface;
/**
* Interface %ClassName%
* @package App\Repository\Interfaces
*/
interface %ClassName% extends RepositoryInterface
{
}
<?php
declare(strict_types=1);
use App\Model\%modelClass%;%otherModel%
use Faker\Factory;
use Hyperf\Database\Seeders\Seeder;
use Hyperf\DbConnection\Db;
class %className% extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Factory::create('zh-CN');
%modelClass%::query()->truncate();
$n = %generateCount%;
for ($i = 0; $i < $n; $i++) {
$instance = %modelClass%::create([
%fields%
]);
%otherProcess%
}
}
}
<?php
/**
* Created by PhpStorm.
* User: %user%
* Date: %date%
* Time: %time%
* Description:
*/
declare(strict_types=1);
namespace App\Validators;
use Meibuyu\Micro\Validator\Contracts\ValidatorInterface;
use Meibuyu\Micro\Validator\HyperfValidator;
class %ModelClass%Validator extends HyperfValidator
{
protected $rules = [
ValidatorInterface::RULE_CREATE => [
%createRules%
],
ValidatorInterface::RULE_UPDATE => [
%updateRules%
],
];
protected $attributes = [
%attributes%
];
}
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/2/11
* Time: 11:49
*/
namespace Meibuyu\Micro;
class ConfigProvider
{
public function __invoke(): array
{
return [
'exceptions' => [
/**
* 以下异常处理器会合并到项目的config/autoload/exceptions.php文件配置数组的前面;
* 请勿在此使用顶级异常捕获处理器,防止项目中异常处理器无效;
*/
'handler' => [
'http' => [
\Meibuyu\Micro\Exceptions\Handler\MicroExceptionHandler::class,
\Meibuyu\Micro\Exceptions\Handler\QueryExceptionHandler::class,
\Meibuyu\Micro\Exceptions\Handler\PhpSpreadsheetExceptionHandler::class,
],
],
],
'dependencies' => [
\Hyperf\ServiceGovernance\Listener\RegisterServiceListener::class => \Meibuyu\Micro\Listener\RegisterServiceListener::class,
],
'commands' => [
\Meibuyu\Micro\Command\MakeModelCommand::class,
],
'annotations' => [
'scan' => [
'paths' => [
__DIR__,
],
],
],
];
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Time: 2020/10/27 9:21
*/
namespace Meibuyu\Micro\Constants;
class Department
{
const WAREHOUSE_LOGISTICS = 5; // 仓储物流部
const WAREHOUSE = 6; // 仓储部
const LOGISTICS = 7; // 物流部
const FINANCE = 10; //财务部
const HR = 18; // 行政人事部
const TECH = 34; // 技术部
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/3/23
* Time: 17:45
*/
namespace Meibuyu\Micro\Constants;
class MaterialCategory
{
const FU_LIAO = 1; // 辅料
const MIAN_LIAO = 2; // 面料
const LI_BU = 3; // 里布
const HAO_CAI = 4; // 耗材
const FU_LIAO_WAREHOUSE = 33;
const MIAN_LIAO_WAREHOUSE = 36;
/**
* 通过原料类型获取对应仓库id
* @param $categoryId
* @return int
* @throws \Exception
*/
public static function getWarehouseId($categoryId)
{
switch ($categoryId) {
case self::FU_LIAO:
case self::HAO_CAI:
return self::FU_LIAO_WAREHOUSE;
case self::MIAN_LIAO:
case self::LI_BU:
return self::MIAN_LIAO_WAREHOUSE;
default:
throw new \Exception("原料类型不存在");
}
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/6/18
* Time: 8:32
*/
namespace Meibuyu\Micro\Constants;
class SpecialUserId
{
const BOSS = 38; // 王大抗
const ZHANG_TING_TING = 83; // 张婷婷
const MA_BAO_TONG = 84; // 马宝同
const CAI_YA_XIANG = 85; // 蔡亚祥
const CAI_HONG_SHAN = 86; // 蔡红山
const ZHAN_YONG_YONG = 87; // 詹永勇
const LI_DONG = 292; // 李东
const BAO_SHI_FANG= 322; // 鲍诗芳
}
<?php
declare(strict_types=1);
namespace Meibuyu\Micro\Exceptions\Handler;
use Exception;
use Hyperf\Amqp\Producer;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Logger\LoggerFactory;
use Meibuyu\Micro\Amqp\Producer\ExceptionLogProducer;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Throwable;
class AppExceptionHandler extends ExceptionHandler
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* @var StdoutLoggerInterface
*/
protected $stdoutLogger;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var ConfigInterface
*/
protected $config;
/**
* @var RequestInterface
*/
protected $request;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->stdoutLogger = $container->get(StdoutLoggerInterface::class);
$this->logger = $container->get(LoggerFactory::class)->get('Uncaught Exception');
$this->config = $container->get(ConfigInterface::class);
$this->request = $container->get(RequestInterface::class);
}
public function handle(Throwable $throwable, ResponseInterface $response)
{
// 捕获所有未捕获的异常
$this->stopPropagation();
$api = sprintf('%s(%s)', $this->request->getUri(), $this->request->getMethod());
$exceptionClass = get_class($throwable);
$message = $throwable->getMessage();
$line = $throwable->getLine();
$file = $throwable->getFile();
$code = $throwable->getCode();
$trace = $throwable->getTraceAsString();
$data = [
'api' => $api,
'server' => $this->config->get('app_name'),
'file' => $file,
'line' => $line,
'message' => $message,
'trace' => $trace,
'code' => $code,
'created_at' => now(),
];
try {
$exceptionLogProducer = new ExceptionLogProducer($data);
$producer = $this->container->get(Producer::class);
$producer->produce($exceptionLogProducer);
} catch (Exception $e) {
put_log('异常日志失败; ' . $e->getMessage(), 'ExceptionLogProducer.log');
}
$msg = sprintf('%s: %s(%s) in %s:%s', $exceptionClass, $message, $code, $file, $line);
$error = sprintf("API: %s\n%s\nStack trace:\n%s", $api, $msg, $trace);
$this->logger->error($error);
$this->stdoutLogger->error($error);
return $response->withStatus(500)->withBody(new SwooleStream($msg));
}
public function isValid(Throwable $throwable): bool
{
return true;
}
}
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/2/7
* Time: 16:39
*/
namespace Meibuyu\Micro\Exceptions\Handler;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Meibuyu\Micro\Exceptions\HttpResponseException;
use Meibuyu\Micro\Exceptions\ValidatorException;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class MicroExceptionHandler extends ExceptionHandler
{
public function handle(Throwable $throwable, ResponseInterface $response)
{
// 判断被捕获到的异常是希望被捕获的异常
if ($throwable instanceof HttpResponseException) {
// 格式化输出
$data = json_encode([
'code' => $throwable->getCode() ?: 400,
'msg' => $throwable->getMessage(),
], JSON_UNESCAPED_UNICODE);
// 阻止异常冒泡
$this->stopPropagation();
return $response
->withAddedHeader('content-type', 'application/json')
->withBody(new SwooleStream($data));
} else if ($throwable instanceof ValidatorException) {
$this->stopPropagation(); // 阻止异常冒泡
/** @var ValidatorException $throwable */
$data = json_encode([
'code' => $throwable->getCode() ?: 401,
'msg' => $throwable->first(),
], JSON_UNESCAPED_UNICODE);
return $response
->withAddedHeader('content-type', 'application/json')
->withBody(new SwooleStream($data));
}
return $response; // 交给下一个异常处理器
}
public function isValid(Throwable $throwable): bool
{
return true;
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/24
* Time: 18:05
*/
namespace Meibuyu\Micro\Exceptions\Handler;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class PhpSpreadsheetExceptionHandler extends ExceptionHandler
{
public function handle(Throwable $throwable, ResponseInterface $response)
{
$error = $throwable->getMessage();
if ($throwable instanceof PhpSpreadsheetException) {
if (strpos($error, 'Formula Error') !== false) {
$msg = '表格公式错误, 请检查是否引用其它表格数据';
return $this->jsonResponse($msg, $response);
}
} else if (strpos($error, 'PhpOffice\PhpSpreadsheet\Writer\Xls::writeSummaryProp()') !== false) {
$msg = '表格格式兼容错误,请上传 xlsx 结尾的excel';
return $this->jsonResponse($msg, $response);
}
return $response;
}
public function jsonResponse($msg, ResponseInterface $response)
{
// 阻止异常冒泡
$this->stopPropagation();
// 格式化输出
$data = json_encode([
'code' => 400,
'msg' => $msg,
], JSON_UNESCAPED_UNICODE);
return $response->withAddedHeader('content-type', 'application/json')->withBody(new SwooleStream($data));
}
public function isValid(Throwable $throwable): bool
{
return true;
}
}
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/5/12
* Time: 16:11
*/
namespace Meibuyu\Micro\Exceptions\Handler;
use Hyperf\Database\Exception\QueryException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class QueryExceptionHandler extends ExceptionHandler
{
public function handle(Throwable $throwable, ResponseInterface $response)
{
$code = $throwable->getCode();
$msg = $throwable->getMessage();
if ($code == 23000 && strpos($msg, 'foreign key') !== false) {
// 格式化输出
$data = json_encode([
'code' => 400,
'msg' => '此数据下有关联的数据,不可进行操作',
], JSON_UNESCAPED_UNICODE);
// 阻止异常冒泡
$this->stopPropagation();
return $response
->withAddedHeader('content-type', 'application/json')
->withBody(new SwooleStream($data));
}
return $response; // 交给下一个异常处理器
}
public function isValid(Throwable $throwable): bool
{
return $throwable instanceof QueryException;
}
}
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/2/7
* Time: 16:18
*/
namespace Meibuyu\Micro\Exceptions;
class HttpResponseException extends \Exception
{
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/3/17
*/
namespace Meibuyu\Micro\Exceptions;
use Throwable;
class ObjectNotExistException extends \Exception
{
public function __construct($message, $code = 0, Throwable $previous = null)
{
parent::__construct($message . ' Not Exist!', $code, $previous);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/2/7
* Time: 15:24
*/
namespace Meibuyu\Micro\Exceptions;
class RepositoryException extends \Exception
{
}
\ No newline at end of file
<?php
namespace Meibuyu\Micro\Exceptions;
use Hyperf\Utils\Contracts\MessageBag;
class ValidatorException extends \Exception
{
/**
* @var MessageBag
*/
protected $messageBag;
/**
* @param MessageBag $messageBag
*/
public function __construct(MessageBag $messageBag)
{
parent::__construct('The given data was invalid.');
$this->messageBag = $messageBag;
}
/**
* @return MessageBag
*/
public function errors()
{
return $this->messageBag;
}
/**
* @return string
*/
public function first()
{
return $this->messageBag->first();
}
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray(): array
{
return [
'error' => 'validation_exception',
'error_description' => $this->errors()
];
}
/**
* Convert the object to its JSON representation.
*
* @param int $options
* @return string
*/
public function toJson($options = 0)
{
return json_encode($this->toArray(), $options);
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/7/3
* Time: 14:27
*/
namespace Meibuyu\Micro\Handler;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\Annotation\Inject;
use Meibuyu\Micro\Exceptions\HttpResponseException;
use Meibuyu\Micro\Model\Auth;
use Meibuyu\Micro\Service\Interfaces\MessageServiceInterface;
class MessageHandler
{
/**
* @Inject()
* @var MessageServiceInterface
*/
private $messageService;
/**
* @Inject
* @var ConfigInterface
*/
protected $config;
/**
* 发送模板消息
* @param $receiverIds
* @param $templateId
* @param array $replace
* @throws HttpResponseException
*/
public function sendTemp($receiverIds, $templateId, $replace = [])
{
$application = $this->config->get('app_name');
if (!$application) {
throw new HttpResponseException("请设置应用名app_name");
}
$receiverIds = is_array($receiverIds) ? $receiverIds : [$receiverIds];
$sendUserId = Auth::id();
$this->messageService->send($receiverIds, $application, $templateId, $sendUserId, $replace);
}
/**
* 发送文本消息
* @param $receiverIds
* @param $content
* @throws HttpResponseException
*/
public function sendText($receiverIds, $content)
{
$application = $this->config->get('app_name');
if (!$application) {
throw new HttpResponseException("请设置应用名app_name");
}
$receiverIds = is_array($receiverIds) ? $receiverIds : [$receiverIds];
$sendUserId = Auth::id();
$this->messageService->send($receiverIds, $application, 0, $sendUserId, [], $content);
}
/**
* 自动发送模板消息
* @param $receiverIds
* @param $templateId
* @param array $replace
* @throws HttpResponseException
*/
public function sendAutoTemp($receiverIds, $templateId, $replace = [])
{
$application = $this->config->get('app_name');
if (!$application) {
throw new HttpResponseException("请设置应用名app_name");
}
$receiverIds = is_array($receiverIds) ? $receiverIds : [$receiverIds];
$this->messageService->send($receiverIds, $application, $templateId, 0, $replace);
}
/**
* 自动发送文本消息
* @param $receiverIds
* @param $content
* @throws \Exception
*/
public function sendAutoText($receiverIds, $content)
{
$application = $this->config->get('app_name');
if (!$application) {
throw new \Exception("请设置应用名app_name");
}
$receiverIds = is_array($receiverIds) ? $receiverIds : [$receiverIds];
$this->messageService->send($receiverIds, $application, 0, 0, [], $content);
}
/**
* 定时任务专用 发送文本消息
* @param $receiverIds
* @param $content
* @throws HttpResponseException
*/
public function sendCrontabText($receiverIds, $content)
{
$application = $this->config->get('app_name');
if (!$application) {
throw new HttpResponseException("请设置应用名app_name");
}
$receiverIds = is_array($receiverIds) ? $receiverIds : [$receiverIds];
$this->messageService->send($receiverIds, $application, 0, 0, [], $content);
}
/**
* 发送markdown 文本消息
* @param $receiverIds
* @param $content
* @param $title
* @throws HttpResponseException
*/
public function sendMarkDownText($receiverIds, $content, $title)
{
$application = $this->config->get('app_name');
if (!$application) {
throw new HttpResponseException("请设置应用名app_name");
}
$receiverIds = is_array($receiverIds) ? $receiverIds : [$receiverIds];
$sendUserId = Auth::id();
$this->messageService->SendMarkDownMessage($receiverIds, $application, 0, $sendUserId, [], $content, $title);
}
/**
* 自动发送markdown 文本消息
* @param $receiverIds
* @param $content
* @param $title
* @throws HttpResponseException
*/
public function sendAutoMarkDownText($receiverIds, $content, $title)
{
$application = $this->config->get('app_name');
if (!$application) {
throw new HttpResponseException("请设置应用名app_name");
}
$receiverIds = is_array($receiverIds) ? $receiverIds : [$receiverIds];
$this->messageService->SendMarkDownMessage($receiverIds, $application, 0, 0, [], $content, $title);
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/4/8
* Time: 16:12
*/
namespace Meibuyu\Micro\Handler;
use Hyperf\Di\Annotation\Inject;
use Meibuyu\Micro\Model\Auth;
use Meibuyu\Micro\Service\Interfaces\UserServiceInterface;
class PermHandler
{
/**
* @Inject()
* @var UserServiceInterface
*/
protected $userServer;
public function check($perm)
{
return $this->userServer->checkPerm(Auth::id(), $perm);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: 梁俊杰
* Date: 2019/12/03
* Time: 16:08
* Description:
*/
namespace Meibuyu\Micro;
class Helper
{
/**
* 获取唯一编号
* @param string $prefix
* @return string
*/
public static function uid($prefix = '')
{
return uniqid($prefix);
}
/**
* 返回成功消息
* @param array|string $data 返回数据 默认空
* @param string $msg 消息,默认 Success
* @param int $code 成功代码,默认200
* @return array
*/
public static function success($data = '', $msg = 'success', $code = 200)
{
return self::response($data, $msg, $code);
}
/**
* 返回失败消息
* @param array|string $data 返回数据 默认空
* @param string $msg 消息,默认 Error
* @param int $code 失败代码,默认400
* @return array
*/
public static function fail($data = '', $msg = 'fail', $code = 400)
{
return self::response($data, $msg, $code);
}
/**
* 返回操作消息
* @param array|string $data 返回数据 默认空
* @param string $msg 消息,默认空
* @param int $code 操作代码,默认200
* @return array
*/
public static function response($data = '', $msg = '', $code = 200)
{
return ['data' => $data, 'msg' => $msg, 'code' => $code];
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/5/6
* Time: 8:54
*/
namespace Meibuyu\Micro\Listener;
use Hyperf\ServiceGovernance\Listener\RegisterServiceListener as BaseRegisterServiceListener;
class RegisterServiceListener extends BaseRegisterServiceListener
{
protected function getServers(): array
{
$result = [];
$servers = $this->config->get('server.servers', []);
foreach ($servers as $server) {
if (!isset($server['name'], $server['host'], $server['port'])) {
continue;
}
if (!$server['name']) {
throw new \InvalidArgumentException('Invalid server name');
}
/**
* 若在docker中运行,会获取到docker环境中的ip
* 这里对配置文件中local_ip判断,如果有,直接使用
*/
$host = isset($server['local_ip']) ? $server['local_ip'] : $server['host'];
if (in_array($host, ['0.0.0.0', 'localhost'])) {
$host = $this->getInternalIp();
}
if (!filter_var($host, FILTER_VALIDATE_IP)) {
throw new \InvalidArgumentException(sprintf('Invalid host %s', $host));
}
$port = $server['port'];
if (!is_numeric($port) || ($port < 0 || $port > 65535)) {
throw new \InvalidArgumentException(sprintf('Invalid port %s', $port));
}
$port = (int)$port;
$result[$server['name']] = [$host, $port];
}
return $result;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/3/30
* Time: 9:56
*/
declare(strict_types=1);
namespace Meibuyu\Micro\Manager;
use Hyperf\HttpMessage\Upload\UploadedFile;
use Meibuyu\Micro\Exceptions\HttpResponseException;
class UploadManager
{
public static $pathPrefix = '/upload/';
public static $options = [
'path' => 'default', // 默认保存路径
'maxSize' => 10 * 1024 * 1024, // 文件大小,10M
'temp' => false, // 是否为临时文件
'mime' => ['jpeg', 'png', 'gif', 'jpg', 'svg', 'txt', 'pdf', 'xlsx', 'xls', 'doc', 'docx', 'rar', 'zip', 'csv'], // 允许上传的文件类型
];
/**
* 图片上传方法
* @param $image
* @param array $options
* @return string
* @throws HttpResponseException
*/
public static function uploadImage($image, $options = [])
{
$imgOptions = [
'path' => 'images',
'mime' => ['jpeg', 'png', 'gif', 'jpg', 'svg']
];
$options = array_merge($imgOptions, $options);
return self::uploadFile($image, $options);
}
/**
* 表格上传方法
* @param $excel
* @param array $options
* @return string
* @throws HttpResponseException
*/
public static function uploadExcel($excel, $options = [])
{
$excelOptions = [
'path' => 'excel',
'mime' => ['xlsx', 'xls', 'csv']
];
$options = array_merge($excelOptions, $options);
return self::uploadFile($excel, $options);
}
/**
* 表格上传方法获取真实地址
* @param $excel
* @param array $options
* @return string
* @throws HttpResponseException
*/
public static function uploadExcelGetRealPath($excel, $options = [])
{
$excelOptions = [
'path' => 'excel',
'mime' => ['xlsx', 'xls', 'csv']
];
$options = array_merge($excelOptions, $options);
return self::uploadFile($excel, $options, true);
}
/**
* 文件上传方法
* @param UploadedFile $file 上传的文件
* @param array $options 配置参数
* @param bool $realPath
* @return string
* @throws HttpResponseException
*/
public static function uploadFile($file, $options = [], $realPath = false)
{
$documentRoot = config('server.settings.document_root');
if (!$documentRoot) {
throw new \RuntimeException('未配置静态资源');
}
$options = self::parseOptions($options);
if ($file->isValid()) {
$extension = strtolower($file->getExtension());
// 通过扩展名判断类型
if (!in_array($extension, $options['mime'])) {
throw new HttpResponseException('文件类型不支持,目前只支持' . implode(',', $options['mime']));
}
// 判断文件大小
if ($file->getSize() > $options['maxSize']) {
throw new HttpResponseException('文件超出系统规定的大小,最大不能超过' . num_2_file_size($options['maxSize']));
}
// 文件重命名,由当前日期时间 + 唯一ID + 扩展名
$fileName = date('YmdHis') . uniqid() . '.' . $extension;
$savePath = self::parsePath($options, $documentRoot) . $fileName;
$file->moveTo($savePath);
if ($file->isMoved()) {
if ($realPath) {
return $savePath;
} else {
return str_replace($documentRoot, '', $savePath);
}
} else {
throw new HttpResponseException('文件保存失败');
}
} else {
throw new HttpResponseException('文件无效');
}
}
/**
* 文件上传方法(micro-api-flow)
* @param UploadedFile $file 上传的文件
* @param array $options 配置参数
* @param bool $realPath
* @return string
* @throws HttpResponseException
*/
public static function uploadFileGetName($file, $options = [], $realPath = false)
{
$documentRoot = config('server.settings.document_root');
if (!$documentRoot) {
throw new \RuntimeException('未配置静态资源');
}
$options = self::parseOptions($options);
if ($file->isValid()) {
$extension = strtolower($file->getExtension());
// 通过扩展名判断类型
if (!in_array($extension, $options['mime'])) {
throw new HttpResponseException('文件类型不支持,目前只支持' . implode(',', $options['mime']));
}
// 判断文件大小
if ($file->getSize() > $options['maxSize']) {
throw new HttpResponseException('文件超出系统规定的大小,最大不能超过' . num_2_file_size($options['maxSize']));
}
// 文件重命名,由当前日期时间 + 唯一ID + 扩展名
$fileName = date('YmdHis') . uniqid() . '.' . $extension;
$name = $file->toArray()['name'];
$savePath = self::parsePath($options, $documentRoot) . $fileName;
$file->moveTo($savePath);
if ($file->isMoved()) {
if ($realPath) {
return $savePath . '?' . $name;
} else {
return str_replace($documentRoot, '', $savePath . '?' . $name);
}
} else {
throw new HttpResponseException('文件保存失败');
}
} else {
throw new HttpResponseException('文件无效');
}
}
/**
* 生成头像
* @return string|string[]
*/
public static function createAvatar()
{
$documentRoot = config('server.settings.document_root');
if (!$documentRoot) {
throw new \RuntimeException('未配置静态资源');
}
$img = imagecreatetruecolor(180, 180);
$bgColor = imagecolorallocate($img, 240, 240, 240);
imagefill($img, 0, 0, $bgColor);
$color = imagecolorallocate($img, rand(90, 230), rand(90, 230), rand(90, 230));
for ($i = 0; $i < 90; $i++) {
for ($y = 0; $y < 180; $y++) {
$ad = rand(10, 50); //随机
if ($ad % 3 == 0) {
for ($xx = $i; $xx < $i + 15; $xx++) {
for ($yy = $y; $yy < $y + 30; $yy++) {
imagesetpixel($img, $xx, $yy, $color);
}
}
$is = ((90 - $i) + 90) - 15; //计算偏移
for ($xx = $is; $xx < $is + 15; $xx++) {
for ($yy = $y; $yy < $y + 30; $yy++) {
imagesetpixel($img, $xx, $yy, $color);
}
}
}
$y += 14;
}
$i += 14;
}
$path = $documentRoot . self::$pathPrefix . 'avatar/default/';
if (!is_dir($path)) {
mkdir($path, 0777, true);
}
$fileName = $path . date('YmdHis') . uniqid() . '.png';
imagepng($img, $fileName);
imagedestroy($img);//释放内存
return str_replace($documentRoot, '', $fileName);
}
/**
* 处理保存路径
* @param $options
* @param $documentRoot
* @return string
*/
public static function parsePath($options, $documentRoot)
{
if (isset($options['temp']) && $options['temp']) {
// 如果是临时文件,修改保存路径为临时路径
$options['path'] = 'temp';
}
$path = $documentRoot . self::$pathPrefix . $options['path'] . '/' . date('Y-m-d');
if (!is_dir($path)) {
// 判断路径是否存在,不存在,则创建
mkdir($path, 0777, true);
}
return $path . '/';
}
/**
* 处理配置参数
* @param array $options
* @return array
*/
public static function parseOptions($options = [])
{
if ($options == []) {
return self::$options;
} else {
return array_merge(self::$options, $options);
}
}
public static function deleteFile($path)
{
$documentRoot = config('server.settings.document_root');
if (!$documentRoot) {
throw new \RuntimeException('未配置静态资源');
}
$path = str_replace(config('app_domain'), '', $path);
$path = $documentRoot . $path;
if (file_exists($path)) {
unlink($path);
}
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/24
* Time: 9:33
*/
namespace Meibuyu\Micro\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class CheckWebhookMiddleware implements MiddlewareInterface
{
/**
* @inheritDoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$domain = $request->getHeader('x-shopify-shop-domain')[0] ?? null;
$topic = $request->getHeader('x-shopify-topic')[0] ?? null;
if ($domain && $topic) {
return $handler->handle($request);
} else {
return response()->withStatus(500);
}
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/4/3
* Time: 10:17
*/
declare(strict_types=1);
namespace Meibuyu\Micro\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class TokenExistMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (token()) {
return $handler->handle($request);
} else {
return response()->json([
'code' => 403,
'msg' => 'token不存在'
]);
}
}
}
<?php
namespace Meibuyu\Micro\Model;
use Hyperf\Utils\Context;
use Meibuyu\Micro\Exceptions\HttpResponseException;
class Auth
{
/**
* @return bool|mixed|string|null
* @throws HttpResponseException
*/
private static function init()
{
if (Context::has('auth')) {
return Context::get('auth');
} else {
$token = token();
if (!$token) throw new HttpResponseException('Token不存在');
$auth = redis()->get($token);
if ($auth) {
$auth = json_decode($auth, true);
Context::set('auth', $auth);
return $auth;
} else {
throw new HttpResponseException('用户不存在');
}
}
}
/**
* @return object
* @throws HttpResponseException
*/
public static function user()
{
return self::init();
}
/**
* @return integer
* @throws HttpResponseException
*/
public static function id()
{
return self::init()['id'];
}
}
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/2/7
* Time: 13:42
*/
namespace Meibuyu\Micro\Repository\Contracts;
interface RepositoryInterface
{
public function list();
public function show($id);
/**
* @param array $columns
* @return mixed
*/
public function all($columns = array('*'));
public function paginate($perPage = 10, $columns = array('*'));
public function create(array $attributes);
public function update(array $attributes, $id);
public function delete($id);
public function find($id, $columns = array('*'));
public function findBy($field, $value, $columns = array('*'));
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/2/7
* Time: 13:47
*/
namespace Meibuyu\Micro\Repository\Eloquent;
use Hyperf\Database\Exception\QueryException;
use Hyperf\Database\Model\Builder;
use Hyperf\DbConnection\Model\Model;
use Hyperf\HttpServer\Contract\RequestInterface;
use Meibuyu\Micro\Exceptions\HttpResponseException;
use Meibuyu\Micro\Exceptions\RepositoryException;
use Meibuyu\Micro\Exceptions\ValidatorException;
use Meibuyu\Micro\Repository\Contracts\RepositoryInterface;
use Meibuyu\Micro\Validator\Contracts\ValidatorInterface;
use Psr\Container\ContainerInterface;
abstract class BaseRepository implements RepositoryInterface
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* @var RequestInterface
*/
protected $request;
/**
* @var Model|Builder
*/
protected $model;
/**
* @var ValidatorInterface
*/
protected $validator;
/**
* BaseRepository constructor.
* @param ContainerInterface $container
* @param RequestInterface $request
* @throws RepositoryException
*/
public function __construct(ContainerInterface $container, RequestInterface $request)
{
$this->container = $container;
$this->request = $request;
$this->makeModel();
$this->makeValidator();
}
/**
* Specify Model class name
* @return mixed
*/
abstract public function model();
/**
* Specify Validator class name
* @return null|mixed
*/
public function validator()
{
return null;
}
/**
* @return Model
* @throws RepositoryException
*/
public function makeModel()
{
$model = $this->container->make($this->model());
if (!$model instanceof Model) {
throw new RepositoryException("Class {$this->model()} must be an instance of Hyperf\\DbConnection\\Model\\Model");
}
return $this->model = $model;
}
/**
* @param null $validator
*
* @return null|ValidatorInterface
* @throws RepositoryException
*/
public function makeValidator($validator = null)
{
$validator = !is_null($validator) ? $validator : $this->validator();
if (!is_null($validator)) {
$this->validator = $this->container->make($validator);
if (!$this->validator instanceof ValidatorInterface) {
throw new RepositoryException("Class {$validator} must be an instance of Meibuyu\\Micro\\Validator\\Contracts\\ValidatorInterface");
}
return $this->validator;
}
return null;
}
/**
* @param $id
* @param array $columns
* @return mixed|Model
* @throws HttpResponseException
*/
public function find($id, $columns = ['*'])
{
$model = $this->model->find($id, $columns);
if (!$model) {
throw new HttpResponseException('数据不存在');
}
return $model;
}
/**
* @param array $columns
* @return mixed
*/
public function all($columns = ['*'])
{
return $this->model->get($columns);
}
public function list()
{
return $this->all();
}
/**
* @param $id
* @return Model|mixed
* @throws HttpResponseException
*/
public function show($id)
{
return $this->find($id);
}
public function paginate($perPage = 10, $columns = ['*'])
{
return $this->model->paginate($perPage, $columns);
}
/**
* @param array $attributes
* @return Model
* @throws ValidatorException
*/
public function create(array $attributes)
{
if (!is_null($this->validator)) {
$this->validator->with($attributes)->passesOrFail(ValidatorInterface::RULE_CREATE);
}
$model = $this->model->newInstance($attributes);
$model->save();
return $model;
}
/**
* @param array $attributes
* @param $id
* @return Model | mixed
* @throws HttpResponseException
* @throws ValidatorException
*/
public function update(array $attributes, $id)
{
if (!is_null($this->validator)) {
$this->validator->with($attributes)->setId($id)->passesOrFail(ValidatorInterface::RULE_UPDATE);
}
$model = $this->find($id);
$model->fill($attributes);
$model->save();
return $model;
}
/**
* @param $id
* @return bool|mixed
* @throws HttpResponseException
*/
public function delete($id)
{
$model = $this->find($id);
try {
$delete = $model->delete();
} catch (QueryException $e) {
$msg = $e->getMessage();
if ($e->getCode() == 23000 && strpos($msg, 'foreign key') !== false) {
throw new HttpResponseException('此数据下有关联的数据,不可进行操作');
} else {
throw new HttpResponseException($msg);
}
} catch (\Exception $e) {
throw new HttpResponseException($e->getMessage());
}
if ($delete !== false) {
return $delete;
} else {
throw new HttpResponseException('删除失败,请刷新重试');
}
}
public function findBy($field, $value, $columns = ['*'])
{
return $this->model->where($field, '=', $value)->first($columns);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: 王源
* Date: 2020/1/9
* Time: 15:08
*/
namespace Meibuyu\Micro\Service;
use Exception;
use Hyperf\DbConnection\Model\Model;
use Meibuyu\Micro\Helper;
class BaseService
{
/**
* @var Model
*/
protected $model;
/**
* 查找一个数据
* @param $id
* @return Model | array
*/
protected function find($id)
{
$model = $this->model->find($id);
return $model;
}
public function all(array $columns = ['*'], array $relations = []): array
{
return $this->model->with($relations)->get($columns)->toArray();
}
/**
* 获取一条数据
* @param int $id
* @param array $columns
* @param array $relations
* @return mixed
*/
public function get(int $id, array $columns = ['*'], array $relations = [])
{
return $this->model->with($relations)->find($id, $columns);
}
/**
* 插入一条数据
* @param array $params
* @return array
*/
public function insert($params)
{
try {
$res = $this->model->insert($params);
return Helper::success($res);
} catch (Exception $e) {
return Helper::fail('', $e->getMessage());
}
}
/**
* 新增一条数据
* @param array $params
* @return array
*/
public function create($params)
{
try {
$model = $this->model->newInstance($params);
$model->save();
return Helper::success($model);
} catch (Exception $e) {
return Helper::fail('', $e->getMessage());
}
}
/**
* 更新数据
* @param $id
* @param array $params
* @return array
*/
public function update($id, $params)
{
try {
$model = $this->find($id);
$model->fill($params);
$model->save();
return Helper::success($model);
} catch (Exception $e) {
return Helper::fail('', $e->getMessage());
}
}
/**
* 删除数据
* @param $id
* @return array
*/
public function delete($id)
{
try {
$model = $this->find($id);
$res = $model->delete();
if ($res) {
return Helper::success($res, '删除成功');
} else {
return Helper::fail($res, '删除失败');
}
} catch (Exception $e) {
return Helper::fail('', $e->getMessage());
}
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/18
* Time: 8:13
*/
namespace Meibuyu\Micro\Shopify;
use Exception;
use Meibuyu\Micro\Shopify\lib\AbstractShopify;
use Meibuyu\Micro\Shopify\lib\Collect;
use Meibuyu\Micro\Shopify\lib\Collection;
use Meibuyu\Micro\Shopify\lib\CustomCollection;
use Meibuyu\Micro\Shopify\lib\Event;
use Meibuyu\Micro\Shopify\lib\Fulfillment;
use Meibuyu\Micro\Shopify\lib\FulfillmentOrder;
use Meibuyu\Micro\Shopify\lib\FulfillmentService;
use Meibuyu\Micro\Shopify\lib\GraphQL;
use Meibuyu\Micro\Shopify\lib\InventoryItem;
use Meibuyu\Micro\Shopify\lib\InventoryLevel;
use Meibuyu\Micro\Shopify\lib\Location;
use Meibuyu\Micro\Shopify\lib\Metafield;
use Meibuyu\Micro\Shopify\lib\Order;
use Meibuyu\Micro\Shopify\lib\Product;
use Meibuyu\Micro\Shopify\lib\ProductVariant;
use Meibuyu\Micro\Shopify\lib\SmartCollection;
use Meibuyu\Micro\Shopify\lib\Webhook;
/**
* Class ShopifyApp
* @package Meibuyu\Shopify
*
* @property-read Webhook $Webhook
* @property-read Collect $Collect
* @property-read Collection $Collection
* @property-read CustomCollection $CustomCollection
* @property-read SmartCollection $SmartCollection
* @property-read Metafield $Metafield
* @property-read Product $Product
* @property-read ProductVariant $ProductVariant
* @property-read InventoryItem $InventoryItem
* @property-read InventoryLevel $InventoryLevel
* @property-read Location $Location
* @property-read Order $Order
* @property-read Event $Event
* @property-read Fulfillment $Fulfillment
* @property-read FulfillmentService $FulfillmentService
* @property-read GraphQL $GraphQL
*
* @method Webhook Webhook(integer $id = null)
* @method Collection Collection(integer $id = null)
* @method CustomCollection CustomCollection(integer $id = null)
* @method SmartCollection SmartCollection(integer $id = null)
* @method Metafield Metafield(integer $id = null)
* @method Product Product(integer $id = null)
* @method ProductVariant ProductVariant(integer $id = null)
* @method InventoryItem InventoryItem(integer $id = null)
* @method InventoryLevel InventoryLevel(integer $id = null)
* @method Location Location(integer $id = null)
* @method Order Order(integer $id = null)
* @method Event Event(integer $id = null)
* @method Fulfillment Fulfillment(integer $id = null)
* @method FulfillmentOrder FulfillmentOrder()
* @method FulfillmentService FulfillmentService(integer $id = null)
* @method GraphQL GraphQL()
*
*/
class ShopifyApp
{
protected $resources = [
'Webhook',
'Collect',
'Collection',
'CustomCollection',
'SmartCollection',
'Metafield',
'Product',
'ProductVariant',
'InventoryItem',
'InventoryLevel',
'Location',
'Order',
'Event',
'Fulfillment',
'FulfillmentOrder',
'FulfillmentService',
'GraphQL',
];
protected $childResources = array(
'Fulfillment' => 'Order',
'FulfillmentEvent' => 'Fulfillment',
'FulfillmentOrder' => 'Order',
'OrderRisk' => 'Order',
'ProductImage' => 'Product',
'ProductVariant' => 'Product',
'DiscountCode' => 'PriceRule',
'Refund' => 'Order',
'Transaction' => 'Order',
);
public $config = [];
public $defaultApiVersion = '2021-07';
/**
* ShopifyApp constructor.
* @param array $config
*/
public function __construct($config)
{
$this->config = [
'api_version' => $this->defaultApiVersion
];
foreach ($config as $key => $value) {
$this->config[$key] = $value;
}
if (isset($config['shop_url'])) {
$this->setApiUrl();
}
}
/**
* 返回AbstractShopify实例
* @param string $className 实现的类名
* @return AbstractShopify
* @throws Exception
*/
public function __get($className)
{
return $this->$className();
}
/**
* 返回AbstractShopify实例
* @param string $className 实现的类名
* @param $arguments
* @return AbstractShopify
* @throws Exception
*/
public function __call($className, $arguments)
{
if (!in_array($className, $this->resources)) {
if (isset($this->childResources[$className])) {
$message = "$className 是属于 {$this->childResources[$className]} 的子集, 无法直接访问";
} else {
$message = "未知类 $className";
}
throw new Exception($message);
}
$resourceID = !empty($arguments) ? $arguments[0] : null;
$resourceClassName = __NAMESPACE__ . "\\lib\\$className";
return new $resourceClassName($this->config, $resourceID);
}
public function setApiUrl()
{
$shopUrl = $this->config['shop_url'];
$shopUrl = preg_replace('#^https?://|/$#', '', $shopUrl);
$apiVersion = $this->config['api_version'];
$this->config['api_url'] = "https://$shopUrl/admin/api/$apiVersion/";
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/19
* Time: 9:25
*/
namespace Meibuyu\Micro\Shopify;
use Hyperf\Contract\ContainerInterface;
class ShopifyFactory
{
/**
* @var ContainerInterface
*/
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function create(array $config = []): ShopifyApp
{
if (method_exists($this->container, 'make')) {
// Create by DI for AOP.
return $this->container->make(ShopifyApp::class, ['config' => $config]);
}
return new ShopifyApp($config);
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/18
* Time: 8:18
*/
namespace Meibuyu\Micro\Shopify\lib;
use Exception;
use Meibuyu\Micro\Shopify\tools\CurlHttpRequestJson;
use Meibuyu\Micro\Shopify\tools\HttpRequestJson;
use Psr\Http\Message\ResponseInterface;
abstract class AbstractShopify
{
/**
* 资源id
* @var int
*/
public $id = null;
protected $httpHeaders = [];
protected $resourceUrl;
protected $resourceKey;
protected $pluralizeKey;
/**
* 无count方法
* @var boolean
*/
public $countEnabled = true;
// 子集资源
protected $childResource = [];
// 自定义请求方法
protected $customGetActions = [];
protected $customPostActions = [];
protected $customPutActions = [];
protected $customDeleteActions = [];
private $config;
/**
* @var HttpRequestJson|CurlHttpRequestJson
*/
private $httpRequestJson;
/**
* AbstractShopify constructor.
* @param $config
* @param null $id
* @param string $parentResourceUrl
* @throws Exception
*/
public function __construct($config, $id = null, $parentResourceUrl = null)
{
$this->config = $config;
$this->id = $id;
$this->pluralizeKey = $this->pluralizeKey ?: $this->resourceKey . 's';
$this->resourceUrl = ($parentResourceUrl ? "$parentResourceUrl/" : $config['api_url']) . $this->pluralizeKey . ($this->id ? "/{$this->id}" : '');
$this->httpRequestJson = make(CurlHttpRequestJson::class);
if (isset($config['api_password'])) {
$this->httpHeaders['X-Shopify-Access-Token'] = $config['api_password'];
} elseif (!isset($config['api_password'])) {
throw new Exception("请设置api_password值");
}
}
/**
* 调用子集资源
* @param $childName
* @return mixed
*/
public function __get($childName)
{
return $this->$childName();
}
/**
* 调用自定义方法
* @param $name
* @param $arguments
* @return mixed
* @throws Exception
*/
public function __call($name, $arguments)
{
if (ctype_upper($name[0])) {
$childKey = array_search($name, $this->childResource);
if ($childKey === false) {
throw new Exception(" $name 不属于 {$this->getResourceName()}");
}
$childClassName = !is_numeric($childKey) ? $childKey : $name;
$childClass = __NAMESPACE__ . "\\" . $childClassName;
$resourceID = !empty($arguments) ? $arguments[0] : null;
return new $childClass($this->config, $resourceID, $this->resourceUrl);
} else {
$actionMaps = [
'post' => 'customPostActions',
'put' => 'customPutActions',
'get' => 'customGetActions',
'delete' => 'customDeleteActions',
];
//Get the array key for the action in the actions array
foreach ($actionMaps as $httpMethod => $actionArrayKey) {
$actionKey = array_search($name, $this->$actionArrayKey);
if ($actionKey !== false) break;
}
if ($actionKey === false) {
throw new Exception("No action named $name is defined for " . $this->getResourceName());
}
//If any associative key is given to the action, then it will be considered as the method name,
//otherwise the action name will be the method name
$customAction = !is_numeric($actionKey) ? $actionKey : $name;
//Get the first argument if provided with the method call
$methodArgument = !empty($arguments) ? $arguments[0] : [];
//Url parameters
$urlParams = [];
//Data body
$dataArray = [];
//Consider the argument as url parameters for get and delete request
//and data array for post and put request
if ($httpMethod == 'post' || $httpMethod == 'put') {
$dataArray = $methodArgument;
} else {
$urlParams = $methodArgument;
}
$url = $this->generateUrl($urlParams, null, $customAction);
if ($httpMethod == 'post' || $httpMethod == 'put') {
return $this->$httpMethod($dataArray, $url, false);
} else {
return $this->$httpMethod($dataArray, $url);
}
}
}
private function getResourceName()
{
return substr(get_called_class(), strrpos(get_called_class(), '\\') + 1);
}
public function generateUrl($urlParams = [], $id = null, $customAction = null)
{
$resourceUrl = $this->resourceUrl;
if ($id) {
if ($this->id) {
if ($id !== $this->id) {
$resourceUrl = str_replace($this->id, $id, $this->resourceUrl);
}
} else {
$resourceUrl = $this->resourceUrl . "/$id";
}
}
return $resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : '');
}
/**
* 获取数量
* @param array $urlParams
* @return mixed
* @throws Exception
*/
public function count($urlParams = [])
{
if (!$this->countEnabled) {
throw new Exception("当前类{$this->getResourceName()}不支持count()方法");
}
$url = $this->generateUrl($urlParams, null, 'count');
return $this->get([], $url, 'count');
}
/**
* @param array $urlParams
* @param null $url
* @param null $dataKey
* @return mixed
* @throws Exception
*/
public function get($urlParams = [], $url = null, $dataKey = null)
{
if (!$url) $url = $this->generateUrl($urlParams);
$response = $this->httpRequestJson->get($url, $this->httpHeaders);
if (!$dataKey) $dataKey = $this->id ? $this->resourceKey : $this->pluralizeKey;
return $this->processResponse($response, $dataKey);
}
/**
* 分页
* @param null $url
* @param array $urlParams
* @return mixed ['data' => [数据], 'next_link' => '下一页链接']
* @throws Exception
*/
public function page($url = null, $urlParams = [])
{
if (!$url) $url = $this->generateUrl($urlParams);
$response = $this->httpRequestJson->get($url, $this->httpHeaders);
return $this->processPageResponse($response, $this->pluralizeKey);
}
/**
* 根据id获取一条数据
* @param $id
* @param array $urlParams
* @return mixed
* @throws Exception
*/
public function show($id, $urlParams = [])
{
$url = $this->generateUrl($urlParams, $id);
$response = $this->httpRequestJson->get($url, $this->httpHeaders);
return $this->processResponse($response, $this->resourceKey);
}
/**
* @param $dataArray
* @param null $url
* @param bool $wrapData
* @return mixed
* @throws Exception
*/
public function post($dataArray, $url = null, $wrapData = true)
{
if (!$url) $url = $this->generateUrl();
if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray);
$response = $this->httpRequestJson->post($url, $dataArray, $this->httpHeaders);
return $this->processResponse($response, $this->resourceKey);
}
/**
* @param int|string $id
* @param $dataArray
* @param null $url
* @param bool $wrapData
* @return mixed
* @throws Exception
*/
public function put($id, $dataArray, $url = null, $wrapData = true)
{
if (!$url) $url = $this->generateUrl([], $id);
if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray);
$response = $this->httpRequestJson->put($url, $dataArray, $this->httpHeaders);
return $this->processResponse($response, $this->resourceKey);
}
/**
* @param int|string $id
* @param array $urlParams
* @param null $url
* @return mixed
* @throws Exception
*/
public function delete($id = null, $urlParams = [], $url = null)
{
if (!$url) $url = $this->generateUrl($urlParams, $id);
$response = $this->httpRequestJson->delete($url, $this->httpHeaders);
return $this->processResponse($response);
}
protected function wrapData($dataArray, $dataKey = null)
{
if (!$dataKey) $dataKey = $this->resourceKey;
return [$dataKey => $dataArray];
}
protected function castString($array)
{
if (!is_array($array)) return (string)$array;
$string = '';
$i = 0;
foreach ($array as $key => $val) {
$string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', ';
$i++;
}
$string = rtrim($string, ', ');
return $string;
}
/**
* 处理响应
* @param array $response
* @param null $dataKey
* @return mixed
* @throws Exception
*/
public function processResponse($response, $dataKey = null)
{
[$code, , $content] = $response;
$content = json_decode($content, true);
if (isset($content['errors'])) {
throw new Exception($this->castString($content['errors']), $code);
}
if ($dataKey && isset($content[$dataKey])) {
return $content[$dataKey];
} else {
return $content;
}
}
/**
* 处理响应
* @param ResponseInterface $response
* @param null $dataKey
* @return mixed
* @throws Exception
*/
public function processPageResponse($response, $dataKey = null)
{
[$code, $headers, $content] = $response;
$content = json_decode($content, true);
$link = $this->getLink($headers);
if (isset($content['errors'])) {
throw new Exception($this->castString($content['errors']), $code);
}
if ($dataKey && isset($content[$dataKey])) {
$data = $content[$dataKey];
} else {
$data = $content;
}
return ['data' => $data, 'next_link' => $link];
}
public function getLink($header, $type = 'next')
{
if (!empty($header['x-shopify-api-version'][0]) && $header['x-shopify-api-version'][0] < '2019-07') {
return null;
}
if (!empty($header['link'][0])) {
$headerLinks = $header['link'][0];
if (stristr($headerLinks, '; rel="' . $type . '"') > -1) {
$headerLinks = explode(',', $headerLinks);
foreach ($headerLinks as $headerLink) {
if (stristr($headerLink, '; rel="' . $type . '"') === -1) {
continue;
}
$pattern = '#<(.*?)>; rel="' . $type . '"#m';
preg_match($pattern, $headerLink, $linkResponseHeaders);
if ($linkResponseHeaders) {
return $linkResponseHeaders[1];
}
}
}
}
return null;
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/24
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
class Collect extends AbstractShopify
{
protected $resourceKey = 'collect';
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/24
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class Collection
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read Metafield $Metafield
*
* @method Metafield Metafield(integer $id = null)
*/
class Collection extends AbstractShopify
{
protected $resourceKey = 'collection';
protected $childResource = [
'Metafield',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/22
* Time: 16:14
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class CustomCollection
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read Metafield $Metafield
*
* @method Metafield Metafield(integer $id = null)
*/
class CustomCollection extends AbstractShopify
{
protected $resourceKey = 'custom_collection';
protected $childResource = [
'Metafield',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class Event
* @package Meibuyu\Micro\Shopify\lib
*/
class Event extends AbstractShopify
{
protected $resourceKey = 'event';
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class Fulfillment
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read Event $Event
*
* @method Event Event(integer $id = null)
*
* @method array update_tracking($info) Update Tracking
* @method array complete() Complete a fulfillment
* @method array open() Open a pending fulfillment
* @method array cancel() Cancel a fulfillment
*/
class Fulfillment extends AbstractShopify
{
protected $resourceKey = 'fulfillment';
protected $childResource = [
'FulfillmentEvent' => 'Event',
];
protected $customPostActions = [
'update_tracking',
'complete',
'open',
'cancel',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class FulfillmentEvent
* @package Meibuyu\Micro\Shopify\lib
*/
class FulfillmentEvent extends AbstractShopify
{
protected $resourceKey = 'event';
}
<?php
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class FulfillmentOrder
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read Event $Event
*
* @method array cancel() Cancel a fulfillment order
* @method array close() Marks a fulfillment order as incomplete
* @method array move() Moves a fulfillment order to a new location
*/
class FulfillmentOrder extends AbstractShopify
{
protected $resourceKey = 'fulfillment_order';
protected $customPostActions = [
'cancel',
'close',
'move',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class FulfillmentService
* @package Meibuyu\Micro\Shopify\lib
*/
class FulfillmentService extends AbstractShopify
{
protected $resourceKey = 'fulfillment_service';
public $countEnabled = false;
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2021/01/08
* Time: 09:34:30
*/
namespace Meibuyu\Micro\Shopify\lib;
use Exception;
use Meibuyu\Micro\Shopify\tools\HttpRequestGraphQL;
/**
* Class GraphQL
* @package Meibuyu\Micro\Shopify\lib
*/
class GraphQL extends AbstractShopify
{
protected $resourceKey = 'graphql';
protected $pluralizeKey = 'graphql';
public function post($graphQL, $url = null, $wrapData = false, $variables = null)
{
if (!$url) $url = $this->generateUrl();
$response = HttpRequestGraphQL::post($url, $graphQL, $this->httpHeaders, $variables);
return $this->processResponse($response);
}
public function get($urlParams = array(), $url = null, $dataKey = null)
{
throw new Exception("GraphQL 只支持 POST 请求!");
}
public function put($id, $dataArray, $url = null, $wrapData = true)
{
throw new Exception("GraphQL 只支持 POST 请求!");
}
public function delete($id = null, $urlParams = [], $url = null)
{
throw new Exception("GraphQL 只支持 POST 请求!");
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/23
* Time: 8:58
*/
namespace Meibuyu\Micro\Shopify\lib;
class InventoryItem extends AbstractShopify
{
protected $resourceKey = 'inventory_item';
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/23
* Time: 8:58
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class InventoryLevel
* @package Meibuyu\Micro\Shopify\lib
*
* @method array adjust($data) Adjust inventory level.
* @method array connect($data) Connect an inventory item to a location.
* @method array set($data) Sets an inventory level for a single inventory item within a location.
*/
class InventoryLevel extends AbstractShopify
{
protected $resourceKey = 'inventory_level';
protected $customPostActions = [
'adjust',
'connect',
'set',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/23
* Time: 8:58
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class Location
* @package Meibuyu\Micro\Shopify\lib
*/
class Location extends AbstractShopify
{
protected $resourceKey = 'location';
protected $childResource = [
'InventoryLevel',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/26
* Time: 15:50
*/
namespace Meibuyu\Micro\Shopify\lib;
class Metafield extends AbstractShopify
{
protected $resourceKey = 'metafield';
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class Order
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read Fulfillment $Fulfillment
* @property-read Event $Event
*
* @method Fulfillment Fulfillment(integer $id = null)
* @method FulfillmentOrder FulfillmentOrder()
* @method Event Event(integer $id = null)
*
* @method array close() Close an Order
* @method array open() Re-open a closed Order
* @method array cancel(array $data) Cancel an Order
*/
class Order extends AbstractShopify
{
protected $resourceKey = 'order';
protected $childResource = [
'Fulfillment',
'FulfillmentOrder',
'Event',
];
protected $customPostActions = [
'close',
'open',
'cancel',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class Product
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read ProductImage $Image
* @property-read ProductVariant $Variant
* @property-read Metafield $Metafield
*
* @method ProductImage Image(integer $id = null)
* @method ProductVariant Variant(integer $id = null)
* @method Metafield Metafield(integer $id = null)
*/
class Product extends AbstractShopify
{
protected $resourceKey = 'product';
protected $childResource = [
'ProductImage' => 'Image',
'ProductVariant' => 'Variant',
'Metafield',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
class ProductImage extends AbstractShopify
{
protected $resourceKey = 'image';
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/2
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**
* Class ProductVariant
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read Metafield $Metafield
*
* @method Metafield Metafield(integer $id = null)
*/
class ProductVariant extends AbstractShopify
{
protected $resourceKey = 'variant';
protected $childResource = [
'Metafield',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/24
* Time: 16:50
*/
namespace Meibuyu\Micro\Shopify\lib;
/**\
* Class SmartCollection
* @package Meibuyu\Micro\Shopify\lib
*
* @property-read Metafield $Metafield
*
* @method Metafield Metafield(integer $id = null)
*/
class SmartCollection extends AbstractShopify
{
protected $resourceKey = 'smart_collection';
protected $childResource = [
'Metafield',
];
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/18
* Time: 8:13
*/
namespace Meibuyu\Micro\Shopify\lib;
class Webhook extends AbstractShopify
{
protected $resourceKey = 'webhook';
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/19
* Time: 9:06
*/
namespace Meibuyu\Micro\Shopify\tools;
use Exception;
/**
* Curl的json格式请求
* Class HttpRequestJson
* @package Meibuyu\Micro\Tools
*/
class CurlHttpRequestJson
{
protected function prepareRequest($headers, $data = [])
{
$data = json_encode($data);
if (!empty($data)) {
$headers['Content-type'] = 'application/json';
$headers['Content-Length'] = strlen($data);
}
return [$headers, $data];
}
/**
* @param $url
* @param array $headers
* @return array
* @throws Exception
* @author zero
*/
public function get($url, $headers = [])
{
return CurlRequest::get($url, $headers);
}
/**
* post请求
* @param $url
* @param $data
* @param array $headers
* @return array
* @throws Exception
* @author zero
*/
public function post($url, $data, $headers = [])
{
[$headers, $data] = self::prepareRequest($headers, $data);
return CurlRequest::post($url, $data, $headers);
}
/**
* put请求
* @param $url
* @param $data
* @param array $headers
* @return array
* @throws Exception
* @author zero
*/
public function put($url, $data, $headers = [])
{
[$headers, $data] = self::prepareRequest($headers, $data);
return CurlRequest::put($url, $data, $headers);
}
/**
* delete请求
* @param $url
* @param array $headers
* @return array
* @throws Exception
* @author zero
*/
public function delete($url, $headers = [])
{
return CurlRequest::delete($url, $headers);
}
}
<?php
namespace Meibuyu\Micro\Shopify\tools;
use Exception;
class CurlRequest
{
protected static function init($url, $httpHeaders = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 100); // 设置超时限制防止死循环
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHPClassic/PHPShopify');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名
$headers = [];
foreach ($httpHeaders as $key => $value) {
$headers[] = "$key: $value";
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
return $ch;
}
/**
* @param $url
* @param array $httpHeaders
* @return array
* @throws Exception
* @author zero
*/
public static function get($url, $httpHeaders = [])
{
$ch = self::init($url, $httpHeaders);
return self::processRequest($ch);
}
/**
* @param $url
* @param $data
* @param array $httpHeaders
* @return array
* @throws Exception
* @author zero
*/
public static function post($url, $data, $httpHeaders = [])
{
$ch = self::init($url, $httpHeaders);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
return self::processRequest($ch);
}
/**
* @param $url
* @param $data
* @param array $httpHeaders
* @return array
* @throws Exception
* @author zero
*/
public static function put($url, $data, $httpHeaders = [])
{
$ch = self::init($url, $httpHeaders);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
return self::processRequest($ch);
}
/**
* @param $url
* @param array $httpHeaders
* @return array
* @throws Exception
* @author zero
*/
public static function delete($url, $httpHeaders = [])
{
$ch = self::init($url, $httpHeaders);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
return self::processRequest($ch);
}
/**
* @param $ch
* @return array
* @throws Exception
* @author zero
*/
protected static function processRequest($ch)
{
$output = curl_exec($ch);
$response = new CurlResponse($output);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// if ($httpCode == 429) {
// $limitHeader = explode('/', $response->getHeader('X-Shopify-Shop-Api-Call-Limit')[0], 2);
// if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) {
// throw new Exception($response->getBody());
// }
// }
if (curl_errno($ch)) {
throw new Exception(curl_errno($ch) . ' : ' . curl_error($ch));
}
curl_close($ch);
return [$httpCode, $response->getHeaders(), $response->getBody()];
}
}
<?php
namespace Meibuyu\Micro\Shopify\tools;
class CurlResponse
{
/** @var array */
private $headers = [];
/** @var string */
private $body;
public function __construct($response)
{
$this->parse($response);
}
/**
* @param string $response
*/
private function parse($response)
{
$response = explode("\r\n\r\n", $response);
if (count($response) > 1) {
// We want the last two parts
$response = array_slice($response, -2, 2);
list($headers, $body) = $response;
foreach (explode("\r\n", $headers) as $header) {
$pair = explode(': ', $header, 2);
if (isset($pair[1])) {
$headerKey = strtolower($pair[0]);
$this->headers[$headerKey][] = $pair[1];
}
}
} else {
$body = $response[0];
}
$this->body = $body;
}
/**
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* @param string $key
*
* @return string
*/
public function getHeader($key)
{
return isset($this->headers[$key]) ? $this->headers[$key] : null;
}
/**
* @return string
*/
public function getBody()
{
return $this->body;
}
public function __toString()
{
$body = $this->getBody();
$body = $body ?: '';
return $body;
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Time: 2021/1/14 9:16
*/
namespace Meibuyu\Micro\Shopify\tools;
class GraphQL
{
public static function parseDeliveryProfileId($gid)
{
if ($gid && stripos($gid, 'gid://shopify/DeliveryProfile/') !== false) {
$gid = str_replace('gid://shopify/DeliveryProfile/', '', $gid);
}
return (int)$gid;
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Time: 2021/1/11 10:10
*/
namespace Meibuyu\Micro\Shopify\tools;
use Exception;
class HttpRequestGraphQL
{
/**
* @param array $headers
* @param array $data
* @param null $variables
* @return array
* @throws Exception
* @author zero
*/
protected static function prepareRequest($headers = [], $data = [], $variables = null)
{
if (is_string($data)) {
$postDataGraphQL = $data;
} else {
throw new Exception("Only GraphQL string is allowed!");
}
if (is_array($variables)) {
$postDataGraphQL = json_encode(['query' => $data, 'variables' => $variables]);
$headers['Content-type'] = 'application/json';
} else {
$headers['Content-type'] = 'application/graphql';
}
return [$headers, $postDataGraphQL];
}
/**
* @param $url
* @param $data
* @param array $headers
* @param null $variables
* @return array
* @throws Exception
* @author zero
*/
public static function post($url, $data, $headers = [], $variables = null)
{
[$headers, $postDataGraphQL] = self::prepareRequest($headers, $data, $variables);
return CurlRequest::post($url, $postDataGraphQL, $headers);
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/8/19
* Time: 9:06
*/
namespace Meibuyu\Micro\Shopify\tools;
use GuzzleHttp\HandlerStack;
use Hyperf\Guzzle\ClientFactory;
use Hyperf\Guzzle\HandlerStackFactory;
use Psr\Http\Message\ResponseInterface;
/**
* json格式请求
* Class HttpRequestJson
* @package Meibuyu\Micro\Tools
*/
class HttpRequestJson
{
/**
* @var ClientFactory
*/
private $clientFactory;
/**
* @var HandlerStack
*/
private $stack;
public function __construct(ClientFactory $clientFactory)
{
$this->clientFactory = $clientFactory;
$factory = new HandlerStackFactory();
$this->stack = $factory->create();
}
/**
* get请求
* @param $url
* @param array $httpHeaders
* @return ResponseInterface
*/
public function get($url, $httpHeaders = [])
{
$client = $this->clientFactory->create(['timeout' => 60, 'handler' => $this->stack,]);
$res = $client->get($url, ['headers' => $httpHeaders]);
return $this->processResponse($res);
}
/**
* post请求
* @param $url
* @param $data
* @param array $httpHeaders
* @return ResponseInterface
*/
public function post($url, $data, $httpHeaders = [])
{
$client = $this->clientFactory->create(['timeout' => 60, 'handler' => $this->stack,]);
$res = $client->post($url, ['headers' => $httpHeaders, 'json' => $data]);
return $this->processResponse($res);
}
/**
* put请求
* @param $url
* @param $data
* @param array $httpHeaders
* @return ResponseInterface
*/
public function put($url, $data, $httpHeaders = [])
{
$client = $this->clientFactory->create(['timeout' => 60, 'handler' => $this->stack,]);
$res = $client->put($url, ['headers' => $httpHeaders, 'json' => $data]);
return $this->processResponse($res);
}
/**
* delete请求
* @param $url
* @param array $httpHeaders
* @return ResponseInterface
*/
public function delete($url, $httpHeaders = [])
{
$client = $this->clientFactory->create(['timeout' => 60, 'handler' => $this->stack,]);
$res = $client->delete($url, ['headers' => $httpHeaders]);
return $this->processResponse($res);
}
/**
* 处理响应
* @param ResponseInterface $response
* @return mixed
*/
public function processResponse($response)
{
$code = $response->getStatusCode();
$headers = $response->getHeaders();
$content = $response->getBody()->getContents();
return [$code, $headers, $content];
}
}
<?php
namespace Meibuyu\Micro\Tools;
use Psr\SimpleCache\CacheInterface;
/**
* Class Cache
* @package Meibuyu\Micro\Tools
* @method static get($key, $default = null)
* @method static set($key, $value, $ttl = null)
* @method static delete($key)
* @method static clear()
* @method static getMultiple($keys, $default = null)
* @method static setMultiple($values, $ttl = null)
* @method static deleteMultiple($keys)
* @method static has($key)
*/
class Cacher
{
/**
* @var CacheInterface
*/
protected static $driver;
/**
* @return CacheInterface
*/
public static function driver()
{
if (!self::$driver) {
self::$driver = container(CacheInterface::class);
}
return self::$driver;
}
public static function __callStatic($name, $arguments)
{
return static::driver()->{$name}(...$arguments);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/7/27
* Time: 11:44
*/
namespace Meibuyu\Micro\Tools;
use Exception;
use Hyperf\Contract\ConfigInterface;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Drawer
{
/**
* @var ConfigInterface
*/
protected $config;
private $rootPath;
private $savePath = '';
private $ossDomain = 'http://huaperfect-file-service.oss-cn-hangzhou.aliyuncs.com';
public function __construct()
{
$this->config = container(ConfigInterface::class);
$this->rootPath = $this->config->get('server.settings.document_root', BASE_PATH . '/public');
}
private function parseImagePath($imagePath)
{
if (strstr($imagePath, 'http') !== false) {
$imagePath = $this->downloadWebImage($imagePath);
}
return $imagePath;
}
/**
* 下载网络图片
* @param string $url
* @param string $path
* @return string
*/
public function downloadWebImage($url, $path = null)
{
$url = $this->parseUrl($url);
// excel画图中下载图片时对图片名做urlencode处理,防止中文名不能正常画图片的bug
$pathinfo = pathinfo($url);
$filename = trim($pathinfo['filename']);
$ext = strtolower($pathinfo['extension']);
$filename = "$filename.$ext";
$savePath = $this->parseSavePath($pathinfo['dirname'], $path);
if (!is_dir($savePath)) {
// 判断路径是否存在,不存在,则创建
mkdir($savePath, 0777, true);
}
$filePath = $savePath . '/' . $filename;
if (!file_exists($filePath)) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
$file = curl_exec($ch);
curl_close($ch);
$resource = fopen($filePath, 'a');
fwrite($resource, $file);
fclose($resource);
}
return $filePath;
}
private function parseUrl($url)
{
if (!preg_match('/%[0-9A-Z]{2}/', $url)) {
$url = rawurlencode($url);
$url = str_replace("%3A", ":", $url);
$url = str_replace("%2F", "/", $url);
$url = str_replace("%3F", "?", $url);
$url = str_replace("%3D", "=", $url);
$url = str_replace("%26", "&", $url);
}
return $this->handleOssUrl($url);
}
/**
* 处理保存路径
* @param $dirname
* @param $path
* @return string
* @author Zero
*/
private function parseSavePath($dirname, $path)
{
$path = trim($path, '/');
$path = $this->rootPath . '/download/images/' . ($path ?: $this->savePath) . '/';
if (strstr($dirname, $this->ossDomain) !== false) {
// 以oss的原路径为保存路径
$dirname = str_replace($this->ossDomain, '', $dirname);
$dirname = trim($dirname, '/');
$path = $path . $dirname . '/';
}
return $path;
}
// 替换阿里云图片内部地址
private function handleOssUrl($url)
{
if ($this->config->get('app_env') !== 'dev') {
$url = str_replace('oss-cn-hangzhou.aliyuncs.com', 'oss-accelerate.aliyuncs.com', $url);
$this->ossDomain = 'http://huaperfect-file-service.oss-accelerate.aliyuncs.com';
}
return $url;
}
/**
* description:下载文件到服务端 增加判断是否是webp 格式的图片处理
* author: fuyunnan
* @param string $url
* @param string $path 文件路径
* @return string
* @throws
* Date: 2020/11/20
*/
public function downLoadImgWebpChannel($url, $path = '')
{
$originUrl = $url;
//如果shopify 去掉版本号
if (strpos($url, 'shopify') !== false) {
$url = substr($url, 0, strpos($url, '?v='));
}
$savePath = $this->rootPath . '/download/images/' . ($path ? $path . '/' : $this->savePath);
create_file_dir($savePath);
$filePath = $this->toWebpImg($url, $savePath);
if (!file_exists($filePath)) {
$filename = trim(pathinfo($url, PATHINFO_FILENAME));
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
$filePath = $savePath . "$filename.$ext";
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
curl_setopt($ch, CURLOPT_URL, $originUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
$file = curl_exec($ch);
curl_close($ch);
$resource = fopen($filePath, 'a');
fwrite($resource, $file);
fclose($resource);
$filePath = $this->toWebpImg($filePath, $savePath);
}
return $filePath;
}
/**
* description:将图像绘到excel表格上
* author: fuyunnan
* @param string $url 图片地址
* @param string $childPath 图片地址 用sku名作为文件夹名称
* @param int $h 图片高度
* @param string $p 单元格索引
* @param Worksheet $sheet
* @return void
* @throws
* Date: 2020/11/23
*/
public function drawImgExcel($url, $childPath, $h, $p, $sheet)
{
try {
//下载图片到本地
$absPath = $this->downLoadImgWebpChannel($url, $childPath);
$drawing = new Drawing();
$drawing->setPath($absPath)
->setCoordinates($p)
->setHeight($h)
->setOffsetX(1)
->setOffsetY(1)
->setWorksheet($sheet);
} catch (Exception $e) {
put_log($e->getMessage(), 'draw2Excel.log');
}
}
/**
* description:检查文件是否是webp格式的文件
* 如果是,转化为jpeg格式的文件,并且返回文件的绝对地址.
* author: fuyunnan
* @param string $filePath 文件的绝对地址
* @param string $savePath 文件重新保存的地址
* @return string
* @throws
* Date: 2020/11/23
*/
private function toWebpImg($filePath, $savePath)
{
$filename = trim(pathinfo($filePath, PATHINFO_FILENAME));
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$imgInfo = '';
//如果存在直接返回 防止每次获取文件资源
if (file_exists($savePath . "$filename.$ext")) {
return $savePath . "$filename.$ext";
}
$filePath && $imgInfo = getimagesize($filePath);
if ($imgInfo && end($imgInfo) == "image/webp") {
$filename = trim(pathinfo($filePath, PATHINFO_FILENAME));
$im = imagecreatefromwebp($filePath); // 加载 WebP 文件
switch ($ext) {
case 'jpg':
$toNewFileName = $savePath . $filename . '.jpg';
break;
case 'png':
$toNewFileName = $savePath . $filename . '.png';
break;
case 'jpeg':
$toNewFileName = $savePath . $filename . '.jpeg';
break;
default:
$toNewFileName = $savePath . $filename . '.jpeg';
break;
}
imagejpeg($im, $toNewFileName, 30);//转化图片比例
imagedestroy($im);
return $toNewFileName;
}
return $savePath . "$filename.$ext";
}
/**
* @param $imgPath
* @param int $px
* @return bool|string
* @throws Exception
*/
public function addBoard($imgPath, $px = 2)
{
$imgPath = $this->parseImagePath($imgPath);
$imgPathInfo = pathinfo($imgPath);
$filename = $imgPathInfo['filename']; // 图片名称
$ext = $this->getImgExt($imgPath);
[$img_w, $img_h] = getimagesize($imgPath); // 图片大小
$savePath = $this->rootPath . '/download/board/' . $this->savePath;
if (!is_dir($savePath)) {
// 判断路径是否存在,不存在,则创建
mkdir($savePath, 0777, true);
}
$imgNewPath = $savePath . '/' . $filename . '.' . $ext;
if (file_exists($imgNewPath)) {
return $imgNewPath;
}
switch ($ext) {
case 'jpeg':
case 'jpg':
$img_create_func = 'imagecreatefromjpeg';
$img_save_func = 'imagejpeg';
break;
case 'png':
$img_create_func = 'imagecreatefrompng';
$img_save_func = 'imagepng';
break;
case 'bmp':
$img_create_func = 'imagecreatefrombmp';
$img_save_func = 'imagebmp';
break;
case 'gif':
$img_create_func = 'imagecreatefromgif';
$img_save_func = 'imagegif';
break;
case 'vnd.wap.wbmp':
$img_create_func = 'imagecreatefromwbmp';
$img_save_func = 'imagewbmp';
break;
case 'xbm':
$img_create_func = 'imagecreatefromxbm';
$img_save_func = 'imagexbm';
break;
default:
throw new Exception("图片类型不支持");
}
// 黑色背景图片
$im = @imagecreatetruecolor(($img_w + $px), ($img_h + $px)) or die ("Cannot Initialize new GD image stream");
// 为真彩色画布创建背景,再设置为透明
$color = imagecolorallocate($im, 0, 0, 0);
imagefill($im, 0, 0, $color);
imageColorTransparent($im, $color);
// 把图片放到黑色背景图片上。边框是1px
$resource = $img_create_func($imgPath);
imagecopy($im, $resource, $px / 2, $px / 2, 0, 0, $img_w, $img_h);
$res = $img_save_func($im, $imgNewPath);
imagedestroy($im);
return $res ? $imgNewPath : false;
}
/**
* 拼图
* @param $imgPathList
* @param string $name
* @return bool|string
*/
public function mergeImages($imgPathList, $name = '')
{
$maxW = $maxH = 0;
$filenameList = [];
$imageList = [];
foreach ($imgPathList as $k => $path) {
$path = $this->parseImagePath($path);
$imgPathInfo = pathinfo($path);
$filename = $imgPathInfo['filename']; // 图片名称
$ext = $imgPathInfo['extension']; // 图片扩展名
[$w, $h] = getimagesize($path); // 图片大小
$imageList[$k] = [
'path' => $path,
'filename' => $filename,
'ext' => $ext,
'w' => $w,
'h' => $h,
];
$filenameList[] = $filename;
$maxW += $w;
if ($maxH < $h) {
$maxH = $h;
}
}
$filenameList = collect($filenameList)->unique()->sort()->toArray();
$filename = implode('_', $filenameList);
$savePath = $this->rootPath . '/download/merge/' . $this->savePath;
if (!is_dir($savePath)) {
// 判断路径是否存在,不存在,则创建
mkdir($savePath, 0777, true);
}
$imgNewPath = $savePath . '/' . ($name ?: $filename) . '.png';
if (file_exists($imgNewPath)) {
return $imgNewPath;
}
// 黑色背景图片
$im = @imagecreatetruecolor($maxW, $maxH) or die ("Cannot Initialize new GD image stream");
// 为真彩色画布创建背景,再设置为透明
$color = imagecolorallocate($im, 0, 0, 0);
imagefill($im, 0, 0, $color);
imageColorTransparent($im, $color);
// 循环画图片
$dstX = 0;
foreach ($imageList as $item) {
$resource = imagecreatefromstring(file_get_contents($item['path']));
imagecopy($im, $resource, $dstX, 0, 0, 0, $item['w'], $item['h']);
$dstX += $item['w'];
}
$res = imagepng($im, $imgNewPath);
imagedestroy($im);
return $res ? $imgNewPath : false;
}
/**
* 画图片
* @param string $path 图片路径
* @param int $h 图片高度
* @param string $p 单元格索引
* @param Worksheet $sheet
* @param int $boardPx
*/
public function draw2Excel($path, $h, $p, $sheet, $boardPx = 0)
{
try {
$path = $this->parseImagePath($path);
if ($boardPx) {
$path = $this->addBoard($path, $boardPx);
}
$drawing = new Drawing();
$drawing->setPath($path)->setCoordinates($p)->setHeight($h)->setOffsetX(1)->setOffsetY(1)->setWorksheet($sheet);
} catch (Exception $e) {
put_log($e->getMessage(), 'draw2Excel.log');
}
}
public function setSavePath($path)
{
$this->savePath = trim($path, '/');
}
/**
* 获取图片扩展名
* @param $path
* @return bool|mixed|string
* @author Zero
*/
public function getImgExt($path)
{
$ext = false;
$info = getimagesize($path);
if (isset($info['mime']) && $info['mime']) {
$ext = explode('/', $info['mime'])[1];
} else {
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$ext = explode('?', $ext)[0] ?? $ext;
}
return $ext;
}
}
<?php
/**
* Created by PhpStorm.
* User: zero
* Date: 2020/6/1
* Time: 14:06
* Description:
*/
namespace Meibuyu\Micro\Tools;
use Hyperf\Contract\ConfigInterface;
use Meibuyu\Micro\Exceptions\HttpResponseException;
use Meibuyu\Micro\Manager\UploadManager;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class ExcelImporter
{
/**
* @var ConfigInterface
*/
private $config;
/**
* @var Spreadsheet|null
*/
private $reader = null;
/**
* @var Worksheet|null
*/
private $sheet = null;
private $rootPath;
private $sheetIndex = 0;
private $filePath;
private $fileType;
private $highestRow = 0; // 总行数
private $highestColumn = 0; // 最后一列字符
private $errorColumn;
/**
* Importer constructor.
* @param $file
* @param int $sheetIndex
* @throws \Meibuyu\Micro\Exceptions\HttpResponseException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
public function __construct($file, $sheetIndex = 0)
{
$this->config = container(ConfigInterface::class);
$this->fileType = ucfirst($file->getExtension());
$this->rootPath = $this->config->get('server.settings.document_root', BASE_PATH . '/public');
$path = UploadManager::uploadExcelGetRealPath($file, ['temp' => true]);
$this->reader = IOFactory::createReaderForFile($path)->load($path);
$this->filePath = $path;
$this->sheet = $this->reader->getSheet($sheetIndex);
$this->sheetIndex = $sheetIndex;
$this->highestRow = $this->sheet->getHighestRow();
$this->highestColumn = $this->sheet->getHighestColumn();
$this->highestColumn++;
}
/**
* 判断excel表中数据不能为空
* @param $least
* @return bool
* @throws HttpResponseException
*/
public function checkEmpty($least)
{
if ($this->highestRow < $least) {
return false;
} else {
return true;
}
}
/**
* 获取表格字符串
* @param string $startCol 开始的列,默认为A
* @param null $endCol 结束的列,默认为表格已有数据的最后一列
* @return string
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function getHeaderStr($startCol = 'A', $endCol = null): string
{
$str = '';
$endCol = $endCol ? $endCol : $this->highestColumn;
for ($i = $startCol; $i != $endCol; $i++) {
$str .= $this->sheet->getCell($i . 1)->getFormattedValue();
}
return $str;
}
/**
* 验证表头
* @param string $headerStr 对比的表头字符串
* @param string $startCol 开始的列,默认为A
* @param null $endCol 结束的列,默认为表格已有数据的最后一列
* @return bool
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function checkHeaderStr(string $headerStr, $startCol = 'A', $endCol = null)
{
$excelField = $this->getHeaderStr($startCol, $endCol);
if (md5($excelField) == md5($headerStr)) {
return true;
} else {
return false;
}
}
/**
* 检测必填数据
* @param $requires
* @param $errorCount
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function checkRequire($requires, &$errorCount)
{
foreach ($requires as $k => $v) {
if ($v == '0') {
continue;
}
if (!$v || $v === '') {
$this->setErrorMessage($k . "不能为空", $errorCount);
return false;
}
}
return true;
}
/**
* 检测数字类型数据
* @param $data
* @param $errorCount
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function checkNumeric($data, &$errorCount)
{
foreach ($data as $k => $v) {
if (!$v || $v === '' || $v == '0') {
continue;
} else {
if (!is_numeric($v)) {
$this->setErrorMessage("{$k}只能是数字", $errorCount);
return false;
}
}
}
return true;
}
/**
* 设置错误标识
* @param $p
* @param $message
* @param $count
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function setErrorMessage($message, &$count)
{
$count++;
$this->sheet->getCell($this->errorColumn)->setValue($message);
$this->sheet->getStyle($this->errorColumn)->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED);
}
/**
* 保存
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
public function save()
{
$this->reader->setActiveSheetIndex(0);
IOFactory::createWriter($this->reader, $this->fileType)->save($this->filePath);
}
/**
* 返回当前激化的sheet表
* @return null|Worksheet
*/
public function getSheet()
{
return $this->sheet;
}
/**
* 返回原生的PhpOffice Reader 手动处理数据
* @return null|Spreadsheet
*/
public function getReader()
{
return $this->reader;
}
/**
* 获取总行数
* @return int
*/
public function getHighestRow()
{
return $this->highestRow;
}
/**
* 获取总列数
* @return int
*/
public function getHighestColumn()
{
return $this->highestColumn;
}
/**
* 返回文件本地地址
* @return string
*/
public function getFilePath()
{
return $this->filePath;
}
/**
* 返回文件网络地址
* @return string
*/
public function getFileUrl()
{
return $this->config->get('app_domain') . str_replace($this->rootPath, '', $this->filePath);
}
public function setErrorColumn($column)
{
$this->errorColumn = $column;
}
public function getErrorColumn()
{
return $this->errorColumn;
}
/**
* 对比不同的数据,返回是否有不同
* @param array $list
* @param array $data
* @return bool
* @see check_diff_val()
* @deprecated 此方法废弃,直接调用check_diff_val()
*/
public static function checkDiffVal(array $list, array $data)
{
foreach ($list as $key => $val) {
if (isset($data[$key]) && $data[$key]) {
if (is_array($val)) {
if (self::checkDiffVal($val, $data[$key])) {
return true;
}
} else {
if ($list[$key] != $data[$key]) {
return true;
}
}
}
}
return false;
}
}
<?php
/**
* Created by PhpStorm.
* User: 梁俊杰
* Date: 2020/5/12
* Time: 11:37 AM
* Description:
*/
namespace Meibuyu\Micro\Tools;
use Hyperf\Contract\ConfigInterface;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Exporter
{
/**
* 以字节流的形式下载
*/
const DOWNLOAD_TYPE_STREAM = 1;
/**
* 保存文件,并返回文件的路径
*/
const DOWNLOAD_TYPE_RETURN_FILE_PATH = 2;
/**
* 保存文件,直接输出下载
*/
const DOWNLOAD_TYPE_SAVE_AND_DOWNLOAD = 3;
/**
* 保存文件,返回文件绝对路径
*/
const DOWNLOAD_TYPE_RETURN_ABSOLUTE_PATH = 4;
const EXPORTER_TYPE_CSV = 1;
const EXPORTER_TYPE_XLS = 2;
const EXPORTER_TYPE_XLSX = 3;
/**
* @var ConfigInterface
*/
protected $config;
private $rootPath;
private $beginRowIndex = 1;
private $fileType = 'Xlsx';
private $isFromTemplate = false;
private $sheet = null;
private $reader = null;
private $sheetIndex = 0;
private $name = "";
private $beginColumnChar = "A";
/**
* Exporter constructor.
* @param int $export_type 类型
* @param string $tempFilePath 模板文件地址 (请放置在项目根目录的storage目录下)
* @param string $name 名称
* @param int $sheetIndex
* @throws PhpSpreadsheetException
* @throws \Exception
*/
public function __construct(int $export_type, $tempFilePath = "", $name = "export_data", $sheetIndex = 0)
{
$this->config = container(ConfigInterface::class);
$this->rootPath = $this->config->get('server.settings.document_root', BASE_PATH . '/public');
if ($tempFilePath) {
$tempFilePath = BASE_PATH . '/storage/' . $tempFilePath;
if (file_exists($tempFilePath)) {
$this->fileType = ucfirst(strtolower(pathinfo($tempFilePath, PATHINFO_EXTENSION)));
$reader = IOFactory::createReader($this->fileType)->load($tempFilePath);
$this->isFromTemplate = true;
} else {
throw new \Exception("模板文件不存在");
}
} else {
switch ($export_type) {
case self::EXPORTER_TYPE_CSV:
$this->fileType = "Csv";
break;
case self::EXPORTER_TYPE_XLS:
$this->fileType = "Xls";
break;
case self::EXPORTER_TYPE_XLSX:
$this->fileType = "Xlsx";
break;
default:
throw new \Exception("类型不支持。");
break;
}
$reader = new Spreadsheet();
}
$this->name = $name;
$this->sheet = $reader->getSheet($sheetIndex);
$this->reader = $reader;
$this->sheetIndex = $sheetIndex;
}
/**
* 设置非保护区间,调用此方法全表将自动保护
* @param mixed $unprotectRange 可传数组,字符串或者多个参数形式('A1:B1', 'C1:D1')
* 例如"A1:B1";从A列B列第一行到数据结束行不需要保护
* @return $this
*/
public function setUnprotectRange($unprotectRange = null)
{
$this->sheet->getProtection()->setSheet(true);
$this->sheet->getProtection()->setPassword('micro-tttt');
if ($unprotectRange) {
$unprotectRange = is_array($unprotectRange) ? $unprotectRange : func_get_args();
foreach ($unprotectRange as $range) {
$this->sheet->getStyle($range)->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
}
}
return $this;
}
/**
* 往Excel里面新增一张sheet表
* @param string $name sheet的名字
* @param bool $activeSheet 是否激货并切换到当前sheet 默认否
* @return $this
* @throws PhpSpreadsheetException
*/
public function addSheet($name, $activeSheet = false)
{
$workSheet = new Worksheet(null, $name);
$this->reader->addSheet($workSheet);
if ($activeSheet) {
$this->setSheetByName($name);
}
return $this;
}
/**
* 根据名字sheet表名激化切换到该表
* @param $name
* @return $this
* @throws PhpSpreadsheetException
*/
public function setSheetByName($name)
{
$this->sheet = $this->reader->setActiveSheetIndexByName($name);
$this->sheetIndex = $this->reader->getActiveSheetIndex();
$this->resetRowIndex();
return $this;
}
/**
* 重置排序
*/
private function resetRowIndex()
{
$this->beginRowIndex = 1;
return $this;
}
/**
* 获取当前数据填充到哪一行了 或者最高行
* @return int
*/
public function getCurrentRowIndex()
{
return $this->sheet->getHighestRow();
}
/**
* 设置从哪一列开始填充数据
* @param string $char 默认 A列
* @return Exporter
*/
public function setBeginColumnChar($char = "A")
{
$this->beginColumnChar = $char;
return $this;
}
/**
* 设置从哪一行开始填充数据
* @param int $index 默认第一行
* @return Exporter
*/
public function setBeginRowIndex($index = 1)
{
$this->beginRowIndex = $index;
return $this;
}
/**
* 根据sheet索引切换到对应sheet
* @param int $sheetIndex
* @return Exporter
* @throws PhpSpreadsheetException
*/
public function setSheetByIndex(int $sheetIndex)
{
$this->sheet = $this->reader->setActiveSheetIndex($sheetIndex);
$this->sheetIndex = $sheetIndex;
$this->resetRowIndex();
return $this;
}
/**
* 往表格里填充数据
* @param array $data 支持一维数组和二位数组,数据顺序与表头顺序相同
* @param array $keys 如果$data是原生数组支持直接根据$keys
* 提取数据,可多层提取例如 ['product_name','color.name','creator.user_status.name']
* 支持默认值,为空时显示默认值 例如['product_name|默认值','color.name|无颜色']
* @return Exporter
* @throws PhpSpreadsheetException
*/
public function append(array $data, $keys = [], $noStyle = false)
{
// 一维数组转二维
foreach ($data as $v) {
if (!is_array($v)) {
$data = [$data];
}
break;
}
if (count($keys)) {
foreach ($data as $k => $v) {
$data[$k] = $this->getKeyValue($v, $keys);
}
}
$this->sheet->fromArray($data, null, $this->beginColumnChar . $this->beginRowIndex);
if (!$noStyle) {
// 美化样式
$this->applyStyle($this->beginColumnChar . $this->beginRowIndex . ":" . $this->sheet->getHighestColumn() . ($this->beginRowIndex + count($data) - 1));
}
$this->beginRowIndex += count($data);
return $this;
}
/**
* 根据key提取数据
* @param array $data
* @param array $keys
* @return array
*/
private function getKeyValue(array $data, array $keys)
{
$result = [];
foreach ($keys as $k => $v) {
if (strpos($v, ".") === false) {
if (isset($data[$v])) {
$result[] = $data[$v];
} else {
$result[] = '';
}
} else {
$separate = explode("|", $v);
$nullValue = isset($separate[1]) ? $separate[1] : "";
$list = explode(".", $separate[0]);
$t = count($list) - 1;
$b = $data;
foreach ($list as $lk => $lv) {
if (isset($b[$lv]) && $b[$lv]) {
$b = $b[$lv];
} else {
$b = "";
}
//提取到最后一个
if ($t === $lk) {
$result[] = (($b === "") ? $nullValue : $b);
}
}
}
}
return $result;
}
/**
* 给表格加样式
* @param $pRange
*/
public function applyStyle($pRange)
{
$styleArray = [
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['argb' => '000000'],
],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
'wrapText' => true,
],
'quotePrefix' => true,
];
$this->sheet->getStyle($pRange)->applyFromArray($styleArray);
}
/**
* 设置列宽
* @param int $width 不传为自动宽度
*/
public function setColumnWidth(int $width = null)
{
$end = $this->sheet->getHighestColumn();
$end++;
for ($i = 'A'; $i !== $end; $i++) {
if ($width) {
$this->sheet->getColumnDimension($i)->setWidth($width);
} else {
$this->sheet->getColumnDimension($i)->setAutoSize(true);
}
}
}
/**
* 下载导出的文件
* @param int $downloadType 下载形式 支持 Exporter::DOWNLOAD_TYPE_STREAM Exporter::DOWNLOAD_TYPE_RETURN_FILE_PATH Exporter::DOWNLOAD_TYPE_SAVE_AND_DOWNLOAD
* @param string $filename 下载的文件名
* @return mixed | void
* @throws \Exception
*/
public function download(int $downloadType, $filename = "")
{
set_time_limit(0);
if (!$filename) {
$filename = $this->name ? $this->name : rand(1, 9999999) . time() . rand(1, 9999999);
}
$this->reader->setActiveSheetIndex(0);
$objWriter = IOFactory::createWriter($this->reader, $this->fileType);
switch ($downloadType) {
case self::DOWNLOAD_TYPE_STREAM:
case self::DOWNLOAD_TYPE_SAVE_AND_DOWNLOAD:
$filename .= "." . strtolower($this->fileType);
if ($this->fileType == 'Xlsx') {
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
} elseif ($this->fileType == 'Xls') {
header('Content-Type: application/vnd.ms-excel');
} else {
header("Content-type:text/csv;");
}
header("Content-Disposition: attachment;filename="
. $filename);
header('Cache-Control: max-age=0');
ob_clean();
if ($downloadType == self::DOWNLOAD_TYPE_STREAM) {
$objWriter->save('php://output');
} else {
$f = $this->rootPath . "/export";
if (!file_exists($f)) {
mkdir($f, 0777, true);
}
$f .= "/" . $filename;
$objWriter->save($f);
header('Content-length:' . filesize($f));
$fp = fopen($f, 'r');
fseek($fp, 0);
ob_start();
while (!feof($fp) && connection_status() == 0) {
$chunk_size = 1024 * 1024 * 2; // 2MB
echo fread($fp, $chunk_size);
ob_flush();
flush();
}
ob_end_clean();
fclose($fp);
@unlink($f);
}
break;
case self::DOWNLOAD_TYPE_RETURN_FILE_PATH:
$filePath = $this->rootPath . "/export/";
!is_dir($filePath) && mkdir($filePath, 0777, true);
$fileReadPath = $filePath . $filename . '-' . date('YmdHis') . "." . strtolower($this->fileType);
$objWriter->save($fileReadPath);
return $this->config->get('app_domain') . str_replace($this->rootPath, '', $fileReadPath);
break;
case self::DOWNLOAD_TYPE_RETURN_ABSOLUTE_PATH:
$filePath = $this->rootPath . "/export/";
!is_dir($filePath) && mkdir($filePath, 0777, true);
$date = date('YmdHis');
$fileReadPath = $filePath . $filename . '-' . $date . "." . strtolower($this->fileType);
$objWriter->save($fileReadPath);
return config('server.settings.document_root') . "/export/" . $filename . '-' . $date . "." .
strtolower($this->fileType);
break;
default:
throw new \Exception('不支持此种下载类型');
break;
}
}
/**
* 返回当前激化的sheet表
* @return null|Worksheet
*/
public function getSheet()
{
return $this->sheet;
}
/**
* 返回原生的PhpOffice Reader 手动处理数据
* @return null|Spreadsheet
*/
public function getReader()
{
return $this->reader;
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/9/17
* Time: 8:40
*/
namespace Meibuyu\Micro\Tools;
use Exception;
/**
* 图片工具类
* Class Imager
* @package Meibuyu\Micro\Tools
*/
class Imager
{
/**
* 图片转base64,暂不支持外部地址图片
* @param $path
* @param bool $noPrefix 不需要前缀
* @return string
* @throws Exception
*/
public static function img2Base64($path, $noPrefix = false)
{
$path = self::parsePath($path);
if (file_exists($path)) {
$fp = fopen($path, "r"); // 图片是否可读权限
if ($fp) {
$imgInfo = getimagesize($path); // 取得图片的大小,类型等
$content = fread($fp, filesize($path));
$content = chunk_split(base64_encode($content)); // base64编码
if ($noPrefix) {
$base64 = $content;
} else {
$base64 = 'data:' . $imgInfo['mime'] . ';base64,' . $content; // 合成图片的base64编码
}
fclose($fp);
return $base64;
} else {
throw new Exception('图片读取失败');
}
} else {
throw new Exception('图片不存在');
}
}
/**
* @param $path
* @return string
* @throws Exception
*/
public static function parsePath($path)
{
if (strstr($path, 'http')) {
$appDomain = config('app_domain');
if (strstr($path, $appDomain)) {
$path = str_replace($appDomain, '', $path);
} else {
throw new Exception('暂不支持外部地址图片');
}
}
$documentRoot = config('server.settings.document_root');
if (!strstr($path, $documentRoot)) {
$path = trim(trim($path), '/');
$path = $documentRoot . '/' . $path;
}
return $path;
}
}
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2021/06/09
* Time: 10:14:32
*/
namespace Meibuyu\Micro\Tools;
use Hyperf\Logger\Logger;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\FormattableHandlerInterface;
use Monolog\Handler\HandlerInterface;
use Monolog\Handler\RotatingFileHandler;
use Psr\Log\LoggerInterface;
/**
* Class Log
* @package App\Services
* @method static emergency($message, string $name = 'app')
* @method static alert($message, string $name = 'app')
* @method static critical($message, string $name = 'app')
* @method static error($message, string $name = 'app')
* @method static warning($message, string $name = 'app')
* @method static notice($message, string $name = 'app')
* @method static info($message, string $name = 'app')
* @method static debug($message, string $name = 'app')
*/
class Log
{
/**
* @var LoggerInterface
*/
protected static $logs;
/**
* @return LoggerInterface
*/
public static function log($channel = 'app')
{
if (!isset(self::$logs[$channel])) {
self::$logs[$channel] = make(Logger::class, [
'name' => $channel,
'handlers' => self::handlers($channel),
]);
}
return self::$logs[$channel];
}
protected static function handlers($channel): array
{
$handlerConfigs = [
[
'class' => RotatingFileHandler::class,
'constructor' => [
'filename' => BASE_PATH . "/runtime/logs/$channel.log",
'level' => Logger::DEBUG,
],
'formatter' => [
'class' => LineFormatter::class,
'constructor' => [
'format' => "[%datetime%] %channel%.%level_name%: %message%\n",
'dateFormat' => 'Y-m-d H:i:s',
'allowInlineLineBreaks' => true,
],
],
],
];
$handlers = [];
foreach ($handlerConfigs as $value) {
$formatterConfig = $value['formatter'];
/** @var HandlerInterface $handler */
$handler = make($value['class'], $value['constructor']);
if ($handler instanceof FormattableHandlerInterface) {
$formatterClass = $formatterConfig['class'];
$formatterConstructor = $formatterConfig['constructor'];
/** @var FormatterInterface $formatter */
$formatter = make($formatterClass, $formatterConstructor);
$handler->setFormatter($formatter);
}
$handlers[] = $handler;
}
return $handlers;
}
public static function __callStatic($name, $arguments)
{
$msg = $arguments[0];
if (is_array($msg)) {
$msg = var_export($msg, true);
}
$channel = $arguments[1] ?? 'app';
return static::log($channel)->{$name}($msg);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Zero
* Time: 2020/11/19 15:35
*/
namespace Meibuyu\Micro\Tools;
class Timer
{
/**
* 获取时间区间
* @param int $num 正数为向后取时间,负数为向前取时间
* @param string $type 支持 year month day hour minute second
* @param string $begin 开始时间,默认此时此刻
* @param string $format
* @return array
* @author Zero
*/
public static function getRange(int $num, string $type, string $begin = null, string $format = null)
{
switch ($type) {
case 'year':
!$format && $format = 'Y';
break;
case 'month':
!$format && $format = 'Y-m';
break;
case 'day':
!$format && $format = 'Y-m-d';
break;
case 'hour':
!$format && $format = 'Y-m-d H';
break;
case 'minute':
!$format && $format = 'Y-m-d H:i';
break;
case 'second':
!$format && $format = 'Y-m-d H:i:s';
break;
}
$timeStr = $type . 's';
$begin = $begin ? strtotime($begin) : time();
$end = strtotime("+{$num} {$timeStr}", $begin);
if ($begin > $end) { // 如果开始时间比结束时间大,则反转
$temp = $begin;
$begin = $end;
$end = $temp;
}
$data = [];
while ($begin <= $end) {
$data[] = date($format, $begin);
$begin = strtotime("+1 {$timeStr}", $begin);
}
return $data;
}
}
<?php
namespace Meibuyu\Micro\Tools;
use Dimsav\UnixZipper;
use Meibuyu\Micro\Exceptions\HttpResponseException;
/**
* 打包器
* Class PackFiler
*/
class Unpacker
{
//文件列表 一位数组
public $fileList;
//文件名
public $fileName;
public function __construct(array $fileList, $fileName)
{
$this->fileList = $fileList;
$this->fileName = $fileName;
}
/**
* 打包
* @return string
* @throws \Exception
*/
public function unpack()
{
if (count($this->fileList) > 0) {
$zipper = new UnixZipper();
$filePath = $this->fileName.'.zip';
foreach ($this->fileList as $k => $v) {
$zipper->add($v);
}
$zipper->setDestination($filePath);
$zipper->compress();
return $filePath;
} else {
throw new HttpResponseException('打包失败,请重试');
}
}
}
\ No newline at end of file
<?php
namespace Meibuyu\Micro\Validator;
use Hyperf\Utils\MessageBag;
use Meibuyu\Micro\Exceptions\ValidatorException;
use Meibuyu\Micro\Validator\Contracts\ValidatorInterface;
abstract class AbstractValidator implements ValidatorInterface
{
/**
* @var int
*/
protected $id = null;
/**
* Validator
*
* @var object
*/
protected $validator;
/**
* Data to be validated
*
* @var array
*/
protected $data = array();
/**
* Validation Rules
*
* @var array
*/
protected $rules = array();
/**
* Validation Custom Messages
*
* @var array
*/
protected $messages = array();
/**
* Validation Custom Attributes
*
* @var array
*/
protected $attributes = array();
/**
* Validation errors
*
* @var MessageBag
*/
protected $errors = array();
/**
* Set Id
*
* @param $id
* @return AbstractValidator
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Set data to validate
*
* @param array $data
* @return $this
*/
public function with(array $data)
{
$this->data = $data;
return $this;
}
/**
* Return errors
*
* @return array
*/
public function errors()
{
return $this->errorsBag()->all();
}
/**
* Errors
*
* @return MessageBag
*/
public function errorsBag()
{
return $this->errors;
}
/**
* Pass the data and the rules to the validator
*
* @param string $action
* @return boolean
*/
abstract public function passes($action = null);
/**
* Pass the data and the rules to the validator or throws ValidatorException
*
* @param string $action
* @return boolean
* @throws ValidatorException
*/
public function passesOrFail($action = null)
{
if (!$this->passes($action)) {
throw new ValidatorException($this->errorsBag());
}
return true;
}
/**
* Get rule for validation by action ValidatorInterface::RULE_CREATE or ValidatorInterface::RULE_UPDATE
*
* Default rule: ValidatorInterface::RULE_CREATE
*
* @param null $action
* @return array
*/
public function getRules($action = null)
{
$rules = $this->rules;
if (isset($this->rules[$action])) {
$rules = $this->rules[$action];
}
return $this->parserValidationRules($rules, $this->id);
}
/**
* Set Rules for Validation
*
* @param array $rules
* @return $this
*/
public function setRules(array $rules)
{
$this->rules = $rules;
return $this;
}
/**
* Get Custom error messages for validation
*
* @return array
*/
public function getMessages()
{
return $this->messages;
}
/**
* Set Custom error messages for Validation
*
* @param array $messages
* @return $this
*/
public function setMessages(array $messages)
{
$this->messages = $messages;
return $this;
}
/**
* Get Custom error attributes for validation
*
* @return array
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Set Custom error attributes for Validation
*
* @param array $attributes
* @return $this
*/
public function setAttributes(array $attributes)
{
$this->attributes = $attributes;
return $this;
}
/**
* Parser Validation Rules
*
* @param $rules
* @param null $id
* @return array
*/
protected function parserValidationRules($rules, $id = null)
{
if (null === $id) {
return $rules;
}
array_walk($rules, function (&$rules, $field) use ($id) {
if (!is_array($rules)) {
$rules = explode("|", $rules);
}
foreach ($rules as $ruleIdx => $rule) {
// get name and parameters
@list($name, $params) = array_pad(explode(":", $rule), 2, null);
// only do someting for the unique rule
if (strtolower($name) != "unique") {
continue; // continue in foreach loop, nothing left to do here
}
$p = array_map("trim", explode(",", $params));
// set field name to rules key ($field) (laravel convention)
if (!isset($p[1])) {
$p[1] = $field;
}
// set 3rd parameter to id given to getValidationRules()
$p[2] = $id;
$params = implode(",", $p);
$rules[$ruleIdx] = $name . ":" . $params;
}
});
return $rules;
}
}
\ No newline at end of file
<?php
namespace Meibuyu\Micro\Validator\Contracts;
use Hyperf\Utils\MessageBag;
use Meibuyu\Micro\Exceptions\ValidatorException;
interface ValidatorInterface
{
const RULE_CREATE = 'create';
const RULE_UPDATE = 'update';
/**
* Set Id
*
* @param $id
* @return ValidatorInterface
*/
public function setId($id);
/**
* With
*
* @param array
* @return $this
*/
public function with(array $input);
/**
* Pass the data and the rules to the validator
*
* @param string $action
* @return boolean
*/
public function passes($action = null);
/**
* Pass the data and the rules to the validator or throws ValidatorException
*
* @param string $action
* @return boolean
* @throws ValidatorException
*/
public function passesOrFail($action = null);
/**
* Errors
*
* @return array
*/
public function errors();
/**
* Errors
*
* @return MessageBag
*/
public function errorsBag();
/**
* Set Rules for Validation
*
* @param array $rules
* @return $this
*/
public function setRules(array $rules);
/**
* Get rule for validation by action ValidatorInterface::RULE_CREATE or ValidatorInterface::RULE_UPDATE
*
* Default rule: ValidatorInterface::RULE_CREATE
*
* @param $action
* @return array
*/
public function getRules($action = null);
}
\ No newline at end of file
<?php
namespace Meibuyu\Micro\Validator;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
class HyperfValidator extends AbstractValidator
{
/**
* @var ValidatorFactoryInterface
*/
protected $validator;
public function __construct(ValidatorFactoryInterface $validator)
{
$this->validator = $validator;
}
/**
* Pass the data and the rules to the validator
*
* @param string $action
* @return boolean
*/
public function passes($action = null)
{
$rules = $this->getRules($action);
$messages = $this->getMessages();
$attributes = $this->getAttributes();
$validator = $this->validator->make($this->data, $rules, $messages, $attributes);
if ($validator->fails()) {
$this->errors = $validator->errors();
return false;
}
return true;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Zero
* Date: 2020/5/7
* Time: 15:12
*/
const DEFAULT_PAGE_SIZE = 10;
const YES = 1;
const NO = 0;
<?php
use Hyperf\Cache\Listener\DeleteListenerEvent;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\Logger\Logger;
use Hyperf\Redis\Redis;
use Hyperf\Utils\ApplicationContext;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\FirePHPHandler;
use Monolog\Handler\StreamHandler;
use Psr\EventDispatcher\EventDispatcherInterface;
/**
* 容器实例
*/
if (!function_exists('container')) {
function container($key = null)
{
if (is_null($key)) {
return ApplicationContext::getContainer();
} else {
return ApplicationContext::getContainer()->get($key);
}
}
}
if (!function_exists('redis')) {
/**
* 获取redis客户端实例
* @return Redis|mixed
*/
function redis()
{
return container(Redis::class);
}
}
/**
* token
*/
if (!function_exists('token')) {
function token()
{
$token = request()->getHeader('Authorization')[0] ?? '';
if (strlen($token) > 0) {
$token = ucfirst($token);
$arr = explode('Bearer ', $token);
$token = $arr[1] ?? '';
if (strlen($token) > 0) {
return $token;
}
}
return false;
}
}
if (!function_exists('request')) {
/**
* 请求实例
* @param array|string|null $key
* @param mixed $default
* @return RequestInterface|string|array|mixed
*/
function request($key = null, $default = null)
{
if (is_null($key)) {
return container(RequestInterface::class);
}
if (is_array($key)) {
return container(RequestInterface::class)->inputs($key);
}
$value = container(RequestInterface::class)->input($key);
return is_null($value) ? value($default) : $value;
}
}
if (!function_exists('response')) {
/**
* 响应实例
* @return mixed|ResponseInterface
*/
function response()
{
return container(ResponseInterface::class);
}
}
if (!function_exists('success')) {
/**
* 成功响应实例
* @param string $msg
* @param mixed $data
* @param int $code
* @return mixed
*/
function success($msg = '', $data = null, $code = 200)
{
return response()->json([
'msg' => $msg,
'data' => $data,
'code' => $code
]);
}
}
if (!function_exists('fail')) {
/**
* 失败响应实例
* @param string $msg
* @param mixed $data
* @param int $code
* @return mixed
*/
function fail($msg = '', $data = null, $code = 400)
{
return response()->json([
'msg' => $msg,
'data' => $data,
'code' => $code
]);
}
}
if (!function_exists('download')) {
/**
* 文件流下载文件
* @param string $filePath 文件路径
* @param string $showName 下载后展示的名称
* @return mixed
*/
function download($filePath = '', $showName = '')
{
return response()->download($filePath, urlencode($showName));
}
}
if (!function_exists('decimal_to_abc')) {
/**
* 数字转换对应26个字母
* @param $num
* @return string
* @deprecated 此方法废弃,请使用int_to_chr()
*/
function decimal_to_abc($num)
{
$str = "";
$ten = $num;
if ($ten == 0) return "A";
while ($ten != 0) {
$x = $ten % 26;
$str .= chr(65 + $x);
$ten = intval($ten / 26);
}
return strrev($str);
}
}
if (!function_exists('diff_between_two_days')) {
/**
* 计算两个日期之间相差的天数
* @param $day1
* @param $day2
* @return float|int
*/
function diff_between_two_days($day1, $day2)
{
$second1 = strtotime($day1);
$second2 = strtotime($day2);
return round((abs($second1 - $second2) / 86400), 0);
}
}
if (!function_exists('decimals_to_percentage')) {
/**
* 将小数转换百分数
* @param float $decimals 小数
* @param int $num 保留小数位
* @return string
*/
function decimals_to_percentage($decimals, $num = 2)
{
return sprintf("%01." . $num . "f", $decimals * 100) . '%';
}
}
if (!function_exists('calculate_grade')) {
/**
*
* 计算一个数的区间范围等级
* @param array $range 区间范围(从大到小排列)
* @param $num
* @return mixed|void
*/
function calculate_grade($range, $num)
{
$max = max($range);
if ($num >= $max) {
return count($range);
}
foreach ($range as $k => $v) {
if ($num < $v) {
return $k;
}
}
}
}
if (!function_exists('convertAmountToCn')) {
/**
* 2 * 将数值金额转换为中文大写金额
* 3 * @param $amount float 金额(支持到分)
* 4 * @param $type int 补整类型,0:到角补整;1:到元补整
* 5 * @return mixed 中文大写金额
* 6 */
function convertAmountToCn($amount, $type = 1)
{
// 判断输出的金额是否为数字或数字字符串
if (!is_numeric($amount)) {
return "要转换的金额只能为数字!";
}
// 金额为0,则直接输出"零元整"
if ($amount == 0) {
return "人民币零元整";
}
// 金额不能为负数
if ($amount < 0) {
return "要转换的金额不能为负数!";
}
// 金额不能超过万亿,即12位
if (strlen($amount) > 12) {
return "要转换的金额不能为万亿及更高金额!";
}
// 预定义中文转换的数组
$digital = array('零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖');
// 预定义单位转换的数组
$position = array('仟', '佰', '拾', '亿', '仟', '佰', '拾', '万', '仟', '佰', '拾', '元');
// 将金额的数值字符串拆分成数组
$amountArr = explode('.', $amount);
// 将整数位的数值字符串拆分成数组
$integerArr = str_split($amountArr[0], 1);
// 将整数部分替换成大写汉字
$result = '人民币';
$integerArrLength = count($integerArr); // 整数位数组的长度
$positionLength = count($position); // 单位数组的长度
for ($i = 0; $i < $integerArrLength; $i++) {
// 如果数值不为0,则正常转换
if ($integerArr[$i] != 0) {
$result = $result . $digital[$integerArr[$i]] . $position[$positionLength - $integerArrLength + $i];
} else {
// 如果数值为0, 且单位是亿,万,元这三个的时候,则直接显示单位
if (($positionLength - $integerArrLength + $i + 1) % 4 == 0) {
$result = $result . $position[$positionLength - $integerArrLength + $i];
}
}
}
// 如果小数位也要转换
if ($type == 0) {
// 将小数位的数值字符串拆分成数组
$decimalArr = str_split($amountArr[1], 1);
// 将角替换成大写汉字. 如果为0,则不替换
if ($decimalArr[0] != 0) {
$result = $result . $digital[$decimalArr[0]] . '角';
}
// 将分替换成大写汉字. 如果为0,则不替换
if ($decimalArr[1] != 0) {
$result = $result . $digital[$decimalArr[1]] . '分';
}
} else {
$result = $result . '整';
}
return $result;
}
}
if (!function_exists('today')) {
/**
* Create a new Carbon instance for the current time.
* @return false|string
*/
function today()
{
return date('Y-m-d', time());
}
}
if (!function_exists('now')) {
/**
* Create a new Carbon instance for the current time.
* @return false|string
*/
function now()
{
return date('Y-m-d H:i:s', time());
}
}
if (!function_exists('get_tree_id')) {
/**
* @param array $array
* @param array $pid
* @return array
*/
function get_tree_id(array $array, $pids = [0])
{
$list = [];
foreach ($array as $key => $value) {
if (in_array($value['pid'], $pids) || in_array($value['id'], $pids)) {
$list[] = $value['id'];
unset($array[$key]);
}
}
if ($list == []) return [];
$children = get_tree_id($array, $list);
return array_merge($list, $children);
}
}
if (!function_exists('list_go_tree')) {
/**
* 列表转树状格式
* @param array $list
* @param string $pk
* @param string $pid
* @param string $children
* @return array
*/
function list_go_tree(array $list = [], $pk = 'id', $pid = 'parent_id', $children = 'children')
{
$tree = $refer = [];
// 创建基于主键的数组引用
foreach ($list as $key => $data) {
$refer[$data[$pk]] = &$list[$key];
}
foreach ($list as $key => $data) {
$parentId = $data[$pid];
// 判断是否存在parent
if (isset($refer[$parentId])) {
$parent = &$refer[$parentId];
$parent[$children][] = &$list[$key];
} else {
$tree[] = &$list[$key];
}
}
return $tree;
}
}
if (!function_exists('flushAnnotationCache')) {
/**
* 刷新注解缓存,清楚注解缓存
* @param string $listener
* @param mixed $keys
* @return bool
*/
function flushAnnotationCache($listener, $keys)
{
$keys = is_array($keys) ? $keys : [$keys];
$dispatcher = ApplicationContext::getContainer()->get(EventDispatcherInterface::class);
foreach ($keys as $key) {
$dispatcher->dispatch(new DeleteListenerEvent($listener, [$key]));
}
return true;
}
}
if (!function_exists('num_2_file_size')) {
/**
* 数字转文件大小
* @param $num
* @return string
*/
function num_2_file_size($num)
{
$p = 0;
$format = 'B';
if ($num > 0 && $num < 1024) {
return number_format($num) . ' ' . $format;
} else if ($num >= 1024 && $num < pow(1024, 2)) {
$p = 1;
$format = 'KB';
} else if ($num >= pow(1024, 2) && $num < pow(1024, 3)) {
$p = 2;
$format = 'MB';
} else if ($num >= pow(1024, 3) && $num < pow(1024, 4)) {
$p = 3;
$format = 'GB';
} else if ($num >= pow(1024, 4) && $num < pow(1024, 5)) {
$p = 4;
$format = 'TB';
}
$num /= pow(1024, $p);
return number_format($num, 2) . ' ' . $format;
}
}
if (!function_exists('select_id_name')) {
function select_id_name($columns = [])
{
$columns = array_merge(['id', 'name'], $columns);
return function ($q) use ($columns) {
$q->select($columns);
};
}
}
if (!function_exists('get_week_start_and_end')) {
function get_week_start_and_end($time = '', $first = 1)
{
//当前日期
if (!$time) $time = time();
$sdefaultDate = date("Y-m-d", $time);
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
$w = date('w', strtotime($sdefaultDate));
//获取本周开始日期,如果$w是0,则表示周日,减去 6 天
$week_start = date('Y-m-d', strtotime("$sdefaultDate -" . ($w ? $w - $first : 6) . ' days'));
//本周结束日期
$week_end = date('Y-m-d', strtotime("$week_start +6 days"));
return array("week_start" => $week_start, "week_end" => $week_end);
}
}
if (!function_exists('putLog')) {
/**
* description:记录日志 文件会生成在当前项目 /runtime/dev/
* author: fuyunnan
* @param string|array $output 日志 内容
* @param string $dir 目录
* @param string $filename 文件名称
* date: 2020/3/18
* @return void
* @throws
*/
function put_log($output = 'out-mes', $filename = '', $dir = BASE_PATH . '/runtime/logs/dev/')
{
!is_dir($dir) && !mkdir($dir, 0777, true);
// 创建一个 Channel,参数 log 即为 Channel 的名字
$log = make(Logger::class, ['']);
// 创建两个 Handler,对应变量 $stream 和 $fire
!$filename && $filename = date('Y-m-d', time()) . '.log';
$stream = make(StreamHandler::class, [$dir . $filename, Logger::WARNING]);
$fire = make(FirePHPHandler::class);
if (is_array($output)) {
$output = var_export($output, true);
}
$output = '[ ' . date('Y-m-d H:i:s', time()) . ' ] --- ' . $output;
$formatter = new LineFormatter($output . "\r\n");
$stream->setFormatter($formatter);
$log->pushHandler($stream);
$log->pushHandler($fire);
$log->alert('');
}
}
if (!function_exists('http_to_server_url')) {
/**
* description:将前端的绝对路径转化为服务端相对路径
* author: fuyunnan
* @param string $path 需要转化的路径
* @return string
* @throws
* Date: 2020/6/24
*/
function http_to_server_url($path)
{
$path = ltrim(parse_url($path, PHP_URL_PATH), '/');
return 'public/' . $path;
}
}
if (!function_exists('empty_string_2_null')) {
/**
* 空字符串转NULL
* @param array $arr
* @return array
*/
function empty_string_2_null(array $arr)
{
if (!empty($arr)) {
foreach ($arr as $key => $value) {
if (is_array($value)) {
$arr[$key] = empty_string_2_null($value);
} else {
if ($value === '') {
$arr[$key] = null;
}
}
}
}
return $arr;
}
}
if (!function_exists('get_collection_values')) {
/**
* 从集合中提取深层数据
* @param mixed $collection 数据集合
* @param string $key 集合中键 支持多层提取,例如"a.b.c"
* @return array | collection
*/
function get_collection_values($collection, string $key)
{
$values = [];
if (!empty($collection) && (is_object($collection) || is_array($collection))) {
$itemKeys = explode(".", $key);
$keyCount = count($itemKeys) - 1;
foreach ($collection as $value) {
foreach ($itemKeys as $k => $ik) {
if (isset($value[$ik])) {
$value = $value[$ik];
if ($k == $keyCount) {
$values[] = $value;
}
} else {
if (is_array($value) || is_countable($collection)) {
foreach ($value as $vv) {
if (isset($vv[$ik])) {
$values[] = $vv[$ik];
}
}
}
break;
}
}
}
}
return $values;
}
}
if (!function_exists('put_collection_values')) {
/**
* 对集合中设置深层数据
* @param array | collection $collection 需要设置的数据集合
* @param array | collection $valueList 需要设置值集合
* @param string $collectionKey 集合中键 支持多层提取,例如"user.info"
* @param string $collectionNewKey 数据集合的新键,例如"infomation",同名会覆盖
* @param string $valueKey $valueList中的值字段,例如"user_id"
* @return array | collection
*/
function put_collection_values($collection, $valueList, string $collectionKey, string $collectionNewKey, $valueKey)
{
if (!empty($collection) && (is_object($collection) || is_array($collection))) {
$itemKeys = explode(".", $collectionKey);
if (!empty($valueList)) {
if (!is_object($valueList)) {
$valueList = collect($valueList);
}
$valueList = $valueList->keyBy($valueKey);
}
if (isset($collection[0])) {
foreach ($collection as $index => $value) {
$collection[$index] = private_put_collection_values($value, $itemKeys, $valueList, $collectionNewKey);
}
} else {
private_put_collection_values($collection, $itemKeys, $valueList, $collectionNewKey);
}
}
return $collection;
}
}
if (!function_exists('private_put_collection_values')) {
/**
* 对集合设置值,不对外公开
* @param $collection
* @param $itemKeys
* @param $valueList
* @param $collectionNewKey
* @return mixed
*/
function private_put_collection_values(&$collection, $itemKeys, $valueList, $collectionNewKey)
{
if (isset($collection[$itemKeys[0]])) {
if (count($itemKeys) != 1) {
$t = $itemKeys[0];
unset($itemKeys[0]);
$itemKeys = array_values($itemKeys);
if (is_array($collection[$t]) || is_countable($collection[$t])) {
foreach ($collection[$t] as $k => $v) {
$collection[$t][$k] = private_put_collection_values($v, $itemKeys, $valueList, $collectionNewKey);
}
} else {
$collection[$t] = private_put_collection_values($collection[$t], $itemKeys, $valueList, $collectionNewKey);
}
} else {
if (isset($valueList[$collection[$itemKeys[0]]])) {
$collection[$collectionNewKey] = $valueList[$collection[$itemKeys[0]]];
} else {
$collection[$collectionNewKey] = null;
}
}
}
return $collection;
}
}
if (!function_exists('human_time')) {
/**
* 计算时间
* @param $time 时间戳
* @return string
*/
function human_time($time)
{
if (!is_numeric($time)) {
$time = strtotime($time);
}
$time = abs($time);
//计算天数
$days = intval($time / 86400);
if ($days != 0) {
return $days . "天";
}
//计算小时数
$remain = $time % 86400;
$hours = intval($remain / 3600);
if ($hours != 0) {
return $hours . "小时";
}
//计算分钟数
$remain = $time % 3600;
$mins = intval($remain / 60);
if ($mins != 0) {
return $mins . "分钟";
}
//计算秒数
$secs = $time % 60;
return $secs . "秒";
}
}
if (!function_exists('info')) {
/**
* 输出数据到控制台
* @param mixed ...$arguments
*/
function info(...$arguments)
{
var_dump(...$arguments);
}
}
if (!function_exists('make_belong_relation_function')) {
/**
* 创建我属于关联关系的函数
* @param string $relationName 关联关系的名字
* @param array $mainModelColumns 使用关联关系的主表要筛选的列
* @param array $relationColumns 关联关系的列 默认['id', 'name']
* @param string $mainModelRelationKey 主表和关联关系对应的字段 空的话为$relationName+"_id"
* @param callable|null $callback 内嵌级联调用
* @return Closure 返回关联关系的匿名函数
*/
function make_belong_relation_function($relationName, array &$mainModelColumns, $relationColumns = ['id', 'name'], $mainModelRelationKey = "", callable $callback = null)
{
$key = $mainModelRelationKey ? $mainModelRelationKey : $relationName . "_id";
if (!in_array($key, $mainModelColumns)) array_push($mainModelColumns, $key);
return make_has_relation_function($relationColumns, $callback);
}
}
if (!function_exists('make_has_relation_function')) {
/**
* 创建我有关联关系的函数
* @param array $relationColumns 关联关系的列 默认['id', 'name']
* @param callable|null $callback 内嵌级联调用
* @return Closure 返回关联关系的匿名函数
* @return Closure
*/
function make_has_relation_function($relationColumns = ['id', 'name'], callable $callback = null)
{
return function ($q) use ($relationColumns, $callback) {
$q->select($relationColumns);
if (is_callable($callback)) {
$callback($q);
}
};
}
}
if (!function_exists('int_to_chr')) {
/**
* 数字转字母 (类似于Excel列标)
* @param mixed $index 索引值
* @param int $start 字母起始值
* @return string 返回字母
*/
function int_to_chr($index, $start = 65)
{
$str = '';
if (floor($index / 26) > 0) {
$str .= int_to_chr(floor($index / 26) - 1);
}
return $str . chr($index % 26 + $start);
}
}
if (!function_exists('check_diff_val')) {
/**
* 判断两个对象数组是否有不同的值
* 后者里有前者的key时,但value不一样,返回true
* 后者里没有前者的key,或有key,但value一样时,返回false
* @param array $list
* @param array $data
* @return bool
*/
function check_diff_val(array $list, array $data)
{
foreach ($list as $key => $val) {
if (isset($data[$key]) && $data[$key]) {
if (is_array($val)) {
if (check_diff_val($val, $data[$key])) {
return true;
}
} else {
if ($list[$key] != $data[$key]) {
return true;
}
}
}
}
return false;
}
}
if (!function_exists('get_diff_val')) {
/**
* 获取两个数组中key相同,value不同的数据
* 返回后者的数据
* @param array $list1
* @param array $list2
* @param array $excludeKey 排除的key数组
* @return array
* @author Zero
*/
function get_diff_val(array $list1, array $list2, array $excludeKey = [])
{
$diff = [];
foreach ($list1 as $key => $val) {
if (!in_array($key, $excludeKey)) {
if (isset($list2[$key]) && $list2[$key] != '') {
if (is_array($val)) {
$temp = get_diff_val($val, $list2[$key], $excludeKey);
!empty($temp) && $diff[$key] = $temp;
} else {
if ($list1[$key] != $list2[$key]) {
$diff[$key] = $list2[$key];
}
}
}
}
}
return $diff;
}
}
if (!function_exists('to_camel_case')) {
/**
* 下划线命名转驼峰命名
* 例:demo_function : demoFunction
* @param $dirSep 分隔符
* @param $str
* @return mixed|string
*/
function to_camel_case($dirSep, $str)
{
$array = explode($dirSep, $str);
$result = $array[0];
$len = count($array);
if ($len > 1) {
for ($i = 1; $i < $len; $i++) {
$result .= ucfirst($array[$i]);
}
}
return $result;
}
}
if (!function_exists('mb_trim')) {
/**
* 去除两边带全角空格的字符串
* @param $str
* @return string
* @author zero
*/
function mb_trim($str)
{
mb_regex_encoding('utf-8');
$str = mb_ereg_replace(' ', '', $str);
return trim($str);
}
}
if (!function_exists('str_replace_first')) {
/**
* 用替换字符串替换第一个出现的搜索字符串
* @param mixed $search 搜索字符串
* @param mixed $replace 替换字符串
* @param mixed $subject 被替换字符串
* @return string
* @author zero
*/
function str_replace_first($search, $replace, $subject)
{
if (($position = strpos($subject, $search)) !== false) {
$replaceLen = strlen($search);
$subject = substr_replace($subject, $replace, $position, $replaceLen);
}
return $subject;
}
}
if (!function_exists('create_file_dir')) {
/**
* 检查当前目录是否存在 不存在就创建一个目录
* @param mixed $dir 文件目录
* @return bool
* @author zero
*/
function create_file_dir($dir)
{
return !is_dir($dir) && mkdir($dir, 0777, true);
}
}
if (!function_exists('get_images_url')) {
/**
* 富文本获取图片信息
* @param $str
* @return mixed
*/
function get_images_url($str)
{
preg_match('/<img.+src=\"?(.+\.(jpg|gif|bmp|bnp|png))\"?.+>/i', $str, $match);
return $match;
}
}
if (!function_exists('download_file')) {
/**
* 下载远程文件
* @param $url 远程文件路径
* @param null $path 系统保存路径
* @return string
*/
function download_file($url, $path = null)
{
$filename = trim(pathinfo($url, PATHINFO_FILENAME));
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
$filename = "$filename.$ext";
$root = config('server.settings.document_root', BASE_PATH . '/public');
$path = $path ? $path : '/download/files';
$savePath = $root . $path;
if (!is_dir($savePath)) {
// 判断路径是否存在,不存在,则创建
mkdir($savePath, 0777, true);
}
$filePath = $savePath . '/' . $filename;
if (!file_exists($filePath)) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
$file = curl_exec($ch);
curl_close($ch);
$resource = fopen($filePath, 'a');
fwrite($resource, $file);
fclose($resource);
}
return $path . '/' . $filename;;
}
}
if (!function_exists('download_file_stream')) {
/**文件流保存到本地
* @param $fileStream 文件流
* @param null $path 保存路径
* @return string
*/
function download_file_stream($fileStream, $path = null)
{
$root = config('server.settings.document_root', BASE_PATH . '/public');
$path = $path ? $path : '/download/files';
$savePath = $root . $path;
if (!is_dir($savePath)) {
// 判断路径是否存在,不存在,则创建
mkdir($savePath, 0777, true);
}
$fileName = '/' . date('YmdHis') . 'track' . '.pdf';
file_put_contents($savePath . $fileName, base64_decode($fileStream), 1);
return $path . $fileName;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment