tiderjian / qscmf
qscmf
Installs: 80
Dependents: 0
Suggesters: 0
Security: 0
Stars: 7
Watchers: 4
Forks: 10
Open Issues: 2
Type:cmf
Requires
- funkjedi/composer-include-files: ^1.0
- quansitech/qscmf-formitem-ueditor: ^1.0.0
- quansitech/qscmf-utils: ^1.5
- tiderjian/think-core: ^13.0
Requires (Dev)
- fakerphp/faker: ^1.10.0
- laravel/dusk: ^6.9.0
- mockery/mockery: ^1.2
- phpunit/phpunit: ^9.3.0
- dev-master
- v13.2.0
- v13.1.2
- v13.1.1
- v13.1.0
- v13.0.4
- v13.0.3
- v13.0.2
- v13.0.1
- v13.0.0
- v12.x-dev
- v12.4.2
- v12.4.1
- 12.4.0
- v12.3.1
- v12.3.0
- v12.2.0
- v12.1.0
- v12.0.6
- v12.0.5
- v12.0.4
- v12.0.3
- v12.0.2
- v12.0.1
- v12.0.0
- v11.x-dev
- v11.3.3
- v11.3.2
- v11.3.1
- v11.3.0
- v11.2.1
- v11.2.0
- v11.1.2
- v11.1.1
- v11.1.0
- v11.0.8
- v11.0.7
- v11.0.6
- v11.0.5
- v11.0.4
- v11.0.3
- v11.0.2
- v11.0.1
- v11.0.0
- v10.7.0
- v10.4.0
- v10.3.0
- v10.0.2
- v10.0.1
- v10.0.0
- v9.2.0
- v9.1.0
- v9.0.11
- v9.0.10
- v9.0.2
- v9.0.1
- v9.0.0
- v8.2.4
- v8.2.3
- v8.1.4
- v8.1.3
- v8.1.2
- v8.1.1
- v8.0.1
- v8.0.0
- 7.x-dev
- v7.1.0
- v7.0.0
- v6.6.2
- v6.6.1
- v6.6.0
- v6.0.2
- v6.0.1
- v6.0.0
- v5.0.0
- v4.0.8
- v4.0.7
- v4.0.6
- v4.0.5
- v4.0.4
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v3.0.8
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- dev-dependabot/npm_and_yarn/react-admin/postcss-8.4.31
- dev-dependabot/npm_and_yarn/react-admin/semver-6.3.1
- dev-rebuild1
- dev-rebuild
This package is auto-updated.
Last update: 2024-04-29 09:55:56 UTC
README
介绍
快速搭建信息管理类系统的框架。基于tp3.2开发,在其基础上添加了许多功能特性。tp3.2已经停止更新,该框架源码也对核心源码做了部分改动。
特性
- 支持composer依赖管理
- 支持phpunit及laravel dusk自动化测试
- 集成laravel数据库管理工具及依赖注入容器
- 支持ListBuilder、FormBuilder后台管理界面模块化开发
- 插件系统
- 简单易用,可自定义的配置管理
- 消息队列系统
- 集成Elasticsearch、可自定义索引重建自制,实现数据库记录与搜索引擎索引同步变动
截图
安装
第一种安装方法
git clone https://github.com/tiderjian/qs_cmf.git
代码拉取完成后,执行composer安装
composer install
第二种安装方法,composer create project
composer create-project tiderjian/qscmf qscmf
完成第一种或者第二种安装后
- 复制.env.example并重命名为.env,配置数据库参数
- 执行migrate数据库迁移命令。
php artisan migrate
将web服务器搭起来后,后台登录地址 协议://域名:端口/admin, 账号:admin 密码:Qs123!@#
维护模式
在.env将 APP_MAINTENANCE 设成true,系统进入维护状态,所有请求都只会提示系统维护中。如需要在维护模式下执行升级脚本,可传递"maintenance"给第三个参数
php index.php Qscmf/UpgradeFix/v300FixSchedule/queue/default maintenance
Elasticsearch
框架为集成Elasticsearch提供了方便的方法, 假设使用者已经具备elasticsearch使用的相关知识。
-
添加 "elasticsearch/elasticsearch": "~6.0" 到composer.json文件,执行composer update 命令安装扩展包。
-
安装elasticsearch, 具体安装方法自行查找,推荐使用laradock作为开发环境,直接集成了elasticsearch的docker安装环境。
-
安装ik插件,安装查找elasticsearch官方文档。
-
在.env下添加 ELASTICSEARCH_HOSTS值,设置为elasticsearch的启动ip和端口,如laradock的默认设置为10.0.75.1:9200,需要配置一组地址,可用“,”隔开。
-
设置需要使用elastic的model和字段
以ChapterModel添加title和summary到全文索引为例
// ChapterModel类必须继承接口 class ChapterModel extends \Gy_Library\GyListModel implements \Qscmf\Lib\Elasticsearch\ElasticsearchModelContract{ // ElasticsearchHelper已经实现了一些帮助函数 use \Qscmf\Lib\Elasticsearch\ElasticsearchHelper; // 初始化全文索引时需要指定该Model要添加的索引记录 public function elasticsearchIndexList() { // 如这里chapter表与course表关联,只有当course及chapter状态都为可用,且非描述类chapter(pid = 0为描述类chapter)才会添加全文索引 return $this->alias('ch')->join('__COURSE__ c ON c.id=ch.course_id and c.status = ' . DBCont::NORMAL_STATUS)->where(['ch.status' => DBCont::NORMAL_STATUS, 'ch.pid' => ['neq', 0]])->field('ch.*')->select(); } // chapter进行增删查改时同时会更新索引内容,该方法是指定什么状态的记录才会进行索引更改 // 返回false为无需索引的记录,true则会进行索引更新 public function isElasticsearchIndex($ent){ if($ent['status'] != DBCont::NORMAL_STATUS || $ent['pid'] == 0){ return false; } $course_ent = D('Course')->find($ent['course_id']); if($course_ent['status'] == DBCont::NORMAL_STATUS){ return true; } else{ return false; } } // 程序会自动生成索引的配置参数,此处是定义生成参数的规则 // 以:开头的字母表示该处会自动替换成相应字段的实际值 // {}表示里面的字符会与替换后的:字段值进行连接,如:id{_chapter}, id实际值为 12,则该处会替换成 12_chapter // | 表示可以将字段的实际值传递给指定的函数进行处理,转换成想要的值。如,description字段是富文本内容,我们将html标签进行索引,可以在model方法里自定义一个叫deleteHtmlTag的方法进行处理,当然也可以定义为全局函数,程序会先查找全局是否存在该函数,如果没有再去对象里查找有无该方法 // index和type的值在建立初始化全文索引时指定,具体查看全文索引初始化说明 public function elasticsearchAddDataParams() { return [ 'index' => 'global_search', 'type' => 'content', 'id' => ':id{_chapter}', 'body' => [ 'title' => ':title', 'desc' => ':description|html_entity_decode' ] ]; } }
-
初始化全文索引
打开Home/Controller/ElasticController.class.php文件, 修改index方法里的$params变量,根据你的需要来设置
-
执行索引初始化,程序会自动检索数据库全部数据表,为需要添加索引的表和字段进行索引添加操作。
//进入app目录,下面有个makeIndex.php文件 php makeIndex.php
-
可通过在config文件设置 "ELASTIC_ALLOW_EXCEPTION" 来禁止抛出异常,即使搜索引擎关闭,也不会影响原来的业务操作。
-
更新操作的索引重建仅会在索引字段发生变化时才会触发。
Controller
Model
数据库迁移
扩展了laravel的迁移功能, 可在执行迁移前后插入一些操作。
class CreateTestTable extends Migration { public function beforeCmmUp() { echo "执行前置命令" . PHP_EOL; } public function beforeCmmDown() { echo "执行前置回滚命令" . PHP_EOL; } /** * Run the migrations. * * @return void */ public function up() { Schema::create('test', function (Blueprint $table) { $table->bigIncrements('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('test'); } public function afterCmmUp() { echo "执行后置命令" . PHP_EOL; } public function afterCmmDown() { echo "执行后置回滚命令" . PHP_EOL; } }
运用场景:
迁移文件是为了方便我们对数据库结构进行变更管理。那么是所有的数据库变更都会放到迁移文件处理吗?当然不是,像一些跟业务逻辑有关的数据处理就不应该放到迁移文件,否则这部分代码跟 业务数据捆绑,很容易导致执行迁移时出错。而只有那些跟业务数据无关,用于构造系统数据存储结构的变更操作才应该放到迁移中。但一个业务系统维护久了,难免必须处理一些数据后才能正常 执行数据库结构变更,例如唯一索引的创建往往就需要我们清理掉一些重复的业务数据。这时就有了不能放在迁移里的业务数据维护脚本的管理需求。
依托上面的场景,就有了在迁移文件中加入了前后置的操作点的构思。默认情况下,只要设置了前后置操作点,执行迁移时就会自动执行一遍,回滚类同。
那么如果只是想执行迁移而不执行前后置操作怎么办呢(例如执行自动测试脚本前,我们会自动构建系统的数据库)。只需在命令后面加入 --no-cmd即可
下面是支持加入--no-cmd操作的命令
php artisan migrate --no-cmd php artisan migrate:rollback --no-cmd php artisan migrate:fresh --no-cmd php artisan migrate:refresh --no-cmd php artisan migrate:reset --no-cmd
-
CmmProcess 该类是为了方便在迁移中调用tp的脚本
用法:
$process = new \Larafortp\CmmMigrate\CmmProcess(); //timeout为程序的超时退出时间,默认60秒 $process->setTimeOut(100)->callTp('/var/www/move/www/index.php', '/home/index/test');
-
ConfigGenerator 迁移中处理系统配置的工具类
addGroup($name) //添加配置分组
deleteGroup($name) //删除配置分组
updateGroup($config_name, $group_name) //将配置转移到指定分组
以下为新增配置项的操作函数
$name 配置名
$title 配置标题
$value 配置值
$remark 配置说明
$group 配置分组
$sort 排序
addNum($name, $title, $value, $remark = '', $group = 1, $sort = 0) //新增数字类型配置值
addText($name, $title, $value, $remark = '', $group = 1, $sort = 0) //新增字符类型配置值
addArray($name, $title, $value, $remark = '', $group = 1, $sort = 0) //新增数组类型配置值
addPicture($name, $title, $value, $remark = '', $group = 1, $sort = 0) //新增图片类型配置值
addUeditor($name, $title, $value, $remark = '', $group = 1, $sort = 0) //新增富文本类型配置值
addSelect($name, $title, $value, $options, $remark = '', $group = 1, $sort = 0) //新增下拉选择配置值 $options 是下拉配置数组
add($name, $type, $title, $group, $extra, $remark, $value, $sort) //新增配置方法,未预设的第三方组件可使用该函数
delete($name) //删除配置
后台JS
ListBuilder
FormBuilder
CompareBuilder
Builder
Cache
upload Api
前台js错误收集
用法
在前端head中引入log.js后调用frontLog方法
<script src="__PUBLIC__/libs/log.js"></script> <script> frontLog({ url:'/api/jsLog/index' }); </script>
数据库帮助函数
generator
低内存消耗迭代函数
参数
- $map 查询参数 默认为空数组
- $count 一次查询的数据量,越大占用的内存会大,但运行效率会更高,根据情况灵活调整 默认为 1
举例
foreach(D("Content")->generator([], 200) as $ent){ var_dump($ent); }
权限功能
微信登录
为解决第三方平台网站应用的PC扫码后openid不可操作问题,统一对PC端微信扫码以及微信端登录进行封装。
-
从微信公众平台中获取公众号的app_id和app_secret,并进行相关配置,放入.env文件
# 微信公众号 WX_APPID= WX_APPSECRET=
-
PC扫码页面,在需要显示二维码的地方加入iframe
<iframe src="{:U('qscmf/weixinLogin/scan')}"></iframe>
PS:
-
构造iframe的src时,可通过goto_url参数来指定PC端扫码后跳转的地址,默认为首页
-
构造iframe的src时,可通过mobile_goto_url参数来指定微信端扫码后跳转的地址,默认为首页
-
微信端获取登录信息
$wx_info=Qscmf\Lib\WeixinLogin::getInstance()->getInfoForMobile();
-
运行/扫码后可用
session('wx_info')
获取微信登录信息 -
若'wx_info'的session值已设置,可通过设置config.php中的'WX_INFO_SESSION_KEY'来改变
场景模拟
一、 PC端实现扫码登录/注册
-
扫码页面(扫码后需要跳转到'/home/index/wxLogin')
<!-- 其它代码 --> <!-- 此处是放入二维码的位置 --> <iframe id="scan" src="{:U('qscmf/weixinLogin/scan',['goto_url'=>urlencode('/home/index/wxLogin')])}"></iframe> <!-- 其它代码 -->
-
登录/注册业务处理(对应上一步的"/home/index/wxLogin")
$wx_info=json_decode(session('wx_info'),true); // 若用户表为member表 $member=D('Member')->where(['openid'=>$wx_info['id']])->find(); if ($member){ //登录 session('mid',$member['id']); }else{ //注册 $ent=[ 'openid'=>$wx_info['id'], 'nickname'=>$wx_info['nickname'] ]; $r=D('Member')->createAdd($ent); if ($r===false){ E(D('Member')->getError()); } session('mid',$r); } redirect(U('home/user/index'));
二、 微信端实现授权登录/注册
-
授权页面 (授权后需要跳转到'/home/index/wxLogin')
$wx_info=Qscmf\Lib\WeixinLogin::getInstance()->getInfoForMobile(); if ($wx_info){ redirect(U('/home/index/wxLogin')); }
-
登录/注册业务处理(对应上一步的"/home/index/wxLogin")
// 与PC端扫码后登录/注册业务处理一致
全局函数
js组件
selectAddr
select框的地址选择器 参数 addressLevel: array 省/市/县的select框默认值,默认为:['选择省','选择市','选择区'] level: int 1|2|3 地址的等级:省/市/区,默认为:3 url: array 分别获取地址的接口url,默认为:['/api/area/getProvince.html','/api/area/getCityByProvince.html','/api/area/getDistrictByCity.html'] onSelected: function (val,changeEle){} 每个select框选择地址后执行自定义function,val: 隐藏域的值 changeEle: 触发事件的select
代码示例
<input type="hidden" id="hidden_position" name="city_id" value="{$city_id}"> <block name="script"> <script type="text/javascript" src="__PUBLIC__/libs/addrSelect/selectAddr.js"></script> <script> jQuery(document).ready(function() { $('#hidden_position').selectAddr({ addressLevel: ['省','市','区'], level: 3, onSelected: function (val,changeEle){ log(val); } }); function log(str) { console.log(str+"-111"); } }); </script> </block>
常量
DOMAIN 域名,可通过env去改写,默认采用$_SERVER["HTTP_HOST"]
ROOT 指定子目录,默认为空, 可通过env改写,如子路径 ROOT=/move
SITE_URL 包含子目录的网站根地址
HTTP_PROTOCOL 返回http或者https协议字符串, 可通过env指定
REQUEST_URI 获取方向代理前的REQUEST_URI值
扩展
部分扩展包会将组件的js/css注入 dashboard_layout 头部
默认只注入路径 T('Admin@default/common/dashboard_layout')
如果项目使用自定义的 layout ,可以通过 QS_INJECT_LAYOUT_PATH 配置需要注入的 layout 路径
// config.php文件加入以下配置 // 只需要配置自定义的 layout 路径 'QS_INJECT_LAYOUT_PATH' => [ T('Admin@default/common/letter/layout') ]
消息队列
测试
在看以下文档时,建议结合qscmf自带的测试用例代码阅读。
http测试
http测试实则是模拟接口请求,测试接口逻辑是否与预期一致。
qscmf使用phpunit作为测试框架,在lara\tests下创建测试类,http测试类需要继承Lara\Tests\TestCase类。
<?php namespace Lara\Tests\Feature; use Lara\Tests\TestCase; class AuthTest extends TestCase { }
构造get请求
/** * @uri 请求url * @header 自定义请求头 数组类型 例如: ['x-header' => 'value'] * @return 返回请求结果 **/ $this->get($uri, $header); 样例代码 lara/tests/Feature/AuthTest.php
构造post请求
/** * @uri 请求url * @data 需发送的数据 数组类型 例如: [ 'uid' => 'admin', 'pwd' => '123456'] * 可存放上传文件 例如: [ 'file' => $file ] $file类型必须为SymfonyUploadedFile类型 * @header 自定义请求头 数组类型 例如: ['x-header' => 'value'] * @return 返回请求结果 **/ $this->post($uri, $data, $header); 样例代码 lara/tests/Feature/AuthTest.php
模拟超级管理员登录
$this->loginSuperAdmin();
模拟普通后台用户登录
/** * $uid 用户id **/ $this->loginUser($uid);
测试上传文件
//构造的SymfonyUploadedFile类文件对象 $data = [ 'file' => UploadedFile::fake()->image('test.jpg', 100, 100) ]; $content = $this->post('api/upload/uploadImage', $data);
测试数据库是否存在记录
/** * $tablename 表名 * $where 查询条件 例如: [ 'name' => 'admin', 'status' => '1' ] **/ $this->assertDatabaseHas($tablename, $where); 样例代码 lara/tests/Feature/UploadTest.php
测试数据库是否不存在记录
/** * $tablename 表名 * $where 查询条件 例如: [ 'name' => 'admin', 'status' => '1' ] **/ $this->assertDatabaseMissing($tablename, $where); 样例代码 lara/tests/Feature/UserTest.php
创建Mock类
如果代码需要请求第三方接口,或者触发一些我们不想在测试里执行的的代码,可以采用Mock类模仿该部分的逻辑,达到只测试接口的目的。
mock类的创建使用phpunit提供的方法
//Foo为需模仿的类,phpunit会自动给我们生成模拟类,方法没有指定返回值,默认返回null $stub = $this->createMock(Foo::class); //也可以指定方法的返回值 $stub->method('say')->willReturn(1); //给Foo类指定Mock实例 app()->instance(Foo::class, $stub); · · · //业务代码的设计需可测试,如Mock模仿的代码必须封装成类,定义接口解耦逻辑 //用laravel的依赖容器自动构造Foo实例,这样可达到测试实例用Mock实例替换实际业务类的目的 $foo = app()->make(Foo::class); //该接口方法在测试执行时,会返回我们指定返回的值 $foo->say(); 样例代码: lara/tests/Feature/MockTest.php
Dusk测试
Dusk 是laravel的浏览器自动化测试 工具 ,qscmf将其稍微封装了一下,只需继承Lara\Tests\DuskTestCase类即可使用,具体的使用方法可查看laravel文档。
样例代码: lara/tests/LoginTest.php
命令行测试
在Testing\TestCase下增加了 cli的命令行模拟执行
$content = $this->cli('app/cliMode', 'Home', 'Controller', 'action', '参数1', '参数2'...); //content为返回的输出结果
在phpunit里调用tp的代码片段
使用runTp方法,参数接收一个匿名函数,匿名函数可调用tp里的代码,return后可在phpunit接收
$test_ent = $this->runTp(function (){ return D('Test')->find(1); }); $this->assertTrue($test_ent['name'] == '测试');
可用该方法测试Tp的代码,但如果只是要验证数据库值,建议还是使用 assertDatabaseHas等测试方法,性能更佳。
压缩前端js代码
压缩办法很多,这里提供一种配置简单的方式,传送门
HEIC格式图片转JPG格式
为解决部分组件暂不支持展示heic格式图片,将其转换为jpg
上传到阿里云oss的图片已处理
-
使用
- 复制数据迁移文件2022_07_18_014941_alter_file_pic_add_mime_type.php,qs_file_pic添加字段 mime_type
-
扩展包需自行注册并实现heic_to_jpg行为
- 定义行为
class HeicToJpgBehavior{ public function run(&$params) { // $params为qs_file_pic的一条数据 // 具体逻辑 $params['url'] = 'your new url'; } }
- 注册行为
\Think\Hook::add('heic_to_jpg', 'xxx\\HeicToJpgBehavior');
- 定义行为
TRACE_ERROR
env增加了TRACE_ERROR配置,如果希望在debug关闭的模式下能收集到错误的报错位置,可以设置为true。这样就无需开启debug模式,也能收集到错误的报错位置。减少日志负担。
后台使用react构建页面
文档
由于工作量大,文档会逐步补全。