20220406
This commit is contained in:
172
vendor/hg/apidoc/src/Auth.php
vendored
Normal file
172
vendor/hg/apidoc/src/Auth.php
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc;
|
||||
|
||||
use think\facade\Config;
|
||||
use think\facade\Request;
|
||||
use hg\apidoc\exception\AuthException;
|
||||
|
||||
class Auth
|
||||
{
|
||||
protected $config = [];
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = Config::get('apidoc')?Config::get('apidoc'):Config::get('apidoc.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
* @param $password
|
||||
* @return false|string
|
||||
*/
|
||||
public function verifyAuth(string $password, string $appKey)
|
||||
{
|
||||
if (!empty($appKey)) {
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$currentApp = $currentApps[count($currentApps) - 1];
|
||||
if (!empty($currentApp) && !empty($currentApp['password'])) {
|
||||
// 应用密码
|
||||
if (md5($currentApp['password']) === $password) {
|
||||
return $this->createToken($currentApp['password']);
|
||||
}
|
||||
throw new AuthException("password error");
|
||||
}
|
||||
}
|
||||
if ($this->config['auth']['enable']) {
|
||||
// 密码验证
|
||||
if (md5($this->config['auth']['password']) === $password) {
|
||||
return $this->createToken($this->config['auth']['password']);
|
||||
}
|
||||
throw new AuthException("password error");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取tokencode
|
||||
* @param string $password
|
||||
* @return string
|
||||
*/
|
||||
protected function getTokenCode(string $password): string
|
||||
{
|
||||
return md5(md5($password) . strtotime(date('Y-m-d', time())));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建token
|
||||
* @param string $password
|
||||
* @return string
|
||||
*/
|
||||
public function createToken(string $password): string
|
||||
{
|
||||
return $this->handleToken($this->getTokenCode($password), "CE");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证token
|
||||
* @param $token
|
||||
* @return bool
|
||||
*/
|
||||
public function checkToken(string $token, string $password): bool
|
||||
{
|
||||
if (empty($password)) {
|
||||
$password = $this->config['auth']['password'];
|
||||
}
|
||||
$decode = $this->handleToken($token, "DE");
|
||||
if ($decode === $this->getTokenCode($password)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $request
|
||||
* @return bool
|
||||
*/
|
||||
public function checkAuth(string $appKey): bool
|
||||
{
|
||||
$config = $this->config;
|
||||
$request = Request::instance();
|
||||
|
||||
$token = $request->param("apidocToken");
|
||||
|
||||
if (!empty($appKey)) {
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$currentApp = $currentApps[count($currentApps) - 1];
|
||||
if (!empty($currentApp) && !empty($currentApp['password'])) {
|
||||
if (empty($token)) {
|
||||
throw new AuthException("token not found");
|
||||
}
|
||||
// 应用密码
|
||||
if ($this->checkToken($token, $currentApp['password'])) {
|
||||
return true;
|
||||
} else {
|
||||
throw new AuthException("token error");
|
||||
}
|
||||
} else if (!(!empty($config['auth']) && $config['auth']['enable'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(!empty($config['auth']) && $config['auth']['enable'] && empty($token)){
|
||||
throw new AuthException("token not found");
|
||||
}else if (!empty($token) && !$this->checkToken($token, "")) {
|
||||
throw new AuthException("token error");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理token
|
||||
* @param $string
|
||||
* @param string $operation
|
||||
* @param string $key
|
||||
* @param int $expiry
|
||||
* @return false|string
|
||||
*/
|
||||
protected function handleToken(string $string, string $operation = 'DE', string $key = '', int $expiry = 0):string
|
||||
{
|
||||
$ckey_length = 4;
|
||||
$key = md5($key ? $key : $this->config['auth']['secret_key']);
|
||||
$keya = md5(substr($key, 0, 16));
|
||||
$keyb = md5(substr($key, 16, 16));
|
||||
$keyc = $ckey_length ? ($operation == 'DE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
|
||||
$cryptkey = $keya . md5($keya . $keyc);
|
||||
$key_length = strlen($cryptkey);
|
||||
$string = $operation == 'DE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
|
||||
$string_length = strlen($string);
|
||||
$result = '';
|
||||
$box = range(0, 255);
|
||||
$rndkey = array();
|
||||
for ($i = 0; $i <= 255; $i++) {
|
||||
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
|
||||
}
|
||||
for ($j = $i = 0; $i < 256; $i++) {
|
||||
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
|
||||
$tmp = $box[$i];
|
||||
$box[$i] = $box[$j];
|
||||
$box[$j] = $tmp;
|
||||
}
|
||||
for ($a = $j = $i = 0; $i < $string_length; $i++) {
|
||||
$a = ($a + 1) % 256;
|
||||
$j = ($j + $box[$a]) % 256;
|
||||
$tmp = $box[$a];
|
||||
$box[$a] = $box[$j];
|
||||
$box[$j] = $tmp;
|
||||
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
|
||||
}
|
||||
if ($operation == 'DE') {
|
||||
if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
|
||||
return substr($result, 26);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
return $keyc . str_replace('=', '', base64_encode($result));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
259
vendor/hg/apidoc/src/Controller.php
vendored
Normal file
259
vendor/hg/apidoc/src/Controller.php
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc;
|
||||
|
||||
use hg\apidoc\exception\AuthException;
|
||||
use hg\apidoc\exception\ErrorException;
|
||||
use hg\apidoc\parseApi\CacheApiData;
|
||||
use hg\apidoc\parseApi\ParseAnnotation;
|
||||
use hg\apidoc\parseApi\ParseMarkdown;
|
||||
use think\App;
|
||||
use think\facade\Config;
|
||||
use think\facade\Request;
|
||||
use hg\apidoc\crud\CreateCrud;
|
||||
|
||||
class Controller
|
||||
{
|
||||
protected $app;
|
||||
|
||||
protected $config;
|
||||
|
||||
protected $defaultConfig=[
|
||||
'crud'=>[
|
||||
'model'=>[
|
||||
'fields_types'=>[
|
||||
"int",
|
||||
"tinyint",
|
||||
"smallint",
|
||||
"mediumint",
|
||||
"integer",
|
||||
"bigint",
|
||||
"bit",
|
||||
"real",
|
||||
"float",
|
||||
"decimal",
|
||||
"numeric",
|
||||
"char",
|
||||
"varchar",
|
||||
"date",
|
||||
"time",
|
||||
"year",
|
||||
"timestamp",
|
||||
"datetime",
|
||||
"tinyblob",
|
||||
"blob",
|
||||
"mediumblob",
|
||||
"longblob",
|
||||
"tinytext",
|
||||
"text",
|
||||
"mediumtext",
|
||||
"longtext",
|
||||
"enum",
|
||||
"set",
|
||||
"binary",
|
||||
"varbinary",
|
||||
"point",
|
||||
"linestring",
|
||||
"polygon",
|
||||
"geometry",
|
||||
"multipoint",
|
||||
"multilinestring",
|
||||
"multipolygon",
|
||||
"geometrycollection"
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->config = Config::get("apidoc")?Config::get("apidoc"):Config::get("apidoc.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getConfig(){
|
||||
$config = $this->config;
|
||||
if (!empty($config['auth'])){
|
||||
unset($config['auth']['auth_password']);
|
||||
unset($config['auth']['password']);
|
||||
unset($config['auth']['key']);
|
||||
}
|
||||
// 处理统一返回信息
|
||||
if (!empty($config['responses']) && is_string($config['responses'])){
|
||||
// 兼容原配置
|
||||
$config['responses'] = [
|
||||
'jsonStr'=>$config['responses']
|
||||
];
|
||||
}else if (!empty($config['responses']) && isset($config['responses']['show_responses']) && !$config['responses']['show_responses'] && !empty($config['responses']['data'])){
|
||||
// 显示在提示中
|
||||
$responsesStr = '{'."\r\n";
|
||||
$responsesMain = "";
|
||||
foreach ($config['responses']['data'] as $item){
|
||||
$responsesStr.='"'.$item['name'].'":"'.$item['desc'].'",'."\r\n";
|
||||
if (!empty($item['main']) && $item['main']==true){
|
||||
$responsesMain = $item;
|
||||
}
|
||||
}
|
||||
$responsesStr.= '}';
|
||||
$config['responses']['jsonStr']=$responsesStr;
|
||||
$config['responses']['main']=$responsesMain;
|
||||
}
|
||||
|
||||
$config['debug']=app()->isDebug();
|
||||
|
||||
if (!empty($config['crud'])){
|
||||
// 无配置可选字段类型,使用默认的
|
||||
if (!empty($config['crud']['model']) && empty($config['crud']['model']['fields_types'])){
|
||||
$config['crud']['model']['fields_types'] = $this->defaultConfig['crud']['model']['fields_types'];
|
||||
}
|
||||
// 过滤route文件配置
|
||||
if (!empty($config['crud']['route'])){
|
||||
unset($config['crud']['route']);
|
||||
}
|
||||
}
|
||||
// 清除apps配置中的password
|
||||
$config['apps'] = (new Utils())->handleAppsConfig($config['apps']);
|
||||
return Utils::showJson(0,"",$config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
* @return false|\think\response\Json
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function verifyAuth(){
|
||||
$config = $this->config;
|
||||
|
||||
$request = Request::instance();
|
||||
$params = $request->param();
|
||||
$password = $params['password'];
|
||||
if (empty($password)){
|
||||
throw new AuthException( "password not found");
|
||||
}
|
||||
$appKey = !empty($params['appKey'])?$params['appKey']:"";
|
||||
|
||||
if (!$appKey && !(!empty($config['auth']) && $config['auth']['enable'])) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$hasAuth = (new Auth())->verifyAuth($password,$appKey);
|
||||
return Utils::showJson(0,"",$hasAuth);
|
||||
} catch (AuthException $e) {
|
||||
return Utils::showJson($e->getCode(),$e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档数据
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getApidoc(){
|
||||
|
||||
$config = $this->config;
|
||||
$request = Request::instance();
|
||||
$params = $request->param();
|
||||
(new Auth())->checkAuth($params['appKey']);
|
||||
|
||||
$cacheData=null;
|
||||
if (!empty($config['cache']) && $config['cache']['enable']){
|
||||
$cacheData = (new CacheApiData())->get($params['appKey'],$params['cacheFileName']);
|
||||
if ($cacheData && $params['reload']=='false'){
|
||||
$apiData = $cacheData['data'];
|
||||
}else{
|
||||
// 生成数据并缓存
|
||||
$apiData = (new ParseAnnotation())->renderApiData($params['appKey']);
|
||||
$cacheData =(new CacheApiData())->set($params['appKey'],$apiData);
|
||||
}
|
||||
}else{
|
||||
// 生成数据
|
||||
$apiData = (new ParseAnnotation())->renderApiData($params['appKey']);
|
||||
}
|
||||
$groups=[['title'=>'全部','name'=>0]];
|
||||
// 获取md
|
||||
$docs = (new ParseMarkdown())->getDocsMenu();
|
||||
if (count($docs)>0){
|
||||
$menu_title = !empty($config['docs']) && !empty($config['docs']['menu_title'])?$config['docs']['menu_title']:'文档';
|
||||
$groups[]=['title'=>$menu_title,'name'=>'markdown_doc'];
|
||||
}
|
||||
if (!empty($config['groups']) && count($config['groups'])>0 && !empty($apiData['groups']) && count($apiData['groups'])>0){
|
||||
$configGroups=[];
|
||||
foreach ($config['groups'] as $group) {
|
||||
if (in_array($group['name'],$apiData['groups'])){
|
||||
$configGroups[]=$group;
|
||||
}
|
||||
}
|
||||
$groups = array_merge($groups,$configGroups);
|
||||
}
|
||||
$json=[
|
||||
'groups'=>$groups,
|
||||
'list'=>$apiData['data'],
|
||||
'docs'=>$docs,
|
||||
'tags'=>$apiData['tags']
|
||||
];
|
||||
if ($cacheData && !empty($cacheData['list'])){
|
||||
$json['cacheFiles']=$cacheData['list'];
|
||||
$json['cacheName']=$cacheData['name'];
|
||||
}
|
||||
return Utils::showJson(0,"",$json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取md文档内容
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function getMdDetail(){
|
||||
$request = Request::instance();
|
||||
$params = $request->param();
|
||||
try {
|
||||
if (empty($params['path'])){
|
||||
throw new ErrorException("mdPath not found");
|
||||
}
|
||||
if (empty($params['appKey'])){
|
||||
throw new ErrorException("appkey not found");
|
||||
}
|
||||
(new Auth())->checkAuth($params['appKey']);
|
||||
$res = (new ParseMarkdown())->getContent($params['appKey'],$params['path']);
|
||||
return Utils::showJson(0,"",$res);
|
||||
|
||||
} catch (ErrorException $e) {
|
||||
return Utils::showJson($e->getCode(),$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建Crud
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function createCrud()
|
||||
{
|
||||
if (!\think\facade\App::isDebug()) {
|
||||
throw new ErrorException("no debug",501);
|
||||
}
|
||||
$request = Request::instance();
|
||||
$params = $request->param();
|
||||
|
||||
if (!isset($params['appKey'])){
|
||||
throw new ErrorException("appkey not found");
|
||||
}
|
||||
(new Auth())->checkAuth($params['appKey']);
|
||||
$res = (new CreateCrud())->create($params);
|
||||
return Utils::showJson(0,"",$res);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
35
vendor/hg/apidoc/src/Service.php
vendored
Normal file
35
vendor/hg/apidoc/src/Service.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc;
|
||||
|
||||
use think\facade\Config;
|
||||
use think\facade\Route;
|
||||
|
||||
class Service extends \think\Service
|
||||
{
|
||||
|
||||
public function boot()
|
||||
{
|
||||
|
||||
$this->registerRoutes(function (){
|
||||
$route_prefix = 'apidoc';
|
||||
$apidocConfig = Config::get("apidoc")?Config::get("apidoc"):Config::get("apidoc.");
|
||||
$routes = function () {
|
||||
$controller_namespace = '\hg\apidoc\Controller@';
|
||||
Route::get('config' , $controller_namespace . 'getConfig');
|
||||
Route::get('apiData' , $controller_namespace . 'getApidoc');
|
||||
Route::get('mdDetail' , $controller_namespace . 'getMdDetail');
|
||||
Route::post('verifyAuth' , $controller_namespace . 'verifyAuth');
|
||||
Route::post('createCrud' , $controller_namespace . 'createCrud');
|
||||
};
|
||||
if (!empty($apidocConfig['allowCrossDomain'])){
|
||||
Route::group($route_prefix, $routes)->allowCrossDomain();
|
||||
}
|
||||
Route::group($route_prefix, $routes);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
330
vendor/hg/apidoc/src/Utils.php
vendored
Normal file
330
vendor/hg/apidoc/src/Utils.php
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc;
|
||||
|
||||
use hg\apidoc\exception\ErrorException;
|
||||
use think\facade\Config;
|
||||
use think\response\Json;
|
||||
|
||||
class Utils
|
||||
{
|
||||
protected static $snakeCache = [];
|
||||
/**
|
||||
* 统一返回json格式
|
||||
* @param int $code
|
||||
* @param string $msg
|
||||
* @param string $data
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function showJson(int $code = 0, string $msg = "", $data = ""):Json
|
||||
{
|
||||
$res = [
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data,
|
||||
];
|
||||
return json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤参数字段
|
||||
* @param $data
|
||||
* @param $fields
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public function filterParamsField(array $data, $fields, string $type = "field"): array
|
||||
{
|
||||
if ($fields && strpos($fields, ',') !== false){
|
||||
$fieldArr = explode(',', $fields);
|
||||
}else{
|
||||
$fieldArr = [$fields];
|
||||
}
|
||||
|
||||
$dataList = [];
|
||||
foreach ($data as $item) {
|
||||
if (!empty($item['name']) && in_array($item['name'], $fieldArr) && $type === 'field') {
|
||||
$dataList[] = $item;
|
||||
} else if (!(!empty($item['name']) && in_array($item['name'], $fieldArr)) && $type == "withoutField") {
|
||||
$dataList[] = $item;
|
||||
}
|
||||
}
|
||||
return $dataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param $fileName
|
||||
* @return false|string
|
||||
*/
|
||||
public static function getFileContent(string $fileName): string
|
||||
{
|
||||
$content = "";
|
||||
if (file_exists($fileName)) {
|
||||
$handle = fopen($fileName, "r");
|
||||
$content = fread($handle, filesize($fileName));
|
||||
fclose($handle);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件
|
||||
* @param $path
|
||||
* @param $str_tmp
|
||||
* @return bool
|
||||
*/
|
||||
public static function createFile(string $path, string $str_tmp): bool
|
||||
{
|
||||
$pathArr = explode("/", $path);
|
||||
unset($pathArr[count($pathArr) - 1]);
|
||||
$dir = implode("/", $pathArr);
|
||||
if (!file_exists($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
$fp = fopen($path, "w") or die("Unable to open file!");
|
||||
fwrite($fp, $str_tmp); //存入内容
|
||||
fclose($fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param $path
|
||||
*/
|
||||
public static function delFile(string $path)
|
||||
{
|
||||
$url = iconv('utf-8', 'gbk', $path);
|
||||
if (PATH_SEPARATOR == ':') { //linux
|
||||
unlink($path);
|
||||
} else { //Windows
|
||||
unlink($url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将tree树形数据转成list数据
|
||||
* @param array $tree tree数据
|
||||
* @param string $childName 子节点名称
|
||||
* @return array 转换后的list数据
|
||||
*/
|
||||
public function treeToList(array $tree, string $childName = 'children',string $key = "id",string $parentField = "parent")
|
||||
{
|
||||
$array = array();
|
||||
foreach ($tree as $val) {
|
||||
$array[] = $val;
|
||||
if (isset($val[$childName])) {
|
||||
$children = $this->treeToList($val[$childName], $childName);
|
||||
if ($children) {
|
||||
$newChildren = [];
|
||||
foreach ($children as $item) {
|
||||
$item[$parentField] = $val[$key];
|
||||
$newChildren[] = $item;
|
||||
}
|
||||
$array = array_merge($array, $newChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据一组keys获取所有关联节点
|
||||
* @param $tree
|
||||
* @param $keys
|
||||
*/
|
||||
public function getTreeNodesByKeys(array $tree, array $keys, string $field = "id", string $childrenField = "children")
|
||||
{
|
||||
$list = $this->TreeToList($tree, $childrenField, "folder");
|
||||
$data = [];
|
||||
foreach ($keys as $k => $v) {
|
||||
$parent = !$k ? "" : $keys[$k - 1];
|
||||
foreach ($list as $item) {
|
||||
if (((!empty($item['parent']) && $item['parent'] === $parent) || empty($item['parent'])) && $item[$field] == $v) {
|
||||
$data[] = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换模板变量
|
||||
* @param $temp
|
||||
* @param $data
|
||||
* @return string|string[]
|
||||
*/
|
||||
public static function replaceTemplate(string $temp, array $data):string
|
||||
{
|
||||
$str = $temp;
|
||||
foreach ($data as $k => $v) {
|
||||
$key = '${' . $k . '}';
|
||||
if (strpos($str, $key) !== false) {
|
||||
$str = str_replace($key, $v, $str);
|
||||
}
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换当前所选应用/版本的变量
|
||||
* @param $temp
|
||||
* @param $currentApps
|
||||
* @return string|string[]
|
||||
*/
|
||||
public function replaceCurrentAppTemplate(string $temp,array $currentApps):string
|
||||
{
|
||||
$str = $temp;
|
||||
if (!empty($currentApps) && count($currentApps) > 0) {
|
||||
$data = [];
|
||||
for ($i = 0; $i <= 3; $i++) {
|
||||
if (isset($currentApps[$i])) {
|
||||
$appItem = $currentApps[$i];
|
||||
foreach ($appItem as $k => $v) {
|
||||
$key = 'app[' . $i . '].' . $k;
|
||||
$data[$key] = $v;
|
||||
}
|
||||
} else {
|
||||
$appItem = $currentApps[0];
|
||||
foreach ($appItem as $k => $v) {
|
||||
$key = 'app[' . $i . '].' . $k;
|
||||
$data[$key] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
$str = $this->replaceTemplate($str, $data);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件获取数组中的值
|
||||
* @param array $array
|
||||
* @param $query
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function getArrayFind(array $array, $query)
|
||||
{
|
||||
$res = null;
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $item) {
|
||||
if ($query($item)) {
|
||||
$res = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并对象数组并根据key去重
|
||||
* @param string $name
|
||||
* @param mixed ...$array
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayMergeAndUnique(string $key = "name", ...$array):array
|
||||
{
|
||||
$mergeArr = [];
|
||||
foreach ($array as $k => $v) {
|
||||
$mergeArr = array_merge($mergeArr, $v);
|
||||
}
|
||||
$keys = [];
|
||||
foreach ($mergeArr as $k => $v) {
|
||||
$keys[] = $v[$key];
|
||||
}
|
||||
$uniqueKeys = array_flip(array_flip($keys));
|
||||
$newArray = [];
|
||||
foreach ($uniqueKeys as $k => $v) {
|
||||
$newArray[] = $mergeArr[$k];
|
||||
}
|
||||
return $newArray;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化当前所选的应用/版本数据
|
||||
* @param $appKey
|
||||
*/
|
||||
public function getCurrentApps(string $appKey):array
|
||||
{
|
||||
$config = Config::get("apidoc")?Config::get("apidoc"):Config::get("apidoc.");
|
||||
if (!(!empty($config['apps']) && count($config['apps']) > 0)) {
|
||||
throw new ErrorException("no config apps", 500);
|
||||
}
|
||||
if (strpos($appKey, '_') !== false) {
|
||||
$keyArr = explode("_", $appKey);
|
||||
} else {
|
||||
$keyArr = [$appKey];
|
||||
}
|
||||
$currentApps = $this->getTreeNodesByKeys($config['apps'], $keyArr, 'folder', 'items');
|
||||
if (!$currentApps) {
|
||||
throw new ErrorException("appKey error", 412, [
|
||||
'appKey' => $appKey
|
||||
]);
|
||||
}
|
||||
return $currentApps;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理apps配置参数
|
||||
* @param array $apps
|
||||
* @return array
|
||||
*/
|
||||
public function handleAppsConfig(array $apps):array
|
||||
{
|
||||
$appsConfig = [];
|
||||
foreach ($apps as $app) {
|
||||
if (!empty($app['password'])) {
|
||||
unset($app['password']);
|
||||
$app['hasPassword'] = true;
|
||||
}
|
||||
if (!empty($app['items']) && count($app['items']) > 0) {
|
||||
$app['items'] = $this->handleAppsConfig($app['items']);
|
||||
}
|
||||
$appsConfig[] = $app;
|
||||
}
|
||||
return $appsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public static function snake(string $value, string $delimiter = '_'): string
|
||||
{
|
||||
$key = $value;
|
||||
|
||||
if (isset(static::$snakeCache[$key][$delimiter])) {
|
||||
return static::$snakeCache[$key][$delimiter];
|
||||
}
|
||||
|
||||
if (!ctype_lower($value)) {
|
||||
$value = preg_replace('/\s+/u', '', $value);
|
||||
|
||||
$value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
|
||||
}
|
||||
|
||||
return static::$snakeCache[$key][$delimiter] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转小写
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function lower(string $value): string
|
||||
{
|
||||
return mb_strtolower($value, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
44
vendor/hg/apidoc/src/annotation/AddField.php
vendored
Normal file
44
vendor/hg/apidoc/src/annotation/AddField.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 添加模型的字段
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class AddField extends Annotation
|
||||
{
|
||||
/**
|
||||
* 字段名
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* 类型
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'string';
|
||||
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
* @var string
|
||||
*/
|
||||
public $default;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
* @var string
|
||||
*/
|
||||
public $desc;
|
||||
|
||||
/**
|
||||
* 必须
|
||||
* @var bool
|
||||
*/
|
||||
public $require = false;
|
||||
}
|
||||
14
vendor/hg/apidoc/src/annotation/Author.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Author.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 作者
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Author extends Annotation
|
||||
{}
|
||||
14
vendor/hg/apidoc/src/annotation/Desc.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Desc.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD","CLASS"})
|
||||
*/
|
||||
class Desc extends Annotation
|
||||
{}
|
||||
14
vendor/hg/apidoc/src/annotation/Field.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Field.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 指定获取模型的字段
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Field extends Annotation
|
||||
{}
|
||||
14
vendor/hg/apidoc/src/annotation/Group.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Group.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 分组
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"CLASS"})
|
||||
*/
|
||||
class Group extends Annotation
|
||||
{}
|
||||
40
vendor/hg/apidoc/src/annotation/Header.php
vendored
Normal file
40
vendor/hg/apidoc/src/annotation/Header.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
|
||||
/**
|
||||
* 请求头
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Header extends ParamBase
|
||||
{
|
||||
/**
|
||||
* 必须
|
||||
* @var bool
|
||||
*/
|
||||
public $require = false;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* 引入
|
||||
* @var string
|
||||
*/
|
||||
public $ref;
|
||||
|
||||
|
||||
/**
|
||||
* 描述
|
||||
* @var string
|
||||
*/
|
||||
public $desc;
|
||||
|
||||
|
||||
}
|
||||
14
vendor/hg/apidoc/src/annotation/Method.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Method.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Url
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Method extends Annotation
|
||||
{}
|
||||
27
vendor/hg/apidoc/src/annotation/Param.php
vendored
Normal file
27
vendor/hg/apidoc/src/annotation/Param.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD","ANNOTATION"})
|
||||
*/
|
||||
final class Param extends ParamBase
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* 必须
|
||||
* @var bool
|
||||
*/
|
||||
public $require = false;
|
||||
|
||||
/**
|
||||
* 引入
|
||||
* @var string
|
||||
*/
|
||||
public $ref;
|
||||
}
|
||||
62
vendor/hg/apidoc/src/annotation/ParamBase.php
vendored
Normal file
62
vendor/hg/apidoc/src/annotation/ParamBase.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
abstract class ParamBase extends Annotation
|
||||
{
|
||||
|
||||
/**
|
||||
* 类型
|
||||
* @Enum({"string", "integer", "int", "boolean", "array", "double", "object", "tree", "file","float","date","time","datetime"})
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'string';
|
||||
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
* @var string
|
||||
*/
|
||||
public $default;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
* @var string
|
||||
*/
|
||||
public $desc;
|
||||
|
||||
/**
|
||||
* 为tree类型时指定children字段
|
||||
* @var string
|
||||
*/
|
||||
public $childrenField = '';
|
||||
|
||||
/**
|
||||
* 为tree类型时指定children字段说明
|
||||
* @var string
|
||||
*/
|
||||
public $childrenDesc = 'children';
|
||||
|
||||
/**
|
||||
* 为array类型时指定子节点类型
|
||||
* @Enum({"string", "int", "boolean", "array", "object"})
|
||||
* @var string
|
||||
*/
|
||||
public $childrenType = '';
|
||||
|
||||
/**
|
||||
* 指定引入的字段
|
||||
* @var string
|
||||
*/
|
||||
public $field;
|
||||
|
||||
/**
|
||||
* 指定从引入中过滤的字段
|
||||
* @var string
|
||||
*/
|
||||
public $withoutField;
|
||||
|
||||
|
||||
}
|
||||
14
vendor/hg/apidoc/src/annotation/ParamType.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/ParamType.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 参数类型
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class ParamType extends Annotation
|
||||
{}
|
||||
34
vendor/hg/apidoc/src/annotation/Returned.php
vendored
Normal file
34
vendor/hg/apidoc/src/annotation/Returned.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 返回参数
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD","ANNOTATION"})
|
||||
*/
|
||||
final class Returned extends ParamBase
|
||||
{
|
||||
|
||||
/**
|
||||
* 必须
|
||||
* @var bool
|
||||
*/
|
||||
public $require = false;
|
||||
|
||||
/**
|
||||
* 引入
|
||||
* @var string
|
||||
*/
|
||||
public $ref;
|
||||
|
||||
/**
|
||||
* 是否替换全局响应体中的参数
|
||||
* @var bool
|
||||
*/
|
||||
public $replaceGlobal = false;
|
||||
|
||||
}
|
||||
17
vendor/hg/apidoc/src/annotation/Route.php
vendored
Normal file
17
vendor/hg/apidoc/src/annotation/Route.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Enum;
|
||||
|
||||
|
||||
final class Route extends Rule
|
||||
{
|
||||
/**
|
||||
* 请求类型
|
||||
* @Enum({"GET","POST","PUT","DELETE","PATCH","OPTIONS","HEAD"})
|
||||
* @var string
|
||||
*/
|
||||
public $method = "GET";
|
||||
|
||||
}
|
||||
77
vendor/hg/apidoc/src/annotation/Rule.php
vendored
Normal file
77
vendor/hg/apidoc/src/annotation/Rule.php
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
abstract class Rule extends Annotation
|
||||
{
|
||||
/**
|
||||
* @var string|array
|
||||
*/
|
||||
public $middleware;
|
||||
|
||||
/**
|
||||
* 后缀
|
||||
* @var string
|
||||
*/
|
||||
public $ext;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $deny_ext;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $https;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $domain;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $complete_match;
|
||||
|
||||
/**
|
||||
* @var string|array
|
||||
*/
|
||||
public $cache;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $ajax;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $pjax;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $json;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $filter;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $append;
|
||||
|
||||
public function getOptions()
|
||||
{
|
||||
return array_intersect_key(get_object_vars($this), array_flip([
|
||||
'middleware', 'ext', 'deny_ext', 'https', 'domain', 'complete_match', 'cache', 'ajax', 'pjax', 'json', 'filter', 'append',
|
||||
]));
|
||||
}
|
||||
|
||||
}
|
||||
14
vendor/hg/apidoc/src/annotation/Sort.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Sort.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"CLASS"})
|
||||
*/
|
||||
class Sort extends Annotation
|
||||
{}
|
||||
14
vendor/hg/apidoc/src/annotation/Tag.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Tag.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Tag
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Tag extends Annotation
|
||||
{}
|
||||
14
vendor/hg/apidoc/src/annotation/Title.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Title.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD","CLASS"})
|
||||
*/
|
||||
class Title extends Annotation
|
||||
{}
|
||||
14
vendor/hg/apidoc/src/annotation/Url.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/Url.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Url
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Url extends Annotation
|
||||
{}
|
||||
14
vendor/hg/apidoc/src/annotation/WithoutField.php
vendored
Normal file
14
vendor/hg/apidoc/src/annotation/WithoutField.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace hg\apidoc\annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* 排除模型的字段
|
||||
* @package hg\apidoc\annotation
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class WithoutField extends Annotation
|
||||
{}
|
||||
62
vendor/hg/apidoc/src/config.php
vendored
Normal file
62
vendor/hg/apidoc/src/config.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
return [
|
||||
// 文档标题
|
||||
'title' => 'APi接口文档',
|
||||
// 文档描述
|
||||
'desc' => '',
|
||||
// 版权申明
|
||||
'copyright' => 'Powered By hg-code',
|
||||
// 默认作者
|
||||
'default_author'=>'',
|
||||
// 默认请求类型
|
||||
'default_method'=>'GET',
|
||||
// 设置应用/版本(必须设置)
|
||||
'apps' => [
|
||||
['title'=>'v1.0','path'=>'app\controller','folder'=>'v1'],
|
||||
],
|
||||
// 控制器分组
|
||||
'groups' => [],
|
||||
// 指定公共注释定义的文件地址
|
||||
'definitions' => "app\controller\Definitions",
|
||||
//指定生成文档的控制器
|
||||
'controllers' => [],
|
||||
// 过滤,不解析的控制器
|
||||
'filter_controllers' => [],
|
||||
// 缓存配置
|
||||
'cache' => [
|
||||
// 是否开启缓存
|
||||
'enable' => false,
|
||||
// 缓存文件路径
|
||||
'path' => '../runtime/apidoc/',
|
||||
// 是否显示更新缓存按钮
|
||||
'reload' => true,
|
||||
// 最大缓存文件数
|
||||
'max' => 5, //最大缓存数量
|
||||
],
|
||||
// 权限认证配置
|
||||
'auth' => [
|
||||
// 是否启用密码验证
|
||||
'enable' => false,
|
||||
// 验证密码
|
||||
'password' => "123456",
|
||||
// 密码加密盐
|
||||
'secret_key' => "apidoc#hg_code",
|
||||
],
|
||||
// 统一的请求Header
|
||||
'headers'=>[],
|
||||
// 统一的请求参数Parameters
|
||||
'parameters'=>[],
|
||||
// 统一的请求响应体,仅显示在文档提示中
|
||||
'responses'=>[
|
||||
['name'=>'code','desc'=>'状态码','type'=>'int'],
|
||||
['name'=>'message','desc'=>'操作描述','type'=>'string'],
|
||||
['name'=>'data','desc'=>'业务数据','main'=>true,'type'=>'object'],
|
||||
],
|
||||
// md文档
|
||||
'docs' => [
|
||||
'menu_title' => '开发文档',
|
||||
'menus' => []
|
||||
],
|
||||
'crud'=>[]
|
||||
|
||||
];
|
||||
383
vendor/hg/apidoc/src/crud/CreateCrud.php
vendored
Normal file
383
vendor/hg/apidoc/src/crud/CreateCrud.php
vendored
Normal file
@@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace hg\apidoc\crud;
|
||||
|
||||
|
||||
use hg\apidoc\exception\ErrorException;
|
||||
use think\Db as Db5;
|
||||
use think\facade\Config;
|
||||
use think\facade\Db;
|
||||
use think\facade\App;
|
||||
use hg\apidoc\Utils;
|
||||
|
||||
|
||||
class CreateCrud
|
||||
{
|
||||
protected $config;
|
||||
|
||||
protected $currentApps;
|
||||
|
||||
protected $controller_layer = "";
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = Config::get('apidoc')?Config::get('apidoc'):Config::get('apidoc.');
|
||||
$this->controller_layer = Config::get('route.controller_layer',"controller");
|
||||
if (!empty($config) && !empty($config['crud'])){
|
||||
$this->config = $config['crud'];
|
||||
}else{
|
||||
throw new ErrorException("no config crud",501);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Crud
|
||||
* @param $params
|
||||
* @return array
|
||||
*/
|
||||
public function create($params){
|
||||
|
||||
$appKey = $params['appKey'];
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
// $currentApp = $currentApps[count($currentApps) - 1];
|
||||
|
||||
$data = $this->renderTemplateData($params,$currentApps);
|
||||
$res = [];
|
||||
// 创建数据表
|
||||
if (!empty($params['model']['table'])){
|
||||
$sqlRes = $this->createModelTable($params['model'],$params['title']);
|
||||
if ($sqlRes == true){
|
||||
$res[]="创建数据表成功";
|
||||
}else{
|
||||
// $msg= $sqlRes?$sqlRes:"数据表创建失败,请检查配置";
|
||||
throw new ErrorException("datatable crud error",500);
|
||||
}
|
||||
}
|
||||
// 生成文件
|
||||
$keys = array_keys($this->config);
|
||||
foreach ($keys as $index=>$key) {
|
||||
// 添加路由
|
||||
if (
|
||||
$key==="route" &&
|
||||
!empty($this->config['route']) &&
|
||||
!empty($this->config['route']['template']) &&
|
||||
!empty($this->config['route']['path'])
|
||||
){
|
||||
$tmp_path = (new Utils())->replaceCurrentAppTemplate($this->config['route']['template'],$currentApps);
|
||||
$tempPath = $tmp_path.".txt";
|
||||
$str_tmp = Utils::getFileContent($tempPath);
|
||||
if (!empty($str_tmp)){
|
||||
$tmp_content = Utils::replaceTemplate($str_tmp,$data);
|
||||
$tmp_content = (new Utils())->replaceCurrentAppTemplate($tmp_content,$currentApps);
|
||||
$routePathStr = (new Utils())->replaceCurrentAppTemplate($this->config['route']['path'],$currentApps);
|
||||
$routePathStr = str_replace("\\","/",$routePathStr);
|
||||
$routePath = App::getAppPath().$routePathStr;
|
||||
$routeContent = Utils::getFileContent($routePath);
|
||||
$routeContent.="\r\n".$tmp_content."\r\n";
|
||||
Utils::createFile($routePath,$routeContent);
|
||||
$res[]="添加路由成功";
|
||||
}
|
||||
|
||||
}else{
|
||||
$currentConfig = $this->config[$key];
|
||||
$currentParam = $params[$key];
|
||||
$tmp_path = (new Utils())->replaceCurrentAppTemplate($currentParam['template'],$currentApps);
|
||||
$tempPath = $tmp_path.".txt";
|
||||
$str_tmp = Utils::getFileContent($tempPath);
|
||||
$file_content = Utils::replaceTemplate($str_tmp,$data);
|
||||
$file_content = (new Utils())->replaceCurrentAppTemplate($file_content,$currentApps);
|
||||
$namespacePath = str_replace("\\","/",$currentParam['path']);
|
||||
$fileName = $data[$key.'.file_name'];
|
||||
$filePath = '../'.$namespacePath.'/'.$fileName.'.php';
|
||||
$fp=Utils::createFile($filePath,$file_content);
|
||||
if ($fp){
|
||||
$res[]="创建文件成功 path:".$filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成模板变量的数据
|
||||
* @param $params
|
||||
* @return array
|
||||
*/
|
||||
public function renderTemplateData($params,array $currentApps){
|
||||
// 模板参数
|
||||
$api_group = "";
|
||||
if (!empty($params['group'])){
|
||||
$api_group = '@Apidoc\Group("'.$params['group'].'")';
|
||||
}
|
||||
$data=[
|
||||
'title'=>!empty($params['title'])?$params['title']:"",
|
||||
'api_group'=>$api_group,
|
||||
];
|
||||
$keys = array_keys($this->config);
|
||||
foreach ($keys as $index=>$key){
|
||||
$currentConfig = $this->config[$key];
|
||||
//验证模板是否存在
|
||||
$tmp_path = (new Utils())->replaceCurrentAppTemplate($currentConfig['template'],$currentApps);
|
||||
if(!file_exists($tmp_path.'.txt')){
|
||||
throw new ErrorException("template not found",404,[
|
||||
'template'=>$currentConfig['template']
|
||||
]);
|
||||
}
|
||||
if ($key==="route"){
|
||||
continue;
|
||||
}
|
||||
$currentParam = $params[$key];
|
||||
if(!preg_match("/^[A-Z]{1}[A-Za-z0-9]{1,32}$/",$currentParam['class_name'])){
|
||||
throw new ErrorException("classname error",412,[
|
||||
'classname'=>$currentParam['class_name']
|
||||
]);
|
||||
}
|
||||
$currentParamPath = str_replace("\\","/",$currentParam['path']);
|
||||
// 验证目录是否存在
|
||||
if(!is_dir('../'.$currentParamPath)){
|
||||
throw new ErrorException("path not found",404,[
|
||||
'path'=>$currentParamPath
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
$appPath = App::getAppPath();
|
||||
$appPathArr = explode("\\", $appPath);
|
||||
$appFolder = $appPathArr[count($appPathArr)-1]?$appPathArr[count($appPathArr)-1]:$appPathArr[count($appPathArr)-2];
|
||||
$namespace = str_replace($appFolder, App::getNamespace(), $currentParam['path']);
|
||||
|
||||
if ($key==="controller"){
|
||||
$pathArr = explode("\\", $namespace);
|
||||
$notArr = array(App::getNamespace(), $this->controller_layer);
|
||||
$url = "/";
|
||||
foreach ($pathArr as $pathItem){
|
||||
if (!in_array($pathItem,$notArr)){
|
||||
$url.=$pathItem."/";
|
||||
}
|
||||
}
|
||||
$data['folder']=$url;
|
||||
$data['api_class_name']=lcfirst($currentParam['class_name']);
|
||||
$data['api_url']=$url.lcfirst($currentParam['class_name']);
|
||||
}else if ($key==="model" && !empty($currentParam['table'])){
|
||||
// 模型
|
||||
// 获取主键
|
||||
foreach ($currentParam['table'] as $item){
|
||||
if ($item['main_key']==true){
|
||||
$data['main_key.field'] = $item['field'];
|
||||
$data['main_key.type'] = $item['type'];
|
||||
$data['main_key.desc'] = $item['desc'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$tp_version = \think\facade\App::version();
|
||||
if (substr($tp_version, 0, 2) == '5.') {
|
||||
$table_prefix = Config::get('database.prefix');
|
||||
}else{
|
||||
$driver = Config::get('database.default');
|
||||
$table_prefix = Config::get('database.connections.' . $driver . '.prefix');
|
||||
}
|
||||
$data[$currentParam['name'] . '.table_prefix'] = $table_prefix;
|
||||
$table_name = Utils::snake($currentParam['class_name']);
|
||||
$data[$currentParam['name'].'.table_name']=$table_name;
|
||||
}
|
||||
|
||||
$namespace = str_replace($appFolder, App::getNamespace(), $currentParam['path']);
|
||||
$data[$currentParam['name'].'.class_name']=$currentParam['class_name'];
|
||||
$data[$currentParam['name'].'.namespace']=$namespace;
|
||||
|
||||
// 验证文件是否已存在
|
||||
$fileName = $currentParam['class_name'];
|
||||
if (!empty($currentConfig['file_name'])){
|
||||
$fileName = (new Utils())->replaceTemplate($currentConfig['file_name'],$data);
|
||||
}
|
||||
$filePath = '../'.$currentParamPath.'/'.$fileName.'.php';
|
||||
$data[$currentParam['name'].'.file_name']=$fileName;
|
||||
if(file_exists($filePath)){
|
||||
throw new ErrorException("file already exists",500,[
|
||||
'filepath'=>$filePath
|
||||
]);
|
||||
}
|
||||
$data[$currentParam['name'].'.use_path']=$namespace."\\".$fileName;
|
||||
$data[$currentParam['name'].'.use_alias']=$fileName.ucwords($currentParam['name']);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 字段过滤数据
|
||||
if (!empty($params['model']['table'])){
|
||||
$checkKeys = ['list','detail','add','edit'];
|
||||
foreach ($checkKeys as $checkKey){
|
||||
$itemArr = ['field'=>[],'withoutField'=>[]];
|
||||
foreach ($params['model']['table'] as $item){
|
||||
if ($item[$checkKey]){
|
||||
$itemArr['field'][]=$item['field'];
|
||||
}else{
|
||||
$itemArr['withoutField'][]=$item['field'];
|
||||
}
|
||||
}
|
||||
$data[$checkKey.'.field']=implode(",", $itemArr['field']);
|
||||
$data[$checkKey.'.withoutField']=implode(",", $itemArr['withoutField']);
|
||||
}
|
||||
// 查询条件、验证规则
|
||||
$query_where='$where=[];'."\r\n";
|
||||
$query_annotation = "";
|
||||
$validate_rule = "["."\r\n";
|
||||
$validate_message = "["."\r\n";
|
||||
$validate_fields = [];
|
||||
$addRequireFields = [];
|
||||
foreach ($params['model']['table'] as $i=>$item){
|
||||
if ($item['query']){
|
||||
$itemArr['field'][]=$item['field'];
|
||||
$query_where.= ' if(!empty($param[\''.$item['field'].'\'])){'."\r\n";
|
||||
$query_where.= ' $where[] = [\''.$item['field'].'\',\'=\',$param[\''.$item['field'].'\']];'."\r\n";
|
||||
$query_where.= ' }'."\r\n";
|
||||
$fh = empty($query_annotation)?'* ':' * ';
|
||||
$require = $item['not_null']==true?'true':'false';
|
||||
$rn="";
|
||||
if (($i+1)<count($params['model']['table'])){
|
||||
$rn="\r\n";
|
||||
}
|
||||
$query_annotation.=$fh.'@Apidoc\Param("'.$item['field'].'",type="'.$item['type'].'",require='.$require.',desc="'.$item['desc'].'")'.$rn;
|
||||
}
|
||||
// 验证规则
|
||||
if (!empty($this->config['validate'])){
|
||||
// 存在配置验证规则
|
||||
if (!empty($item['validate']) && $this->config['validate']['rules']){
|
||||
$rulesConfig = $this->config['validate']['rules'];
|
||||
$currentRuleConfig = "";
|
||||
foreach ($rulesConfig as $rule){
|
||||
if ($rule['rule'] === $item['validate']){
|
||||
$currentRuleConfig = $rule;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty($currentRuleConfig)){
|
||||
$validate_rule.=' \''.$item['field'].'\' => \''.$item['validate'].'\','."\r\n";
|
||||
if (!empty($currentRuleConfig['message']) ){
|
||||
if (is_array($currentRuleConfig['message']) && count($currentRuleConfig['message'])>0){
|
||||
foreach ($currentRuleConfig['message'] as $ruleKey=>$ruleMessage){
|
||||
if ($ruleKey=='0'){
|
||||
$ruleKeyStr = $item['field'];
|
||||
}else{
|
||||
$ruleKeyStr = Utils::replaceTemplate($ruleKey,$item);
|
||||
}
|
||||
$ruleMessageStr = Utils::replaceTemplate($ruleMessage,$item);
|
||||
$validate_message.=' \''.$ruleKeyStr.'\' => \''.$ruleMessageStr.'\','."\r\n";
|
||||
}
|
||||
}else{
|
||||
$ruleMessageStr = Utils::replaceTemplate($currentRuleConfig['message'],$item);
|
||||
$validate_message.=' \''.$item['field'].'\' => \''.$ruleMessageStr.'\','."\r\n";
|
||||
}
|
||||
}
|
||||
$validate_fields[]=$item['field'];
|
||||
if($item['field'] !== $data['main_key.field']){
|
||||
$addRequireFields[]=$item['field'];
|
||||
}
|
||||
}
|
||||
}else if($item['not_null']){
|
||||
$validate_fields[]=$item['field'];
|
||||
if($item['field'] !== $data['main_key.field']){
|
||||
$addRequireFields[]=$item['field'];
|
||||
}
|
||||
// 根据not_null生成必填
|
||||
$validate_rule.=' \''.$item['field'].'\' => \'require\','."\r\n";
|
||||
$validate_message.=' \''.$item['field'].'\' => \''.$item['field'].'不可为空\','."\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$validate_rule.=' ];'."\r\n";
|
||||
$validate_message.=' ];'."\r\n";
|
||||
if (!empty($this->config['validate'])) {
|
||||
$data['validate.rule'] = $validate_rule;
|
||||
$data['validate.message'] = $validate_message;
|
||||
$data['validate.scene.edit'] = json_encode($validate_fields);
|
||||
$data['validate.scene.add'] = json_encode($addRequireFields)=='[]'?'[\'\']':json_encode($addRequireFields);
|
||||
$data['validate.scene.delete'] = '[\'' . $data['main_key.field'] . '\']';
|
||||
}
|
||||
|
||||
$data['query.where']=$query_where;
|
||||
$data['query.annotation']=$query_annotation;
|
||||
}
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建数据表
|
||||
* @return mixed
|
||||
*/
|
||||
public function createModelTable($params,$title=""){
|
||||
$data = $params['table'];
|
||||
if (empty($title)){
|
||||
$title =$params['class_name'];
|
||||
}
|
||||
$driver = Config::get('database.default');
|
||||
$table_prefix=Config::get('database.connections.'.$driver.'.prefix');
|
||||
$table_name = $table_prefix.Utils::snake($params['class_name']);
|
||||
$table_data = '';
|
||||
$main_keys = '';
|
||||
foreach ($data as $item){
|
||||
$table_field="`".$item['field']."` ".$item['type'];
|
||||
if (!empty($item['length'])){
|
||||
$table_field.="(".$item['length'].")";
|
||||
}
|
||||
if ($item['main_key']){
|
||||
$main_keys.=$item['field'];
|
||||
$table_field.=" NOT NULL";
|
||||
}else if ($item['not_null']){
|
||||
$table_field.=" NOT NULL";
|
||||
}
|
||||
if ($item['incremental']==true){
|
||||
$table_field.=" AUTO_INCREMENT";
|
||||
}
|
||||
if (!empty($item['default']) || $item['default']=="0"){
|
||||
$table_field.=" DEFAULT '".$item['default']."'";
|
||||
}
|
||||
$table_field.=" COMMENT '".$item['desc']."',";
|
||||
$table_data.=$table_field;
|
||||
}
|
||||
$sql = "CREATE TABLE IF NOT EXISTS `$table_name` (
|
||||
$table_data
|
||||
PRIMARY KEY (`$main_keys`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='$title' AUTO_INCREMENT=1 ;";
|
||||
|
||||
$tp_version = \think\facade\App::version();
|
||||
if (substr($tp_version, 0, 2) == '5.'){
|
||||
Db5::startTrans();
|
||||
try {
|
||||
Db5::query($sql);
|
||||
// 提交事务
|
||||
Db5::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
// 回滚事务
|
||||
Db5::rollback();
|
||||
return $e->getMessage();
|
||||
}
|
||||
}else{
|
||||
Db::startTrans();
|
||||
try {
|
||||
Db::query($sql);
|
||||
// 提交事务
|
||||
Db::commit();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
// 回滚事务
|
||||
Db::rollback();
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
33
vendor/hg/apidoc/src/exception/AuthException.php
vendored
Normal file
33
vendor/hg/apidoc/src/exception/AuthException.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace hg\apidoc\exception;
|
||||
|
||||
|
||||
use think\Exception;
|
||||
use think\exception\HttpException;
|
||||
|
||||
class AuthException extends HttpException
|
||||
{
|
||||
|
||||
protected $exceptions = [
|
||||
'password error' => ['code' => 4001, 'msg' => '密码不正确,请重新输入'],
|
||||
'password not found' => ['code' => 4002, 'msg' => '密码不可为空'],
|
||||
'token error' => ['code' => 4003, 'msg' => '不合法的Token'],
|
||||
'token not found' => ['code' => 4004, 'msg' => '不存在Token'],
|
||||
];
|
||||
|
||||
public function __construct(string $exceptionCode)
|
||||
{
|
||||
$exception = $this->getException($exceptionCode);
|
||||
parent::__construct(401, $exception['msg'], null, [], $exception['code']);
|
||||
}
|
||||
|
||||
public function getException($exceptionCode)
|
||||
{
|
||||
if (isset($this->exceptions[$exceptionCode])) {
|
||||
return $this->exceptions[$exceptionCode];
|
||||
}
|
||||
throw new Exception('exceptionCode "' . $exceptionCode . '" Not Found');
|
||||
}
|
||||
}
|
||||
44
vendor/hg/apidoc/src/exception/ErrorException.php
vendored
Normal file
44
vendor/hg/apidoc/src/exception/ErrorException.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace hg\apidoc\exception;
|
||||
|
||||
|
||||
use hg\apidoc\Utils;
|
||||
use think\Exception;
|
||||
use think\exception\HttpException;
|
||||
|
||||
class ErrorException extends HttpException
|
||||
{
|
||||
|
||||
protected $exceptions = [
|
||||
'appkey not found' => ['code' => 4005, 'msg' => '缺少必要参数appKey'],
|
||||
'mdPath not found' => ['code' => 4006, 'msg' => '缺少必要参数path'],
|
||||
'appKey error' => ['code' => 4007, 'msg' => '不存在 folder为${appKey}的apps配置'],
|
||||
'template not found' => ['code' => 4008, 'msg' => '${template}模板不存在'],
|
||||
'path not found' => ['code' => 4009, 'msg' => '${path}目录不存在'],
|
||||
'classname error' => ['code' => 4010, 'msg' => '${classname}文件名不合法'],
|
||||
'no config apps' => ['code' => 5000, 'msg' => 'apps配置不可为空'],
|
||||
'no debug' => ['code' => 5001, 'msg' => '请在debug模式下,使用该功能'],
|
||||
'no config crud' => ['code' => 5002, 'msg' => 'crud未配置'],
|
||||
'datatable crud error' => ['code' => 5003, 'msg' => '数据表创建失败,请检查配置'],
|
||||
'file already exists' => ['code' => 5004, 'msg' => '${filepath}文件已存在'],
|
||||
|
||||
];
|
||||
|
||||
public function __construct(string $exceptionCode, int $statusCode = 412, array $data = [])
|
||||
{
|
||||
$exception = $this->getException($exceptionCode);
|
||||
$msg = Utils::replaceTemplate($exception['msg'], $data);
|
||||
parent::__construct($statusCode, $msg, null, [], $exception['code']);
|
||||
}
|
||||
|
||||
public function getException($exceptionCode)
|
||||
{
|
||||
if (isset($this->exceptions[$exceptionCode])) {
|
||||
return $this->exceptions[$exceptionCode];
|
||||
}
|
||||
throw new Exception('exceptionCode "' . $exceptionCode . '" Not Found');
|
||||
}
|
||||
|
||||
}
|
||||
127
vendor/hg/apidoc/src/parseApi/CacheApiData.php
vendored
Normal file
127
vendor/hg/apidoc/src/parseApi/CacheApiData.php
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
|
||||
use hg\apidoc\Utils;
|
||||
use think\facade\Config;
|
||||
|
||||
class CacheApiData
|
||||
{
|
||||
protected $config = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = Config::get('apidoc');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存目录
|
||||
* @param string $appKey
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheFolder(string $appKey):string
|
||||
{
|
||||
$config = $this->config;
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$configPath = !empty($config['cache']) && !empty($config['cache']['path']) ? $config['cache']['path'] : '../runtime/apidoc/';
|
||||
$cacheAppFolder = "";
|
||||
if (!empty($currentApps) && count($currentApps) > 0) {
|
||||
foreach ($currentApps as $keyIndex => $appItem) {
|
||||
$cacheAppFolder .= $appItem['folder'] . "/";
|
||||
}
|
||||
}
|
||||
$cacheFolder = $configPath . $cacheAppFolder;
|
||||
return $cacheFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定目录下缓存文件名列表
|
||||
* @param string $folder
|
||||
* @return array
|
||||
*/
|
||||
public function getCacheFileList(string $folder):array
|
||||
{
|
||||
$filePaths = glob($folder . '*.json');
|
||||
$cacheFiles = [];
|
||||
if (count($filePaths) > 0) {
|
||||
foreach ($filePaths as $item) {
|
||||
$cacheFiles[] = str_replace(".json", "", basename($item));
|
||||
}
|
||||
}
|
||||
return $cacheFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取接口缓存数据
|
||||
* @param string $appKey
|
||||
* @param string $cacheFileName
|
||||
* @return array|false
|
||||
*/
|
||||
public function get(string $appKey, string $cacheFileName)
|
||||
{
|
||||
$cacheFolder = $this->getCacheFolder($appKey);
|
||||
$cacheFileList = $this->getCacheFileList($cacheFolder);
|
||||
if (!file_exists($cacheFolder)) {
|
||||
return false;
|
||||
}
|
||||
if (empty($cacheFileName) && count($cacheFileList) > 0) {
|
||||
// 默认最后一个缓存文件
|
||||
$cacheFileName = $cacheFileList[count($cacheFileList) - 1];
|
||||
}
|
||||
$cacheFilePath = $cacheFolder . "/" . $cacheFileName . '.json';
|
||||
if (file_exists($cacheFilePath)) {
|
||||
// 存在缓存文件
|
||||
$fileContent = file_get_contents($cacheFilePath);
|
||||
if (empty($fileContent)) {
|
||||
return false;
|
||||
}
|
||||
$json = json_decode($fileContent);
|
||||
if (is_object($json)) {
|
||||
$json = [
|
||||
"data" => $json->data,
|
||||
"tags" => $json->tags,
|
||||
"groups" => $json->groups,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'name' => $cacheFileName,
|
||||
'data' => $json,
|
||||
'list' => $cacheFileList
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置接口缓存
|
||||
* @param string $appKey
|
||||
* @param array $json
|
||||
* @return array|false
|
||||
*/
|
||||
public function set(string $appKey, array $json):array
|
||||
{
|
||||
if (empty($json)) {
|
||||
return false;
|
||||
}
|
||||
$config = $this->config;
|
||||
$fileName = date("Y-m-d H_i_s");
|
||||
$fileContent = json_encode($json);
|
||||
$cacheFolder = $this->getCacheFolder($appKey);
|
||||
$path = $cacheFolder . $fileName . ".json";
|
||||
Utils::createFile($path, $fileContent);
|
||||
$filePaths = $this->getCacheFileList($cacheFolder);
|
||||
if ($config['cache']['max'] && count($filePaths) >= $config['cache']['max']) {
|
||||
//达到最大数量,删除第一个
|
||||
$filePath = $cacheFolder . $filePaths[0] . ".json";
|
||||
Utils::delFile($filePath);
|
||||
}
|
||||
return [
|
||||
"name" => $fileName,
|
||||
"data" => $json,
|
||||
"list" => $this->getCacheFileList($cacheFolder)
|
||||
];
|
||||
}
|
||||
}
|
||||
663
vendor/hg/apidoc/src/parseApi/ParseAnnotation.php
vendored
Normal file
663
vendor/hg/apidoc/src/parseApi/ParseAnnotation.php
vendored
Normal file
@@ -0,0 +1,663 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use hg\apidoc\Utils;
|
||||
use ReflectionClass;
|
||||
use Symfony\Component\ClassLoader\ClassMapGenerator;
|
||||
use think\annotation\route\Resource;
|
||||
use think\annotation\Route;
|
||||
use hg\apidoc\annotation\Group;
|
||||
use hg\apidoc\annotation\Sort;
|
||||
use hg\apidoc\annotation\Param;
|
||||
use hg\apidoc\annotation\Title;
|
||||
use hg\apidoc\annotation\Desc;
|
||||
use hg\apidoc\annotation\Author;
|
||||
use hg\apidoc\annotation\Tag;
|
||||
use hg\apidoc\annotation\Header;
|
||||
use hg\apidoc\annotation\Returned;
|
||||
use hg\apidoc\annotation\ParamType;
|
||||
use hg\apidoc\annotation\Url;
|
||||
use hg\apidoc\annotation\Method;
|
||||
use think\annotation\route\Group as RouteGroup;
|
||||
use think\facade\App;
|
||||
use think\facade\Config;
|
||||
|
||||
class ParseAnnotation
|
||||
{
|
||||
|
||||
protected $config = [];
|
||||
|
||||
protected $reader;
|
||||
|
||||
//tags,当前应用/版本所有的tag
|
||||
protected $tags = array();
|
||||
|
||||
//groups,当前应用/版本的分组name
|
||||
protected $groups = array();
|
||||
|
||||
protected $controller_layer = "";
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->reader = new AnnotationReader();
|
||||
$this->config = Config::get('apidoc')?Config::get('apidoc'):Config::get('apidoc.');
|
||||
$this->controller_layer = Config::get('route.controller_layer',"controller");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成api接口数据
|
||||
* @param string $appKey
|
||||
* @return array
|
||||
*/
|
||||
public function renderApiData(string $appKey): array
|
||||
{
|
||||
$config = $this->config;
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$currentApp = $currentApps[count($currentApps) - 1];
|
||||
|
||||
if (!empty($config['controllers']) && count($config['controllers']) > 0) {
|
||||
// 配置的控制器列表
|
||||
$controllers = $this->getConfigControllers($currentApp['path']);
|
||||
} else {
|
||||
// 默认读取所有的
|
||||
$controllers = $this->getDirControllers($currentApp['path']);
|
||||
}
|
||||
$apiData = [];
|
||||
if (!empty($controllers) && count($controllers) > 0) {
|
||||
foreach ($controllers as $class) {
|
||||
$classData = $this->parseController($class);
|
||||
if ($classData !== false) {
|
||||
$apiData[] = $classData;
|
||||
}
|
||||
}
|
||||
}
|
||||
$json = array(
|
||||
"data" => $apiData,
|
||||
"tags" => $this->tags,
|
||||
"groups" => $this->groups
|
||||
);
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成文档的控制器列表
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfigControllers(string $path): array
|
||||
{
|
||||
$config = $this->config;
|
||||
$controllers = [];
|
||||
|
||||
$configControllers = $config['controllers'];
|
||||
if (!empty($configControllers) && count($configControllers) > 0) {
|
||||
foreach ($configControllers as $item) {
|
||||
if ( strpos($item, $path) !== false && class_exists($item)) {
|
||||
$controllers[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目录下的控制器列表
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function getDirControllers(string $path): array
|
||||
{
|
||||
if ($path) {
|
||||
if (strpos(App::getRootPath(), '/') !== false) {
|
||||
$pathStr = str_replace("\\", "/", $path);
|
||||
} else {
|
||||
$pathStr = $path;
|
||||
}
|
||||
$dir = App::getRootPath() . $pathStr;
|
||||
} else {
|
||||
$dir = App::getRootPath() . $this->controller_layer;
|
||||
}
|
||||
$controllers = [];
|
||||
if (is_dir($dir)) {
|
||||
$controllers = $this->scanDir($dir, $path);
|
||||
}
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理指定目录下的控制器
|
||||
* @param string $dir
|
||||
* @param string $appPath
|
||||
* @return array
|
||||
*/
|
||||
protected function scanDir(string $dir, string $appPath): array
|
||||
{
|
||||
$list = [];
|
||||
foreach (ClassMapGenerator::createMap($dir) as $class => $path) {
|
||||
if (
|
||||
!isset($this->config['filter_controllers']) ||
|
||||
(isset($this->config['filter_controllers']) && !in_array($class, $this->config['filter_controllers'])) &&
|
||||
$this->config['definitions'] != $class
|
||||
) {
|
||||
if (strpos($class, '\\') === false) {
|
||||
$list[] = $appPath . "\\" . $class;
|
||||
} else {
|
||||
$list[] = $class;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function parseController($class)
|
||||
{
|
||||
$config = $this->config;
|
||||
$data = [];
|
||||
$refClass = new ReflectionClass($class);
|
||||
$classTextAnnotations = $this->parseTextAnnotation($refClass);
|
||||
if (in_array("NotParse", $classTextAnnotations)) {
|
||||
return false;
|
||||
}
|
||||
$title = $this->reader->getClassAnnotation($refClass, Title::class);
|
||||
$group = $this->reader->getClassAnnotation($refClass, Group::class);
|
||||
$sort = $this->reader->getClassAnnotation($refClass, Sort::class);
|
||||
|
||||
$routeGroup = $this->reader->getClassAnnotation($refClass, RouteGroup::class);
|
||||
$controllersNameArr = explode("\\", $class);
|
||||
$controllersName = $controllersNameArr[count($controllersNameArr) - 1];
|
||||
$data['controller'] = $controllersName;
|
||||
$data['group'] = !empty($group->value) ? $group->value : null;
|
||||
$data['sort'] = !empty($sort->value) ? $sort->value : null;
|
||||
if (!empty($data['group']) && !in_array($data['group'], $this->groups)) {
|
||||
$this->groups[] = $data['group'];
|
||||
}
|
||||
$data['title'] = !empty($title) && !empty($title->value) ? $title->value : "";
|
||||
|
||||
if (empty($title)) {
|
||||
if (!empty($classTextAnnotations) && count($classTextAnnotations) > 0) {
|
||||
$data['title'] = $classTextAnnotations[0];
|
||||
} else {
|
||||
$data['title'] = $controllersName;
|
||||
}
|
||||
}
|
||||
$methodList = [];
|
||||
$filter_method = !empty($config['filter_method']) ? $config['filter_method'] : [];
|
||||
$data['menu_key'] = $data['controller'] . "_" . mt_rand(10000, 99999);
|
||||
|
||||
foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $refMethod) {
|
||||
if (!empty($refMethod->name) && !in_array($refMethod->name, $filter_method)) {
|
||||
$methodItem = $this->parseAnnotation($refMethod, true,"controller");
|
||||
if (!count((array)$methodItem)) {
|
||||
continue;
|
||||
}
|
||||
$textAnnotations = $this->parseTextAnnotation($refMethod);
|
||||
// 标注不解析的方法
|
||||
if (in_array("NotParse", $textAnnotations)) {
|
||||
continue;
|
||||
}
|
||||
// 无标题,且有文本注释
|
||||
if (empty($methodItem['title']) && !empty($textAnnotations) && count($textAnnotations) > 0) {
|
||||
$methodItem['title'] = $textAnnotations[0];
|
||||
}
|
||||
// 添加统一headers请求头参数
|
||||
if (!empty($config['headers']) && !in_array("NotHeaders", $textAnnotations)) {
|
||||
if (!empty($methodItem['header'])) {
|
||||
$methodItem['header'] = Utils::arrayMergeAndUnique("name", $config['headers'], $methodItem['header']);
|
||||
} else {
|
||||
$methodItem['header'] = $config['headers'];
|
||||
}
|
||||
}
|
||||
// 添加统一params请求参数
|
||||
if (!empty($config['parameters']) && !in_array("NotParameters", $textAnnotations)) {
|
||||
if (!empty($methodItem['param'])) {
|
||||
$methodItem['param'] = Utils::arrayMergeAndUnique("name", $config['parameters'], $methodItem['param']);
|
||||
} else {
|
||||
$methodItem['param'] = $config['parameters'];
|
||||
}
|
||||
}
|
||||
// 添加responses统一响应体
|
||||
if (
|
||||
!empty($config['responses']) &&
|
||||
!is_string($config['responses']) &&
|
||||
!in_array("NotResponses", $textAnnotations)
|
||||
) {
|
||||
// 显示在响应体中
|
||||
$returned = [];
|
||||
$hasMian = false;
|
||||
if (isset($config['responses']['data']) && !$config['responses']['show_responses']) {
|
||||
$responsesData = [];
|
||||
} else if (isset($config['responses']['data']) && $config['responses']['show_responses'] === true) {
|
||||
$responsesData = $config['responses']['data'];
|
||||
} else {
|
||||
$responsesData = $config['responses'];
|
||||
}
|
||||
// 合并统一响应体及响应参数相同的字段
|
||||
$returnData = [];
|
||||
$resKeys = [];
|
||||
foreach ($responsesData as $resItem) {
|
||||
$resKeys[]=$resItem['name'];
|
||||
}
|
||||
foreach ($methodItem['return'] as $returnItem){
|
||||
if (!(in_array($returnItem['name'],$resKeys) && $returnItem['source']==='controller' && !empty($returnItem['replaceGlobal']))){
|
||||
$returnData[]=$returnItem;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($responsesData as $resItem) {
|
||||
$resData = $resItem;
|
||||
$globalFind = Utils::getArrayFind($methodItem['return'],function ($item)use ($resItem){
|
||||
if ($item['name'] == $resItem['name'] && $item['source']==='controller' && !empty($item['replaceGlobal'])){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (!empty($globalFind)){
|
||||
$resData = array_merge($resItem,$globalFind);
|
||||
}else if (!empty($resData['main']) && $resData['main'] === true) {
|
||||
$resData['params'] = $returnData;
|
||||
|
||||
$resData['resKeys']=$resKeys;
|
||||
$hasMian = true;
|
||||
}
|
||||
$resData['find'] =$globalFind;
|
||||
$returned[] = $resData;
|
||||
}
|
||||
if (!$hasMian) {
|
||||
$returned = Utils::arrayMergeAndUnique("name", $returned, $methodItem['return']);
|
||||
}
|
||||
$methodItem['return'] = $returned;
|
||||
}
|
||||
// 默认method
|
||||
if (empty($methodItem['method'])) {
|
||||
$methodItem['method'] = !empty($config['default_method']) ? $config['default_method'] : 'GET';
|
||||
}
|
||||
// 默认default_author
|
||||
if (empty($methodItem['author']) && !empty($config['default_author']) && !in_array("NotDefaultAuthor", $textAnnotations)) {
|
||||
$methodItem['author'] = $config['default_author'];
|
||||
}
|
||||
|
||||
// Tags
|
||||
if (!empty($methodItem['tag'])) {
|
||||
if (strpos($methodItem['tag'], ' ') !== false) {
|
||||
$tagArr = explode(" ", $methodItem['tag']);
|
||||
foreach ($tagArr as $tag) {
|
||||
if (!in_array($tag, $this->tags)) {
|
||||
$this->tags[] = $tag;
|
||||
}
|
||||
}
|
||||
} else if (!in_array($methodItem['tag'], $this->tags)) {
|
||||
$this->tags[] = $methodItem['tag'];
|
||||
}
|
||||
}
|
||||
|
||||
// 无url,自动生成
|
||||
if (empty($methodItem['url'])) {
|
||||
$methodItem['url'] = $this->autoCreateUrl($refMethod);
|
||||
} else if (!empty($routeGroup->value)) {
|
||||
// 路由分组,url加上分组
|
||||
$methodItem['url'] = '/' . $routeGroup->value . '/' . $methodItem['url'];
|
||||
}
|
||||
if (!empty($methodItem['url']) && substr($methodItem['url'], 0, 1) != "/") {
|
||||
$methodItem['url'] = "/" . $methodItem['url'];
|
||||
}
|
||||
$methodItem['name'] = $refMethod->name;
|
||||
$methodItem['menu_key'] = $methodItem['method'] . "_" . $refMethod->name . "_" . mt_rand(10000, 99999);
|
||||
|
||||
$methodList[] = $methodItem;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
$data['children'] = $methodList;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成url
|
||||
* @param $method
|
||||
* @return string
|
||||
*/
|
||||
protected function autoCreateUrl($method): string
|
||||
{
|
||||
if (Config::get("controller_auto_search") || !empty($this->config['controller_auto_search'])){
|
||||
$pathArr = explode("\\", $method->class );
|
||||
}else{
|
||||
$searchString = $this->controller_layer . '\\';
|
||||
$substr = substr(strstr($method->class, $searchString), strlen($searchString));
|
||||
$pathArr = explode("\\", str_replace($substr, str_replace('\\', '.', $substr), $method->class));
|
||||
}
|
||||
// 控制器地址
|
||||
$filterPathNames = array(App::getNamespace(), $this->controller_layer);
|
||||
$classPathArr = [];
|
||||
foreach ($pathArr as $item) {
|
||||
if (!in_array($item, $filterPathNames)) {
|
||||
if (!empty($this->config['auto_url_rule'])){
|
||||
switch ($this->config['auto_url_rule']) {
|
||||
case 'lcfirst':
|
||||
$classPathArr[] = lcfirst($item);
|
||||
break;
|
||||
case 'ucfirst':
|
||||
$classPathArr[] = ucfirst($item);
|
||||
break;
|
||||
default:
|
||||
$classPathArr[] = $item;
|
||||
}
|
||||
}else{
|
||||
$classPathArr[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
$classPath = implode('/', $classPathArr);
|
||||
return '/' . $classPath . '/' . $method->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* ref引用
|
||||
* @param $refPath
|
||||
* @param bool $enableRefService
|
||||
* @return false|string[]
|
||||
*/
|
||||
protected function renderRef(string $refPath, bool $enableRefService = true): array
|
||||
{
|
||||
$res = ['type' => 'model'];
|
||||
// 通用定义引入
|
||||
if (strpos($refPath, '\\') === false) {
|
||||
$config = $this->config;
|
||||
$refPath = $config['definitions'] . '\\' . $refPath;
|
||||
$data = $this->renderService($refPath);
|
||||
$res['type'] = "service";
|
||||
$res['data'] = $data;
|
||||
return $res;
|
||||
}
|
||||
// 模型引入
|
||||
$modelData = (new ParseModel($this->reader))->renderModel($refPath);
|
||||
if ($modelData !== false) {
|
||||
$res['data'] = $modelData;
|
||||
return $res;
|
||||
}
|
||||
if ($enableRefService === false) {
|
||||
return false;
|
||||
}
|
||||
$data = $this->renderService($refPath);
|
||||
$res['type'] = "service";
|
||||
$res['data'] = $data;
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析注释引用
|
||||
* @param $refPath
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function renderService(string $refPath)
|
||||
{
|
||||
$pathArr = explode("\\", $refPath);
|
||||
$methodName = $pathArr[count($pathArr) - 1];
|
||||
unset($pathArr[count($pathArr) - 1]);
|
||||
$classPath = implode("\\", $pathArr);
|
||||
$classReflect = new \ReflectionClass($classPath);
|
||||
$methodName = trim($methodName);
|
||||
$refMethod = $classReflect->getMethod($methodName);
|
||||
$res = $this->parseAnnotation($refMethod, true);
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Param/Returned的字段名name、params子级参数
|
||||
* @param $values
|
||||
* @return array
|
||||
*/
|
||||
protected function handleParamValue($values, string $field = 'param'): array
|
||||
{
|
||||
$name = "";
|
||||
$params = [];
|
||||
if (!empty($values) && is_array($values) && count($values) > 0) {
|
||||
foreach ($values as $item) {
|
||||
if (is_string($item)) {
|
||||
$name = $item;
|
||||
} else if (is_object($item)) {
|
||||
if (!empty($item->ref)) {
|
||||
$refRes = $this->renderRef($item->ref, true);
|
||||
$params = $this->handleRefData($params, $refRes, $item, $field);
|
||||
} else {
|
||||
$param = [
|
||||
"name" => "",
|
||||
"type" => $item->type,
|
||||
"desc" => $item->desc,
|
||||
"default" => $item->default,
|
||||
"require" => $item->require,
|
||||
"childrenType"=> $item->childrenType
|
||||
];
|
||||
$children = $this->handleParamValue($item->value);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
}
|
||||
$params[] = $param;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$name = $values;
|
||||
}
|
||||
return ['name' => $name, 'params' => $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析方法注释
|
||||
* @param $refMethod
|
||||
* @param bool $enableRefService 是否终止service的引入
|
||||
* @param string $source 注解来源
|
||||
* @return array
|
||||
*/
|
||||
protected function parseAnnotation($refMethod, bool $enableRefService = true,$source=""): array
|
||||
{
|
||||
$data = [];
|
||||
if ($annotations = $this->reader->getMethodAnnotations($refMethod)) {
|
||||
$headers = [];
|
||||
$params = [];
|
||||
$returns = [];
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
switch (true) {
|
||||
case $annotation instanceof Param:
|
||||
$params = $this->handleParamAndReturned($params,$annotation,'param',$enableRefService);
|
||||
break;
|
||||
case $annotation instanceof Returned:
|
||||
|
||||
$returns = $this->handleParamAndReturned($returns,$annotation,'return',$enableRefService,$source);
|
||||
break;
|
||||
case $annotation instanceof Header:
|
||||
if (!empty($annotation->ref)) {
|
||||
$refRes = $this->renderRef($annotation->ref, $enableRefService);
|
||||
$headers = $this->handleRefData($headers, $refRes, $annotation, 'header');
|
||||
} else {
|
||||
$param = [
|
||||
"name" => $annotation->value,
|
||||
"desc" => $annotation->desc,
|
||||
"require" => $annotation->require,
|
||||
"type" => $annotation->type,
|
||||
"default" => $annotation->default,
|
||||
];
|
||||
$headers[] = $param;
|
||||
}
|
||||
break;
|
||||
case $annotation instanceof Route:
|
||||
if (empty($data['method'])) {
|
||||
$data['method'] = $annotation->method;
|
||||
}
|
||||
if (empty($data['url'])) {
|
||||
$data['url'] = $annotation->value;
|
||||
}
|
||||
break;
|
||||
case $annotation instanceof Author:
|
||||
$data['author'] = $annotation->value;
|
||||
break;
|
||||
|
||||
case $annotation instanceof Title:
|
||||
$data['title'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Desc:
|
||||
$data['desc'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof ParamType:
|
||||
$data['paramType'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Url:
|
||||
$data['url'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Method:
|
||||
$data['method'] = $annotation->value;
|
||||
break;
|
||||
case $annotation instanceof Tag:
|
||||
$data['tag'] = $annotation->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($headers && count($headers) > 0) {
|
||||
$data['header'] = $headers;
|
||||
}
|
||||
$data['param'] = $params;
|
||||
$data['return'] = $returns;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理请求参数与返回参数
|
||||
* @param $params
|
||||
* @param $annotation
|
||||
* @param string $type
|
||||
* @param false $enableRefService
|
||||
* @param string $source 注解来源
|
||||
* @return array
|
||||
*/
|
||||
protected function handleParamAndReturned($params,$annotation,$type="param",$enableRefService=false,$source=""){
|
||||
if (!empty($annotation->ref)) {
|
||||
$refRes = $this->renderRef($annotation->ref, $enableRefService);
|
||||
$params = $this->handleRefData($params, $refRes, $annotation, $type,$source);
|
||||
} else {
|
||||
$param = [
|
||||
"name" => "",
|
||||
"type" => $annotation->type,
|
||||
"desc" => $annotation->desc,
|
||||
"default" => $annotation->default,
|
||||
"require" => $annotation->require,
|
||||
"childrenType" => $annotation->childrenType,
|
||||
"source" => $source,
|
||||
"replaceGlobal" =>!empty($annotation->replaceGlobal)?$annotation->replaceGlobal:""
|
||||
];
|
||||
$children = $this->handleParamValue($annotation->value, $type);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
if ($annotation->type === 'tree' && !empty($annotation->childrenField)) {
|
||||
// 类型为tree的
|
||||
$param['params'][] = [
|
||||
'params' => $children['params'],
|
||||
'name' => $annotation->childrenField,
|
||||
'type' => 'array',
|
||||
'desc' => $annotation->childrenDesc,
|
||||
];
|
||||
}
|
||||
}
|
||||
// 合并同级已有的字段
|
||||
$params = Utils::arrayMergeAndUnique("name", $params, [$param]);
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析非注解文本注释
|
||||
* @param $refMethod
|
||||
* @return array|false
|
||||
*/
|
||||
protected function parseTextAnnotation($refMethod): array
|
||||
{
|
||||
$annotation = $refMethod->getDocComment();
|
||||
if (empty($annotation)) {
|
||||
return [];
|
||||
}
|
||||
if (preg_match('#^/\*\*(.*)\*/#s', $annotation, $comment) === false)
|
||||
return [];
|
||||
$comment = trim($comment [1]);
|
||||
if (preg_match_all('#^\s*\*(.*)#m', $comment, $lines) === false)
|
||||
return [];
|
||||
$data = [];
|
||||
foreach ($lines[1] as $line) {
|
||||
$line = trim($line);
|
||||
if (!empty ($line) && strpos($line, '@') !== 0) {
|
||||
$data[] = $line;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理param、returned 参数
|
||||
* @param $params
|
||||
* @param $refRes
|
||||
* @param $annotation
|
||||
* @param string|null $source 注解来源
|
||||
* @return array
|
||||
*/
|
||||
protected function handleRefData($params, $refRes, $annotation, string $field,$source=""): array
|
||||
{
|
||||
if ($refRes['type'] === "model" && count($refRes['data']) > 0) {
|
||||
// 模型引入
|
||||
$data = $refRes['data'];
|
||||
} else if ($refRes['type'] === "service" && !empty($refRes['data']) && !empty($refRes['data'][$field])) {
|
||||
// service引入
|
||||
$data = $refRes['data'][$field];
|
||||
} else {
|
||||
return $params;
|
||||
}
|
||||
// 过滤field
|
||||
if (!empty($annotation->field)) {
|
||||
$data = (new Utils())->filterParamsField($data, $annotation->field, 'field');
|
||||
}
|
||||
// 过滤withoutField
|
||||
if (!empty($annotation->withoutField)) {
|
||||
$data = (new Utils())->filterParamsField($data, $annotation->withoutField, 'withoutField');
|
||||
}
|
||||
|
||||
if (!empty($annotation->value)) {
|
||||
$item = [
|
||||
'name' => $annotation->value,
|
||||
'desc' => $annotation->desc,
|
||||
'type' => $annotation->type,
|
||||
'require' => $annotation->require,
|
||||
'default' => $annotation->default,
|
||||
'params' => $data,
|
||||
'source'=>$source,
|
||||
"replaceGlobal" =>!empty($annotation->replaceGlobal)?$annotation->replaceGlobal:""
|
||||
];
|
||||
$children = $this->handleParamValue($annotation->value, 'param');
|
||||
$item['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$item['params'] = Utils::arrayMergeAndUnique("name",$data,$children['params']);
|
||||
if ($annotation->type === 'tree' && !empty($annotation->childrenField)) {
|
||||
// 类型为tree的
|
||||
$item['params'][] = [
|
||||
'params' => $item['params'],
|
||||
'name' => $annotation->childrenField,
|
||||
'type' => 'array',
|
||||
'desc' => $annotation->childrenDesc,
|
||||
];
|
||||
}
|
||||
}
|
||||
$params[] = $item;
|
||||
|
||||
|
||||
} else {
|
||||
$params = array_merge($params, $data);
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
71
vendor/hg/apidoc/src/parseApi/ParseMarkdown.php
vendored
Normal file
71
vendor/hg/apidoc/src/parseApi/ParseMarkdown.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
use think\facade\App;
|
||||
use hg\apidoc\Utils;
|
||||
use think\facade\Config;
|
||||
|
||||
class ParseMarkdown
|
||||
{
|
||||
protected $config = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = Config::get('apidoc')?Config::get('apidoc'):Config::get('apidoc.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取md文档菜单
|
||||
* @return array
|
||||
*/
|
||||
public function getDocsMenu(): array
|
||||
{
|
||||
$config = $this->config;
|
||||
$docData = [];
|
||||
if (!empty($config['docs']) && !empty($config['docs']['menus']) && count($config['docs']['menus']) > 0) {
|
||||
$docData = $this->handleDocsMenuData($config['docs']['menus']);
|
||||
}
|
||||
return $docData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理md文档菜单数据
|
||||
* @param array $menus
|
||||
* @return array
|
||||
*/
|
||||
protected function handleDocsMenuData(array $menus): array
|
||||
{
|
||||
$list = [];
|
||||
foreach ($menus as $item) {
|
||||
if (!empty($item['items']) && count($item['items']) > 0) {
|
||||
$item['items'] = $this->handleDocsMenuData($item['items']);
|
||||
$item['group'] = 'markdown_doc';
|
||||
$item['menu_key'] = "md_group_" . mt_rand(10000, 99999);
|
||||
$list[] = $item;
|
||||
} else {
|
||||
$item['type'] = 'md';
|
||||
$item['menu_key'] = "md_" . mt_rand(10000, 99999);
|
||||
$list[] = $item;
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取md文档内容
|
||||
* @param string $appKey
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(string $appKey, string $path): string
|
||||
{
|
||||
$currentApps = (new Utils())->getCurrentApps($appKey);
|
||||
$mdPath = (new Utils())->replaceCurrentAppTemplate($path, $currentApps);
|
||||
$filePath = App::getRootPath() . $mdPath . '.md';
|
||||
$contents = Utils::getFileContent($filePath);
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
236
vendor/hg/apidoc/src/parseApi/ParseModel.php
vendored
Normal file
236
vendor/hg/apidoc/src/parseApi/ParseModel.php
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace hg\apidoc\parseApi;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use think\Db as Db5;
|
||||
use think\facade\Db;
|
||||
use hg\apidoc\annotation\Field;
|
||||
use hg\apidoc\annotation\WithoutField;
|
||||
use hg\apidoc\annotation\AddField;
|
||||
use think\helper\Str;
|
||||
use hg\apidoc\Utils;
|
||||
|
||||
class ParseModel
|
||||
{
|
||||
protected $reader;
|
||||
|
||||
public function __construct(Reader $reader)
|
||||
{
|
||||
$this->reader = $reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成模型数据
|
||||
* @param string $path
|
||||
* @return array|false
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function renderModel(string $path)
|
||||
{
|
||||
$modelClassArr = explode("\\", $path);
|
||||
$modelActionName = $modelClassArr[count($modelClassArr) - 1];
|
||||
$modelClassName = $modelClassArr[count($modelClassArr) - 2];
|
||||
unset($modelClassArr[count($modelClassArr) - 1]);
|
||||
$modelClassPath = implode("\\", $modelClassArr);
|
||||
$classReflect = new \ReflectionClass($modelClassPath);
|
||||
$modelActionName = trim($modelActionName);
|
||||
$methodAction = $classReflect->getMethod($modelActionName);
|
||||
// 获取所有模型属性
|
||||
$propertys = $classReflect->getDefaultProperties();
|
||||
|
||||
// 获取表字段
|
||||
$model = $this->getModel($methodAction, $modelClassName);
|
||||
if (!is_callable(array($model, 'getTable'))) {
|
||||
return false;
|
||||
}
|
||||
$table = $this->getTableDocument($model, $propertys);
|
||||
|
||||
// 模型注释-field
|
||||
if ($fieldAnnotations = $this->reader->getMethodAnnotation($methodAction, Field::class)) {
|
||||
$table = (new Utils())->filterParamsField($table, $fieldAnnotations->value, 'field');
|
||||
}
|
||||
// 模型注释-withoutField
|
||||
if ($fieldAnnotations = $this->reader->getMethodAnnotation($methodAction, WithoutField::class)) {
|
||||
$table = (new Utils())->filterParamsField($table, $fieldAnnotations->value, 'withoutField');
|
||||
}
|
||||
// 模型注释-addField
|
||||
if ($annotations = $this->reader->getMethodAnnotations($methodAction)) {
|
||||
foreach ($annotations as $annotation) {
|
||||
switch (true) {
|
||||
case $annotation instanceof AddField:
|
||||
$param = [
|
||||
"name" => "",
|
||||
"desc" => $annotation->desc,
|
||||
"require" => $annotation->require,
|
||||
"type" => $annotation->type,
|
||||
"default" => $annotation->default
|
||||
];
|
||||
$children = $this->handleParamValue($annotation->value);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
}
|
||||
$isExists = false;
|
||||
$newTable = [];
|
||||
foreach ($table as $item) {
|
||||
if ($param['name'] === $item['name']) {
|
||||
$isExists = true;
|
||||
$newTable[] = $param;
|
||||
} else {
|
||||
$newTable[] = $item;
|
||||
}
|
||||
}
|
||||
$table = $newTable;
|
||||
if (!$isExists) {
|
||||
$table[] = $param;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理字段参数
|
||||
* @param $values
|
||||
* @return array
|
||||
*/
|
||||
protected function handleParamValue($values): array
|
||||
{
|
||||
$name = "";
|
||||
$params = [];
|
||||
if (!empty($values) && is_array($values) && count($values) > 0) {
|
||||
foreach ($values as $item) {
|
||||
if (is_string($item)) {
|
||||
$name = $item;
|
||||
} else if (is_object($item)) {
|
||||
$param = [
|
||||
"name" => "",
|
||||
"type" => $item->type,
|
||||
"desc" => $item->desc,
|
||||
"default" => $item->default,
|
||||
"require" => $item->require,
|
||||
];
|
||||
$children = $this->handleParamValue($item->value);
|
||||
$param['name'] = $children['name'];
|
||||
if (count($children['params']) > 0) {
|
||||
$param['params'] = $children['params'];
|
||||
}
|
||||
$params[] = $param;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$name = $values;
|
||||
}
|
||||
return ['name' => $name, 'params' => $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型实例
|
||||
* @param $method
|
||||
* @param $modelClassName
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getModel($method, $modelClassName)
|
||||
{
|
||||
if (!empty($method->class)) {
|
||||
$relationModelClass = $this->getIncludeClassName($method->class, $modelClassName);
|
||||
if ($relationModelClass) {
|
||||
$modelInstance = new $relationModelClass();
|
||||
return $modelInstance;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取模型类
|
||||
* @param $mainClass
|
||||
* @param $class
|
||||
* @return string
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function getIncludeClassName($mainClass, $class)
|
||||
{
|
||||
$classReflect = new \ReflectionClass($mainClass);
|
||||
$possibleClass = $classReflect->getNamespaceName() . "\\" . $class;
|
||||
if (class_exists($possibleClass)) {
|
||||
return $possibleClass;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型注解数据
|
||||
* @param $model
|
||||
* @param $propertys
|
||||
* @return array
|
||||
*/
|
||||
public function getTableDocument($model,array $propertys):array
|
||||
{
|
||||
|
||||
$tp_version = \think\facade\App::version();
|
||||
if (substr($tp_version, 0, 2) == '5.'){
|
||||
$createSQL = Db5::query("show create table " . $model->getTable())[0]['Create Table'];
|
||||
}else{
|
||||
$createSQL = Db::query("show create table " . $model->getTable())[0]['Create Table'];
|
||||
}
|
||||
// $createSQL = Db::query("show create table " . $model->getTable())[0]['Create Table'];
|
||||
preg_match_all("#[^KEY]`(.*?)` (.*?) (.*?),\n#", $createSQL, $matches);
|
||||
$fields = $matches[1];
|
||||
$types = $matches[2];
|
||||
$contents = $matches[3];
|
||||
$fieldComment = [];
|
||||
//组织注释
|
||||
for ($i = 0; $i < count($matches[0]); $i++) {
|
||||
$key = $fields[$i];
|
||||
$type = $types[$i];
|
||||
$default = "";
|
||||
$require = "0";
|
||||
$desc = "";
|
||||
$content = $contents[$i];
|
||||
if (strpos($type, '(`') !== false) {
|
||||
continue;
|
||||
}
|
||||
if (strpos($content, 'COMMENT') !== false) {
|
||||
// 存在字段注释
|
||||
preg_match_all("#COMMENT\s*'(.*?)'#", $content, $edscs);
|
||||
if (!empty($edscs[1]) && !empty($edscs[1][0]))
|
||||
$desc = $edscs[1][0];
|
||||
}
|
||||
if (strpos($content, 'DEFAULT') !== false) {
|
||||
// 存在字段默认值
|
||||
preg_match_all("#DEFAULT\s*'(.*?)'#", $content, $defaults);
|
||||
$default = $defaults[1] && is_array($defaults[1])?$defaults[1][0]:"";
|
||||
}
|
||||
|
||||
if (strpos($content, 'NOT NULL') !== false) {
|
||||
// 必填字段
|
||||
$require = "1";
|
||||
}
|
||||
|
||||
$name = $key;
|
||||
// 转换字段名为驼峰命名(用于输出)
|
||||
if (isset($propertys['convertNameToCamel']) && $propertys['convertNameToCamel'] === true) {
|
||||
$name = Str::camel($key);
|
||||
}
|
||||
$fieldComment[] = [
|
||||
"name" => $name,
|
||||
"type" => $type,
|
||||
"desc" => $desc,
|
||||
"default" => $default,
|
||||
"require" => $require,
|
||||
];
|
||||
}
|
||||
return $fieldComment;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user