PHP code example of quansitech / qscmf-utils
1. Go to this page and download the library: Download quansitech/qscmf-utils library . Choose the download type require .
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
quansitech / qscmf-utils example snippets
composer
> $process = new \Qscmf\Utils\MigrationHelper\CmmProcess();
> //timeout为程序的超时退出时间,默认60秒
> $process->setTimeOut(100)->callTp('/var/www/move/www/index.php', '/home/index/test');
>
$this->nodeData = [
'新闻中心'=> [
[
'name' => 'index',
'title' => '新闻分类',
'controller'=> 'NewsCate',
],
[
'name' => 'index',
'title' => '内容管理',
'controller'=> 'News',
'sort' => 1,
],
],
];
$menuGenerate = new Qscmf\Utils\MigrationHelper\MenuGenerate();
$menuGenerate->insertAll($this->nodeData);
// 撤销
$menuGenerate->insertAllRollback($this->nodeData);
$data = [
[
'title' => '平台2', //标题 (必填)
'module' => 'newsAdmin', //模块英文名 (必填)
'module_name'=> '后台管理', //模块中文名 (必填)
'url' => '', //url (必填)
'type' => '', //类型 (选填)
'sort' => 0, //排序 (选填)
'icon' => '', //icon (选填)
'status' => 1, //状态 (选填)
'top_menu' => [
'新闻中心'=> [
[
'name' => 'index', //(必填)
'title' => '测试新闻中心', //(必填)'
'controller'=> 'NewsController', //(必填)
'sort' => 1, //排序 //(选填)
'icon' => '', //图标 //(选填)
'remark' => '', //备注 //(选填)
'status' => 1, //状态 //(选填)
],
],
],
],
];
$menuGenerate = new Qscmf\Utils\MigrationHelper\MenuGenerate();
$menuGenerate->insertNavigationAll($data);
// 撤销
$menuGenerate->insertNavigationAllRollback($data);
$this->nodeData = [
'新闻中心'=> [
[
'name' => 'index',
'title' => '新闻分类',
'controller'=> 'NewsCate',
'controller_title'=> '新闻分类管理',
],
[
'name' => 'index',
'title' => '内容管理',
'controller'=> 'News',
'controller_title'=> '新闻管理',
'sort' => 1,
],
],
];
$menuGenerate = new Qscmf\Utils\MigrationHelper\MenuGenerate();
$menuGenerate->insertAll($this->nodeData);
// 撤销
$menuGenerate->insertAllRollback($this->nodeData);
$reader_ents = D("Reader")->where(['status' => 1])->select();
$school_ref = new RefModel(D('School'), 'id'); //设置目标表的model类 设置目标表的关联id
$school_ref->fill($reader_ents, 'school_id'); // 通过$reader_ents的school_id预提取关联表的关联数据
foreach($reader_ents as &$v){
$v['school_name'] = $school_ref->pick($v['school_id'], 'school_name'); //从预提取到的关联数据拿目标值, 当第二个参数传递null,则会返回包含表全部字段的数组
}
//通过传递闭包函数来获取更复杂的关联数据
//如用一般用法只能获取到读者头像id对应的本地图片路径,如果还需要进一步获取imageproxy的代理地址,则可传递一个闭包函数实现
$reader_ents = D("Reader")->where(['status' => 1])->select();
$pic_ref = new RefModel(D('FilePic'));
$pic_ref->fill($reader_ents, 'avatar');
foreach($reader_ents as &$v){
$v['avatar_url'] = $pic_ref->pick($v['avatar'], null, function($file_ent){
return \Qscmf\Utils\Libs\Common::imageproxy('100x100', $file_ent);
}); //闭包函数接收由第一二个参数决定的提取值,这里的imageproxy可以接收一条file_pic的数据库记录来拼接出图片的代理地址,因此我们可以第二个参数传递null来简化数据库的查询次数。
}
//跨两张表查询数据
//apply是读者申请表, return_reason表是申请退回原因定义表, 要查对着被退回的原因
//status = 2是退回
$reader_ents = D("Reader")->where(['status' => 2])->select();
$apply_ref = new RefModel(D('Apply'), 'reader_id');
$apply_ref->fill($reader_ents, 'id');
$return_ref = new RefModel(D('ReturnReason'));
$return_ref->fill($apply_ref->pickAll(), 'reason_id');
foreach($reader_ents as &$v){
$v['return_reason_text'] = $apply_ref->pick($v['id'], 'reason_id', function($reason_id) use ($return_ref){
return $return_ref->pick($reason_id, 'desc');
});
}
public function execShell(){
$redis_lock = \Qscmf\Utils\Libs\RedisLock::getInstance();
$is_lock = $redis_lock->lock('exec_shell_lock_key', 60);
$is_lock === false && $this->error('请一分钟后再操作');
shell_exec('ll >/dev/null');
$redis_lock->unlock('exec_shell_lock_key');
}
public function getRes(){
$cache_data = S("api_cache_data");
if(!$cache_data){
$redis_lock_cls = new RedisLock();
list($is_lock, $cache_data) = $redis_lock_cls->lockWithCallback($this->genLockKey(),30, [$this,"fetchCacheData"],30, 100000);
if ($is_lock === false){
$res = ['info' => "系统繁忙,请稍后再试", 'status' => 0];
}elseif($is_lock === true){
// 业务逻辑
$data = []; // 获取数据库数据
$res = ['info' => "成功", 'status' => 1, 'data' => $data];
S("api_cache_data", json_encode($res));
$redis_lock_cls->unlock($this->genLockKey());
}else{
$res = $cache_data;
}
}else{
$res = $cache_data;
}
return $res;
}
protected function genLockKey():string{
return 'api_redis_lock';
}
public function fetchCacheData(){
$data = S("api_cache_data");
$flag = is_array($data)
return [$flag, $data];
}
//排他锁
$lock = new ExclusiveLock(string $key, in $expire = 5, int $timeout = 0)
//上锁
$lock->lock();
//解锁
$lock->unlock();
//注册共享锁
$lock->register(SharedLock $lock);
//共享锁
$share_lock = new SharedLock(string $key, int $type = self::TYPE_SHARED);
//创建排他锁
$all_lock = new ExclusiveLock('all_lock', 3600);
//注册共享锁,并且该锁是独占类型。意思只要该排他锁生成,其余用了相同key的共享锁则不能产生
$all_lock->register(new SharedLock('single_lock', SharedLock::TYPE_EXCLUSIVE));
$all_lock->lock();
sleep(10);
$all_lock->unlock();
//创建排他锁2
$fund_lock = new ExclusiveLock('single_lock_1', 3600);
//注册共享类型的共享锁,意思是相同key的共享类型共享锁可以存在多个,但和独占类型的共享锁互斥
$fund_lock->register(new SharedLock('single_lock', SharedLock::TYPE_SHARED));
$fund_lock->lock();
sleep(10);
$fund_lock->unlock();
//创建排他锁3
$fund_lock = new ExclusiveLock('single_lock_2', 3600);
$fund_lock->register(new SharedLock('single_lock', SharedLock::TYPE_SHARED));
$fund_lock->lock();
sleep(10);
$fund_lock->unlock();
// imageproxy图片格式处理
// options 图片处理规则
// file_id 图片id,若为ulr,则返回该url, 也可以是file_pic的数据库行记录(省略数据库查询操作)
// cache 默认为空,不开启缓存,否则可设置缓存时间,单位秒
// return 返回与.env配置格式对应的图片地址
\Qscmf\Utils\Libs\Common::imageproxy($options, $file_id, $cache)
//.env文件
IMAGEPROXY_URL={schema}://{domain}/{options}/{remote_uri}
IMAGEPROXY_REMOTE=http://www.test.com
//imageporxy生成的地址
$url = \Qscmf\Utils\Libs\Common::imageproxy('1920x540',$banner_id);
echo $url;
//http://www.test.com/1920x540/http://localhost/Uploads/images/xxxx.jpg
//参数说明
//第一个参数为匿名函数,实现获取数据的业务逻辑
//第二个参数为缓存过期时间,单位秒
//第三个参数为缓存key,若为空则使用匿名函数的参数作为key
//第四个参数为分组标识,若不为空,则产生的key将会归入该分组,使用clearCachedGroup方法可以清除该分组的缓存
//用法举例
//以下方法要从数据库读取数据,如果该页面是热点页,则无法承载太多的并发请求,需要针对其进行缓存
$ent = D('Project')->getOneProject($map);
//使用Common::cached方法快速实现该工作
//以下为改造后效果
//生成缓存函数便于重复使用
$project_cached = Common::cached(function($map){
$ent = D('Project')->getOneProject($map);
return $ent;
}, 60);
//使用生成的缓存函数完成数据获取和缓存的工作
$ent = $project_cached($map);
//指定缓存key举例
$project_cached = Common::cached(function($map){
$donate_amount = D('ProjectDonate')->donateAmount($map);
return $donate_amount;
}, 3600, 'project_donate_amount_' . $project_id, 'project_donate_amount');
//指定缓存key后,可实现对缓存值不落盘更新
$redis = Cache::getInstance('redis');
//incrByFloat 方法必须升级到think-core v13.3.0以上版本才能使用
$redis->incrByFloat("project_donate_amount_{$project_id}", $donate_amount);
//清除分组缓存
Common::clearCachedGroup('project_donate_amount');
// 参数说明
// $module_name 模块名
// $controller_name 控制器名
// $action_name 权限点名
// $title 权限点标题
// $pid 父节点,若为空则根据模块名、控制器名查找
Qscmf\Utils\MigrationHelper\AuthNodeGenerate::addAuthNode('admin', 'user', 'add', '新增');
Qscmf\Utils\MigrationHelper\AuthNodeGenerate::addAuthNode('admin', 'user', 'edit', '编辑');
// 修改模块、控制器标题
Qscmf\Utils\MigrationHelper\AuthNodeGenerate::addAuthNode(['UserAdmin','用户'], ['user', '用户管理'], 'add', '新增');
// 参数说明
// $module_name 模块名
// $controller_name 控制器名
// $action_name 权限点名,若为空则删除该控制器下的所有权限点
// 只删除一个权限点
Qscmf\Utils\MigrationHelper\AuthNodeGenerate::deleteAuthNode('admin', 'user', 'add');
Qscmf\Utils\MigrationHelper\AuthNodeGenerate::deleteAuthNode('admin', 'user', 'edit');
// 删除控制器下所有权限点
Qscmf\Utils\MigrationHelper\AuthNodeGenerate::deleteAuthNode('admin', 'user', '');
\Qscmf\Utils\Libs\DBComment::buildChangeSql($comment_mapping);
// 参数说明
// $comment_mapping结构为
// ['数据表名称'=>['name'=>'数据表名称','comment'=>'数据表注释', 'column' =>['字段名1'=>'字段1注释','字段名2'=>'字段2注释']]]
$comment_mapping = [
'migrations' => [
'name' => 'migrations',
'comment' => '数据迁移表',
'column' => [
'id' => '流水号,主键',
'migration' => '文件名',
'before' => '运行前执行情况',
'run' => '脚本执行情况',
'after' => '运行前执行情况',
'batch' => '批次',
]
],
'qs_access' =>
[
'name' => 'qs_access',
'comment'=> '用户组关联权限点表',
'column'=> [
'role_id' => '用户组id,qs_role主键',
'node_id' => '权限点id,qs_node主键',
'level' => '权限点类型',
'module' => '权限点名称',
],
],
];
\Qscmf\Utils\MigrationHelper\DBComment::buildChangeSql($comment_mapping);
// 输出结果为一下内容,可使用information_schema.columns数据表核对字段定义部分
/**
ALTER TABLE
`migrations` COMMENT = '数据迁移表',
CHANGE COLUMN `id` `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '流水号,主键',
CHANGE COLUMN `migration` `migration` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文件名',
CHANGE COLUMN `before` `before` TINYINT(1) NOT NULL COMMENT '运行前执行情况',
CHANGE COLUMN `run` `run` TINYINT(1) NOT NULL COMMENT '脚本执行情况',
CHANGE COLUMN `after` `after` TINYINT(1) NOT NULL COMMENT '运行前执行情况',
CHANGE COLUMN `batch` `batch` INT NOT NULL COMMENT '批次';
ALTER TABLE
`qs_access` COMMENT = '用户组关联权限点表',
CHANGE COLUMN `role_id` `role_id` SMALLINT UNSIGNED NOT NULL COMMENT '用户组id,qs_role主键',
CHANGE COLUMN `node_id` `node_id` SMALLINT UNSIGNED NOT NULL COMMENT '权限点id,qs_node主键',
CHANGE COLUMN `level` `level` TINYINT NOT NULL COMMENT '权限点类型',
CHANGE COLUMN `module` `module` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '权限点名称';
**/
public function gets(){
$get_data = I('get.');
$count = $get_data['count'] ?: C("HOME_PER_PAGE_NUM",null, 20);
$order = 'sort asc,id desc';
$scroller = new Scroller($order);
$map = [];
$map['status'] = \Gy_Library\DBCont::NORMAL_STATUS;
//检查参数里是否包含下一次的查询条件,有则调用applyLastCondition方法构造查询条件
if(isset($get_data['last_condition']) && !qsEmpty($get_data['last_condition'])){
$scroller->applyLastCondition($map, $get_data['last_condition']);
}
$list = D("Gift")->getGiftList($map, 1, $row_count, $order);
$res = [
'list' => $list,
'last_condition' => qsEmpty($list) ? "" : $scroller->toLastCondition($list[count($list) - 1]), //toLastCondition从最后一条数据生成下一次查询的条件
];
return new Response("获取成功", 1, $res);
}
$db_data = [
['id' => 1, 'name' => 'item1'],
['id' => 2, 'name' => 'item2'],
['id' => 3, 'name' => 'item3']
];
$new_data = [
['name' => 'item4'], // 新增
['id' => 2, 'name' => 'item2_updated'], // 更新
['id' => 4, 'name' => 'item4'], // 新增
];
$cud = new AnalysisCUD($db_data, $new_data);
$result = $cud->analysis();
print_r($result);
/*
Array
(
[to_insert] => Array
(
[0] => Array
(
[name] => item4
)
[1] => Array
(
[id] => 4
[name] => item4
)
)
[to_update] => Array
(
[0] => Array
(
[id] => 2
[name] => item2_updated
)
)
[to_delete] => Array
(
[0] => Array
(
[id] => 1
[name] => item1
)
[1] => Array
(
[id] => 3
[name] => item3
)
)
)
*/
\Qscmf\Utils\Libs\DingTalkRobot::send("【系统标识】Mysql 与 ES 数据同步差异较大。");
\Qscmf\Utils\Libs\DingTalkRobot::send("【系统标识】Mysql 与 ES 数据同步差异较大。", "your access_token");
##
## RefModel
从关联表预提取关联数组(解决N+1循环取数导致数据库频繁访问的问题)
#### API
1. fill($data_ents, $key, $extra_where = null)
> 用处:给关联对象填充关联值
>
> data_ents 关联数据源
>
> key 从关联数据源提取关联表数据的键值
>
> extra_where 附加查询条件
2. pick($value, $field = null, $callback = null)
> 用处:从关联对象中提取值
>
> value 关联数据源的对应数据,与fill方法的key对应
>
> field 指定提取的字段,默认null,表示提取所有字段
>
> callback 回调函数,接收一个参数,为关联数据中,field指定的数据, return 作为最终提取数据
3. pickAll()
> 用处: 从关联对象中提取全部数据
#### 用法
一般用法
高级用法
##
## RedisLock
基于Redis改造的悲观锁
+ 先获取锁再执行业务逻辑,执行结束释放锁。
+ 保证同一个方法的并发重复操作请求只有一个请求可以获取锁,在不进行高延迟事务处理的场景下可以使用。
+ 若只要一次获取锁成功,其它等待的请求可以通过callback处理,提前退出
+