You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
shipin/app/services/system/SystemCrudServices.php

1210 lines
46 KiB

<?php
/**
* +----------------------------------------------------------------------
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
* +----------------------------------------------------------------------
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
* +----------------------------------------------------------------------
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
* +----------------------------------------------------------------------
* | Author: CRMEB Team <admin@crmeb.com>
* +----------------------------------------------------------------------
*/
namespace app\services\system;
use app\dao\system\SystemCrudDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\crud\Controller;
use crmeb\services\crud\Dao;
use crmeb\services\crud\enum\FormTypeEnum;
use crmeb\services\crud\enum\SearchEnum;
use crmeb\services\crud\Make;
use crmeb\services\crud\Model;
use crmeb\services\crud\Route;
use crmeb\services\crud\Service;
use crmeb\services\crud\Validate;
use crmeb\services\crud\ViewApi;
use crmeb\services\crud\ViewPages;
use crmeb\services\crud\ViewRouter;
use crmeb\services\FileService;
use Phinx\Db\Adapter\AdapterFactory;
use think\facade\Db;
use think\helper\Str;
use think\migration\db\Column;
use think\migration\db\Table;
/**
* Class SystemCrudServices
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/6
* @package app\services\system
*/
class SystemCrudServices extends BaseServices
{
//不能生成的系统自带表
const NOT_CRUD_TABANAME = [
'system_config', 'system_attachment', 'system_attachment_category', 'system_config_tab',
'system_admin', 'eb_system_city', 'system_log', 'system_menus', 'system_notice',
'system_notice_admin', 'system_notification', 'system_role', 'system_route',
'system_route_cate', 'system_storage', 'system_timer', 'system_user_level',
'system_crud', 'wechat_key', 'user_label_relation', 'user_brokerage_frozen',
'user_brokerage', 'store_product_cate', 'store_bargain_user_help', 'shipping_templates_region',
'shipping_templates_no_delivery', 'shipping_templates_free', 'other_order_status', 'lang_code',
'lang_country', 'app_version', 'user', 'wechat_user', 'template_message', 'store_order',
'other_order', 'store_order_cart_info', 'store_order_economize', 'store_order_invoice', 'store_order_refund',
'store_order_status', 'store_pink', 'agent_level', 'agent_level_task', 'agent_level_task_record',
'agreement', 'app_version', 'article', 'article_category', 'article_content', 'auxiliary', 'cache',
'capital_flow', 'category', 'diy', 'express', 'lang_type', 'live_anchor', 'live_goods', 'live_room',
'live_room_goods', 'luck_lottery', 'luck_lottery_record', 'luck_prize', 'member_card',
'member_card_batch', 'member_right', 'member_ship', 'message_system', 'other_order',
'other_order_status', 'out_account', 'out_interface', 'page_categroy', 'page_link', 'qrcode',
'shipping_templates', 'shipping_templates_free', 'shipping_templates_no_delivery',
'shipping_templates_region', 'sms_record', 'store_advance', 'store_bargain', 'store_bargain_user',
'store_bargain_user_help', 'store_cart', 'store_category', 'store_combination', 'store_coupon_issue',
'store_coupon_issue_user', 'store_coupon_product', 'store_coupon_user', 'store_integral',
'store_integral_order', 'store_integral_order_status', 'store_order', 'store_order_cart_info',
'store_order_economize', 'store_order_invoice', 'store_order_refund', 'store_order_status',
'store_pink', 'store_product', 'store_product_attr', 'store_product_attr_result',
'store_product_attr_value', 'store_product_cate', 'store_product_coupon', 'store_product_description',
'store_product_log', 'store_product_relation', 'store_service', 'store_service_feedback',
'store_product_reply', 'store_product_rule', 'store_product_virtual', 'store_seckill', 'store_seckill_time',
'store_service_log', 'store_service_record', 'store_service_speechcraft', 'store_visit',
'system_attachment', 'system_attachment_category', 'system_city', 'system_config',
'system_config_tab', 'system_file', 'system_file_info', 'system_group', 'system_group_data',
'system_log', 'system_notice', 'system_notice_admin', 'system_notification',
'system_role', 'system_route', 'system_route_cate', 'system_storage', 'system_store',
'system_store_staff', 'system_timer', 'system_user_level', 'template_message', 'upgrade_log',
'user', 'user_address', 'user_bill', 'user_brokerage', 'user_brokerage_frozen', 'user_cancel',
'user_enter', 'user_extract', 'user_friends', 'user_group', 'user_invoice', 'user_label',
'user_label_relation', 'user_level', 'user_money', 'user_notice', 'user_notice_see',
'user_recharge', 'user_search', 'user_sign', 'user_spread', 'user_visit', 'wechat_key',
'wechat_media', 'wechat_message', 'wechat_news_category', 'wechat_qrcode', 'wechat_qrcode_cate',
'wechat_qrcode_record', 'wechat_reply', 'wechat_user', 'system_crud_data', 'admins',
];
//表字符集
const TABLR_COLLATION = 'utf8mb4_general_ci';
/**
* SystemCrudServices constructor.
* @param SystemCrudDao $dao
*/
public function __construct(SystemCrudDao $dao)
{
$this->dao = $dao;
}
/**
* @return array
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/11
*/
public function getList()
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->selectList([], 'add_time,id,name,table_name,table_comment,table_collation', $page, $limit, 'id desc');
$count = $this->dao->count();
return compact('list', 'count');
}
/**
* 数据库字段类型
* @return \string[][]
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/11
*/
public function getTabelRule()
{
$rule = [
'varchar' => 'string',
'int' => 'integer',
'biginteger' => 'bigint',
'tinyint' => 'boolean',
];
return [
'types' => [
'varchar',
'char',
'text',
'longtext',
'tinytext',
'enum',
'blob',
'binary',
'varbinary',
'datetime',
'timestamp',
'time',
'date',
'year',
'boolean',
'tinyint',
'int',
'decimal',
'float',
'json',
],
'form' => [
[
'value' => FormTypeEnum::INPUT,
'label' => '输入框',
'field_type' => 'varchar',
'limit' => 255
],
[
'value' => FormTypeEnum::NUMBER,
'label' => '数字输入框',
'field_type' => 'int',
'limit' => 11
],
[
'value' => FormTypeEnum::TEXTAREA,
'label' => '多行文本框',
'field_type' => 'text',
'limit' => null
],
[
'value' => FormTypeEnum::DATE_TIME,
'label' => '单选日期时间',
'field_type' => 'varchar',
'limit' => 200
],
[
'value' => FormTypeEnum::DATE_TIME_RANGE,
'label' => '日期时间区间选择',
'field_type' => 'varchar',
'limit' => 200
],
[
'value' => FormTypeEnum::CHECKBOX,
'label' => '多选框',
'field_type' => 'varchar',
'limit' => 200
],
[
'value' => FormTypeEnum::RADIO,
'label' => '单选框',
'field_type' => 'int',
'limit' => 11
],
[
'value' => FormTypeEnum::SWITCH,
'label' => '开关',
'field_type' => 'int',
'limit' => 11
],
[
'value' => FormTypeEnum::SELECT,
'label' => '下拉框',
'field_type' => 'int',
'limit' => 11
],
[
'value' => FormTypeEnum::FRAME_IMAGE_ONE,
'label' => '单图选择',
'field_type' => 'varchar',
'limit' => 200
],
[
'value' => FormTypeEnum::FRAME_IMAGES,
'label' => '多图选择',
'field_type' => 'varchar',
'limit' => 200
],
],
'search_type' => [
[
'value' => SearchEnum::SEARCH_TYPE_EQ,
'label' => '等于搜索',
],
[
'value' => SearchEnum::SEARCH_TYPE_LTEQ,
'label' => '小于等于搜索',
],
[
'value' => SearchEnum::SEARCH_TYPE_GTEQ,
'label' => '大于等于搜索',
],
[
'value' => SearchEnum::SEARCH_TYPE_NEQ,
'label' => '不等于搜索',
],
[
'value' => SearchEnum::SEARCH_TYPE_LIKE,
'label' => '模糊搜索',
],
[
'value' => SearchEnum::SEARCH_TYPE_BETWEEN,
'label' => '用来时间区间搜索',
],
],
'default_type' => [
[
'value' => '-1',
'label' => '无',
'disabled' => false,
],
[
'value' => '1',
'label' => '自定义',
'disabled' => true,
],
[
'value' => '2',
'label' => 'NULL',
'disabled' => false,
],
[
'value' => '3',
'label' => 'CURRENT_TIMESTAMP',
'disabled' => false,
],
],
'rule' => $rule
];
}
/**
* 改变数据库类型
* @param string $type
* @return string
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/13
*/
public function changeTabelRule(string $type)
{
if (!in_array($type, $this->getTabelRule()['types'])) {
throw new AdminException(500044);
}
return $this->getTabelRule()['rule'][$type] ?? $type;
}
/**
* @param string $tableName
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/14
*/
public function getTableInfo(string $tableName)
{
$sql = 'SELECT * FROM `information_schema`.`TABLES` WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?';
$tableInfo = Db::query($sql, [config('database.connections.mysql.database'), $this->getTableName($tableName)]);
return $tableInfo[0] ?? [];
}
/**
* 获取表字段
* @param string $tableName
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/7
*/
public function getColumnNamesList(string $tableName)
{
$sql = 'SELECT * FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = ? AND table_name = ? ORDER BY ORDINAL_POSITION';
$column = Db::query($sql, [config('database.connections.mysql.database'), $this->getTableName($tableName)]);
$columns = [];
foreach ($column as $item) {
$column = [
'name' => $item['COLUMN_NAME'],
'type' => $item['DATA_TYPE'],
'dataType' => stripos($item['COLUMN_TYPE'], '(') !== false ? substr_replace($item['COLUMN_TYPE'], '', stripos($item['COLUMN_TYPE'], ')') + 1) : $item['COLUMN_TYPE'],
'default' => $item['COLUMN_DEFAULT'],
'null' => $item['IS_NULLABLE'] == 'YES',
'primaryKey' => $item['COLUMN_KEY'] == 'PRI',
'unsigned' => (bool)stripos($item['COLUMN_TYPE'], 'unsigned'),
'autoIncrement' => stripos($item['EXTRA'], 'auto_increment') !== false,
'comment' => $item['COLUMN_COMMENT'],
'limit' => $item['CHARACTER_MAXIMUM_LENGTH'] ?: $item['NUMERIC_PRECISION'],
];
$columns[$item['COLUMN_NAME']] = $column;
}
return $columns;
}
/**
* 获取当前数据库所有表名
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/8/2
*/
public function getTableAll()
{
$sql = "SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = ?";
$tableAll = Db::query($sql, [config('database.connections.mysql.database')]);
$data = [];
foreach ($tableAll as $item) {
$item['TABLE_NAME'] = str_replace(config('database.connections.mysql.prefix'), '', $item['TABLE_NAME']);
// if (!in_array($item['TABLE_NAME'], self::NOT_CRUD_TABANAME)) {
$data[] = [
'value' => $item['TABLE_NAME'],
'label' => $item['TABLE_COMMENT'] ?: $item['TABLE_NAME'],
];
// }
}
return $data;
}
/**
* @param array $data
* @return array
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/12
*/
public function valueReplace(array $data)
{
$replace = ['phar://'];
$newData = [];
foreach ($data as $key => $item) {
if (is_array($item)) {
$item = $this->valueReplace($item);
} else {
$item = str_replace($replace, '', $item);
}
$newData[str_replace($replace, '', $key)] = $item;
}
return $newData;
}
/**
* 更新表字段
* @param string $tableName
* @param string $field
* @param string $changeFiled
* @param string $type
* @param string $limit
* @param string $default
* @param string $comment
* @param array $options
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/24
*/
protected function updateAlter(string $tableName, string $field, string $changeFiled, string $prevFiled, string $type, $limit = '', string $default = '', string $comment = '', array $options = [])
{
$tableName = $this->getTableName($tableName);
$comment = addslashes($comment);
$field = addslashes($field);
$changeFiled = addslashes($changeFiled);
$prevFiled = addslashes($prevFiled);
$type = addslashes($type);
$default = addslashes($default);
if ($prevFiled) {
$after = "AFTER `$prevFiled`";
} else {
$after = "";
}
if (isset($options['default_type'])) {
switch ($options['default_type']) {
case '-1':
$default = 'NULL';
break;
case '1'://自定义
$default = "NOT NULL DEFAULT '$default'";
break;
case '2'://为null
$default = 'NULL DEFAULT NULL';
break;
case '3'://时间
$default = 'NULL DEFAULT CURRENT_TIMESTAMP';
break;
}
}
if (in_array(strtolower($type), ['text', 'longtext', 'tinytext'])) {
$sql = "ALTER TABLE `$tableName` CHANGE `$field` `$changeFiled` $type CHARACTER SET utf8mb4 COLLATE " . self::TABLR_COLLATION . " NULL COMMENT '$comment' $after;";
} else if (strtolower($type) == 'enum') {
$enum = [];
foreach ($options['options'] as $option) {
$enum[] = "'$option'";
}
$enumStr = implode(',', $enum);
$sql = "ALTER TABLE `$tableName` CHANGE `$field` `$changeFiled` $type($enumStr) $default COMMENT '$comment' $after;";
} else {
$sql = "ALTER TABLE `$tableName` CHANGE `$field` `$changeFiled` $type($limit) $default COMMENT '$comment' $after;";
}
return Db::execute($sql);
}
/**
* 添加字段
* @param string $tableName
* @param string $field
* @param string $prevFiled
* @param string $type
* @param string $limit
* @param string $default
* @param string $comment
* @param array $options
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/24
*/
public function addAlter(string $tableName, string $field, string $prevFiled, string $type, $limit = '', string $default = '', string $comment = '', array $options = [])
{
$tableName = $this->getTableName($tableName);
$comment = addslashes($comment);
$field = addslashes($field);
$prevFiled = addslashes($prevFiled);
$type = addslashes($type);
$default = addslashes($default);
if ($prevFiled) {
$after = "AFTER `$prevFiled`";
} else {
$after = "";
}
if (isset($options['default_type'])) {
switch ($options['default_type']) {
case '-1':
$default = 'NULL';
break;
case '1'://自定义
$default = "NOT NULL DEFAULT '$default'";
break;
case '2'://为null
$default = 'NULL DEFAULT NULL';
break;
case '3'://时间
$default = 'NULL DEFAULT CURRENT_TIMESTAMP';
break;
}
}
if (in_array(strtolower($type), ['text', 'longtext', 'tinytext'])) {
$sql = "ALTER TABLE `$tableName` ADD `$field` $type NULL COMMENT '$comment' $after;";
} else {
$defaultSql = $default;
//处理时间字段默认值
if (in_array(strtolower($type), ['datetime', 'timestamp', 'time', 'date', 'year'])) {
switch ($field) {
case 'delete_time':
$defaultSql = 'NULL DEFAULT NULL';
break;
case 'create_time':
case 'update_time':
$defaultSql = 'NOT NULL DEFAULT CURRENT_TIMESTAMP';
break;
}
}
//兼容枚举字段
if (strtolower($type) == 'enum') {
$enum = [];
foreach ($options['options'] as $option) {
$enum[] = "'$option'";
}
$enumStr = implode(',', $enum);
$limitSql = $enumStr ? '(' . $enumStr . ')' : '';
} else {
$limitSql = $limit ? '(' . $limit . ')' : '';
}
$sql = "ALTER TABLE `$tableName` ADD `$field` $type$limitSql $defaultSql COMMENT '$comment' $after;";
}
return Db::execute($sql);
}
/**
* 删除表字段
* @param string $tableName
* @param string $field
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/24
*/
protected function deleteAlter(string $tableName, string $field)
{
$tableName = $this->getTableName($tableName);
$field = addslashes($field);
$sql = "ALTER TABLE `$tableName` DROP `$field`";
return Db::execute($sql);
}
/**
* 修改表备注
* @param string $tableName
* @param string $common
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/24
*/
protected function updateFromCommon(string $tableName, string $common)
{
$tableName = $this->getTableName($tableName);
$common = addslashes($common);
$sql = "ALTER TABLE `$tableName` COMMENT = '$common';";
return Db::execute($sql);
}
/**
* 对比字段变动了更改
* @param string $tableName
* @param array $deleteField
* @param array $tableField
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/24
*/
protected function diffAlter(string $tableName, array $deleteField, array $tableField)
{
$updateAlter = [];
$addAlter = [];
$columns = $this->getColumnNamesList($tableName);
$fieldAll = array_column($columns, 'name');
//对比数据库字段
foreach ($tableField as $i => $item) {
if ($item['primaryKey'] || $item['field'] == 'delete_time') {
continue;
}
$prevFiled = $i ? ($tableField[$i - 1]['field'] ?? 'id') : 'id';
//前台新增的字段进行添加
if (!(isset($item['default_field']) &&
isset($item['default_field_type']) &&
isset($item['default_limit']) &&
isset($item['default_comment']) &&
isset($item['default_default']) &&
isset($item['default_default_type']))
) {
if (!in_array($item['field'], $fieldAll)) {
$addAlter[] = [
'prev_filed' => $prevFiled,
'field' => $item['field'],
'limit' => $item['limit'],
'type' => $item['field_type'],
'comment' => $item['comment'],
'default' => $item['default'],
'default_type' => $item['default_type'],
'values' => $item['field_type'] == 'enum' ? $item['limit'] : [],
];
}
continue;
} else {
//从数据库中新增的字段,并没有记录在表中做兼容处理;
//默认字段没有在数据库中,需要添加字段;
if (!in_array($item['default_field'], $fieldAll)) {
$addAlter[] = [
'prev_filed' => $prevFiled,
'field' => $item['field'],
'limit' => $item['limit'],
'type' => $item['field_type'],
'comment' => $item['comment'],
'default' => $item['default'],
'default_type' => $item['default_type'],
'values' => $item['field_type'] == 'enum' ? $item['limit'] : [],
];
continue;
}
}
if ($item['default_field'] != $item['field'] && in_array($item['field_type'], ['addTimestamps', 'addSoftDelete'])) {
throw new AdminException($item['field'] . '字段不允许被更改');
}
//数据库表存在的,字段,并且被修改
if (!in_array($item['field'], ['id', 'create_time', 'update_time'])) {
$updateAlter[] = [
'default_field' => $item['default_field'],
'prev_filed' => $prevFiled,
'field' => $item['field'],
'limit' => $item['limit'],
'type' => $item['field_type'],
'comment' => $item['comment'],
'default' => $item['default'],
'default_type' => $item['default_type'],
'values' => $item['field_type'] == 'enum' ? $item['limit'] : [],
];
}
}
//添加字段
foreach ($addAlter as $item) {
$this->addAlter($tableName, $item['field'], $item['prev_filed'], $item['type'], $item['limit'], $item['default'], $item['comment'], [
'options' => $item['values'],
'default_type' => $item['default_type'],
]);
}
//删除多余字段
foreach ($deleteField as $item) {
$this->deleteAlter($tableName, $item);
}
//更新数据库字段
foreach ($updateAlter as $item) {
$this->updateAlter($tableName, $item['default_field'], $item['field'], $item['prev_filed'], $item['type'], $item['limit'], $item['default'], $item['comment'], [
'options' => $item['values'],
'default_type' => $item['default_type'],
]);
}
}
/**
* 创建
* @param array $data
* @return mixed
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/11
*/
public function createCrud(int $id, array $data)
{
$tableName = $data['tableName'];
$tableField = $this->valueReplace($data['tableField']);
$filePath = $this->valueReplace($data['filePath']);
$modelName = !empty($data['modelName']) ? $data['modelName'] : $tableName;
$tableComment = !empty($data['tableComment']) ? $data['tableComment'] : $modelName;
//检测是否为系统表
if (in_array($tableName, self::NOT_CRUD_TABANAME)) {
throw new AdminException(500041);
}
$data['softDelete'] = false;
$tableInfo = null;
//先检查表存在则
if ($id) {
$this->updateFromCommon($tableName, $tableComment);
//读取数据库表
$tableInfo = $this->getTableInfo($tableName);
if ($tableInfo) {
//对比字段进行更新/删除字段
$this->diffAlter($tableName, $data['deleteField'], $tableField);
}
} else {
if ($this->dao->count(['table_name' => $tableName])) {
throw new AdminException('表已经被生成过,请在列表中进行修改');
}
}
//创建数据库
$tableCreateInfo = null;
if ($tableField && (!$data['isTable'] || !$tableInfo)) {
$tableCreateInfo = $this->makeDatebase($tableName, $tableComment, $tableField);
if ($tableCreateInfo['softDelete']) {
$data['softDelete'] = true;
}
}
//获取主键
foreach ($tableField as $value) {
if ($value['primaryKey']) {
$data['key'] = $value['field'];
break;
}
}
$routeName = 'crud/' . Str::snake($tableName);
$uniqueAuth = Str::snake($tableName) . '-crud-list-index';
//增加保存的绝对路径
foreach ($filePath as $k => $i) {
if (in_array($k, ['pages', 'router', 'api'])) {
$filePath[$k] = Make::adminTemplatePath() . $i;
} else {
$filePath[$k] = app()->getRootPath() . $i;
}
}
//创建菜单
if (!$data['menuName']) {
$data['menuName'] = $tableName;
}
$dataMenu = [
'pid' => $data['pid'],
'menu_name' => $data['menuName'],
'menu_path' => '/' . $routeName,
'auth_type' => 1,
'is_show' => 1,
'is_show_path' => 1,
'is_del' => 0,
'unique_auth' => $uniqueAuth,
'is_header' => $data['pid'] ? 0 : 1,
];
$crudInfo = null;
if ($id) {
$crudInfo = $this->dao->get($id);
}
$res = $this->transaction(function () use ($tableComment, $tableCreateInfo, $crudInfo, $modelName, $filePath, $tableName, $routeName, $data, $dataMenu) {
$routeService = app()->make(SystemRouteServices::class);
$meunService = app()->make(SystemMenusServices::class);
//修改菜单名称
if ($crudInfo) {
//菜单存在的时候进行修改
if ($crudInfo->menu_id && $meunService->value(['id' => [$crudInfo->menu_id]], 'id')) {
$meunService->update($crudInfo->menu_id, $dataMenu);
$menuInfo = (object)['id' => $crudInfo->menu_id];
} else {
$menuInfo = $meunService->save($dataMenu);
}
//删除掉添加的路由权限
if ($crudInfo->route_ids) {
$routeService->deleteRoutes($crudInfo->route_ids);
}
//删除掉权限路由
if ($crudInfo->menu_ids) {
app()->make(SystemMenusServices::class)->deleteMenus($crudInfo->menu_ids);
}
} else {
$menuInfo = $meunService->save($dataMenu);
}
//写入路由权限
$cateId = app()->make(SystemRouteServices::class)->topCateId('adminapi', 'CRUD');
$ruleData = [
[
'path' => $routeName,
'method' => 'GET',
'name' => $modelName . '列表接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => '',
'add_time' => date('Y-m-d H:i:s')
],
[
'path' => $routeName . '/create',
'method' => 'GET',
'name' => $modelName . '获取创建表单接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => Str::snake($tableName) . '-add',
'add_time' => date('Y-m-d H:i:s')
],
[
'path' => $routeName,
'method' => 'POST',
'name' => $modelName . '保存接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => '',
'add_time' => date('Y-m-d H:i:s')
],
[
'path' => $routeName . '/<id>/edit',
'method' => 'GET',
'name' => $modelName . '获取修改表单接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => '',
'add_time' => date('Y-m-d H:i:s')
],
[
'path' => $routeName . '/<id>',
'method' => 'GET',
'name' => $modelName . '查看数据接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => '',
'add_time' => date('Y-m-d H:i:s')
],
[
'path' => $routeName . '/<id>',
'method' => 'PUT',
'name' => $modelName . '修改接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => '',
'add_time' => date('Y-m-d H:i:s')
],
[
'path' => $routeName . '/status/<id>',
'method' => 'PUT',
'name' => $modelName . '修改状态接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => '',
'add_time' => date('Y-m-d H:i:s')
],
[
'path' => $routeName . '/<id>',
'method' => 'DELETE',
'name' => $modelName . '删除接口',
'app_name' => 'adminapi',
'cate_id' => $cateId,
'unique_auth' => '',
'add_time' => date('Y-m-d H:i:s')
],
];
$routeList = $routeService->saveAll($ruleData);
$routeIds = array_column($routeList->toArray(), 'id');
//记录权限加入菜单表
$menuData = [];
foreach ($ruleData as $item) {
$menuData[] = [
'pid' => $menuInfo->id ?: 0,
'methods' => $item['method'],
'api_url' => $item['path'],
'unique_auth' => $item['unique_auth'],
'menu_name' => $item['name'],
'is_del' => 0,
'auth_type' => 2,
];
}
$menus = app()->make(SystemMenusServices::class)->saveAll($menuData);
$menuIds = array_column($menus->toArray(), 'id');
//生成文件
$make = $this->makeFile($tableName, $routeName, true, $data, $filePath);
$makePath = [];
foreach ($make as $key => $item) {
$makePath[$key] = $item['path'];
}
if ($tableCreateInfo && isset($tableCreateInfo['table']) && $tableCreateInfo['table'] instanceof Table) {
//创建数据库
$tableCreateInfo['table']->create();
}
$crudDate = [
'pid' => $data['pid'],
'name' => $data['menuName'],
'model_name' => $data['modelName'],
'table_name' => $tableName,
'table_comment' => $tableComment,
'table_collation' => self::TABLR_COLLATION,
'field' => json_encode($data),//提交的数据
'menu_ids' => json_encode($menuIds),//生成的菜单id
'menu_id' => $menuInfo->id,//生成的菜单id
'make_path' => json_encode($makePath),
'route_ids' => json_encode($routeIds),
];
if ($crudInfo) {
$res = $this->dao->update($crudInfo->id, $crudDate);
} else {
$crudDate['add_time'] = time();
//记录crud生成
$res = $this->dao->save($crudDate);
}
return $res;
});
return $res->toArray();
}
/**
* 获取数据库配置
* @return array
*/
protected function getDbConfig(): array
{
$default = app()->config->get('database.default');
$config = app()->config->get("database.connections.{$default}");
if (0 == $config['deploy']) {
$dbConfig = [
'adapter' => $config['type'],
'host' => $config['hostname'],
'name' => $config['database'],
'user' => $config['username'],
'pass' => $config['password'],
'port' => $config['hostport'],
'charset' => $config['charset'],
'table_prefix' => $config['prefix'],
];
} else {
$dbConfig = [
'adapter' => explode(',', $config['type'])[0],
'host' => explode(',', $config['hostname'])[0],
'name' => explode(',', $config['database'])[0],
'user' => explode(',', $config['username'])[0],
'pass' => explode(',', $config['password'])[0],
'port' => explode(',', $config['hostport'])[0],
'charset' => explode(',', $config['charset'])[0],
'table_prefix' => explode(',', $config['prefix'])[0],
];
}
$table = app()->config->get('database.migration_table', 'migrations');
$dbConfig['default_migration_table'] = $dbConfig['table_prefix'] . $table;
return $dbConfig;
}
public function getAdapter()
{
$options = $this->getDbConfig();
$adapter = AdapterFactory::instance()->getAdapter($options['adapter'], $options);
if ($adapter->hasOption('table_prefix') || $adapter->hasOption('table_suffix')) {
$adapter = AdapterFactory::instance()->getWrapper('prefix', $adapter);
}
return $adapter;
}
/**
* 创建数据库
* @param string $tableName
* @param string $tableComment
* @param array $tableField
* @return array
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/7
*/
public function makeDatebase(string $tableName, string $tableComment, array $tableField = [], string $collation = self::TABLR_COLLATION)
{
$timestampsField = [];
$softDelete = false;
$timestamps = false;
$indexField = [];
//创建表
$table = new Table($tableName, ['comment' => $tableComment, 'collation' => $collation], $this->getAdapter());
//创建字段
foreach ($tableField as $item) {
if (isset($item['primaryKey']) && $item['primaryKey']) {
continue;
}
$option = [];
if (isset($item['limit'])) {
$option['limit'] = (int)$item['limit'];
}
if (isset($item['default']) && isset($item['default_type'])) {
switch ($item['default_type']) {
case '1'://自定义
$option['default'] = $item['default'];
break;
case '2'://为null
$option['null'] = true;
break;
case '3'://时间
$option['default'] = Db::raw('CURRENT_TIMESTAMP');
break;
}
}
//创建伪删除
if ($item['field_type'] === 'addSoftDelete') {
$table->addSoftDelete();
$softDelete = true;
} else if ($item['field_type'] == 'timestamp' &&
($item['field'] === 'create_time' || $item['field'] === 'update_time')) {
$timestampsField[] = $item;
} else {
$option['comment'] = $item['comment'];
$fieldType = $this->changeTabelRule($item['field_type']);
if (in_array($fieldType, ['text', 'longtext', 'tinytext'])) {
unset($option['limit']);
}
//判断字段类型
if ($fieldType == 'boolean' && isset($option['default']) && $option['default'] === '') {
unset($option['default']);
}
//兼容枚举字段
if ($fieldType == 'enum') {
unset($option['limit']);
$option['values'] = $item['limit'];
}
$table->addColumn($item['field'], $this->changeTabelRule($item['field_type']), $option);
}
}
//创建索引
if (!empty($data['tableIndex'])) {
$indexField = $data['tableIndex'];
foreach ($data['tableIndex'] as $item) {
$table->addIndex($item);
}
}
//如果是成对出现的create_time和update_time就直接增加修改和添加时间
if (count($timestampsField) == 2) {
//创建修改和增加时间
$table->addTimestamps();
$timestamps = true;
} else {
//如果是一个数组,增加一列
foreach ($timestampsField as $item) {
$option['comment'] = $item['comment'];
$table->addColumn($item['field'], $this->changeTabelRule($item['field_type']), $option);
}
}
return compact('indexField', 'softDelete', 'timestamps', 'table');
}
/**
* 创建文件返回文件路径和内容
* @param string $tableName
* @param string $routeName
* @param bool $isMake
* @param array $options
* @param array $filePath
* @param string $basePath
* @return array[]
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/7
*/
public function makeFile(string $tableName, string $routeName, bool $isMake = false, array $options = [], array $filePath = [], string $basePath = '')
{
$options['fromField'] = is_array($options['fromField']) ? $options['fromField'] : [];
$options['columnField'] = is_array($options['columnField']) ? $options['columnField'] : [];
//生成模型
$model = app()->make(Model::class);
$model->setFilePathName($filePath['model'] ?? '')->setbasePath($basePath)->handle($tableName, $options);
//生成dao
$dao = app()->make(Dao::class);
$dao->setFilePathName($filePath['dao'] ?? '')->setbasePath($basePath)->handle($tableName, [
'usePath' => $model->getUsePath(),
'modelName' => $options['modelName'] ?? '',
'searchField' => $options['searchField'] ?? [],
]);
//生成service
$service = app()->make(Service::class);
$service->setFilePathName($filePath['service'] ?? '')->setbasePath($basePath)->handle($tableName, [
'field' => $options['fromField'],
'columnField' => $options['columnField'],
'key' => $options['key'],
'usePath' => $dao->getUsePath(),
'modelName' => $options['modelName'] ?? '',
'hasOneField' => $options['hasOneField'] ?? [],
]);
//生成验证器
$validate = app()->make(Validate::class);
$validate->setFilePathName($filePath['validate'] ?? '')->setbasePath($basePath)->handle($tableName, [
'field' => $options['fromField'],
'modelName' => $options['modelName'] ?? '',
]);
//生成控制器
$controller = app()->make(Controller::class);
$controller->setFilePathName($filePath['controller'] ?? '')->setbasePath($basePath)->handle($tableName, [
'usePath' => $service->getUsePath(),
'modelName' => $options['modelName'] ?? '',
'searchField' => $options['searchField'] ?? [],
'columnField' => $options['columnField'] ?? [],
'validateName' => '\\' . str_replace('/', '\\', $validate->getUsePath()) . 'Validate::class',
'field' => array_column($options['fromField'], 'field'),
]);
//生成路由
$route = app()->make(Route::class);
$route->setFilePathName($filePath['route'] ?? '')->setbasePath($basePath)->handle($tableName, [
'menus' => $options['modelName'] ?? $options['menuName'],
'route' => $routeName
]);
//生成前台路由
$viewRouter = app()->make(ViewRouter::class);
$viewRouter->setFilePathName($filePath['router'] ?? '')->setbasePath($basePath)->handle($tableName, [
'route' => $routeName,
'menuName' => $options['menuName'],
'modelName' => $options['modelName'] ?? $options['menuName'],
]);
//生成前台接口
$viewApi = app()->make(ViewApi::class);
$viewApi->setFilePathName($filePath['api'] ?? '')->setbasePath($basePath)->handle($tableName, [
'route' => $routeName,
]);
//生成前台页面
$viewPages = app()->make(ViewPages::class);
$viewPages->setFilePathName($filePath['pages'] ?? '')->setbasePath($basePath)->handle($tableName, [
'field' => $options['columnField'],
'tableFields' => $options['tableField'] ?? [],
'searchField' => $options['searchField'] ?? [],
'route' => $routeName,
'key' => $options['key'],
'pathApiJs' => '@/' . str_replace('\\', '/', str_replace([Make::adminTemplatePath(), '.js'], '', $viewApi->getPath())),
]);
//创建文件
if ($isMake) {
FileService::batchMakeFiles([$model, $validate, $dao, $service, $controller, $route, $viewApi, $viewPages, $viewRouter]);
}
return [
'controller' => [
'path' => $this->replace($controller->getPath()),
'content' => $controller->getContent()
],
'model' => [
'path' => $this->replace($model->getPath()),
'content' => $model->getContent()
],
'dao' => [
'path' => $this->replace($dao->getPath()),
'content' => $dao->getContent()
],
'route' => [
'path' => $this->replace($route->getPath()),
'content' => $route->getContent()
],
'service' => [
'path' => $this->replace($service->getPath()),
'content' => $service->getContent()
],
'validate' => [
'path' => $this->replace($validate->getPath()),
'content' => $validate->getContent()
],
'router' => [
'path' => $this->replace($viewRouter->getPath()),
'content' => $viewRouter->getContent()
],
'api' => [
'path' => $this->replace($viewApi->getPath()),
'content' => $viewApi->getContent()
],
'pages' => [
'path' => $this->replace($viewPages->getPath()),
'content' => $viewPages->getContent()
],
];
}
protected function replace(string $path)
{
return str_replace([app()->getRootPath(), Make::adminTemplatePath()], '', $path);
}
/**
* @param string $tableName
* @param bool $fullName
* @return string
* @author 等风来
* @email 136327134@qq.com
* @date 2023/4/7
*/
public function getTableName(string $tableName, bool $fullName = true)
{
$tablePrefix = config('database.connections.mysql.prefix');
$pattern = '/^' . $tablePrefix . '/i';
return ($fullName ? $tablePrefix : '') . (preg_replace($pattern, '', $tableName));
}
}