From 49d3c3c14b24a92e1e5f44ddd24000d4dbb3531b Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 03:08:02 +0000 Subject: [PATCH 001/300] * [misc] move sql. --- db/update21.1.sql | 5 +++++ db/update21.2.sql | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 db/update21.2.sql diff --git a/db/update21.1.sql b/db/update21.1.sql index 494f0cc7d3..f97943c568 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -27,3 +27,8 @@ ALTER TABLE `zt_review` ADD `toAuditBy` varchar(30) not NULL default '' AFTER `l ALTER TABLE `zt_review` ADD `toAuditDate` datetime NULL AFTER `toAuditBy`; ALTER TABLE `zt_design` ADD `storyVersion` smallint(6) UNSIGNED NOT NULL DEFAULT '1' AFTER `story`; + +ALTER TABLE zt_dataview MODIFY `fields` text NULL; +ALTER TABLE zt_dataview MODIFY `objects` text NULL; +ALTER TABLE zt_dataview MODIFY `mode` varchar(50) NOT NULL DEFAULT 'builder'; +ALTER TABLE zt_dataview ADD `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql' AFTER `code`; diff --git a/db/update21.2.sql b/db/update21.2.sql deleted file mode 100644 index 4215f8afca..0000000000 --- a/db/update21.2.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE zt_dataview MODIFY `fields` text NULL; -ALTER TABLE zt_dataview MODIFY `objects` text NULL; -ALTER TABLE zt_dataview MODIFY `mode` varchar(50) NOT NULL DEFAULT 'builder'; -ALTER TABLE zt_dataview ADD `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql' AFTER `code`; From aff4b3f88fe8fb5f4248b86f456f1a7103771df2 Mon Sep 17 00:00:00 2001 From: daitingting Date: Wed, 20 Nov 2024 02:24:35 +0000 Subject: [PATCH 002/300] * [task#133009,doint,1h] Add pivotSpec table, adjust fields. --- db/update20.2.0.sql | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/db/update20.2.0.sql b/db/update20.2.0.sql index acc392bf5f..0a77210960 100644 --- a/db/update20.2.0.sql +++ b/db/update20.2.0.sql @@ -18,3 +18,31 @@ CREATE INDEX `idx_repo_deleted` ON `zt_job` (`repo`,`deleted`); UPDATE `zt_workflowfield` SET options = '{"wait":"\\u5f85\\u5ba1\\u6279","doing":"\\u5ba1\\u6279\\u4e2d","pass":"\\u901a\\u8fc7","reject":"\\u4e0d\\u901a\\u8fc7","reverting":"\\u56de\\u9000\\u4e2d"}' WHERE field = 'reviewStatus'; UPDATE `zt_workflowaction` SET `role` = 'buildin' WHERE `buildin` = '1' AND `module` IN ('requirement', 'epic'); + +CREATE TABLE `zt_pivotSpec` ( + `pivot` mediumint(8) NOT NULL, + `version` varchar(10) NOT NULL, + `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', + `mode` varchar(10) NOT NULL default 'builder', + `name` text NULL, + `desc` text NULL, + `sql` text NULL, + `fields` text NULL, + `langs` text NULL, + `vars` text NULL, + `objects` text NULL, + `settings` text NULL, + `filters` text NULL, + `createdDate` datetime NULL, + UNIQUE KEY `pivot` (`pivot`, `version`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL AFTER `builtin`; +ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varch(10) NOT NULL DEFAULT 'builder'; +ALTER TABLE `zt_pivot` CHANGE `sql` `sql` text NULL; +ALTER TABLE `zt_pivot` CHANGE `fields` `fields` text NULL; +ALTER TABLE `zt_pivot` CHANGE `langs` `langs` text NULL; +ALTER TABLE `zt_pivot` CHANGE `vars` `vars` text NULL; +ALTER TABLE `zt_pivot` CHANGE `objects` `objects` text NULL; +ALTER TABLE `zt_pivot` CHANGE `settings` `settings` text NULL; +ALTER TABLE `zt_pivot` CHANGE `filters` `filters` text NULL; From 1e6ea6f9d6da7139a4c04f21530a85da6457efa9 Mon Sep 17 00:00:00 2001 From: daitingting Date: Wed, 20 Nov 2024 03:12:54 +0000 Subject: [PATCH 003/300] * [task#133009,doing,1h] Adjust table. --- config/zentaopms.php | 1 + db/update20.2.0.sql | 2 +- db/zentao.sql | 37 ++++++++++++++++++++++++++++--------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/config/zentaopms.php b/config/zentaopms.php index 1b20104d47..0d99e58d50 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -490,6 +490,7 @@ if(!defined('TABLE_SEARCHDICT')) define('TABLE_SEARCHDICT', $config->db->prefi define('TABLE_SCREEN', '`' . $config->db->prefix . 'screen`'); define('TABLE_CHART', '`' . $config->db->prefix . 'chart`'); define('TABLE_PIVOT', '`' . $config->db->prefix . 'pivot`'); +define('TABLE_PIVOTSPEC', '`' . $config->db->prefix . 'pivotspec`'); define('TABLE_PIVOTDRILL', '`' . $config->db->prefix . 'pivotdrill`'); define('TABLE_DASHBOARD', '`' . $config->db->prefix . 'dashboard`'); define('TABLE_DATASET', '`' . $config->db->prefix . 'dataset`'); diff --git a/db/update20.2.0.sql b/db/update20.2.0.sql index 0a77210960..cac3b3f7bb 100644 --- a/db/update20.2.0.sql +++ b/db/update20.2.0.sql @@ -19,7 +19,7 @@ UPDATE `zt_workflowfield` SET options = '{"wait":"\\u5f85\\u5ba1\\u6279","doing" UPDATE `zt_workflowaction` SET `role` = 'buildin' WHERE `buildin` = '1' AND `module` IN ('requirement', 'epic'); -CREATE TABLE `zt_pivotSpec` ( +CREATE TABLE `zt_pivotspec` ( `pivot` mediumint(8) NOT NULL, `version` varchar(10) NOT NULL, `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', diff --git a/db/zentao.sql b/db/zentao.sql index ac8cd51d07..efb30c0040 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -15354,22 +15354,23 @@ CREATE TABLE IF NOT EXISTS `zt_pivot` ( `dimension` mediumint(8) unsigned NOT NULL DEFAULT 0, `group` varchar(255) NOT NULL DEFAULT '', `code` varchar(255) NOT NULL DEFAULT '', - `driver` enum('mysql', 'duckdb') not NULL default 'mysql', - `mode` enum('text', 'builder') not NULL default 'builder', + `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', + `mode` varchar(10) NOT NULL default 'builder', `name` text NULL, `desc` text NULL, `acl` enum('open','private') NOT NULL DEFAULT 'open', `whitelist` text NULL, - `sql` mediumtext NULL, - `fields` mediumtext NULL, - `langs` mediumtext NULL, - `vars` mediumtext NULL, - `objects` mediumtext NULL, - `settings` mediumtext NULL, - `filters` mediumtext NULL, + `sql` text NULL, + `fields` text NULL, + `langs` text NULL, + `vars` text NULL, + `objects` text NULL, + `settings` text NULL, + `filters` text NULL, `step` tinyint(3) unsigned NOT NULL DEFAULT '0', `stage` enum('draft','published') NOT NULL DEFAULT 'draft', `builtin` enum('0', '1') NOT NULL DEFAULT '0', + `version` varchar(10) NOT NULL, `createdBy` varchar(30) NOT NULL DEFAULT '', `createdDate` datetime NULL, `editedBy` varchar(30) NOT NULL DEFAULT '', @@ -15380,6 +15381,24 @@ CREATE TABLE IF NOT EXISTS `zt_pivot` ( CREATE INDEX `dimension` ON `zt_pivot` (`dimension`); CREATE INDEX `group` ON `zt_pivot` (`group`); +CREATE TABLE `zt_pivotspec` ( + `pivot` mediumint(8) NOT NULL, + `version` varchar(10) NOT NULL, + `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', + `mode` varchar(10) NOT NULL default 'builder', + `name` text NULL, + `desc` text NULL, + `sql` text NULL, + `fields` text NULL, + `langs` text NULL, + `vars` text NULL, + `objects` text NULL, + `settings` text NULL, + `filters` text NULL, + `createdDate` datetime NULL, + UNIQUE KEY `pivot` (`pivot`, `version`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8; + -- DROP TABLE IF EXISTS `zt_sqlbuilder`; CREATE TABLE IF NOT EXISTS `zt_sqlbuilder` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, From 934d12489b0fee657625f7b02f01dce3f6c6df2d Mon Sep 17 00:00:00 2001 From: daitingting Date: Wed, 20 Nov 2024 03:17:55 +0000 Subject: [PATCH 004/300] * [task#133009,doing,0.1h] Move it. --- db/update20.2.0.sql | 28 ---------------------------- db/update21.1.sql | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/db/update20.2.0.sql b/db/update20.2.0.sql index cac3b3f7bb..acc392bf5f 100644 --- a/db/update20.2.0.sql +++ b/db/update20.2.0.sql @@ -18,31 +18,3 @@ CREATE INDEX `idx_repo_deleted` ON `zt_job` (`repo`,`deleted`); UPDATE `zt_workflowfield` SET options = '{"wait":"\\u5f85\\u5ba1\\u6279","doing":"\\u5ba1\\u6279\\u4e2d","pass":"\\u901a\\u8fc7","reject":"\\u4e0d\\u901a\\u8fc7","reverting":"\\u56de\\u9000\\u4e2d"}' WHERE field = 'reviewStatus'; UPDATE `zt_workflowaction` SET `role` = 'buildin' WHERE `buildin` = '1' AND `module` IN ('requirement', 'epic'); - -CREATE TABLE `zt_pivotspec` ( - `pivot` mediumint(8) NOT NULL, - `version` varchar(10) NOT NULL, - `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', - `mode` varchar(10) NOT NULL default 'builder', - `name` text NULL, - `desc` text NULL, - `sql` text NULL, - `fields` text NULL, - `langs` text NULL, - `vars` text NULL, - `objects` text NULL, - `settings` text NULL, - `filters` text NULL, - `createdDate` datetime NULL, - UNIQUE KEY `pivot` (`pivot`, `version`) -) ENGINE = InnoDB DEFAULT CHARSET=utf8; - -ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL AFTER `builtin`; -ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varch(10) NOT NULL DEFAULT 'builder'; -ALTER TABLE `zt_pivot` CHANGE `sql` `sql` text NULL; -ALTER TABLE `zt_pivot` CHANGE `fields` `fields` text NULL; -ALTER TABLE `zt_pivot` CHANGE `langs` `langs` text NULL; -ALTER TABLE `zt_pivot` CHANGE `vars` `vars` text NULL; -ALTER TABLE `zt_pivot` CHANGE `objects` `objects` text NULL; -ALTER TABLE `zt_pivot` CHANGE `settings` `settings` text NULL; -ALTER TABLE `zt_pivot` CHANGE `filters` `filters` text NULL; diff --git a/db/update21.1.sql b/db/update21.1.sql index f97943c568..225c6cbc8c 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -32,3 +32,31 @@ ALTER TABLE zt_dataview MODIFY `fields` text NULL; ALTER TABLE zt_dataview MODIFY `objects` text NULL; ALTER TABLE zt_dataview MODIFY `mode` varchar(50) NOT NULL DEFAULT 'builder'; ALTER TABLE zt_dataview ADD `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql' AFTER `code`; + +CREATE TABLE `zt_pivotspec` ( + `pivot` mediumint(8) NOT NULL, + `version` varchar(10) NOT NULL, + `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', + `mode` varchar(10) NOT NULL default 'builder', + `name` text NULL, + `desc` text NULL, + `sql` text NULL, + `fields` text NULL, + `langs` text NULL, + `vars` text NULL, + `objects` text NULL, + `settings` text NULL, + `filters` text NULL, + `createdDate` datetime NULL, + UNIQUE KEY `pivot` (`pivot`, `version`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL AFTER `builtin`; +ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varch(10) NOT NULL DEFAULT 'builder'; +ALTER TABLE `zt_pivot` CHANGE `sql` `sql` text NULL; +ALTER TABLE `zt_pivot` CHANGE `fields` `fields` text NULL; +ALTER TABLE `zt_pivot` CHANGE `langs` `langs` text NULL; +ALTER TABLE `zt_pivot` CHANGE `vars` `vars` text NULL; +ALTER TABLE `zt_pivot` CHANGE `objects` `objects` text NULL; +ALTER TABLE `zt_pivot` CHANGE `settings` `settings` text NULL; +ALTER TABLE `zt_pivot` CHANGE `filters` `filters` text NULL; From e16d43495151ec5366a579268057a439f8cb695c Mon Sep 17 00:00:00 2001 From: zhouxin Date: Wed, 20 Nov 2024 13:51:38 +0800 Subject: [PATCH 005/300] * [misc] fix spelling. --- db/update21.1.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/update21.1.sql b/db/update21.1.sql index 225c6cbc8c..f7b83cf0be 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -52,7 +52,7 @@ CREATE TABLE `zt_pivotspec` ( ) ENGINE = InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL AFTER `builtin`; -ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varch(10) NOT NULL DEFAULT 'builder'; +ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varchar(10) NOT NULL DEFAULT 'builder'; ALTER TABLE `zt_pivot` CHANGE `sql` `sql` text NULL; ALTER TABLE `zt_pivot` CHANGE `fields` `fields` text NULL; ALTER TABLE `zt_pivot` CHANGE `langs` `langs` text NULL; From 4877392daaee6098b85d4eba795b7c40073b4f0a Mon Sep 17 00:00:00 2001 From: caoyanyi Date: Wed, 20 Nov 2024 14:31:39 +0800 Subject: [PATCH 006/300] * [task#131820,doing,0.2h] add message for deploy. --- module/message/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/message/model.php b/module/message/model.php index 92196c58b3..05627bd702 100755 --- a/module/message/model.php +++ b/module/message/model.php @@ -271,7 +271,7 @@ class messageModel extends model if($toList == 'closed') $toList = ''; if($objectType == 'feedback' && $object->status == 'replied') $toList = ',' . $object->openedBy . ','; - if(strpos(',story,epic,requirement,ticket,review,', ",{$objectType},") !== false && $actionID) + if(strpos(',story,epic,requirement,ticket,review,deploy,', ",{$objectType},") !== false && $actionID) { $action = $this->loadModel('action')->getById($actionID); list($toList, $ccList) = $this->loadModel($objectType)->getToAndCcList($object, $action->action); From d4df867780202b5cbcc6b70b7d6a973d7380dde7 Mon Sep 17 00:00:00 2001 From: caoyanyi Date: Wed, 20 Nov 2024 14:40:22 +0800 Subject: [PATCH 007/300] * [task#131820,doing,0.1h] add message for deploy. --- module/mail/tao.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/mail/tao.php b/module/mail/tao.php index ad132a1332..91310e905f 100644 --- a/module/mail/tao.php +++ b/module/mail/tao.php @@ -237,7 +237,7 @@ class mailTao extends mailModel $objectModel = $this->loadModel($objectType); if(!$objectModel) return false; - if(strpos(',story,meeting,review,', ",{$objectType},") !== false) return $objectModel->getToAndCcList($object, $action->action); + if(strpos(',story,meeting,review,deploy,', ",{$objectType},") !== false) return $objectModel->getToAndCcList($object, $action->action); if($objectType == 'ticket') return $objectModel->getToAndCcList($object, $action); return $objectModel->getToAndCcList($object); } From 52524df08860fe10637c394cac9c3b63f898248b Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 05:43:07 +0000 Subject: [PATCH 008/300] * [story#69519,0.5h] add zt_mark table. --- config/zentaopms.php | 2 ++ db/update21.1.sql | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/config/zentaopms.php b/config/zentaopms.php index 0d99e58d50..aef9fc9a6b 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -464,6 +464,7 @@ define('TABLE_JOB', '`' . $config->db->prefix . 'job`'); define('TABLE_COMPILE', '`' . $config->db->prefix . 'compile`'); define('TABLE_MR', '`' . $config->db->prefix . 'mr`'); define('TABLE_MRAPPROVAL', '`' . $config->db->prefix . 'mrapproval`'); +define('TABLE_MARK', '`' . $config->db->prefix . 'mark`'); define('TABLE_SERVERROOM', '`' . $config->db->prefix . 'serverroom`'); define('TABLE_HOST', '`' . $config->db->prefix . 'host`'); @@ -726,6 +727,7 @@ $config->objectTables['workflowgroup'] = TABLE_WORKFLOWGROUP; $config->objectTables['productline'] = TABLE_MODULE; $config->objectTables['repocommit'] = TABLE_REPOHISTORY; $config->objectTables['system'] = TABLE_SYSTEM; +$config->objectTables['mark'] = TABLE_MARK; $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions', 'aiPrompts', 'promptDesign', 'promptExec'); $config->disabledFeatures = ''; diff --git a/db/update21.1.sql b/db/update21.1.sql index f7b83cf0be..42f8d40a2d 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -60,3 +60,15 @@ ALTER TABLE `zt_pivot` CHANGE `vars` `vars` text NULL; ALTER TABLE `zt_pivot` CHANGE `objects` `objects` text NULL; ALTER TABLE `zt_pivot` CHANGE `settings` `settings` text NULL; ALTER TABLE `zt_pivot` CHANGE `filters` `filters` text NULL; + +CREATE TABLE IF NOT EXISTS `zt_mark` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(50) NOT NULL DEFAULT '', + `objectID` mediuumint(8) unsigned NOT NULL DEFAULT 0, + `version` varchar(50) NOT NULL DEFAULT '', + `account` char(30) NOT NULL DEFAULT '', + `date` date NOT NULL DEFAULT NULL, + `mark` varchar(50) NOT NULL DEFAULT '', + `extra` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; From 3751236c6e0a717992a40d0e9a2b813c0fe00921 Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 06:55:15 +0000 Subject: [PATCH 009/300] * [story#69519,0.5h] fix sql. --- db/update21.1.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db/update21.1.sql b/db/update21.1.sql index 42f8d40a2d..fe480af039 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -33,7 +33,7 @@ ALTER TABLE zt_dataview MODIFY `objects` text NULL; ALTER TABLE zt_dataview MODIFY `mode` varchar(50) NOT NULL DEFAULT 'builder'; ALTER TABLE zt_dataview ADD `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql' AFTER `code`; -CREATE TABLE `zt_pivotspec` ( +CREATE TABLE IF NOT EXISTS `zt_pivotspec` ( `pivot` mediumint(8) NOT NULL, `version` varchar(10) NOT NULL, `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', @@ -47,11 +47,11 @@ CREATE TABLE `zt_pivotspec` ( `objects` text NULL, `settings` text NULL, `filters` text NULL, - `createdDate` datetime NULL, - UNIQUE KEY `pivot` (`pivot`, `version`) + `createdDate` datetime NULL ) ENGINE = InnoDB DEFAULT CHARSET=utf8; +CREATE UNIQUE INDEX `idx_pivot` ON `zt_pivotspec`(`pivot`); -ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL AFTER `builtin`; +ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL DEFAULT '1' AFTER `builtin`; ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varchar(10) NOT NULL DEFAULT 'builder'; ALTER TABLE `zt_pivot` CHANGE `sql` `sql` text NULL; ALTER TABLE `zt_pivot` CHANGE `fields` `fields` text NULL; From ea418b8202e38f082a5ae6bd56087de961a93b99 Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 07:01:43 +0000 Subject: [PATCH 010/300] * [story#69519,0.5h] fix sql. --- db/update21.1.sql | 4 +++- db/zentao.sql | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/db/update21.1.sql b/db/update21.1.sql index fe480af039..05404214eb 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS `zt_pivotspec` ( `filters` text NULL, `createdDate` datetime NULL ) ENGINE = InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `idx_pivot` ON `zt_pivotspec`(`pivot`); +CREATE UNIQUE INDEX `idx_pivot_version` ON `zt_pivotspec`(`pivot`, `version`); ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL DEFAULT '1' AFTER `builtin`; ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varchar(10) NOT NULL DEFAULT 'builder'; @@ -72,3 +72,5 @@ CREATE TABLE IF NOT EXISTS `zt_mark` ( `extra` varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE INDEX `idx_object` ON `zt_mark`(`objectType`,`objectID`); +CREATE INDEX `idx_account` ON `zt_mark`(`account`); diff --git a/db/zentao.sql b/db/zentao.sql index efb30c0040..e887d67b59 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -15381,6 +15381,7 @@ CREATE TABLE IF NOT EXISTS `zt_pivot` ( CREATE INDEX `dimension` ON `zt_pivot` (`dimension`); CREATE INDEX `group` ON `zt_pivot` (`group`); +-- DROP TABLE IF EXISTS `zt_pivotspec`; CREATE TABLE `zt_pivotspec` ( `pivot` mediumint(8) NOT NULL, `version` varchar(10) NOT NULL, @@ -15395,9 +15396,9 @@ CREATE TABLE `zt_pivotspec` ( `objects` text NULL, `settings` text NULL, `filters` text NULL, - `createdDate` datetime NULL, - UNIQUE KEY `pivot` (`pivot`, `version`) + `createdDate` datetime NULL ) ENGINE = InnoDB DEFAULT CHARSET=utf8; +CREATE UNIQUE INDEX `idx_pivot_version` ON `zt_pivotspec`(`pivot`, `version`); -- DROP TABLE IF EXISTS `zt_sqlbuilder`; CREATE TABLE IF NOT EXISTS `zt_sqlbuilder` ( @@ -16124,6 +16125,7 @@ REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, ` ('zh-tw', 'custom', 'relationList', '3', '{\"relation\":\"\\u91cd\\u8907\",\"relativeRelation\":\"\\u91cd\\u8907\"}', '0', 'all'), ('zh-tw', 'custom', 'relationList', '4', '{\"relation\":\"\\u5f15\\u7528\",\"relativeRelation\":\"\\u88ab\\u5f15\\u7528\"}', '0', 'all'); +-- DROP TABLE IF EXISTS `zt_system`; CREATE TABLE IF NOT EXISTS `zt_system` ( `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL DEFAULT '', @@ -16143,3 +16145,18 @@ CREATE TABLE IF NOT EXISTS `zt_system` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE INDEX `idx_product` ON `zt_system`(`product`); CREATE INDEX `idx_status` ON `zt_system`(`status`); + +-- DROP TABLE IF EXISTS `zt_mark`; +CREATE TABLE IF NOT EXISTS `zt_mark` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(50) NOT NULL DEFAULT '', + `objectID` mediuumint(8) unsigned NOT NULL DEFAULT 0, + `version` varchar(50) NOT NULL DEFAULT '', + `account` char(30) NOT NULL DEFAULT '', + `date` date NOT NULL DEFAULT NULL, + `mark` varchar(50) NOT NULL DEFAULT '', + `extra` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE INDEX `idx_object` ON `zt_mark`(`objectType`,`objectID`); +CREATE INDEX `idx_account` ON `zt_mark`(`account`); From 4ee679a8aa4e3937ec7505be33394bfec3f87811 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:24:51 +0800 Subject: [PATCH 011/300] * [task#132857] add create app lang. --- module/system/lang/zh-cn.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/module/system/lang/zh-cn.php b/module/system/lang/zh-cn.php index e41e94fa2c..329ad58731 100644 --- a/module/system/lang/zh-cn.php +++ b/module/system/lang/zh-cn.php @@ -30,10 +30,12 @@ $lang->system->serviceQuantity = '服务数'; $lang->system->cpuUsage = 'CPU(核)'; $lang->system->memUsage = '内存(GB)'; $lang->system->name = '应用名称'; +$lang->system->integrated = '集成应用'; $lang->system->latestRelease = '最新版本号'; $lang->system->children = '包含应用'; $lang->system->latestRelease = '最新版本号'; $lang->system->status = '状态'; +$lang->system->desc = '描述'; $lang->system->browse = '应用列表'; $lang->system->create = '创建应用'; $lang->system->edit = '编辑应用'; @@ -41,6 +43,10 @@ $lang->system->delete = '删除应用'; $lang->system->active = '上架应用'; $lang->system->inactive = '下架应用'; +$lang->system->integratedList = array(); +$lang->system->integratedList[0] = '否'; +$lang->system->integratedList[1] = '是'; + $lang->system->statusList = array(); $lang->system->statusList['active'] = '已上架'; $lang->system->statusList['inactive'] = '已下架'; From 751f2725c5acd667e1721e2bb5dcc4a8d3cc8891 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:24:54 +0800 Subject: [PATCH 012/300] * [task#132857] add create app lang. --- module/system/lang/de.php | 6 ++++++ module/system/lang/en.php | 6 ++++++ module/system/lang/fr.php | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/module/system/lang/de.php b/module/system/lang/de.php index fa5b35a2b3..29d4e65961 100644 --- a/module/system/lang/de.php +++ b/module/system/lang/de.php @@ -30,10 +30,12 @@ $lang->system->serviceQuantity = 'Number of services'; $lang->system->cpuUsage = 'CPU(Core)'; $lang->system->memUsage = 'Memory(GB)'; $lang->system->name = 'Application name'; +$lang->system->integrated = 'Integrated application'; $lang->system->latestRelease = 'Latest version'; $lang->system->children = 'Included applications'; $lang->system->latestRelease = 'Latest version'; $lang->system->status = 'Status'; +$lang->system->desc = 'Description'; $lang->system->browse = 'Application list'; $lang->system->create = 'Create application'; $lang->system->edit = 'Edit application'; @@ -41,6 +43,10 @@ $lang->system->delete = 'Delete application'; $lang->system->active = 'Online application'; $lang->system->inactive = 'Offline application'; +$lang->system->integratedList = array(); +$lang->system->integratedList[0] = 'No'; +$lang->system->integratedList[1] = 'Yes'; + $lang->system->statusList = array(); $lang->system->statusList['active'] = 'Active'; $lang->system->statusList['inactive'] = 'Inactive'; diff --git a/module/system/lang/en.php b/module/system/lang/en.php index fa5b35a2b3..29d4e65961 100644 --- a/module/system/lang/en.php +++ b/module/system/lang/en.php @@ -30,10 +30,12 @@ $lang->system->serviceQuantity = 'Number of services'; $lang->system->cpuUsage = 'CPU(Core)'; $lang->system->memUsage = 'Memory(GB)'; $lang->system->name = 'Application name'; +$lang->system->integrated = 'Integrated application'; $lang->system->latestRelease = 'Latest version'; $lang->system->children = 'Included applications'; $lang->system->latestRelease = 'Latest version'; $lang->system->status = 'Status'; +$lang->system->desc = 'Description'; $lang->system->browse = 'Application list'; $lang->system->create = 'Create application'; $lang->system->edit = 'Edit application'; @@ -41,6 +43,10 @@ $lang->system->delete = 'Delete application'; $lang->system->active = 'Online application'; $lang->system->inactive = 'Offline application'; +$lang->system->integratedList = array(); +$lang->system->integratedList[0] = 'No'; +$lang->system->integratedList[1] = 'Yes'; + $lang->system->statusList = array(); $lang->system->statusList['active'] = 'Active'; $lang->system->statusList['inactive'] = 'Inactive'; diff --git a/module/system/lang/fr.php b/module/system/lang/fr.php index fa5b35a2b3..29d4e65961 100644 --- a/module/system/lang/fr.php +++ b/module/system/lang/fr.php @@ -30,10 +30,12 @@ $lang->system->serviceQuantity = 'Number of services'; $lang->system->cpuUsage = 'CPU(Core)'; $lang->system->memUsage = 'Memory(GB)'; $lang->system->name = 'Application name'; +$lang->system->integrated = 'Integrated application'; $lang->system->latestRelease = 'Latest version'; $lang->system->children = 'Included applications'; $lang->system->latestRelease = 'Latest version'; $lang->system->status = 'Status'; +$lang->system->desc = 'Description'; $lang->system->browse = 'Application list'; $lang->system->create = 'Create application'; $lang->system->edit = 'Edit application'; @@ -41,6 +43,10 @@ $lang->system->delete = 'Delete application'; $lang->system->active = 'Online application'; $lang->system->inactive = 'Offline application'; +$lang->system->integratedList = array(); +$lang->system->integratedList[0] = 'No'; +$lang->system->integratedList[1] = 'Yes'; + $lang->system->statusList = array(); $lang->system->statusList['active'] = 'Active'; $lang->system->statusList['inactive'] = 'Inactive'; From 828384df969f03c0257c3a89d157fa358a802040 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:26:35 +0800 Subject: [PATCH 013/300] * [task#132857] add create app config. --- module/system/config.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/module/system/config.php b/module/system/config.php index 060869a42c..d6b592508c 100644 --- a/module/system/config.php +++ b/module/system/config.php @@ -2,6 +2,9 @@ global $lang; $config->system = new stdclass(); +$config->system->create = new stdclass(); +$config->system->create->requiredFields = 'name'; + $config->system->groupPrivs = array(); $config->system->groupPrivs['dashboard'] = 'backup|index'; $config->system->groupPrivs['deletebackup'] = 'backup|delete'; From 44a82af3da9f396027c54b27d8ac0d00db5b2fe6 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:26:48 +0800 Subject: [PATCH 014/300] * [task#132857] add create app config. --- module/system/config/dtable.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/module/system/config/dtable.php b/module/system/config/dtable.php index 74030c5906..2f76a3ea1b 100644 --- a/module/system/config/dtable.php +++ b/module/system/config/dtable.php @@ -20,15 +20,17 @@ $config->system->dtable->fieldList['latestRelease']['title'] = $lang->system->la $config->system->dtable->fieldList['latestRelease']['name'] = 'latestRelease'; $config->system->dtable->fieldList['latestRelease']['type'] = 'text'; -$config->system->dtable->fieldList['children']['title'] = $lang->system->children; -$config->system->dtable->fieldList['children']['name'] = 'children'; -$config->system->dtable->fieldList['children']['type'] = 'text'; +$config->system->dtable->fieldList['children']['title'] = $lang->system->children; +$config->system->dtable->fieldList['children']['name'] = 'children'; +$config->system->dtable->fieldList['children']['type'] = 'text'; +$config->system->dtable->fieldList['children']['delimiter'] = ','; -$config->system->dtable->fieldList['status']['title'] = $lang->system->status; -$config->system->dtable->fieldList['status']['name'] = 'status'; -$config->system->dtable->fieldList['status']['type'] = 'status'; -$config->system->dtable->fieldList['status']['map'] = $lang->system->statusList; -$config->system->dtable->fieldList['status']['sortType'] = true; +$config->system->dtable->fieldList['status']['title'] = $lang->system->status; +$config->system->dtable->fieldList['status']['name'] = 'status'; +$config->system->dtable->fieldList['status']['type'] = 'status'; +$config->system->dtable->fieldList['status']['statusMap'] = $lang->system->statusList; +$config->system->dtable->fieldList['status']['width'] = 100; +$config->system->dtable->fieldList['status']['sortType'] = true; $config->system->dtable->fieldList['actions']['name'] = 'actions'; $config->system->dtable->fieldList['actions']['title'] = $lang->actions; From d4f068ae0d47e75ed4610eb9519c978b2719d76b Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:27:10 +0800 Subject: [PATCH 015/300] * [task#132857] add create app config. --- module/system/config/form.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/module/system/config/form.php b/module/system/config/form.php index df5d69ecb8..2ae2d9985c 100644 --- a/module/system/config/form.php +++ b/module/system/config/form.php @@ -3,3 +3,9 @@ $config->system->form = new stdclass(); $config->system->form->editDomain['customDomain'] = array('type' => 'string', 'required' => true, 'default' => ''); $config->system->form->editDomain['certPem'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->system->form->editDomain['certKey'] = array('type' => 'string', 'required' => false, 'default' => ''); + +$config->system->form->create['integrated'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->system->form->create['name'] = array('type' => 'string', 'required' => true, 'filter' => 'trim'); +$config->system->form->create['children'] = array('type' => 'array', 'required' => false, 'default' => array(), 'filter' => 'join'); +$config->system->form->create['desc'] = array('type' => 'string', 'required' => false, 'default' => '', 'filter' => 'trim'); +$config->system->form->create['createdDate'] = array('type' => 'datetime', 'required' => false, 'default' => helper::now()); From 39334803f2c7c7911eb419a8aa8ce3f4f5d6afc3 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:27:45 +0800 Subject: [PATCH 016/300] * [task#132856] fix app list. --- module/system/ui/browse.html.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/module/system/ui/browse.html.php b/module/system/ui/browse.html.php index 848b8fb48b..91476d96ee 100644 --- a/module/system/ui/browse.html.php +++ b/module/system/ui/browse.html.php @@ -11,9 +11,10 @@ declare(strict_types=1); namespace zin; $canCreate = hasPriv('system', 'create'); -$createLink = $this->createLink('system', 'create'); +$createLink = $this->createLink('system', 'create', 'productID=' . $productID); $createItem = array('text' => $lang->system->create, 'url' => $createLink, 'class' => 'primary', 'icon' => 'plus', 'data-toggle' => 'modal'); +$config->system->dtable->fieldList['children']['map'] = $appPairs; $tableData = initTableData($appList, $config->system->dtable->fieldList, $this->system); featureBar @@ -35,7 +36,7 @@ dtable ( set::cols($config->system->dtable->fieldList), set::data($tableData), - set::sortLink(createLink('system', 'browse', "orderBy={name}_{sortType}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}")), + set::sortLink(createLink('system', 'browse', "productID={$productID}&branch={$branch}&orderBy={name}_{sortType}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}")), set::orderBy($orderBy), set::footPager(usePager()) ); From 77c8186555c4c912f53898f9cd49af762895cd4f Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:29:23 +0800 Subject: [PATCH 017/300] * [task#132857] add create control method. --- module/system/control.php | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/module/system/control.php b/module/system/control.php index 095679ad0f..6c95057bab 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -542,10 +542,30 @@ class system extends control $this->app->loadClass('pager', true); $pager = pager::init($recTotal, $recPerPage, $pageID); - $this->view->title = $this->lang->system->browse; - $this->view->appList = $this->system->getList($orderBy, $pager); - $this->view->orderBy = $orderBy; - $this->view->pager = $pager; + $systems = $this->system->getList($orderBy, $pager); + foreach($systems as &$system) + { + $system->latestRelease = $system->latestRelease ? $system->latestRelease : ''; + } + + $this->view->title = $this->lang->system->browse; + $this->view->productID = $productID; + $this->view->appList = $systems; + $this->view->appPairs = $this->system->getPairs(); + $this->view->orderBy = $orderBy; + $this->view->pager = $pager; $this->display(); } + + /** + * 创建应用。 + * Create application. + * + * @param int $productID + * @access public + * @return void + */ + public function create($productID) + { + } } From 84d9e3d4978f24a4d5fa1421f114cb0b29746fe8 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:29:58 +0800 Subject: [PATCH 018/300] * [task#132857] add create control method. --- module/system/control.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/system/control.php b/module/system/control.php index 6c95057bab..a9c6b4bbfe 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -567,5 +567,25 @@ class system extends control */ public function create($productID) { + if($_POST) + { + $integrated = $this->post->integrated; + if($integrated) $this->config->system->create->requiredFields .= ',children'; + + $formData = form::data($this->config->system->form->create) + ->setDefault('product', $productID) + ->setDefault('status', 'active') + ->setDefault('createdBy', $this->app->user->account) + ->get(); + $systemID = $this->system->create($formData); + if(dao::isError()) return $this->sendError(dao::getError()); + + if($systemID) $this->loadModel('action')->create('system', $systemID, 'created'); + $this->sendSuccess(array('load' => true)); + } + + $this->view->title = $this->lang->system->create; + $this->view->systemList = $this->system->getPairs('0'); + $this->display(); } } From 31c028068a13d77e0040b7f8ec45283f21884353 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:30:05 +0800 Subject: [PATCH 019/300] * [task#132857] add create control method. --- module/system/control.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/system/control.php b/module/system/control.php index a9c6b4bbfe..7320876721 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -577,6 +577,7 @@ class system extends control ->setDefault('status', 'active') ->setDefault('createdBy', $this->app->user->account) ->get(); + $systemID = $this->system->create($formData); if(dao::isError()) return $this->sendError(dao::getError()); From 2cc09da276a0fdad040f00c007bd2da3f44e2a5e Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:31:37 +0800 Subject: [PATCH 020/300] * [task#132857] add get pairs method. --- module/system/model.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/module/system/model.php b/module/system/model.php index 2071e677ea..5d9bd994f0 100644 --- a/module/system/model.php +++ b/module/system/model.php @@ -31,6 +31,23 @@ class systemModel extends model ->fetchAll(); } + /** + * 获取应用键值对。 + * Get app pairs. + * + * @param string $integrated + * @access public + * @return array + */ + public function getPairs(string $integrated = ''): array + { + return $this->dao->select('id, name')->from(TABLE_SYSTEM) + ->where('deleted')->eq('0') + ->beginIF($integrated)->andWhere('integrated')->eq($integrated)->fi() + ->fetchPairs('id', 'name'); + } + + /** * 获取自定义的域名设置。 * Get customized domain settings. From d44f067646fd7d30fb4c00f0be42c0efe776d125 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:31:58 +0800 Subject: [PATCH 021/300] * [task#132857] add create method. --- module/system/model.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/module/system/model.php b/module/system/model.php index 5d9bd994f0..f34d064005 100644 --- a/module/system/model.php +++ b/module/system/model.php @@ -47,6 +47,25 @@ class systemModel extends model ->fetchPairs('id', 'name'); } + /** + * 创建应用。 + * Create an app. + * + * @param object $formData + * @access public + * @return bool|int + */ + public function create(object $formData): bool|int + { + $this->dao->insert(TABLE_SYSTEM)->data($formData) + ->check('name', 'unique') + ->batchCheck($this->config->system->create->requiredFields, 'notempty') + ->autoCheck() + ->exec(); + + if(dao::isError()) return false; + return $this->dao->lastInsertID(); + } /** * 获取自定义的域名设置。 From 3304c57b7d48ac4fbc3cb40b94e23d84ea750e66 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:33:00 +0800 Subject: [PATCH 022/300] + [task#132857] add create page. --- module/system/ui/create.html.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 module/system/ui/create.html.php diff --git a/module/system/ui/create.html.php b/module/system/ui/create.html.php new file mode 100644 index 0000000000..985f54a457 --- /dev/null +++ b/module/system/ui/create.html.php @@ -0,0 +1,18 @@ + + * @package system + * @link https://www.zentao.net + */ +namespace zin; + +formPanel +( + set::id('systemCreateForm'), + set::title($lang->system->create), + set::submitBtnText($lang->save), +); From d76879b34cd90183c47a0acf6ee40b4f8ad15ac9 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:33:16 +0800 Subject: [PATCH 023/300] + [task#132857] add create page. --- module/system/ui/create.html.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/module/system/ui/create.html.php b/module/system/ui/create.html.php index 985f54a457..2fd9fe7ece 100644 --- a/module/system/ui/create.html.php +++ b/module/system/ui/create.html.php @@ -15,4 +15,23 @@ formPanel set::id('systemCreateForm'), set::title($lang->system->create), set::submitBtnText($lang->save), + formGroup + ( + set::name('integrated'), + set::width('1/2'), + set::label($lang->system->integrated), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::control('radioListInline'), + set::value(0), + set::items($lang->system->integratedList), + on::change()->do('$("#children").toggleClass("hidden");') + ), + formGroup + ( + set::name('name'), + set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::label($lang->system->name), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::required(true) + ), ); From e45ce4a53d764522a9b6c774873d05628c257ee4 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 14:33:22 +0800 Subject: [PATCH 024/300] + [task#132857] add create page. --- module/system/ui/create.html.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/system/ui/create.html.php b/module/system/ui/create.html.php index 2fd9fe7ece..93f1d1a8ed 100644 --- a/module/system/ui/create.html.php +++ b/module/system/ui/create.html.php @@ -34,4 +34,24 @@ formPanel set::labelWidth(common::checkNotCN() ? '160px' : '100px'), set::required(true) ), + formGroup + ( + setID('children'), + setClass('hidden'), + set::name('children'), + set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::required(true), + set::label($lang->system->children), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::control('picker'), + set::items($systemList), + set::multiple(true) + ), + formGroup + ( + set::name('desc'), + set::label($lang->system->desc), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::control(array('type' => 'textarea', 'rows' => '4')) + ) ); From 26ee7435839e6377be04afe53a1abae2a4146fbe Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:16:58 +0800 Subject: [PATCH 025/300] * [task#132856] fix system test yaml. --- test/data/system.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/data/system.yaml b/test/data/system.yaml index bbcce2514d..d3cb285db8 100644 --- a/test/data/system.yaml +++ b/test/data/system.yaml @@ -10,13 +10,13 @@ fields: - field: product range: 1-100 - field: integrated - range: 0 + range: 0-1 - field: latestRelease range: 0 - field: latestDate range: [] - field: children - range: [] + range: "[],1" - field: status range: active - field: desc From 5696df42ab2488941354f945d33722250484fef0 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:18:02 +0800 Subject: [PATCH 026/300] * [task#132856] add getPairs method unit test. --- module/system/test/model/getpairs.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 module/system/test/model/getpairs.php diff --git a/module/system/test/model/getpairs.php b/module/system/test/model/getpairs.php new file mode 100755 index 0000000000..52aff8c6f5 --- /dev/null +++ b/module/system/test/model/getpairs.php @@ -0,0 +1,20 @@ +#!/usr/bin/env php +gen(10); + +/** +title=测试 systemModel::getPairs(); +timeout=0 +cid=1 + +*/ +global $tester; +$system = $tester->loadModel('system'); + +r($system->getPairs()) && p('1') && e('应用1'); // 查询默认键值对 +r(count($system->getPairs())) && p() && e('10'); // 查询默认键值对数量 +r($system->getPairs('0')) && p('1') && e('应用1'); // 查询非集成应用 +r($system->getPairs('1')) && p('2') && e('应用2'); // 查询集成应用 +r(count($system->getPairs('1'))) && p() && e('5'); // 查询集成应用数量 From 64422e63d7e9533f320e28bafacb7037bb4e2ca5 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:18:59 +0800 Subject: [PATCH 027/300] * [task#132857] add create method unit test. --- module/system/test/model/create.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 module/system/test/model/create.php diff --git a/module/system/test/model/create.php b/module/system/test/model/create.php new file mode 100755 index 0000000000..69cfbefdfa --- /dev/null +++ b/module/system/test/model/create.php @@ -0,0 +1,20 @@ +#!/usr/bin/env php +gen(0); + +/** + +title=测试 systemModel::create(); +timeout=0 +cid=1 + + +*/ +global $tester; +$system = $tester->loadModel('system'); +$default = new stdclass(); +$default->name = '应用10'; +$default->product = 1; +$default->integrated = 0; From b737db46353783e8fb62a4a97b41890d95c2be93 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:19:15 +0800 Subject: [PATCH 028/300] * [task#132857] add create method unit test. --- module/system/test/model/create.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/module/system/test/model/create.php b/module/system/test/model/create.php index 69cfbefdfa..dff043564d 100755 --- a/module/system/test/model/create.php +++ b/module/system/test/model/create.php @@ -14,7 +14,23 @@ cid=1 */ global $tester; $system = $tester->loadModel('system'); + $default = new stdclass(); $default->name = '应用10'; $default->product = 1; $default->integrated = 0; +$default->children = '1'; +$default->status = 'active'; +$default->desc = '应用描述'; +$default->createdBy = 'admin'; +$default->createdDate = '2024-01-01 00:00:00'; + +r($system->create($default)) && p() && e('1'); // 查询默认排序应用 +$system->create($default); +r(dao::getError()) && p('name:0') && e('『应用名称』已经有『应用10』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。'); // 查询默认排序应用 +$default->name = ''; +$system->create($default); +r(dao::getError()) && p('name:0') && e('『应用名称』不能为空。'); +$default->name = '应用11'; +$default->product = '字符串'; +$system->create($default); From 4addb0b835ce9e4d0e9d8b30be5921a0874334b8 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:19:21 +0800 Subject: [PATCH 029/300] * [task#132857] add create method unit test. --- module/system/test/model/create.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/system/test/model/create.php b/module/system/test/model/create.php index dff043564d..70914c6dc9 100755 --- a/module/system/test/model/create.php +++ b/module/system/test/model/create.php @@ -34,3 +34,8 @@ r(dao::getError()) && p('name:0') && e('『应用名称』不能为空。'); $default->name = '应用11'; $default->product = '字符串'; $system->create($default); +r(dao::getError()) && p('product:0') && e('『product』应当是数字。'); +$default->product = 1; +$default->status = 'test'; +$system->create($default); +r(dao::getError()) && p('status:0') && e('『状态』不符合格式,应当为:『/active|inactive/』。'); From 31314b6c62ed930096ad810ee8c661e0954df468 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:26:48 +0800 Subject: [PATCH 030/300] * [task#132857] add create method unit test. --- module/system/test/model/create.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/module/system/test/model/create.php b/module/system/test/model/create.php index 70914c6dc9..99b7e3bae5 100755 --- a/module/system/test/model/create.php +++ b/module/system/test/model/create.php @@ -10,7 +10,11 @@ title=测试 systemModel::create(); timeout=0 cid=1 - +- 查询默认排序应用 @1 +- 查询默认排序应用第name条的0属性 @『应用名称』已经有『应用10』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。 +- 执行第name条的0属性 @『应用名称』不能为空。 +- 执行第product条的0属性 @『product』应当是数字。 +- 执行第status条的0属性 @『状态』不符合格式,应当为:『/active|inactive/』。 */ global $tester; $system = $tester->loadModel('system'); From af805b23e985a3af30dabe93b95deaf12b38e74a Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:27:05 +0800 Subject: [PATCH 031/300] * [task#132857] add getList method unit test. --- module/system/test/model/getlist.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/system/test/model/getlist.php b/module/system/test/model/getlist.php index 610f65a401..f98b571511 100755 --- a/module/system/test/model/getlist.php +++ b/module/system/test/model/getlist.php @@ -10,6 +10,11 @@ title=测试 systemModel::getList(); timeout=0 cid=1 +- 查询默认排序应用第0条的name属性 @应用10 +- 获取所有应用数量 @10 +- 查询默认排序应用下的状态第0条的status属性 @active +- 查询ID正序排序应用第0条的name属性 @应用1 +- 查询状态倒序排序应用第0条的name属性 @应用1 */ global $tester; From 98b1784c97a4f77d065d68f518b10f210955bda8 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:27:19 +0800 Subject: [PATCH 032/300] * [task#132857] add getPairs method unit test. --- module/system/test/model/getpairs.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/module/system/test/model/getpairs.php b/module/system/test/model/getpairs.php index 52aff8c6f5..a670569428 100755 --- a/module/system/test/model/getpairs.php +++ b/module/system/test/model/getpairs.php @@ -9,6 +9,12 @@ title=测试 systemModel::getPairs(); timeout=0 cid=1 +- 查询默认键值对属性1 @应用1 +- 查询默认键值对数量 @10 +- 查询非集成应用属性1 @应用1 +- 查询集成应用属性2 @应用2 +- 查询集成应用数量 @5 + */ global $tester; $system = $tester->loadModel('system'); From c5e6ad3fccc3a97b1b41d1e3a2d6c92a217f9748 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:31:55 +0800 Subject: [PATCH 033/300] * [task#132857] add getPairs method unit test. --- db/zentao.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zentao.sql b/db/zentao.sql index e887d67b59..c495283791 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -16150,7 +16150,7 @@ CREATE INDEX `idx_status` ON `zt_system`(`status`); CREATE TABLE IF NOT EXISTS `zt_mark` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `objectType` varchar(50) NOT NULL DEFAULT '', - `objectID` mediuumint(8) unsigned NOT NULL DEFAULT 0, + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, `version` varchar(50) NOT NULL DEFAULT '', `account` char(30) NOT NULL DEFAULT '', `date` date NOT NULL DEFAULT NULL, From e9548b0b9e555d52e858237669bf381ceee7ef16 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:38:55 +0800 Subject: [PATCH 034/300] * [task#132857] add getPairs method unit test. --- db/update21.1.sql | 4 ++-- db/zentao.sql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/update21.1.sql b/db/update21.1.sql index 05404214eb..d210a228bd 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -64,10 +64,10 @@ ALTER TABLE `zt_pivot` CHANGE `filters` `filters` text NULL; CREATE TABLE IF NOT EXISTS `zt_mark` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `objectType` varchar(50) NOT NULL DEFAULT '', - `objectID` mediuumint(8) unsigned NOT NULL DEFAULT 0, + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, `version` varchar(50) NOT NULL DEFAULT '', `account` char(30) NOT NULL DEFAULT '', - `date` date NOT NULL DEFAULT NULL, + `date` datetime NULL, `mark` varchar(50) NOT NULL DEFAULT '', `extra` varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`) diff --git a/db/zentao.sql b/db/zentao.sql index c495283791..1cbd64d773 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -16153,7 +16153,7 @@ CREATE TABLE IF NOT EXISTS `zt_mark` ( `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, `version` varchar(50) NOT NULL DEFAULT '', `account` char(30) NOT NULL DEFAULT '', - `date` date NOT NULL DEFAULT NULL, + `date` datetime NULL, `mark` varchar(50) NOT NULL DEFAULT '', `extra` varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`) From 43cba5fad233d6712c9998a8e06208ef76ea0215 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 09:13:39 +0800 Subject: [PATCH 035/300] + [perf] add configuration of redis. --- config/redis.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 config/redis.php diff --git a/config/redis.php b/config/redis.php new file mode 100644 index 0000000000..4de623df87 --- /dev/null +++ b/config/redis.php @@ -0,0 +1,25 @@ +redis = new stdClass(); +$config->redis->host = '127.0.0.1'; +$config->redis->port = 6379; +$config->redis->timeout = 10; +$config->redis->username = null; +$config->redis->password = null; + +$config->redis->tables = []; +$config->redis->tables[TABLE_PROJECT] = new stdClass(); +$config->redis->tables[TABLE_PROJECT]->key = 'id'; +$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'raw', 'name' => 'execution']; +$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'programIdList', 'condition' => "`type` = 'program'"]; +$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'projectIdList', 'condition' => "`type` = 'project'"]; +$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'executionIdList', 'condition' => "`type` IN ('sprint', 'stage', 'kanban')"]; + +$config->redis->tables[TABLE_PRODUCT] = new stdClass(); +$config->redis->tables[TABLE_PRODUCT]->key = 'id'; +$config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'raw', 'name' => 'product']; +$config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'raw', 'name' => 'productIdList']; + +$config->redis->tables[TABLE_USER] = new stdClass(); +$config->redis->tables[TABLE_USER]->key = 'account'; +$config->redis->tables[TABLE_USER]->caches[] = ['type' => 'raw', 'name' => 'user']; +$config->redis->tables[TABLE_USER]->caches[] = ['type' => 'raw', 'name' => 'userAccountList']; From a3927838fdd61b176487d5a35c1ed932578208b2 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 09:14:13 +0800 Subject: [PATCH 036/300] * [perf] load configuration file of redis. --- config/config.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/config.php b/config/config.php index 7e4c05d0cc..84d12df1f7 100644 --- a/config/config.php +++ b/config/config.php @@ -246,6 +246,10 @@ if(file_exists($actionsMapConfig)) include $actionsMapConfig; $routesConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'routes.php'; if(file_exists($routesConfig)) include $routesConfig; +/* 引用 Redis 的配置。 Include the redis config file. */ +$redisConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'redis.php'; +if(file_exists($redisConfig)) include $redisConfig; + /* Include extension config files. */ $extConfigFiles = glob(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ext/*.php'); if($extConfigFiles) foreach($extConfigFiles as $extConfigFile) include $extConfigFile; From f12f4caf373761ddec3daa6b6ce6767161163193 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 09:14:36 +0800 Subject: [PATCH 037/300] - [perf] remove unused code. --- config/cache.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/cache.php b/config/cache.php index c59749cd5c..3024c8700a 100644 --- a/config/cache.php +++ b/config/cache.php @@ -39,5 +39,3 @@ $config->cache->dao->driver = 'Apcu'; // 缓存驱动,可以为 $config->cache->client = new stdClass(); $config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not. - -$config->redis = null; \ No newline at end of file From 46dbb9dff6101d7d0d7ad83ceb944db19493eb70 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 09:21:15 +0800 Subject: [PATCH 038/300] * [perf] replace the global $config with $config property of baseRouter class. --- framework/base/router.class.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 97b55ae3c0..b836fc92c5 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3520,9 +3520,7 @@ class baseRouter { if(!extension_loaded('redis')) return false; - global $config; - - if(empty($config->redis)) return false; + if(empty($this->config->redis)) return false; try { @@ -3531,12 +3529,12 @@ class baseRouter $version = phpversion('redis'); if(version_compare($version, '5.3.0', 'ge')) { - $redis->connect($config->redis->host , $config->redis->port, $config->redis->timeout, '', 0, 0, ['auth' => [$config->redis->username, $config->redis->password]]); + $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0, ['auth' => [$this->config->redis->username, $this->config->redis->password]]); } else { - $redis->connect($config->redis->host , $config->redis->port, $config->redis->timeout, '', 0, 0); - $redis->auth(['pass' => $config->redis->password]); + $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); + $redis->auth(['pass' => $this->config->redis->password]); } if(!$redis->ping()) return false; From d33ec696d23509af733d37bcad0cac0d3d21497d Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 09:22:41 +0800 Subject: [PATCH 039/300] + [perf] load data from database to redis. --- framework/base/router.class.php | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index b836fc92c5..43d8fd6c94 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3538,6 +3538,8 @@ class baseRouter } if(!$redis->ping()) return false; + + $this->loadToRedis($redis); } catch(RedisException $e) { @@ -3546,6 +3548,40 @@ class baseRouter if($redis) return $redis; } + + /** + * 把数据载入 redis。 + * Load data to redis. + * + * @param Redis $redis + * @access public + * @return void + */ + public function loadToRedis($redis) + { + $redis->flushDB(); + foreach($this->config->redis->tables as $table => $setting) + { + foreach($setting->caches as $cache) + { + $cache = (object)$cache; + $condition = isset($cache->condition) ? $cache->condition : ''; + $objects = $this->dao->select('*')->from($table)->beginIF($condition)->where($condition)->fi()->fetchAll(); + if($cache->type == 'raw') + { + $pairs = []; + foreach($objects as $object) $pairs[$cache->type . ':' . $cache->name . ':' . $object->{$setting->key}] = json_encode($object); + $redis->mset($pairs); + } + if($cache->type == 'set') + { + $members = []; + foreach($objects as $object) $members[] = $object->{$setting->key}; + $redis->sadd($cache->type . ':' . $cache->name, $members); + } + } + } + } } /** From e24949ed41faedc6ee665dcfa3e0b4aea279d9cd Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 13:12:23 +0800 Subject: [PATCH 040/300] * [perf] fix type error. --- config/redis.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/redis.php b/config/redis.php index 4de623df87..e674821949 100644 --- a/config/redis.php +++ b/config/redis.php @@ -17,9 +17,9 @@ $config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'e $config->redis->tables[TABLE_PRODUCT] = new stdClass(); $config->redis->tables[TABLE_PRODUCT]->key = 'id'; $config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'raw', 'name' => 'product']; -$config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'raw', 'name' => 'productIdList']; +$config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'set', 'name' => 'productIdList']; $config->redis->tables[TABLE_USER] = new stdClass(); $config->redis->tables[TABLE_USER]->key = 'account'; $config->redis->tables[TABLE_USER]->caches[] = ['type' => 'raw', 'name' => 'user']; -$config->redis->tables[TABLE_USER]->caches[] = ['type' => 'raw', 'name' => 'userAccountList']; +$config->redis->tables[TABLE_USER]->caches[] = ['type' => 'set', 'name' => 'userAccountList']; From a2a7aacca984a8b60f5fb35562fe3e0121a53b53 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 13:14:41 +0800 Subject: [PATCH 041/300] - [perf] remove unused code to flush redis. --- framework/base/router.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 43d8fd6c94..76cfdd5d45 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3559,7 +3559,6 @@ class baseRouter */ public function loadToRedis($redis) { - $redis->flushDB(); foreach($this->config->redis->tables as $table => $setting) { foreach($setting->caches as $cache) From aba9669e6e653e0309cc1341327fb2604f98d0d8 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 13:18:58 +0800 Subject: [PATCH 042/300] * [perf] fix error of add members to redis. --- framework/base/router.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 76cfdd5d45..2eeeb25878 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3576,7 +3576,7 @@ class baseRouter { $members = []; foreach($objects as $object) $members[] = $object->{$setting->key}; - $redis->sadd($cache->type . ':' . $cache->name, $members); + $redis->sadd($cache->type . ':' . $cache->name, ...$members); } } } From a381015419c094120c174b49e1895b2a21c083c5 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 14:42:05 +0800 Subject: [PATCH 043/300] + [perf] enable redis in develop environment. --- config/redis.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/redis.php b/config/redis.php index e674821949..698387bd14 100644 --- a/config/redis.php +++ b/config/redis.php @@ -1,5 +1,6 @@ redis = new stdClass(); +$config->redis->enable = true; $config->redis->host = '127.0.0.1'; $config->redis->port = 6379; $config->redis->timeout = 10; From 0f2418199ac05f94d9e93170fd81df4044a41f2c Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 14:44:05 +0800 Subject: [PATCH 044/300] + [perf] add library of redis. --- lib/base/dao/dao.class.php | 7 +- lib/zredis/zredis.class.php | 186 ++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 lib/zredis/zredis.class.php diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 3754442b06..468473531c 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -1047,16 +1047,21 @@ class baseDAO /* Real-time save log. */ if(dao::$realTimeLog && dao::$realTimeFile) file_put_contents(dao::$realTimeFile, $sql . "\n", FILE_APPEND); - $table = trim($this->table, '`'); + $table = trim($this->table, '`'); + $method = $this->method; $this->reset(); /* Force to query from master db, if db has been changed. */ $this->slaveDBH = false; + $this->app->redis->prepareSync($table, $method, $sql); + $result = $this->dbh->exec($sql); /* See: https://www.php.net/manual/en/pdo.lastinsertid.php .*/ $this->_lastInsertID = $this->dbh->lastInsertId(); + if($result) $this->app->redis->sync(); + $this->setTableCache($sql); if($this->config->enableDuckdb) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php new file mode 100644 index 0000000000..5d856d7404 --- /dev/null +++ b/lib/zredis/zredis.class.php @@ -0,0 +1,186 @@ +app = $app; + $this->config = $config; + $this->dao = $dao; + + if(empty($this->config->redis->enable)) helper::end('Redis is not enabled in the configuration file.'); + + try + { + $this->redis = new Redis(); + + $version = phpversion('redis'); + if(version_compare($version, '5.3.0', 'ge')) + { + $this->redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0, ['auth' => [$this->config->redis->username, $this->config->redis->password]]); + } + else + { + $this->redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); + $this->redis->auth(['pass' => $this->config->redis->password]); + } + + if(!$this->redis->ping()) helper::end('Can not connect to Redis server.'); + + if(!$this->redis->exists('inited')) $this->init(); + + return $this; + } + catch(RedisException $e) + { + $this->app->triggerError($e->getMessage(), __FILE__, __LINE__, true); + } + } + + public function init() + { + if(empty($this->redis)) helper::end('Redis is not initialized.'); + + $this->redis->multi(); + + foreach($this->config->redis->tables as $table => $setting) + { + foreach($setting->caches as $cache) + { + $cache = (object)$cache; + $condition = isset($cache->condition) ? $cache->condition : ''; + $objects = $this->dao->select('*')->from($table)->beginIF($condition)->where($condition)->fi()->fetchAll(); + if($cache->type == 'raw') + { + $pairs = []; + foreach($objects as $object) $pairs["raw:{$cache->name}:{$object->{$setting->key}}"] = json_encode($object); + $this->redis->mset($pairs); + } + if($cache->type == 'set') + { + $members = []; + foreach($objects as $object) $members[] = $object->{$setting->key}; + $this->redis->sadd("set:{$cache->name}", ...$members); + } + } + } + + $this->redis->set('inited', date('Y-m-d H:i:s')); + + $this->redis->exec(); + } + + public function prepareSync($table, $event, $sql) + { + $this->event = $event; + $this->table = $table; + $this->sql = $sql; + + if($event = 'delete') + { + $condition = ''; + $idList = []; + if($method == 'update' || $method == 'delete') + { + $whereLen = strlen(DAO::WHERE); + $wherePOS = strrpos($sql, DAO::WHERE); // The position of WHERE keyword. + $groupPOS = strrpos($sql, DAO::GROUPBY); // The position of GROUP BY keyword. + $havingPOS = strrpos($sql, DAO::HAVING); // The position of HAVING keyword. + $orderPOS = strrpos($sql, DAO::ORDERBY); // The position of ORDERBY keyword. + $limitPOS = strrpos($sql, DAO::LIMIT); // The position of LIMIT keyword. + $splitPOS = $orderPOS ? $orderPOS : $limitPOS; // If $orderPOS, use it instead of $limitPOS. + $splitPOS = $havingPOS ? $havingPOS : $splitPOS; // If $havingPOS, use it instead of $orderPOS. + $splitPOS = $groupPOS ? $groupPOS : $splitPOS; // If $groupPOS, use it instead of $havingPOS. + $condition = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); + + if($method == 'delete') $idList = $this->dao->select('id')->from($table)->where($condition)->fetchPairs(); + } + + $this->conditions = $condition; + $this->idList = $idList; + } + } + + public function sync() + { + } + + public function update($table) + { + } + + public function delete($table, $idList) + { + if(empty($this->redis)) helper::end('Redis is not initialized.'); + if(empty($table)) helper::end('Table name is required.'); + if(empty($idList)) helper::end('ID list is required.'); + if(empty($this->config->redis->tables[$table]->caches)) helper::end('No cache settings for table ' . $table); + + foreach($this->config->redis->tables[$table]->caches as $cache) + { + if($cache->type == 'raw') + { + $keys = []; + foreach($idList as $id) $keys[] = "raw:{$cache->name}:{$id}"; + $this->redis->del($keys); + } + if($cache->type == 'set') + { + $this->redis->srem("set:{$cache->name}", ...$idList); + } + } + } + + public function reset() + { + $this->event = ''; + $this->tabel = ''; + $this->sql = ''; + $this->condition = ''; + $this->idList = ''; + } + + public function __call($method, $args) + { + if(empty($this->redis)) helper::end('Redis is not initialized.'); + + $this->redis->$method(...$args); + } +} From 7df002e23da5e8522f385551212d43cb7d7a2fc5 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 14:44:54 +0800 Subject: [PATCH 045/300] - [perf] remove unused code of redis. --- framework/control.class.php | 4 ---- framework/model.class.php | 9 --------- 2 files changed, 13 deletions(-) diff --git a/framework/control.class.php b/framework/control.class.php index 64c3b83f90..89b4f0a73e 100644 --- a/framework/control.class.php +++ b/framework/control.class.php @@ -20,8 +20,6 @@ include __DIR__ . '/base/control.class.php'; class control extends baseControl { - public $redis = false; - /** * Check requiredFields and set exportFields for workflow. * @@ -37,8 +35,6 @@ class control extends baseControl $this->app->setOpenApp(); - if($this->config->redis) $this->redis = $this->app->redis; - if($this->config->edition == 'open') return false; /* Code for task #9224. Set requiredFields for workflow. */ diff --git a/framework/model.class.php b/framework/model.class.php index fc17aa518e..626b0d1678 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -20,15 +20,6 @@ include __DIR__ . '/base/model.class.php'; class model extends baseModel { - public $redis = false; - - public function __construct($appName = '') - { - parent::__construct($appName); - - if($this->config->redis) $this->redis = $this->app->redis; - } - /** * 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。 * 调用父类的loadModel方法来避免这个错误。 From 445f995a26a362d5b67b47fc950f2e3d478a397a Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 14:45:35 +0800 Subject: [PATCH 046/300] * [perf] connect and initlize redis in zredis class. --- framework/base/router.class.php | 78 +++------------------------------ 1 file changed, 5 insertions(+), 73 deletions(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 2eeeb25878..c1d30b0783 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -487,7 +487,11 @@ class baseRouter if($this->config->framework->autoConnectDB) $this->connectDB(); - if($this->config->redis) $this->redis = $this->connectToRedis(); + if($this->config->redis->enable) + { + $this->loadClass('zredis', true); + $this->redis = new zredis($this->dao); + } $this->setupProfiling(); $this->setupXhprof(); @@ -3509,78 +3513,6 @@ class baseRouter return $installed; } - - /** - * Connect to Redis Server. - * @link https://pecl.php.net/package/redis - * @link https://github.com/phpredis/phpredis/ - * @return Redis|object|false - */ - public function connectToRedis() - { - if(!extension_loaded('redis')) return false; - - if(empty($this->config->redis)) return false; - - try - { - $redis = new Redis(); - - $version = phpversion('redis'); - if(version_compare($version, '5.3.0', 'ge')) - { - $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0, ['auth' => [$this->config->redis->username, $this->config->redis->password]]); - } - else - { - $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); - $redis->auth(['pass' => $this->config->redis->password]); - } - - if(!$redis->ping()) return false; - - $this->loadToRedis($redis); - } - catch(RedisException $e) - { - $this->triggerError($e->getMessage(), __FILE__, __LINE__, true); - } - - if($redis) return $redis; - } - - /** - * 把数据载入 redis。 - * Load data to redis. - * - * @param Redis $redis - * @access public - * @return void - */ - public function loadToRedis($redis) - { - foreach($this->config->redis->tables as $table => $setting) - { - foreach($setting->caches as $cache) - { - $cache = (object)$cache; - $condition = isset($cache->condition) ? $cache->condition : ''; - $objects = $this->dao->select('*')->from($table)->beginIF($condition)->where($condition)->fi()->fetchAll(); - if($cache->type == 'raw') - { - $pairs = []; - foreach($objects as $object) $pairs[$cache->type . ':' . $cache->name . ':' . $object->{$setting->key}] = json_encode($object); - $redis->mset($pairs); - } - if($cache->type == 'set') - { - $members = []; - foreach($objects as $object) $members[] = $object->{$setting->key}; - $redis->sadd($cache->type . ':' . $cache->name, ...$members); - } - } - } - } } /** From 5fa08107dab3651febadaae14a8de1269f18e05d Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 17:40:40 +0800 Subject: [PATCH 047/300] * [perf] refactor the zredis class. --- lib/zredis/zredis.class.php | 322 +++++++++++++++++++++++++++++------- 1 file changed, 258 insertions(+), 64 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 5d856d7404..022906aba4 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -2,7 +2,7 @@ class zredis { /** - * 全局 $app 对象。 + * 全局对象 $app。 * The global $app object. * * @var object @@ -37,13 +37,56 @@ class zredis */ public $redis = null; - public function __construct($dao) + /** + * 触发缓存更新的表名。 + * The table name to trigger cache update. + * + * @var string + * @access public + */ + public $table = ''; + + /** + * 触发缓存更新的事件类型,包括 insert, update, delete。 + * The event type to trigger cache update, including insert, update, delete. + * + * @var string + * @access public + */ + public $event = ''; + + /** + * 触发缓存更新的条件。 + * The condition to trigger cache update. + * + * @var string + * @access public + */ + public $condition = ''; + + /** + * 触发缓存删除的键列表。实际存储的值是根据缓存的 key 字段来决定的。 + * The ID list to trigger cache delete. The actual stored value is determined by the key field of the cache. + * + * @var array + * @access public + */ + public $keyList = []; + + /** + * 连接 Redis 服务器并初始化缓存。 + * Connect to Redis server and initialize cache. + * + * @param dao $dao + * @access public + */ + public function __construct($app) { - global $app, $config; + global $config; $this->app = $app; $this->config = $config; - $this->dao = $dao; + $this->dao = $app->dao; if(empty($this->config->redis->enable)) helper::end('Redis is not enabled in the configuration file.'); @@ -70,113 +113,264 @@ class zredis } catch(RedisException $e) { - $this->app->triggerError($e->getMessage(), __FILE__, __LINE__, true); + $this->triggerError($e->getMessage(), __FILE__, __LINE__, true); } } + /** + * 初始化缓存。 + * Initialize cache. + * + * @access public + * @return void + */ public function init() { if(empty($this->redis)) helper::end('Redis is not initialized.'); $this->redis->multi(); - foreach($this->config->redis->tables as $table => $setting) + foreach(array_keys($this->config->redis->tables) as $table) { - foreach($setting->caches as $cache) - { - $cache = (object)$cache; - $condition = isset($cache->condition) ? $cache->condition : ''; - $objects = $this->dao->select('*')->from($table)->beginIF($condition)->where($condition)->fi()->fetchAll(); - if($cache->type == 'raw') - { - $pairs = []; - foreach($objects as $object) $pairs["raw:{$cache->name}:{$object->{$setting->key}}"] = json_encode($object); - $this->redis->mset($pairs); - } - if($cache->type == 'set') - { - $members = []; - foreach($objects as $object) $members[] = $object->{$setting->key}; - $this->redis->sadd("set:{$cache->name}", ...$members); - } - } + $this->table = $table; + $this->condition = ''; + $this->update(); } - $this->redis->set('inited', date('Y-m-d H:i:s')); + $this->redis->set('initialized', date('Y-m-d H:i:s')); $this->redis->exec(); } - public function prepareSync($table, $event, $sql) + /** + * 同步缓存前准备相关参数。 + * Prepare related parameters before synchronizing cache. + * + * @param string $table + * @param string $event + * @param string $sql + * @access public + * @return void + */ + public function prepare($table, $event, $sql) { - $this->event = $event; + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($event)) return $this->log('Event type is required.'); + if(empty($sql)) return $this->log('SQL is required.'); + if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + $this->table = $table; - $this->sql = $sql; + $this->event = $event; - if($event = 'delete') + $condition = ''; + $keyList = []; + if($event == 'update' || $event == 'delete') { - $condition = ''; - $idList = []; - if($method == 'update' || $method == 'delete') - { - $whereLen = strlen(DAO::WHERE); - $wherePOS = strrpos($sql, DAO::WHERE); // The position of WHERE keyword. - $groupPOS = strrpos($sql, DAO::GROUPBY); // The position of GROUP BY keyword. - $havingPOS = strrpos($sql, DAO::HAVING); // The position of HAVING keyword. - $orderPOS = strrpos($sql, DAO::ORDERBY); // The position of ORDERBY keyword. - $limitPOS = strrpos($sql, DAO::LIMIT); // The position of LIMIT keyword. - $splitPOS = $orderPOS ? $orderPOS : $limitPOS; // If $orderPOS, use it instead of $limitPOS. - $splitPOS = $havingPOS ? $havingPOS : $splitPOS; // If $havingPOS, use it instead of $orderPOS. - $splitPOS = $groupPOS ? $groupPOS : $splitPOS; // If $groupPOS, use it instead of $havingPOS. - $condition = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); + /* 获取 WHERE 子句的内容。Get the content of WHERE clause. */ + $whereLen = strlen(DAO::WHERE); + $wherePOS = strrpos($sql, DAO::WHERE); + $groupPOS = strrpos($sql, DAO::GROUPBY); + $havingPOS = strrpos($sql, DAO::HAVING); + $orderPOS = strrpos($sql, DAO::ORDERBY); + $limitPOS = strrpos($sql, DAO::LIMIT); + $splitPOS = $orderPOS ? $orderPOS : $limitPOS; + $splitPOS = $havingPOS ? $havingPOS : $splitPOS; + $splitPOS = $groupPOS ? $groupPOS : $splitPOS; + $condition = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); - if($method == 'delete') $idList = $this->dao->select('id')->from($table)->where($condition)->fetchPairs(); + if($event == 'delete') + { + /* 执行删除操作后数据已经被删除,所以需要提前获取 ID 列表。*/ + $field = $this->config->redis->tables[$table]->key; + $keyList = $this->dao->select($field)->from($table)->where($condition)->fetchPairs(); + } + } + + $this->condition = $condition; + $this->keyList = $keyList; + } + + /** + * 同步缓存。 + * Sync cache. + * + * @access public + * @return void + */ + public function sync() + { + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($this->table)) return $this->log('Table name is required.'); + if(empty($this->event)) return $this->log('Event type is required.'); + if(empty($this->config->redis->tables[$this->table]->caches)) return $this->log('No cache settings for table ' . $this->table); + + $this->redis->multi(); + + if($this->event == 'insert') $this->create(); + if($this->event == 'update') $this->update(); + if($this->event == 'delete') $this->delete(); + + $this->redis->set('synchronized', date('Y-m-d H:i:s')); + + $this->redis->exec(); + + $this->reset(); + } + + /** + * 检查参数。 + * Check parameters. + * + * @access private + * @return void + */ + private function check() + { + if(empty($this->table)) helper::end('Table name is required.'); + if(empty($this->redis)) helper::end('Redis is not initialized.'); + if(empty($this->config->redis->tables[$this->table]->caches)) helper::end('No cache settings for table ' . $this->table); + } + + /** + * 创建缓存。 + * Create cache. + * + * @access private + * @return void + */ + public function create() + { + $this->check(); + + $objectID = $this->dao->lastInsertID(); + if(!$objectID) return $this->log('Failed to fetch last insert id.'); + + $this->condition = "`id` = '{$objectID}'"; + + $this->update(); + } + + /** + * 更新缓存。 + * Update cache. + * + * @access private + * @return void + */ + public function update() + { + $this->check(); + + $setting = $this->config->redis->tables[$this->table]; + + foreach($setting->caches as $cache) + { + $cache = (object)$cache; + $cacheCondition = isset($cache->condition) ? $cache->condition : ''; + + $objects = $this->dao->select('*')->from($this->table) + ->where('1=1') + ->beginIF($this->condition)->andWhere($this->condition)->fi() + ->beginIF($cacheCondition)->andWhere($cacheCondition)->fi() + ->fetchAll(); + if(!$objects) + { + $this->log("Failed to fetch objects to update table {$this->table}.", $this->dao->get()); + continue; } - $this->conditions = $condition; - $this->idList = $idList; + if($cache->type == 'raw') + { + $pairs = []; + foreach($objects as $object) $pairs["raw:{$cache->name}:{$object->{$setting->key}}"] = json_encode($object); + $this->redis->mset($pairs); + continue; + } + + if($cache->type == 'set') + { + $members = []; + foreach($objects as $object) $members[] = $object->{$setting->key}; + $this->redis->sadd("set:{$cache->name}", ...$members); + } } } - public function sync() + /** + * 删除缓存。 + * Delete cache. + * + * @access public + * @return void + */ + public function delete() { - } + $this->check(); - public function update($table) - { - } + if(empty($this->keyList)) return $this->log('Failed to fetch id list to delete.'); - public function delete($table, $idList) - { - if(empty($this->redis)) helper::end('Redis is not initialized.'); - if(empty($table)) helper::end('Table name is required.'); - if(empty($idList)) helper::end('ID list is required.'); - if(empty($this->config->redis->tables[$table]->caches)) helper::end('No cache settings for table ' . $table); - - foreach($this->config->redis->tables[$table]->caches as $cache) + foreach($this->config->redis->tables[$this->table]->caches as $cache) { if($cache->type == 'raw') { $keys = []; - foreach($idList as $id) $keys[] = "raw:{$cache->name}:{$id}"; + foreach($keyList as $key) $keys[] = "raw:{$cache->name}:{$key}"; $this->redis->del($keys); } if($cache->type == 'set') { - $this->redis->srem("set:{$cache->name}", ...$idList); + $this->redis->srem("set:{$cache->name}", ...$keyList); } } } + /** + * 重置参数。 + * Reset parameters. + * + * @access public + * @return void + */ public function reset() { + $this->table = ''; $this->event = ''; - $this->tabel = ''; - $this->sql = ''; $this->condition = ''; - $this->idList = ''; + $this->keyList = []; } + /** + * 记录日志。 + * Log. + * + * @param string $log + * @param string $sql + * @access private + * @return void + */ + private function log($log, $sql = '') + { + $runMode = PHP_SAPI == 'cli' ? '_cli' : ''; + $logFile = $this->app->getLogRoot() . 'redis' . $runMode . '.' . date('Ymd') . '.log.php'; + if(!file_exists($logFile)) file_put_contents($logFile, '\n"); + + $content = date('Ymd H:i:s') . ': ' . $this->app->getURI() . "\n{$log}\n"; + + if($sql) $content .= "The sql is: {$sql}\n"; + + file_put_contents($logFile, $content, FILE_APPEND); + } + + /** + * 调用 Redis 对象的方法。 + * Call the method of Redis object. + * + * @param string $method + * @param array $args + * @access public + * @return void + */ public function __call($method, $args) { if(empty($this->redis)) helper::end('Redis is not initialized.'); From e6c1a08437f627dbc46bc18c8f5874cfd83e47be Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 17:41:26 +0800 Subject: [PATCH 048/300] * [perf] pass instance of baseRouter class to zredis. --- framework/base/router.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index c1d30b0783..ee9d92360d 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -490,7 +490,7 @@ class baseRouter if($this->config->redis->enable) { $this->loadClass('zredis', true); - $this->redis = new zredis($this->dao); + $this->redis = new zredis($this); } $this->setupProfiling(); From 9816cf18d95c03f0303bc8cd776f5af615a7d98d Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 16 Oct 2024 17:42:38 +0800 Subject: [PATCH 049/300] * [perf] calling method of zredis to update cache. --- lib/base/dao/dao.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 468473531c..a6c3f45cd4 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -1054,13 +1054,13 @@ class baseDAO /* Force to query from master db, if db has been changed. */ $this->slaveDBH = false; - $this->app->redis->prepareSync($table, $method, $sql); + $this->app->redis->prepare($table, $method, $sql); $result = $this->dbh->exec($sql); /* See: https://www.php.net/manual/en/pdo.lastinsertid.php .*/ $this->_lastInsertID = $this->dbh->lastInsertId(); - if($result) $this->app->redis->sync(); + $result ? $this->app->redis->sync() : $this->app->redis->reset(); $this->setTableCache($sql); From 088b153df4b8f1cec4f6607652982a1050e28eea Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 09:19:15 +0800 Subject: [PATCH 050/300] * [perf] add method to get request uri. --- lib/zredis/zredis.class.php | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 022906aba4..7bc54d21ee 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -355,13 +355,40 @@ class zredis $logFile = $this->app->getLogRoot() . 'redis' . $runMode . '.' . date('Ymd') . '.log.php'; if(!file_exists($logFile)) file_put_contents($logFile, '\n"); - $content = date('Ymd H:i:s') . ': ' . $this->app->getURI() . "\n{$log}\n"; + $content = date('Ymd H:i:s') . ': ' . $this->getURI() . "\n{$log}\n"; if($sql) $content .= "The sql is: {$sql}\n"; file_put_contents($logFile, $content, FILE_APPEND); } + /** + * 获取 URI。 + * Get URI. + * + * @access private + * @return string + */ + private function getURI(): string + { + $uri = $this->app->getURI(); + if($uri) return $uri; + + if($this->config->requestType == 'GET') return $_SERVER['REQUEST_URI']; + + if($this->config->requestType == 'PATH_INFO' || $this->config->requestType == 'PATH_INFO2') + { + $pathInfo = $this->app->getPathInfo(); + if(empty($pathInfo)) return ''; + + $dotPos = strrpos($pathInfo, '.'); + if($dotPos) return substr($pathInfo, 0, $dotPos); + return $pathInfo; + } + + return ''; + } + /** * 调用 Redis 对象的方法。 * Call the method of Redis object. From 3b30db22230c2fcc789d2071a4a2e78b11196813 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 10:09:02 +0800 Subject: [PATCH 051/300] * [perf] Keep the backticks to solve the problem of not matching the cache configuration. --- lib/base/dao/dao.class.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index a6c3f45cd4..cef623b010 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -1047,7 +1047,7 @@ class baseDAO /* Real-time save log. */ if(dao::$realTimeLog && dao::$realTimeFile) file_put_contents(dao::$realTimeFile, $sql . "\n", FILE_APPEND); - $table = trim($this->table, '`'); + $table = $this->table; $method = $this->method; $this->reset(); @@ -1066,12 +1066,13 @@ class baseDAO if($this->config->enableDuckdb) { - $now = helper::now(); - $queueTable = trim(TABLE_DUCKDBQUEUE, '`'); + $queueTable = TABLE_DUCKDBQUEUE; if(!empty($table) && $table != $queueTable) { - $this->dbh->exec("UPDATE {$queueTable} SET updatedTime = '$now' WHERE object = '$table'"); - $this->dbh->exec("INSERT INTO {$queueTable} (object, updatedTime, syncTime) SELECT '$table', '$now', NULL WHERE NOT EXISTS (SELECT 1 FROM {$queueTable} WHERE object = '$table' );"); + $now = helper::now(); + $object = trim($table, '`'); + $this->dbh->exec("UPDATE {$queueTable} SET updatedTime = '$now' WHERE object = '$object'"); + $this->dbh->exec("INSERT INTO {$queueTable} (`object`, `updatedTime`, `syncTime`) SELECT '$object', '$now', NULL WHERE NOT EXISTS (SELECT 1 FROM {$queueTable} WHERE `object` = '$object' );"); } } From db71378c1c55aced5ca5b9c746e55d3dd7646950 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 10:47:44 +0800 Subject: [PATCH 052/300] * [perf] change the access permission to private. --- lib/zredis/zredis.class.php | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 7bc54d21ee..d35b95ede6 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -6,72 +6,72 @@ class zredis * The global $app object. * * @var object - * @access public + * @access private */ - public $app; + private $app; /** * 全局 $config 对象。 * The global $config object. * * @var object - * @access public + * @access private */ - public $config; + private $config; /** * $dao 对象,用于访问或者更新数据库。 * The $dao object, used to access or update database. * * @var dao - * @access public + * @access private */ - public $dao; + private $dao; /** * $redis 对象,用于访问或者更新 Redis。 * The $redis object, used to access or update database. * * @var redis - * @access public + * @access private */ - public $redis = null; + private $redis = null; /** * 触发缓存更新的表名。 * The table name to trigger cache update. * * @var string - * @access public + * @access private */ - public $table = ''; + private $table = ''; /** * 触发缓存更新的事件类型,包括 insert, update, delete。 * The event type to trigger cache update, including insert, update, delete. * * @var string - * @access public + * @access private */ - public $event = ''; + private $event = ''; /** * 触发缓存更新的条件。 * The condition to trigger cache update. * * @var string - * @access public + * @access private */ - public $condition = ''; + private $condition = ''; /** * 触发缓存删除的键列表。实际存储的值是根据缓存的 key 字段来决定的。 * The ID list to trigger cache delete. The actual stored value is determined by the key field of the cache. * * @var array - * @access public + * @access private */ - public $keyList = []; + private $keyList = []; /** * 连接 Redis 服务器并初始化缓存。 @@ -239,7 +239,7 @@ class zredis * @access private * @return void */ - public function create() + private function create() { $this->check(); @@ -258,7 +258,7 @@ class zredis * @access private * @return void */ - public function update() + private function update() { $this->check(); @@ -301,10 +301,10 @@ class zredis * 删除缓存。 * Delete cache. * - * @access public + * @access private * @return void */ - public function delete() + private function delete() { $this->check(); From e355f5236490e27892024fbe586eba2ee8544674 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 10:49:04 +0800 Subject: [PATCH 053/300] * [perf] fix error of fetching where clause. --- lib/zredis/zredis.class.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index d35b95ede6..fdf8e23029 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -177,13 +177,20 @@ class zredis $splitPOS = $orderPOS ? $orderPOS : $limitPOS; $splitPOS = $havingPOS ? $havingPOS : $splitPOS; $splitPOS = $groupPOS ? $groupPOS : $splitPOS; - $condition = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); + if($splitPOS) + { + $condition = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); + } + else + { + $condition = substr($sql, $wherePOS + $whereLen); + } if($event == 'delete') { /* 执行删除操作后数据已经被删除,所以需要提前获取 ID 列表。*/ $field = $this->config->redis->tables[$table]->key; - $keyList = $this->dao->select($field)->from($table)->where($condition)->fetchPairs(); + $keyList = $this->dao->select($field)->from($table)->beginIF($condition)->where($condition)->fi()->fetchPairs(); } } From 793731b7ba05a3eb0b4f744bd9ddd92c870b909e Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 10:55:30 +0800 Subject: [PATCH 054/300] * [perf] declare the initialization flag and synchronization flag as const. --- lib/zredis/zredis.class.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index fdf8e23029..f184238403 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -1,6 +1,24 @@ redis->ping()) helper::end('Can not connect to Redis server.'); - if(!$this->redis->exists('inited')) $this->init(); + if(!$this->redis->exists(zredis::INITIALIZED)) $this->init(); return $this; } @@ -137,7 +155,7 @@ class zredis $this->update(); } - $this->redis->set('initialized', date('Y-m-d H:i:s')); + $this->redis->set(zredis::INITIALIZED, date('Y-m-d H:i:s')); $this->redis->exec(); } From 1181edf394f69ef3df9f6fd8f6c0e2d381e78252 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 10:58:29 +0800 Subject: [PATCH 055/300] * [perf] change the synchronization flag to const. --- lib/zredis/zredis.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index f184238403..6cad893b52 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -236,7 +236,7 @@ class zredis if($this->event == 'update') $this->update(); if($this->event == 'delete') $this->delete(); - $this->redis->set('synchronized', date('Y-m-d H:i:s')); + $this->redis->set(zredis::SYNCHRONIZED, date('Y-m-d H:i:s')); $this->redis->exec(); From 6e61afddd353aaf75b64a640a00eaf76fb6c3ff4 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 11:22:38 +0800 Subject: [PATCH 056/300] + [perf] add comment. --- lib/zredis/zredis.class.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 6cad893b52..7c20b1deba 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -1,4 +1,13 @@ + * @package zredis + * @link https://www.zentao.net + */ class zredis { /** From b44a49f6b3151a4037519302aac7b31c3437d9e8 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 11:23:03 +0800 Subject: [PATCH 057/300] - [perf] remove extra blank. --- lib/zredis/zredis.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 7c20b1deba..332e8a23db 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -17,7 +17,7 @@ class zredis * @var string * @access public */ - const INITIALIZED = 'sys:initialized'; + const INITIALIZED = 'sys:initialized'; /** * 同步标识。 From d2ff16b305902ba9421fc2b7bb72e6115c094d42 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 11:34:10 +0800 Subject: [PATCH 058/300] * [perf] declare parameter type and return type. --- lib/zredis/zredis.class.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 332e8a23db..1feca82c66 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -1,4 +1,5 @@ redis)) helper::end('Redis is not initialized.'); @@ -179,7 +181,7 @@ class zredis * @access public * @return void */ - public function prepare($table, $event, $sql) + public function prepare(string $table, string $event, string $sql) { if(empty($this->redis)) return $this->log('Redis is not initialized.'); if(empty($table)) return $this->log('Table name is required.'); @@ -259,7 +261,7 @@ class zredis * @access private * @return void */ - private function check() + private function check(): void { if(empty($this->table)) helper::end('Table name is required.'); if(empty($this->redis)) helper::end('Redis is not initialized.'); @@ -292,7 +294,7 @@ class zredis * @access private * @return void */ - private function update() + private function update(): void { $this->check(); @@ -366,7 +368,7 @@ class zredis * @access public * @return void */ - public function reset() + public function reset(): void { $this->table = ''; $this->event = ''; @@ -383,7 +385,7 @@ class zredis * @access private * @return void */ - private function log($log, $sql = '') + private function log(string $log, string $sql = ''): void { $runMode = PHP_SAPI == 'cli' ? '_cli' : ''; $logFile = $this->app->getLogRoot() . 'redis' . $runMode . '.' . date('Ymd') . '.log.php'; @@ -432,7 +434,7 @@ class zredis * @access public * @return void */ - public function __call($method, $args) + public function __call(string $method, array $args) { if(empty($this->redis)) helper::end('Redis is not initialized.'); From 69c7e6e70f23291fdaf8bff4c15c876011023fae Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 11:38:10 +0800 Subject: [PATCH 059/300] - [perf] remove unused method. --- lib/zredis/zredis.class.php | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 1feca82c66..5aa03b6531 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -254,20 +254,6 @@ class zredis $this->reset(); } - /** - * 检查参数。 - * Check parameters. - * - * @access private - * @return void - */ - private function check(): void - { - if(empty($this->table)) helper::end('Table name is required.'); - if(empty($this->redis)) helper::end('Redis is not initialized.'); - if(empty($this->config->redis->tables[$this->table]->caches)) helper::end('No cache settings for table ' . $this->table); - } - /** * 创建缓存。 * Create cache. @@ -277,8 +263,6 @@ class zredis */ private function create() { - $this->check(); - $objectID = $this->dao->lastInsertID(); if(!$objectID) return $this->log('Failed to fetch last insert id.'); @@ -296,8 +280,6 @@ class zredis */ private function update(): void { - $this->check(); - $setting = $this->config->redis->tables[$this->table]; foreach($setting->caches as $cache) @@ -342,8 +324,6 @@ class zredis */ private function delete() { - $this->check(); - if(empty($this->keyList)) return $this->log('Failed to fetch id list to delete.'); foreach($this->config->redis->tables[$this->table]->caches as $cache) From a69c3daf1884794ca53bcaa10d1cfc869e415d82 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 11:38:42 +0800 Subject: [PATCH 060/300] * [perf] modify id list to key list. --- lib/zredis/zredis.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 5aa03b6531..41015cb4a5 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -324,7 +324,7 @@ class zredis */ private function delete() { - if(empty($this->keyList)) return $this->log('Failed to fetch id list to delete.'); + if(empty($this->keyList)) return $this->log('Failed to fetch key list to delete.'); foreach($this->config->redis->tables[$this->table]->caches as $cache) { From 015e5a80d4d8f464b851a5ab99ec93c41829a06b Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 11:41:24 +0800 Subject: [PATCH 061/300] * [perf] fetch last insert id only for insert execution. --- lib/base/dao/dao.class.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index cef623b010..c274304c34 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -1057,8 +1057,9 @@ class baseDAO $this->app->redis->prepare($table, $method, $sql); $result = $this->dbh->exec($sql); + /* See: https://www.php.net/manual/en/pdo.lastinsertid.php .*/ - $this->_lastInsertID = $this->dbh->lastInsertId(); + if($method == 'insert') $this->_lastInsertID = $this->dbh->lastInsertID(); $result ? $this->app->redis->sync() : $this->app->redis->reset(); From 70a870832756f642f86d6c04d677e2de3217b2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Thu, 17 Oct 2024 11:43:01 +0800 Subject: [PATCH 062/300] * [perf] merge redis config. --- config/redis.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/redis.php b/config/redis.php index 698387bd14..0c39b1d273 100644 --- a/config/redis.php +++ b/config/redis.php @@ -11,6 +11,7 @@ $config->redis->tables = []; $config->redis->tables[TABLE_PROJECT] = new stdClass(); $config->redis->tables[TABLE_PROJECT]->key = 'id'; $config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'raw', 'name' => 'execution']; +$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'dbProjectIdList']; $config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'programIdList', 'condition' => "`type` = 'program'"]; $config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'projectIdList', 'condition' => "`type` = 'project'"]; $config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'executionIdList', 'condition' => "`type` IN ('sprint', 'stage', 'kanban')"]; @@ -24,3 +25,7 @@ $config->redis->tables[TABLE_USER] = new stdClass(); $config->redis->tables[TABLE_USER]->key = 'account'; $config->redis->tables[TABLE_USER]->caches[] = ['type' => 'raw', 'name' => 'user']; $config->redis->tables[TABLE_USER]->caches[] = ['type' => 'set', 'name' => 'userAccountList']; + +$config->redis->tables[TABLE_CONFIG] = new stdClass(); +$config->redis->tables[TABLE_CONFIG]->key = 'id'; +$config->redis->tables[TABLE_CONFIG]->caches[] = ['type' => 'raw', 'name' => 'config']; From aa5528372ba51c0edf8161d7e2e2e2c4a3c707a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Thu, 17 Oct 2024 11:50:25 +0800 Subject: [PATCH 063/300] + [perf] Add redis cache class. --- framework/base/router.class.php | 90 ++++++ lib/base/cache/cache.class.php | 489 ++++++++++++++++++++++++++++++++ lib/cache/cache.class.php | 72 +---- 3 files changed, 581 insertions(+), 70 deletions(-) create mode 100644 lib/base/cache/cache.class.php diff --git a/framework/base/router.class.php b/framework/base/router.class.php index ee9d92360d..2c75777f3a 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3072,6 +3072,7 @@ class baseRouter } $this->loadDAO(); + $this->loadCache(); } /** @@ -3094,6 +3095,22 @@ class baseRouter $this->dao = $dao; } + /** + * 加载cache。 + * Load cache. + * + * @access public + * @return void + */ + public function loadCache() + { + $classFile = $this->coreLibRoot . 'cache' . DS . 'cache.class.php'; + include($classFile); + + $cache = new cache($this); + $this->cache = $cache; + } + /** * Init config of slave db. * @@ -3513,6 +3530,79 @@ class baseRouter return $installed; } + + /** + * Connect to Redis Server. + * @link https://pecl.php.net/package/redis + * @link https://github.com/phpredis/phpredis/ + * @return Redis|object|false + */ + public function connectToRedis() + { + if(!extension_loaded('redis')) return false; + + if(empty($this->config->redis)) return false; + + try + { + $redis = new Redis(); + + $version = phpversion('redis'); + if(version_compare($version, '5.3.0', 'ge')) + { + $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0, ['auth' => [$this->config->redis->username, $this->config->redis->password]]); + } + else + { + $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); + $redis->auth(['pass' => $this->config->redis->password]); + } + + if(!$redis->ping()) return false; + + $this->loadToRedis($redis); + } + catch(RedisException $e) + { + $this->triggerError($e->getMessage(), __FILE__, __LINE__, true); + } + + if($redis) return $redis; + } + + /** + * 把数据载入 redis。 + * Load data to redis. + * + * @param Redis $redis + * @access public + * @return void + */ + public function loadToRedis($redis) + { + foreach($this->config->redis->tables as $table => $setting) + { + foreach($setting->caches as $cache) + { + $cache = (object)$cache; + $condition = isset($cache->condition) ? $cache->condition : ''; + $objects = $this->dao->select('*')->from($table)->beginIF($condition)->where($condition)->fi()->fetchAll(); + if($cache->type == 'raw') + { + $pairs = []; + foreach($objects as $object) $pairs[$cache->type . ':' . $cache->name . ':' . $object->{$setting->key}] = json_encode($object); + $redis->mset($pairs); + } + if($cache->type == 'set') + { + $members = []; + foreach($objects as $object) $members[] = $object->{$setting->key}; + + if(!empty($members)) $redis->sadd($cache->type . ':' . $cache->name, ...$members); + } + } + } + } } /** diff --git a/lib/base/cache/cache.class.php b/lib/base/cache/cache.class.php new file mode 100644 index 0000000000..b5ff074520 --- /dev/null +++ b/lib/base/cache/cache.class.php @@ -0,0 +1,489 @@ +app = $app; + $this->config = $config; + + $this->reset(); + } + + /** + * 设置$table属性。 + * Set the $table property. + * + * @param string $table + * @access public + * @return void + */ + public function setTable($table) + { + $this->table = $table; + } + + /** + * 设置$fields属性。 + * Set the $fields property. + * + * @param string $fields + * @access public + * @return void + */ + public function setFields($fields) + { + $this->fields = $fields; + } + + /** + * 设置条件。 + * Set the $conditions property. + * + * @param string $conditions + * @access public + * @return void + */ + public function setConditions($conditions) + { + $this->conditions = $conditions; + $this->resetCondition(); + } + + /** + * 重置组装条件。 + * Reset the $condition property. + * + * @access public + * @return void + */ + public function resetCondition() + { + $this->condition = array('field' => '', 'operator' => '', 'value' => null); + } + + /** + * 添加条件。 + * Add the $conditions property. + * + * @param string $condition + * @access public + * @return void + */ + public function addCondition() + { + $this->conditions[] = $this->condition; + } + + /** + * 重置属性。 + * Reset the vars. + * + * @access public + * @return void + */ + public function reset() + { + $this->setFields(''); + $this->setTable(''); + $this->setConditions([]); + } + + /** + * 开始事务。 + * Begin Transaction + * + * @access public + * @return void + */ + public function begin() + { + $this->app->redis->multi(); + } + + /** + * 提交事务。 + * Commits a transaction. + * + * @access public + * @return void + */ + public function commit() + { + $this->app->redis->exec(); + } + + /** + * select方法,调用sql::select()。 + * The select method, call sql::select(). + * + * @param string $fields + * @access public + * @return static|sql|baseDAO the dao object self. + */ + public function select($fields = '*') + { + $this->setFields(explode(',', str_replace(' ', '', $fields))); + return $this; + } + + /** + * 设置要操作的表。 + * Set the from table. + * + * @param string $table + * @access public + * @return static|sql the dao object self. + */ + public function from($table) + { + $this->setTable($table); + return $this; + } + + /** + * 创建WHERE部分。 + * Create the where part. + * + * @param string $field the field name + * @access public + * @return static the dao object. + */ + public function where($field) + { + $this->resetCondition(); + $this->condition['field'] = $field; + return $this; + } + + /** + * 创建andWHERE部分。 + * Create the andWhere part. + * + * @param string $field the field name + * @access public + * @return static the dao object. + */ + public function andWhere($field) + { + $this->resetCondition(); + $this->condition['field'] = $field; + return $this; + } + + /** + * 创建eq部分。 + * Create the eq part. + * + * @param string $value + * @access public + * @return static the dao object. + */ + public function eq($value) + { + $this->condition['operator'] = 'eq'; + $this->condition['value'] = $value; + $this->addCondition(); + + return $this; + } + + /** + * 创建ne部分。 + * Create the ne part. + * + * @param string $value + * @access public + * @return static the dao object. + */ + public function ne($value) + { + $this->condition['operator'] = 'ne'; + $this->condition['value'] = $value; + $this->addCondition(); + + return $this; + } + + /** + * 创建in部分。 + * Create the in part. + * + * @param string $value + * @access public + * @return static the dao object. + */ + public function in($value) + { + $this->condition['operator'] = 'in'; + $this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value; + $this->addCondition(); + + return $this; + } + + /** + * 创建notin部分。 + * Create the in part. + * + * @param string $value + * @access public + * @return static the dao object. + */ + public function notin($value) + { + $this->condition['operator'] = 'notin'; + $this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value; + $this->addCondition(); + + return $this; + } + + //-------------------- Fetch相关方法(Fetch related methods) -------------------// + + /** + * 判断匹配条件。 + * Check condition is matched. + * + * @param object $object + * @param array $conditions + * @access public + * @return bool + */ + private function isConditionMatched($object, $conditions) + { + foreach($conditions as $condition) + { + $value = $object->{$condition['field']}; + if($condition['operator'] == 'eq') + { + if($condition['value'] !== $value) return false; + } + elseif($condition['operator'] == 'eq') + { + if($condition['value'] === $value) return false; + } + elseif($condition['operator'] == 'in') + { + if(!in_array($value, $condition['value'])) return false; + } + elseif($condition['operator'] == 'notin') + { + if(in_array($value, $condition['value'])) return false; + } + else + { + return false; + } + } + + return true; + } + + /** + * 获取一个记录。 + * Fetch one record. + * + * @param string $field 如果已经设置获取的字段,则只返回这个字段的值,否则返回这个记录。 + * if the field is set, only return the value of this field, else return this record + * @access public + * @return object|mixed + */ + public function fetch($field = '') + { + $cacheTable = $this->config->redis->tables[$this->table]->caches[1]['name']; + $keyIdList = $this->app->redis->smembers("set:$cacheTable"); + + if(empty($keyIdList)) return []; + + $keyPrefix = $this->config->redis->tables[$this->table]->caches[0]['name']; + $keyList = []; + foreach($keyIdList as $keyId) + { + $keyList[] = "raw:$keyPrefix:$keyId"; + } + $rawResult = $this->app->redis->mget($keyList); + + if(empty($rawResult)) return ''; + + foreach($rawResult as $row) + { + $object = json_decode($row); + + if(!$this->isConditionMatched($object, $this->conditions)) continue; + + return $field ? $object->$field : $object; + } + + return ''; + } + + /** + * 获取所有记录。 + * Fetch all records. + * + * @param string $keyField 返回以该字段做键的记录 + * the key field, thus the return records is keyed by this field + * @access public + * @return array the records + */ + public function fetchAll() + { + $cacheTable = $this->config->redis->tables[$this->table]->caches[1]['name']; + $keyIdList = $this->app->redis->smembers("set:$cacheTable"); + + if(empty($keyIdList)) return []; + + $keyPrefix = $this->config->redis->tables[$this->table]->caches[0]['name']; + $keyList = []; + foreach($keyIdList as $keyId) + { + $keyList[] = "raw:$keyPrefix:$keyId"; + } + $rawResult = $this->app->redis->mget($keyList); + + if(empty($rawResult)) return []; + + $result = []; + foreach($rawResult as $row) + { + $object = json_decode($row); + + if(!$this->isConditionMatched($object, $this->conditions)) continue; + + $data = new stdclass(); + foreach($this->fields as $field) + { + if($field == '*') + { + $data = $object; + break; + } + $data->$field = $object->$field; + } + + $result[] = $data; + } + + return $result; + } + + /** + * 获取的记录是以关联数组的形式 + * Fetch array like key=>value. + * + * 如果没有设置参数,用首末两键作为参数。 + * If the keyFiled and valueField not set, use the first and last in the record. + * + * @param string $keyField + * @param string $valueField + * @access public + * @return array + */ + public function fetchPairs($keyField = '', $valueField = '') + { + $pairs = []; + + $rows = $this->fetchAll(); + if(empty($rows)) return []; + + if(empty($keyField)) $keyField = $this->fields[0]; + if(empty($valueField)) $valueField = $this->fields[1]; + + foreach($rows as $row) + { + $pairs[$row->$keyField] = $row->$valueField; + } + + return $pairs; + } +} diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 4cea47d51f..c3c7c85e1f 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -9,76 +9,8 @@ * @link https://www.zentao.net */ -helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheInterface.php'); -helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheException.php'); -helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'InvalidArgumentException.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'ApcuDriver.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'RedisDriver.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'FileDriver.php'); +helper::import(dirname(__FILE__, 2) . '/base/cache/cache.class.php'); -use ZenTao\Cache\SimpleCache\InvalidArgumentException; - -class cache +class cache extends baseCache { - const DRIVER_APCU = 'Apcu'; - - const DRIVER_FILE = 'File'; - - const DRIVER_YAC = 'Yac'; - - const DRIVER_REDIS = 'Redis'; - - /** - * @var ZenTao\Cache\SimpleCache\CacheInterface - */ - protected $client; - - public function __construct($driver = 'File', $namespace = '', $defaultLifetime = 0) - { - $driver = ucfirst(strtolower($driver)); - switch($driver) - { - case self::DRIVER_APCU: - $className = 'ZenTao\Cache\Driver\ApcuDriver'; - break; - case self::DRIVER_YAC: - $className = 'ZenTao\Cache\Driver\YacDriver'; - break; - case self::DRIVER_FILE: - $className = 'ZenTao\Cache\Driver\FileDriver'; - break; - case self::DRIVER_REDIS: - $className = 'ZenTao\Cache\Driver\RedisDriver'; - break; - default: - throw new InvalidArgumentException("Driver {$driver} is not supported."); - } - - if($driver != self::DRIVER_FILE && !extension_loaded($driver)) throw new InvalidArgumentException("Driver ext-{$driver} is not loaded."); - - global $app; - $this->client = new $className($namespace, $defaultLifetime, $app->getCacheRoot()); - } - - /** - * Create a cache instance. - * - * @param string $driver - * @param string $namespace - * @param int $defaultLifetime - * @access public - * @return object - */ - public static function create($driver = 'File', $namespace = '', $defaultLifetime = 0) - { - return new self($driver, $namespace, $defaultLifetime); - } - - public function __call($name, $arguments) - { - if(!method_exists($this->client, $name)) throw new InvalidArgumentException("Method {$name} does not exist."); - - return call_user_func_array(array($this->client, $name), $arguments); - } } From 4a893d7efcf536a281af50f8c7e4aa17d372e062 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 11:58:20 +0800 Subject: [PATCH 064/300] * [perf] fix error of deleting cache in redis. --- lib/zredis/zredis.class.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 41015cb4a5..5e98ca8a28 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -224,7 +224,7 @@ class zredis } $this->condition = $condition; - $this->keyList = $keyList; + $this->keyList = array_values($keyList); } /** @@ -328,15 +328,16 @@ class zredis foreach($this->config->redis->tables[$this->table]->caches as $cache) { + $cache = (object)$cache; if($cache->type == 'raw') { $keys = []; - foreach($keyList as $key) $keys[] = "raw:{$cache->name}:{$key}"; + foreach($this->keyList as $key) $keys[] = "raw:{$cache->name}:{$key}"; $this->redis->del($keys); } if($cache->type == 'set') { - $this->redis->srem("set:{$cache->name}", ...$keyList); + $this->redis->srem("set:{$cache->name}", ...$this->keyList); } } } From 4fe0dc4146ac5042149ef81c19f434c7c4d7327f Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 13:57:54 +0800 Subject: [PATCH 065/300] + [perf] add redis configuration. --- config/redis.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/config/redis.php b/config/redis.php index 0c39b1d273..2c7ed74d92 100644 --- a/config/redis.php +++ b/config/redis.php @@ -21,6 +21,21 @@ $config->redis->tables[TABLE_PRODUCT]->key = 'id'; $config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'raw', 'name' => 'product']; $config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'set', 'name' => 'productIdList']; +$config->redis->tables[TABLE_BUILD] = new stdClass(); +$config->redis->tables[TABLE_BUILD]->key = 'id'; +$config->redis->tables[TABLE_BUILD]->caches[] = ['type' => 'raw', 'name' => 'build']; +$config->redis->tables[TABLE_BUILD]->caches[] = ['type' => 'set', 'name' => 'buildIdList']; + +$config->redis->tables[TABLE_RELEASE] = new stdClass(); +$config->redis->tables[TABLE_RELEASE]->key = 'id'; +$config->redis->tables[TABLE_RELEASE]->caches[] = ['type' => 'raw', 'name' => 'release']; +$config->redis->tables[TABLE_RELEASE]->caches[] = ['type' => 'set', 'name' => 'releaseIdList']; + +$config->redis->tables[TABLE_MODULE] = new stdClass(); +$config->redis->tables[TABLE_MODULE]->key = 'id'; +$config->redis->tables[TABLE_MODULE]->caches[] = ['type' => 'raw', 'name' => 'module']; +$config->redis->tables[TABLE_MODULE]->caches[] = ['type' => 'set', 'name' => 'moduleIdList']; + $config->redis->tables[TABLE_USER] = new stdClass(); $config->redis->tables[TABLE_USER]->key = 'account'; $config->redis->tables[TABLE_USER]->caches[] = ['type' => 'raw', 'name' => 'user']; From 081b55d6726c0d777b148ba9bbdeaa6857bf3697 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 14:15:28 +0800 Subject: [PATCH 066/300] * [perf] don't encode chinese character to unicode. --- lib/zredis/zredis.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 5e98ca8a28..7c2074ac6b 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -301,7 +301,7 @@ class zredis if($cache->type == 'raw') { $pairs = []; - foreach($objects as $object) $pairs["raw:{$cache->name}:{$object->{$setting->key}}"] = json_encode($object); + foreach($objects as $object) $pairs["raw:{$cache->name}:{$object->{$setting->key}}"] = json_encode($object, JSON_UNESCAPED_UNICODE); $this->redis->mset($pairs); continue; } From 7fc4ed37826f8990e36b34e7cf379e7c8cd28e38 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 14:20:43 +0800 Subject: [PATCH 067/300] * [perf] calling redis->setnx to implement a simple lock. --- lib/zredis/zredis.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 7c2074ac6b..45d1c57a50 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -166,7 +166,7 @@ class zredis $this->update(); } - $this->redis->set(zredis::INITIALIZED, date('Y-m-d H:i:s')); + $this->redis->setnx(zredis::INITIALIZED, date('Y-m-d H:i:s')); $this->redis->exec(); } From 46b69df3ccf2029311094af116d2c2f9dc87623b Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 17 Oct 2024 14:34:15 +0800 Subject: [PATCH 068/300] * [perf] fix the magic method didn't return result. --- lib/zredis/zredis.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 45d1c57a50..ec748ed417 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -419,6 +419,6 @@ class zredis { if(empty($this->redis)) helper::end('Redis is not initialized.'); - $this->redis->$method(...$args); + return $this->redis->$method(...$args); } } From 6df83d2655f78f4430774f1673bca08c212be45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Fri, 18 Oct 2024 10:15:09 +0800 Subject: [PATCH 069/300] + [perf] Add beginIf for cache. --- lib/base/cache/cache.class.php | 123 +++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/lib/base/cache/cache.class.php b/lib/base/cache/cache.class.php index b5ff074520..97e7208f62 100644 --- a/lib/base/cache/cache.class.php +++ b/lib/base/cache/cache.class.php @@ -82,6 +82,24 @@ class baseCache */ public $condition; + /** + * 是否在判断条件成立。 + * Checking condition. + * + * @var bool + * @access public + */ + public $isConditionChecking; + + /** + * 条件是否成立。 + * Condition is true. + * + * @var bool + * @access public + */ + public $conditionIsTrue; + /** * 构造方法。 * The construct method. @@ -230,6 +248,44 @@ class baseCache return $this; } + /** + * 开始条件判断。 + * Begin condition judge. + * + * @param bool $condition + * @access public + * @return static|sql the sql object. + */ + public function beginIF($conditionResult) + { + $this->isConditionChecking = true; + $this->conditionIsTrue = $conditionResult; + return $this; + } + + /** + * 结束条件判断。 + * End the condition judge. + * + * @access public + * @return static|sql the sql object. + */ + public function fi() + { + $this->isConditionChecking = false; + + if(!$this->conditionIsTrue) + { + $this->resetCondition(); + return $this; + } + + $this->addCondition(); + $this->resetCondition(); + + return $this; + } + /** * 创建WHERE部分。 * Create the where part. @@ -272,7 +328,12 @@ class baseCache { $this->condition['operator'] = 'eq'; $this->condition['value'] = $value; - $this->addCondition(); + + if(!$this->isConditionChecking) + { + $this->addCondition(); + $this->resetCondition(); + } return $this; } @@ -289,7 +350,12 @@ class baseCache { $this->condition['operator'] = 'ne'; $this->condition['value'] = $value; - $this->addCondition(); + + if(!$this->isConditionChecking) + { + $this->addCondition(); + $this->resetCondition(); + } return $this; } @@ -306,7 +372,12 @@ class baseCache { $this->condition['operator'] = 'in'; $this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value; - $this->addCondition(); + + if(!$this->isConditionChecking) + { + $this->addCondition(); + $this->resetCondition(); + } return $this; } @@ -323,8 +394,39 @@ class baseCache { $this->condition['operator'] = 'notin'; $this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value; - $this->addCondition(); + if(!$this->isConditionChecking) + { + $this->addCondition(); + $this->resetCondition(); + } + + return $this; + } + + /** + * 创建ORDER BY部分。 + * Create the order by part. + * + * @param string $order + * @access public + * @return static|sql the sql object. + */ + public function orderBy($order) + { + return $this; + } + + /** + * 创建LIMIT部分。 + * Create the limit part. + * + * @param string $limit + * @access public + * @return static|sql the sql object. + */ + public function limit($limit) + { return $this; } @@ -348,7 +450,7 @@ class baseCache { if($condition['value'] !== $value) return false; } - elseif($condition['operator'] == 'eq') + elseif($condition['operator'] == 'ne') { if($condition['value'] === $value) return false; } @@ -416,7 +518,7 @@ class baseCache * @access public * @return array the records */ - public function fetchAll() + public function fetchAll($keyField = 0) { $cacheTable = $this->config->redis->tables[$this->table]->caches[1]['name']; $keyIdList = $this->app->redis->smembers("set:$cacheTable"); @@ -451,7 +553,14 @@ class baseCache $data->$field = $object->$field; } - $result[] = $data; + if($keyField) + { + $result[$object->$keyField] = $data; + } + else + { + $result[] = $data; + } } return $result; From 8292846100207dc7d5e1d801d143784b3d051ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Fri, 18 Oct 2024 10:18:32 +0800 Subject: [PATCH 070/300] * [perf] add redis set: config. --- config/redis.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/redis.php b/config/redis.php index 2c7ed74d92..25c5fffc1a 100644 --- a/config/redis.php +++ b/config/redis.php @@ -44,3 +44,4 @@ $config->redis->tables[TABLE_USER]->caches[] = ['type' => 'set', 'name' => 'user $config->redis->tables[TABLE_CONFIG] = new stdClass(); $config->redis->tables[TABLE_CONFIG]->key = 'id'; $config->redis->tables[TABLE_CONFIG]->caches[] = ['type' => 'raw', 'name' => 'config']; +$config->redis->tables[TABLE_CONFIG]->caches[] = ['type' => 'set', 'name' => 'configIdList']; From 178ca8685842aeb4366d5f7661a5b2f123e30353 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 18 Oct 2024 13:30:48 +0800 Subject: [PATCH 071/300] * [perf] use optimistic locking to prevent duplicate cache initialization. --- lib/zredis/zredis.class.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index ec748ed417..eb7b0c7d3c 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -157,6 +157,7 @@ class zredis { if(empty($this->redis)) helper::end('Redis is not initialized.'); + $this->redis->watch(zredis::INITIALIZED); $this->redis->multi(); foreach(array_keys($this->config->redis->tables) as $table) @@ -166,9 +167,9 @@ class zredis $this->update(); } - $this->redis->setnx(zredis::INITIALIZED, date('Y-m-d H:i:s')); + $this->redis->set(zredis::INITIALIZED, date('Y-m-d H:i:s')); - $this->redis->exec(); + $this->redis->exec() ? $this->log('Redis cache initialization succeeded.') : $this->log('Redis cache initialization failed.'); } /** From 31c7691c57870cc8b140f38e7690cdef1947dac8 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 18 Oct 2024 13:51:11 +0800 Subject: [PATCH 072/300] + [perf] add methods to fetch data from redis cache. --- lib/zredis/zredis.class.php | 106 ++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index eb7b0c7d3c..f5171cc750 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -255,6 +255,112 @@ class zredis $this->reset(); } + /** + * 根据表名和键获取对象。 + * Get object by table name and key. + * + * @param string $table + * @param int|string $key + * @access public + * @return object + */ + public function getObject(string $table, int|string $key) + { + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($key)) return $this->log('Key is required.'); + if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + + foreach($this->config->redis->tables[$table]->caches as $cache) + { + $cache = (object)$cache; + if($cache->type == 'raw') + { + $object = $this->redis->get("raw:{$cache->name}:{$key}"); + if($object) $object = json_decode($object); + + return $object; + } + } + + return null; + } + + /** + * 根据表名获取对象列表。 + * Get object list by table name. + * + * @param string $table + * @param array $keyList + * @access public + * @return array + */ + public function getObjects(string $table, array $keyList = []) + { + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + + if(!$keyList) + { + foreach($this->config->redis->tables[$table]->caches as $cache) + { + $cache = (object)$cache; + if($cache->type == 'set' && empty($cache->condition))) + { + $keyList = $this->redis->smembers("set:{$cache->name}"); + break; + } + } + } + foreach($this->config->redis->tables[$table]->caches as $cache) + { + $cache = (object)$cache; + if($cache->type == 'raw') + { + $keys = []; + foreach($keyList as $key) $keys[] = "raw:{$cache->name}:{$key}"; + + $objects = $this->redis->mget(...$keyList); + foreach($objects as $key => $object) + { + if($object) $objects[$key] = json_decode($object); + } + + return $objects; + } + } + + return []; + } + + /** + * 根据表名和键获取键值对。 + * Get key-value pairs by table name and key. + * + * @param string $table + * @param string $key + * @param string $value + * @param array $keyList + * @access public + * @return array + */ + public function getPairs(string $table, string $key, string $value, array $keyList = []) + { + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($key)) return $this->log('Key is required.'); + if(empty($value)) return $this->log('Value is required.'); + if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + + $objects = $this->getObjects($table, $keyList); + if(!$objects) return []; + + $paris = []; + foreach($objects as $object) $pairs[$object->{$key}] = $object->{$value}; + return $pairs; + } + /** * 创建缓存。 * Create cache. From b0e979d66447aa2f2857986227af31d2a41ab6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Mon, 21 Oct 2024 14:40:12 +0800 Subject: [PATCH 073/300] + [perf] Add cache model. --- framework/base/control.class.php | 1 - framework/base/model.class.php | 16 +------ framework/base/router.class.php | 74 ++++++++++++++------------------ module/cache/config.php | 0 module/cache/model.php | 17 ++++++++ 5 files changed, 50 insertions(+), 58 deletions(-) create mode 100644 module/cache/config.php create mode 100644 module/cache/model.php diff --git a/framework/base/control.class.php b/framework/base/control.class.php index 15f55f8f32..b18aa6d2ac 100644 --- a/framework/base/control.class.php +++ b/framework/base/control.class.php @@ -324,7 +324,6 @@ class baseControl $this->{$moduleName} = $model; $this->dao = $model->dao; - $this->cache = $model->cache; return $model; } diff --git a/framework/base/model.class.php b/framework/base/model.class.php index 15fac010db..f2410395e5 100644 --- a/framework/base/model.class.php +++ b/framework/base/model.class.php @@ -153,6 +153,7 @@ class baseModel $this->lang = $lang; $this->dbh = $dbh; $this->dao = $dao; + $this->cache = $app->cacheModel; $this->appName = empty($appName) ? $this->app->getAppName() : $appName; $moduleName = $this->getModuleName(); @@ -160,7 +161,6 @@ class baseModel if($moduleName != 'common') $this->app->loadModuleConfig($moduleName, $this->appName); $this->setSuperVars(); - if($this->config->cache->enable) $this->loadCache(); /** * 读取当前模块的tao类。 @@ -310,20 +310,6 @@ class baseModel return $extensionObject; } - /** - * 加载缓存类。 - * Load cache class. - * - * @access public - * @return void - */ - public function loadCache() - { - $this->app->loadClass('cache', $static = true); - $namespace = isset($this->session->user->account) ? $this->session->user->account : 'guest'; - $this->cache = cache::create($this->config->cache->driver, $namespace, $this->config->cache->lifetime); - } - /** * 删除记录。 * Delete one record. diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 2c75777f3a..03cf85692c 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -436,6 +436,14 @@ class baseRouter */ public $clientCacheTime = 0; + /** + * 缓存Model。 + * The cache model. + * + * @var object + */ + public $cacheModel; + /** * 构造方法, 设置路径,类,超级变量等。注意: * 1.应该使用createApp()方法实例化router类; @@ -499,8 +507,6 @@ class baseRouter $this->setEdition(); $this->setClient(); - - $this->loadCacheConfig(); } /** @@ -1582,6 +1588,26 @@ class baseRouter //-------------------- 模块及扩展设置(Module and extension) --------------------// + /** + * 加载cache模块。 + * Load the cache module + * + * @access public + * @return void. + */ + public function loadCache(): void + { + if(!$this->checkInstalled()) return; + + $this->setModuleName('cache'); + $cacheModelFile = $this->setModelFile('cache'); + if(!file_exists($cacheModelFile)) return; + + helper::import($cacheModelFile); + + $this->cacheModel = new cacheModel(); + } + /** * 加载common模块。 * @@ -1600,6 +1626,8 @@ class baseRouter */ public function loadCommon(): object|bool { + $this->loadCache(); + $this->setModuleName('common'); $commonModelFile = $this->setModelFile('common'); if(!file_exists($commonModelFile)) return false; @@ -1628,6 +1656,8 @@ class baseRouter $this->setDebug(); + $common->cache = $this->cacheModel; + return $common; } @@ -2802,29 +2832,6 @@ class baseRouter include $mainConfigFile; } - /** - * 从数据库加载缓存配置。 - * Load the cache config from the database. - * - * @access public - * @return void - */ - public function loadCacheConfig() - { - if(!$this->checkInstalled()) return false; - - $globalCache = $this->dao->select('`value`')->from(TABLE_CONFIG)->where('`module`')->eq('common')->andWhere('`section`')->eq('global')->andWhere('`key`')->eq('cache')->limit(1)->fetch('value'); - if(!$globalCache) return false; - - $caches = json_decode($globalCache); - foreach($caches as $cacheKey => $cache) - { - if(!isset($this->config->cache->$cacheKey)) $this->config->cache->$cacheKey = new stdClass(); - - foreach($cache as $key => $value) $this->config->cache->$cacheKey->$key = $value; - } - } - /** * 当multiSite功能打开的时候,加载额外的配置文件。 * When multiSite feature enabled, load extra config file. @@ -3072,7 +3079,6 @@ class baseRouter } $this->loadDAO(); - $this->loadCache(); } /** @@ -3095,22 +3101,6 @@ class baseRouter $this->dao = $dao; } - /** - * 加载cache。 - * Load cache. - * - * @access public - * @return void - */ - public function loadCache() - { - $classFile = $this->coreLibRoot . 'cache' . DS . 'cache.class.php'; - include($classFile); - - $cache = new cache($this); - $this->cache = $cache; - } - /** * Init config of slave db. * diff --git a/module/cache/config.php b/module/cache/config.php new file mode 100644 index 0000000000..e69de29bb2 diff --git a/module/cache/model.php b/module/cache/model.php new file mode 100644 index 0000000000..ac3d46c2e2 --- /dev/null +++ b/module/cache/model.php @@ -0,0 +1,17 @@ + + * @package caselib + * @version $Id: model.php 5114 2013-07-12 06:02:59Z chencongzhi520@gmail.com $ + * @link https://www.zentao.net + */ +?> + Date: Mon, 21 Oct 2024 15:16:26 +0800 Subject: [PATCH 074/300] * [perf] remove extra brackets. --- lib/zredis/zredis.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index f5171cc750..03cf853d40 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -306,7 +306,7 @@ class zredis foreach($this->config->redis->tables[$table]->caches as $cache) { $cache = (object)$cache; - if($cache->type == 'set' && empty($cache->condition))) + if($cache->type == 'set' && empty($cache->condition)) { $keyList = $this->redis->smembers("set:{$cache->name}"); break; From a3c1dcaacf4a60a43a40f3c0f79839090fc94da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Mon, 21 Oct 2024 16:20:08 +0800 Subject: [PATCH 075/300] + [perf] Add cache handler. --- lib/base/cache/cache.class.php | 5 ++--- module/cache/handler/bug.php | 0 module/cache/handler/setting.php | 34 ++++++++++++++++++++++++++++++ module/cache/model.php | 36 ++++++++++++++++++++++++++++++-- module/setting/model.php | 7 +------ 5 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 module/cache/handler/bug.php create mode 100644 module/cache/handler/setting.php diff --git a/lib/base/cache/cache.class.php b/lib/base/cache/cache.class.php index 97e7208f62..876ecfef00 100644 --- a/lib/base/cache/cache.class.php +++ b/lib/base/cache/cache.class.php @@ -104,13 +104,12 @@ class baseCache * 构造方法。 * The construct method. * - * @param object $app * @access public * @return void */ - public function __construct($app) + public function __construct() { - global $config; + global $config, $app; $this->app = $app; $this->config = $config; diff --git a/module/cache/handler/bug.php b/module/cache/handler/bug.php new file mode 100644 index 0000000000..e69de29bb2 diff --git a/module/cache/handler/setting.php b/module/cache/handler/setting.php new file mode 100644 index 0000000000..3423c99cf2 --- /dev/null +++ b/module/cache/handler/setting.php @@ -0,0 +1,34 @@ + + * @package action + * @version $Id$ + * @link https://www.zentao.net + */ +class settingHandler +{ + /** + * 获取系统及用户的配置信息。 + * Get system and personal config. + * + * @param string $account + * @access public + * @return array + */ + public function getSysAndPersonalConfig(string $account) + { + $owner = 'system,' . ($account ? $account : ''); + $records = $this->cache->select('*')->from(TABLE_CONFIG) + ->where('owner')->in($owner) + ->beginIF(!$this->app->upgrading)->andWhere('vision')->in(array('', $this->config->vision))->fi() + ->orderBy('id') + ->fetchAll('id'); + + return $records; + } +} diff --git a/module/cache/model.php b/module/cache/model.php index ac3d46c2e2..bb61958561 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -10,8 +10,40 @@ declare(strict_types=1); * @version $Id: model.php 5114 2013-07-12 06:02:59Z chencongzhi520@gmail.com $ * @link https://www.zentao.net */ -?> -cache = $this->app->loadClass('cache'); + } + + /** + * 魔术方法,加载子类。 + * Magic get method. + * + * @param string $handlerName + * @access public + * @return object + */ + public function __get(string $handlerName) + { + include dirname(__FILE__) . DS . 'handler' . DS . strtolower($handlerName) . '.php'; + $handlerName = $handlerName . 'Handler'; + + $handler = new $handlerName(); + $handler->cache = $this->cache; + $handler->app = $this->app; + $handler->config = $this->config; + + return $handler; + } } diff --git a/module/setting/model.php b/module/setting/model.php index c2f1281ee8..a32c992d9b 100644 --- a/module/setting/model.php +++ b/module/setting/model.php @@ -244,12 +244,7 @@ class settingModel extends model */ public function getSysAndPersonalConfig(string $account = ''): array { - $owner = 'system,' . ($account ? $account : ''); - $records = $this->dao->select('*')->from(TABLE_CONFIG) - ->where('owner')->in($owner) - ->beginIF(!$this->app->upgrading)->andWhere('vision')->in(array('', $this->config->vision))->fi() - ->orderBy('id') - ->fetchAll('id'); + $records = $this->cache->setting->getSysAndPersonalConfig($account); if(!$records) return array(); $vision = $this->config->vision; From 0a2a352327c9210e4b766e1aec0d04719f2b27dc Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 21 Oct 2024 16:48:56 +0800 Subject: [PATCH 076/300] * [perf] simplify configuration of redis cache. --- config/redis.php | 48 +++++++++++------------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/config/redis.php b/config/redis.php index 25c5fffc1a..430a931a3d 100644 --- a/config/redis.php +++ b/config/redis.php @@ -7,41 +7,15 @@ $config->redis->timeout = 10; $config->redis->username = null; $config->redis->password = null; -$config->redis->tables = []; -$config->redis->tables[TABLE_PROJECT] = new stdClass(); -$config->redis->tables[TABLE_PROJECT]->key = 'id'; -$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'raw', 'name' => 'execution']; -$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'dbProjectIdList']; -$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'programIdList', 'condition' => "`type` = 'program'"]; -$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'projectIdList', 'condition' => "`type` = 'project'"]; -$config->redis->tables[TABLE_PROJECT]->caches[] = ['type' => 'set', 'name' => 'executionIdList', 'condition' => "`type` IN ('sprint', 'stage', 'kanban')"]; +// Format : $config->redis->caches[TABLE_NAME] = 'KEY_FIELD'; +// The TABLE_NAME is the name of the table in the database. +// The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table. -$config->redis->tables[TABLE_PRODUCT] = new stdClass(); -$config->redis->tables[TABLE_PRODUCT]->key = 'id'; -$config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'raw', 'name' => 'product']; -$config->redis->tables[TABLE_PRODUCT]->caches[] = ['type' => 'set', 'name' => 'productIdList']; - -$config->redis->tables[TABLE_BUILD] = new stdClass(); -$config->redis->tables[TABLE_BUILD]->key = 'id'; -$config->redis->tables[TABLE_BUILD]->caches[] = ['type' => 'raw', 'name' => 'build']; -$config->redis->tables[TABLE_BUILD]->caches[] = ['type' => 'set', 'name' => 'buildIdList']; - -$config->redis->tables[TABLE_RELEASE] = new stdClass(); -$config->redis->tables[TABLE_RELEASE]->key = 'id'; -$config->redis->tables[TABLE_RELEASE]->caches[] = ['type' => 'raw', 'name' => 'release']; -$config->redis->tables[TABLE_RELEASE]->caches[] = ['type' => 'set', 'name' => 'releaseIdList']; - -$config->redis->tables[TABLE_MODULE] = new stdClass(); -$config->redis->tables[TABLE_MODULE]->key = 'id'; -$config->redis->tables[TABLE_MODULE]->caches[] = ['type' => 'raw', 'name' => 'module']; -$config->redis->tables[TABLE_MODULE]->caches[] = ['type' => 'set', 'name' => 'moduleIdList']; - -$config->redis->tables[TABLE_USER] = new stdClass(); -$config->redis->tables[TABLE_USER]->key = 'account'; -$config->redis->tables[TABLE_USER]->caches[] = ['type' => 'raw', 'name' => 'user']; -$config->redis->tables[TABLE_USER]->caches[] = ['type' => 'set', 'name' => 'userAccountList']; - -$config->redis->tables[TABLE_CONFIG] = new stdClass(); -$config->redis->tables[TABLE_CONFIG]->key = 'id'; -$config->redis->tables[TABLE_CONFIG]->caches[] = ['type' => 'raw', 'name' => 'config']; -$config->redis->tables[TABLE_CONFIG]->caches[] = ['type' => 'set', 'name' => 'configIdList']; +$config->redis->caches = []; +$config->redis->caches[TABLE_CONFIG] = 'id'; +$config->redis->caches[TABLE_BUILD] = 'id'; +$config->redis->caches[TABLE_MODULE] = 'id'; +$config->redis->caches[TABLE_PRODUCT] = 'id'; +$config->redis->caches[TABLE_PROJECT] = 'id'; +$config->redis->caches[TABLE_RELEASE] = 'id'; +$config->redis->caches[TABLE_USER] = 'account'; From a6ed9b3b22fedcea197526b088bb18b0412c0384 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 21 Oct 2024 16:49:28 +0800 Subject: [PATCH 077/300] * [refac] refactor for the simplified configuration. --- lib/zredis/zredis.class.php | 167 +++++++++++++----------------------- 1 file changed, 58 insertions(+), 109 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 03cf853d40..b4bc9992ed 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -160,7 +160,7 @@ class zredis $this->redis->watch(zredis::INITIALIZED); $this->redis->multi(); - foreach(array_keys($this->config->redis->tables) as $table) + foreach(array_keys($this->config->redis->caches) as $table) { $this->table = $table; $this->condition = ''; @@ -184,11 +184,11 @@ class zredis */ public function prepare(string $table, string $event, string $sql) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($event)) return $this->log('Event type is required.'); - if(empty($sql)) return $this->log('SQL is required.'); - if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($event)) return $this->log('Event type is required.'); + if(empty($sql)) return $this->log('SQL is required.'); + if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); $this->table = $table; $this->event = $event; @@ -219,7 +219,7 @@ class zredis if($event == 'delete') { /* 执行删除操作后数据已经被删除,所以需要提前获取 ID 列表。*/ - $field = $this->config->redis->tables[$table]->key; + $field = $this->config->redis->caches[$table]; $keyList = $this->dao->select($field)->from($table)->beginIF($condition)->where($condition)->fi()->fetchPairs(); } } @@ -237,10 +237,10 @@ class zredis */ public function sync() { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($this->table)) return $this->log('Table name is required.'); - if(empty($this->event)) return $this->log('Event type is required.'); - if(empty($this->config->redis->tables[$this->table]->caches)) return $this->log('No cache settings for table ' . $this->table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($this->table)) return $this->log('Table name is required.'); + if(empty($this->event)) return $this->log('Event type is required.'); + if(empty($this->config->redis->caches[$this->table])) return $this->log('No cache settings for table ' . $this->table); $this->redis->multi(); @@ -266,24 +266,16 @@ class zredis */ public function getObject(string $table, int|string $key) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($key)) return $this->log('Key is required.'); - if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($key)) return $this->log('Key is required.'); + if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); - foreach($this->config->redis->tables[$table]->caches as $cache) - { - $cache = (object)$cache; - if($cache->type == 'raw') - { - $object = $this->redis->get("raw:{$cache->name}:{$key}"); - if($object) $object = json_decode($object); + $code = str_replace(['`', $this->config->db->prefix], '', $table); + $object = $this->redis->get("raw:{$code}:{$key}"); + if(!$object) return $object; - return $object; - } - } - - return null; + return json_decode($object); } /** @@ -297,41 +289,22 @@ class zredis */ public function getObjects(string $table, array $keyList = []) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); - if(!$keyList) + $keys = []; + $code = str_replace(['`', $this->config->db->prefix], '', $table); + if(!$keyList) $keyList = $this->redis->smembers("set:{$code}List"); + foreach($keyList as $key) $keys[] = "raw:{$code}:{$key}"; + + $objects = $this->redis->mget($keys); + foreach($objects as $key => $object) { - foreach($this->config->redis->tables[$table]->caches as $cache) - { - $cache = (object)$cache; - if($cache->type == 'set' && empty($cache->condition)) - { - $keyList = $this->redis->smembers("set:{$cache->name}"); - break; - } - } - } - foreach($this->config->redis->tables[$table]->caches as $cache) - { - $cache = (object)$cache; - if($cache->type == 'raw') - { - $keys = []; - foreach($keyList as $key) $keys[] = "raw:{$cache->name}:{$key}"; - - $objects = $this->redis->mget(...$keyList); - foreach($objects as $key => $object) - { - if($object) $objects[$key] = json_decode($object); - } - - return $objects; - } + if($object) $objects[$key] = json_decode($object); } - return []; + return $objects; } /** @@ -347,17 +320,17 @@ class zredis */ public function getPairs(string $table, string $key, string $value, array $keyList = []) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($key)) return $this->log('Key is required.'); - if(empty($value)) return $this->log('Value is required.'); - if(empty($this->config->redis->tables[$table]->caches)) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($key)) return $this->log('Key is required.'); + if(empty($value)) return $this->log('Value is required.'); + if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); $objects = $this->getObjects($table, $keyList); if(!$objects) return []; $paris = []; - foreach($objects as $object) $pairs[$object->{$key}] = $object->{$value}; + foreach($objects as $object) $pairs[$object->$key] = $object->$value; return $pairs; } @@ -385,41 +358,23 @@ class zredis * @access private * @return void */ - private function update(): void + private function update() { - $setting = $this->config->redis->tables[$this->table]; + $objects = $this->dao->select('*')->from($this->table) + ->beginIF($this->condition)->where($this->condition)->fi() + ->fetchAll(); + if(!$objects) return $this->log("Failed to fetch objects to update table {$this->table}.", $this->dao->get()); - foreach($setting->caches as $cache) - { - $cache = (object)$cache; - $cacheCondition = isset($cache->condition) ? $cache->condition : ''; + $code = str_replace(['`', $this->config->db->prefix], '', $this->table); + $key = $this->config->redis->caches[$this->table]; - $objects = $this->dao->select('*')->from($this->table) - ->where('1=1') - ->beginIF($this->condition)->andWhere($this->condition)->fi() - ->beginIF($cacheCondition)->andWhere($cacheCondition)->fi() - ->fetchAll(); - if(!$objects) - { - $this->log("Failed to fetch objects to update table {$this->table}.", $this->dao->get()); - continue; - } + $pairs = []; + foreach($objects as $object) $pairs["raw:{$code}:{$object->$key}"] = json_encode($object, JSON_UNESCAPED_UNICODE); + $this->redis->mset($pairs); - if($cache->type == 'raw') - { - $pairs = []; - foreach($objects as $object) $pairs["raw:{$cache->name}:{$object->{$setting->key}}"] = json_encode($object, JSON_UNESCAPED_UNICODE); - $this->redis->mset($pairs); - continue; - } - - if($cache->type == 'set') - { - $members = []; - foreach($objects as $object) $members[] = $object->{$setting->key}; - $this->redis->sadd("set:{$cache->name}", ...$members); - } - } + $members = []; + foreach($objects as $object) $members[] = $object->$key; + $this->redis->sadd("set:{$code}List", ...$members); } /** @@ -433,20 +388,14 @@ class zredis { if(empty($this->keyList)) return $this->log('Failed to fetch key list to delete.'); - foreach($this->config->redis->tables[$this->table]->caches as $cache) - { - $cache = (object)$cache; - if($cache->type == 'raw') - { - $keys = []; - foreach($this->keyList as $key) $keys[] = "raw:{$cache->name}:{$key}"; - $this->redis->del($keys); - } - if($cache->type == 'set') - { - $this->redis->srem("set:{$cache->name}", ...$this->keyList); - } - } + $keyList = array_values($this->keyList); + + $keys = []; + $code = str_replace(['`', $this->config->db->prefix], '', $this->table); + foreach($keyList as $key) $keys[] = "raw:{$code}:{$key}"; + + $this->redis->del($keys); + $this->redis->srem("set:{$code}List", ...$keyList); } /** From d86ca596cb9f4e3f871bc871063a27d6e18f2520 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 21 Oct 2024 16:50:27 +0800 Subject: [PATCH 078/300] * [refac] calling methods in zredis to simplify. --- lib/base/cache/cache.class.php | 44 +++++++--------------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/lib/base/cache/cache.class.php b/lib/base/cache/cache.class.php index 876ecfef00..84547bf62d 100644 --- a/lib/base/cache/cache.class.php +++ b/lib/base/cache/cache.class.php @@ -481,28 +481,14 @@ class baseCache */ public function fetch($field = '') { - $cacheTable = $this->config->redis->tables[$this->table]->caches[1]['name']; - $keyIdList = $this->app->redis->smembers("set:$cacheTable"); - - if(empty($keyIdList)) return []; - - $keyPrefix = $this->config->redis->tables[$this->table]->caches[0]['name']; - $keyList = []; - foreach($keyIdList as $keyId) - { - $keyList[] = "raw:$keyPrefix:$keyId"; - } - $rawResult = $this->app->redis->mget($keyList); - + $rawResult = $this->app->redis->getObjects($this->table); if(empty($rawResult)) return ''; foreach($rawResult as $row) { - $object = json_decode($row); + if(!$this->isConditionMatched($row, $this->conditions)) continue; - if(!$this->isConditionMatched($object, $this->conditions)) continue; - - return $field ? $object->$field : $object; + return $field ? $row->$field : $row; } return ''; @@ -519,42 +505,28 @@ class baseCache */ public function fetchAll($keyField = 0) { - $cacheTable = $this->config->redis->tables[$this->table]->caches[1]['name']; - $keyIdList = $this->app->redis->smembers("set:$cacheTable"); - - if(empty($keyIdList)) return []; - - $keyPrefix = $this->config->redis->tables[$this->table]->caches[0]['name']; - $keyList = []; - foreach($keyIdList as $keyId) - { - $keyList[] = "raw:$keyPrefix:$keyId"; - } - $rawResult = $this->app->redis->mget($keyList); - + $rawResult = $this->app->redis->getObjects($this->table); if(empty($rawResult)) return []; $result = []; foreach($rawResult as $row) { - $object = json_decode($row); - - if(!$this->isConditionMatched($object, $this->conditions)) continue; + if(!$this->isConditionMatched($row, $this->conditions)) continue; $data = new stdclass(); foreach($this->fields as $field) { if($field == '*') { - $data = $object; + $data = $row; break; } - $data->$field = $object->$field; + $data->$field = $row->$field; } if($keyField) { - $result[$object->$keyField] = $data; + $result[$row->$keyField] = $data; } else { From 69ca0a9ddc5b19c7c731cfc65c9acfc16aec553e Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 22 Oct 2024 14:49:28 +0800 Subject: [PATCH 079/300] * [perf] remove extra blank. --- config/redis.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/redis.php b/config/redis.php index 430a931a3d..bc57247ec2 100644 --- a/config/redis.php +++ b/config/redis.php @@ -7,7 +7,7 @@ $config->redis->timeout = 10; $config->redis->username = null; $config->redis->password = null; -// Format : $config->redis->caches[TABLE_NAME] = 'KEY_FIELD'; +// Format : $config->redis->caches[TABLE_NAME] = 'KEY_FIELD'; // The TABLE_NAME is the name of the table in the database. // The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table. From 6cfdbae44e6a93c275c1d02b6439a2d6d497348f Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:08:40 +0800 Subject: [PATCH 080/300] * [perf] define table constant for cache. --- config/redis.php | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/config/redis.php b/config/redis.php index bc57247ec2..03a60f9fbe 100644 --- a/config/redis.php +++ b/config/redis.php @@ -7,15 +7,23 @@ $config->redis->timeout = 10; $config->redis->username = null; $config->redis->password = null; +define('CACHE_CONFIG', TABLE_CONFIG); +define('CACHE_BUILD', TABLE_BUILD); +define('CACHE_MODULE', TABLE_MODULE); +define('CACHE_PRODUCT', TABLE_PRODUCT); +define('CACHE_PROJECT', TABLE_PROJECT); +define('CACHE_RELEASE', TABLE_RELEASE); +define('CACHE_USER', TABLE_USER); + // Format : $config->redis->caches[TABLE_NAME] = 'KEY_FIELD'; // The TABLE_NAME is the name of the table in the database. // The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table. $config->redis->caches = []; -$config->redis->caches[TABLE_CONFIG] = 'id'; -$config->redis->caches[TABLE_BUILD] = 'id'; -$config->redis->caches[TABLE_MODULE] = 'id'; -$config->redis->caches[TABLE_PRODUCT] = 'id'; -$config->redis->caches[TABLE_PROJECT] = 'id'; -$config->redis->caches[TABLE_RELEASE] = 'id'; -$config->redis->caches[TABLE_USER] = 'account'; +$config->redis->caches[CACHE_CONFIG] = 'id'; +$config->redis->caches[CACHE_BUILD] = 'id'; +$config->redis->caches[CACHE_MODULE] = 'id'; +$config->redis->caches[CACHE_PRODUCT] = 'id'; +$config->redis->caches[CACHE_PROJECT] = 'id'; +$config->redis->caches[CACHE_RELEASE] = 'id'; +$config->redis->caches[CACHE_USER] = 'account'; From 71adeffc32b28309406602d27544449ebce97e35 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:09:19 +0800 Subject: [PATCH 081/300] * [perf] rename methods to fetch cache from redis. --- lib/base/cache/cache.class.php | 4 ++-- lib/zredis/zredis.class.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/base/cache/cache.class.php b/lib/base/cache/cache.class.php index 84547bf62d..cdfe40c897 100644 --- a/lib/base/cache/cache.class.php +++ b/lib/base/cache/cache.class.php @@ -481,7 +481,7 @@ class baseCache */ public function fetch($field = '') { - $rawResult = $this->app->redis->getObjects($this->table); + $rawResult = $this->app->redis->fetchAll($this->table); if(empty($rawResult)) return ''; foreach($rawResult as $row) @@ -505,7 +505,7 @@ class baseCache */ public function fetchAll($keyField = 0) { - $rawResult = $this->app->redis->getObjects($this->table); + $rawResult = $this->app->redis->fetchAll($this->table); if(empty($rawResult)) return []; $result = []; diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index b4bc9992ed..f2dc4564fc 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -264,7 +264,7 @@ class zredis * @access public * @return object */ - public function getObject(string $table, int|string $key) + public function fetch(string $table, int|string $key) { if(empty($this->redis)) return $this->log('Redis is not initialized.'); if(empty($table)) return $this->log('Table name is required.'); @@ -287,7 +287,7 @@ class zredis * @access public * @return array */ - public function getObjects(string $table, array $keyList = []) + public function fetchAll(string $table, array $keyList = []) { if(empty($this->redis)) return $this->log('Redis is not initialized.'); if(empty($table)) return $this->log('Table name is required.'); @@ -318,7 +318,7 @@ class zredis * @access public * @return array */ - public function getPairs(string $table, string $key, string $value, array $keyList = []) + public function fetchPairs(string $table, string $key, string $value, array $keyList = []) { if(empty($this->redis)) return $this->log('Redis is not initialized.'); if(empty($table)) return $this->log('Table name is required.'); @@ -326,7 +326,7 @@ class zredis if(empty($value)) return $this->log('Value is required.'); if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); - $objects = $this->getObjects($table, $keyList); + $objects = $this->fetchAll($table, $keyList); if(!$objects) return []; $paris = []; From 68dfcea21056e936df35e0c5fe3c259a23b8cc0b Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:11:22 +0800 Subject: [PATCH 082/300] + [perf] add configuration of cached keys. --- module/cache/config.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/module/cache/config.php b/module/cache/config.php index e69de29bb2..7a14fbf008 100644 --- a/module/cache/config.php +++ b/module/cache/config.php @@ -0,0 +1,7 @@ +cache->keys = []; +$config->cache->keys['user'] = []; +$config->cache->keys['user']['getpairs'] = ['type' => 'array', 'filter' => 'encode']; + +$config->cache->keys['bug']['openedby'] = ['type' => 'array']; +$config->cache->keys['bug']['assignedto'] = ['type' => 'array']; From e278cffb605333224fa542a1b15f29c7caf71349 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:11:47 +0800 Subject: [PATCH 083/300] * [refac] refactor the cacheModel. --- module/cache/model.php | 371 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 359 insertions(+), 12 deletions(-) diff --git a/module/cache/model.php b/module/cache/model.php index bb61958561..edec3d556a 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -12,6 +12,51 @@ declare(strict_types=1); */ class cacheModel extends model { + /** + * 缓存的模块。 + * The module of cache. + * + * @access private + * @var string + */ + private $module = ''; + + /** + * 缓存的方法。 + * The method of cache. + * + * @access private + * @var string + */ + private $method = '' + + /** + * 缓存的键。 + * The key of cache. + * + * @access private + * @var string + */ + private $key = ''; + + /** + * 缓存的值。 + * The value of cache. + * + * @access private + * @var int|string|object|array + */ + private $value = ''; + + /** + * 缓存相关的键。 + * The related keys of cache. + * + * @access private + * @var array + */ + private $relatedKeys = []; + /** * 构造方法。 * Constructor. @@ -19,7 +64,7 @@ class cacheModel extends model * @access public * @return void */ - public function __construct() + public function __construct(): void { parent::__construct(); @@ -30,20 +75,322 @@ class cacheModel extends model * 魔术方法,加载子类。 * Magic get method. * - * @param string $handlerName + * @param string $module * @access public + * @return cacheModel + */ + public function __get(string $module): cacheModel + { + $this->reset(); + + if(!isset($this->config->cache->keys[$module])) return $this->log("The cache configuration of the module {$module} is not exist."); + + $this->setModule($module); + + if(isset($this->$module)) return $this; + + $file = dirname(__FILE__) . DS . 'handler' . DS . strtolower($module) . '.php'; + if(!helper::import($file)) return $this->log("The handler file {$file} is not exist."); + + $class = $module . 'Handler'; + $this->$module = new $class(); + + return $this; + } + + /** + * 魔术方法,调用子类方法。 + * Magic call method. + * + * @param string $method + * @param array $args + * @access public + * @return cacheModel + */ + public function __call(string $method, array $args): cacheModel + { + if(!isset($this->config->cache->keys[$this->module])) return $this->log('The module is not exist.'); + + $this->setMethod($method); + + $key = "res:{$this->module}:{$method}"; + $this->setKey($key); + + if(empty($this->$module)) return $this; + if(!method_exists($this->$module, $method)) return $this->log("The method {$method} is not exist."); + + $key = call_user_func_array([$this->$module, $method], $args); + $this->setKey($key); + + return $this; + } + + /** + * 设置缓存的模块。 + * Set the module of cache. + * + * @param string $module + * @access private + * @return void + */ + private function setModule(string $module): void + { + $this->module = $module; + } + + /** + * 设置缓存的方法。 + * Set the method of cache. + * + * @param string $method + * @access private + * @return void + */ + private function setMethod(string $method): void + { + $this->method = $method; + } + + /** + * 设置缓存的键。 + * Set the key of cache. + * + * @param string $key + * @access private + * @return void + */ + private function setKey(string $key): void + { + $this->key = $key; + } + + /** + * 设置缓存的值。 + * Set the value of cache. + * + * @param int|string|object|array $value + * @access private + * @return void + */ + private function setValue(int|string|object|array $value): void + { + $this->value = $value; + } + + /** + * 设置缓存相关的键。 + * Set the related keys of cache. + * + * @param array $relatedKeys + * @access private + * @return void + */ + private function setRelatedKeys(array $relatedKeys): void + { + $this->relatedKeys = $relatedKeys; + } + + /** + * 重置缓存相关设置。 + * Reset the cache settings. + * + * @access private + * @return void + */ + private function reset(): void + { + $this->setModule(''); + $this->setMethod(''); + $this->setKey(''); + $this->setValue(''); + $this->setRelatedKeys([]); + } + + /** + * 设置缓存的值。 + * Set the value of cache. + * + * @param int|string|object|array $value + * @access public + * @return cacheModel + */ + public function value(int|string|object|array $value): cacheModel + { + if(empty($this->config->cache->keys[$this->module][$this->method]['type'])) return $this->log("The cache configuration of [{$this->module}][{$this->method}] is not exist."); + + $config = $this->config->cache->keys[$this->module][$this->method]; + $type = $config['type']; + $filter = isset($config['filter']) ? $config['filter'] : ''; + + if($type == 'int' && !is_int($value)) return $this->log('The value must be int.'); + if($type == 'string' && !is_string($value)) return $this->log('The value must be string.'); + if($type == 'object' && !is_object($value)) return $this->log('The value must be object.'); + if($type == 'array') + { + if(!is_array($value)) return $this->log('The value must be array.'); + if($filter != 'encode' && !$this->isIndexedArray($array)) return $this->log('The value must be indexed array.'); + } + + if($type == 'object' || ($type == 'array' && $filter == 'encode')) $value = json_encode($value); + + $this->setValue($value); + + return $this; + } + + /** + * 设置缓存相关的键。 + * Set the related keys of cache. + * + * @param array $relatedKeys + * @access public + * @return cacheModel + */ + public function relatedKeys(array $relatedKeys): cacheModel + { + if(!is_array($relatedKeys)) return $this->log('The relatedKeys must be array.'); + if(!$this->isIndexedArray($relatedKeys)) return $this->log('The relatedKeys must be indexed array.'); + + $this->setRelatedKeys($relatedKeys); + return $this; + } + + /** + * 获取缓存。 + * Fetch the cache. + * + * @access public + * @return false|int|string|object|array + */ + public function fetch(): bool|int|string|object|array + { + if(empty($this->config->cache->keys[$this->module][$this->method]['type'])) + { + $this->log("The cache configuration of [{$this->module}][{$this->method}] is not exist."); + return false; + } + + $config = $this->config->cache->keys[$this->module][$this->method]; + $type = $config['type']; + $filter = isset($config['filter']) ? $config['filter'] : ''; + + if($type == 'int') return (int)$this->app->redis->get($this->key); + if($type == 'string') return $this->app->redis->get($this->key); + if($type == 'object') return json_decode($this->app->redis->get($this->key)); + if($type == 'array') + { + if($filter == 'encode') return json_decode($this->app->redis->get($this->key)); + + return $this->app->redis->smembers($this->key); + } + return false; + } + + /** + * 保存缓存。 + * Save the cache. + * + * @access public + * @return false|int|string|object|array + */ + public function save(): bool|int|string|object|array + { + if(empty($this->value)) + { + $this->log('The value is empty.'); + return $this->result(false); + } + if(empty($this->config->cache->keys[$this->module][$this->method]['type'])) + { + $this->log("The cache configuration of [{$this->module}][{$this->method}] is not exist."); + return $this->result(false); + } + + $config = $this->config->cache->keys[$this->module][$this->method]; + $type = $config['type']; + $filter = isset($config['filter']) ? $config['filter'] : ''; + + if($type == 'int') return $this->result($this->app->redis->set($this->key, $this->value)); + if($type == 'string') return $this->result($this->app->redis->set($this->key, $this->value)); + if($type == 'object') return $this->result($this->app->redis->set($this->key, $this->value)); + if($type == 'array') + { + if($filter == 'encode') return $this->result($this->app->redis->set($this->key, $this->value)); + + return return $this->result($this->app->redis->sadd($this->key, ...$this->value)); + } + return $this->result(false); + } + + /** + * 返回结果。 + * Return the result. + * + * @param bool|int|string|object|array $result + * @access private + * @return bool|int|string|object|array + */ + private function result(bool|int|string|object|array $result): bool|int|string|object|array + { + if($result) $this->updateRelated(); + $this->reset(); + return $result; + } + + /** + * 更新相关的缓存。 + * Update the related cache. + * + * @access private + * @return bool + */ + private function updateRelatedKeys(): bool + { + if(empty($this->config->objectTables[$this->module])) return false; + + $pairs = []; + $table = $this->config->objectTables[$this->module]; + $code = str_replace(['`', $this->config->db->prefix], '', $table); + $object = $this->app->redis->fetchAll($table, $this->relatedKeys); + $key = $this->config->redis->caches[$table]; + foreach($objects as $object) + { + $relatedKeys = isset($object->_relatedKeys) ? $object->_relatedKeys : []; + $relatedKeys[] = $this->key; + $object->_relatedKeys = array_unique($relatedKeys); + $pairs["raw:{$code}:{$object->$key}"] = json_encode($object, JSON_UNESCAPED_UNICODE); + } + + $this->app->redis->mset($pairs); + + return true; + } + + /** + * 检查是否为索引数组。 + * Check if it is an indexed array. + * + * @param array $array + * @access private + * @return bool + */ + private function isIndexedArray(array $array): bool + { + if(function_exists('array_is_list')) return array_is_list($value); + return $value === array_values($value); + } + + /** + * 记录日志。 + * Save log. + * + * @param string $message + * @access private * @return object */ - public function __get(string $handlerName) + private function log(string $message) { - include dirname(__FILE__) . DS . 'handler' . DS . strtolower($handlerName) . '.php'; - $handlerName = $handlerName . 'Handler'; - - $handler = new $handlerName(); - $handler->cache = $this->cache; - $handler->app = $this->app; - $handler->config = $this->config; - - return $handler; + if($this->config->debug) $this->app->triggerError($message, __FILE__, __LINE__, $this->config->debug >= 2); + return $this; } } From 19176ed1486b6ddcc6000fac04878f819dd8cd23 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:20:08 +0800 Subject: [PATCH 084/300] * [refac] modify class comment. --- module/cache/model.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/module/cache/model.php b/module/cache/model.php index edec3d556a..9274257a52 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -1,13 +1,12 @@ - * @package caselib - * @version $Id: model.php 5114 2013-07-12 06:02:59Z chencongzhi520@gmail.com $ + * @author Gang Liu + * @package cache * @link https://www.zentao.net */ class cacheModel extends model From 3839b68c3fbb72a7657a30dc5cfe5aa1f74d0a44 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:20:30 +0800 Subject: [PATCH 085/300] + [perf] add example of cache handler. --- module/cache/handler/bug.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/module/cache/handler/bug.php b/module/cache/handler/bug.php index e69de29bb2..5c72c6f536 100644 --- a/module/cache/handler/bug.php +++ b/module/cache/handler/bug.php @@ -0,0 +1,26 @@ + + * @package cache + * @link https://www.zentao.net + */ +class bugHandler +{ + /** + * 获取指派给某人的bug的缓存键名。 + * Get the cache key name of bugs assigned to someone. + * + * @param string $account + * @access public + * @return string + */ + public function assignedTo(string $account): string + { + return 'res:bug:assignedTo:' . $account; + } +} From 90e52d53becdbf92309739e9cd7999b232af6e81 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:22:36 +0800 Subject: [PATCH 086/300] - [refac] remove unused methods. --- framework/base/router.class.php | 73 --------------------------------- 1 file changed, 73 deletions(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 03cf85692c..b0d3b02da6 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3520,79 +3520,6 @@ class baseRouter return $installed; } - - /** - * Connect to Redis Server. - * @link https://pecl.php.net/package/redis - * @link https://github.com/phpredis/phpredis/ - * @return Redis|object|false - */ - public function connectToRedis() - { - if(!extension_loaded('redis')) return false; - - if(empty($this->config->redis)) return false; - - try - { - $redis = new Redis(); - - $version = phpversion('redis'); - if(version_compare($version, '5.3.0', 'ge')) - { - $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0, ['auth' => [$this->config->redis->username, $this->config->redis->password]]); - } - else - { - $redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); - $redis->auth(['pass' => $this->config->redis->password]); - } - - if(!$redis->ping()) return false; - - $this->loadToRedis($redis); - } - catch(RedisException $e) - { - $this->triggerError($e->getMessage(), __FILE__, __LINE__, true); - } - - if($redis) return $redis; - } - - /** - * 把数据载入 redis。 - * Load data to redis. - * - * @param Redis $redis - * @access public - * @return void - */ - public function loadToRedis($redis) - { - foreach($this->config->redis->tables as $table => $setting) - { - foreach($setting->caches as $cache) - { - $cache = (object)$cache; - $condition = isset($cache->condition) ? $cache->condition : ''; - $objects = $this->dao->select('*')->from($table)->beginIF($condition)->where($condition)->fi()->fetchAll(); - if($cache->type == 'raw') - { - $pairs = []; - foreach($objects as $object) $pairs[$cache->type . ':' . $cache->name . ':' . $object->{$setting->key}] = json_encode($object); - $redis->mset($pairs); - } - if($cache->type == 'set') - { - $members = []; - foreach($objects as $object) $members[] = $object->{$setting->key}; - - if(!empty($members)) $redis->sadd($cache->type . ':' . $cache->name, ...$members); - } - } - } - } } /** From aa0f7e32790e606f25da8a9dbb061e088acda0e8 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 15:24:14 +0800 Subject: [PATCH 087/300] - [refac] remove unused code of zredis. --- lib/zredis/zredis.class.php | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index f2dc4564fc..a118e91b1b 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -11,15 +11,6 @@ declare(strict_types=1); */ class zredis { - /** - * 初始化标识。 - * Initialization flag. - * - * @var string - * @access public - */ - const INITIALIZED = 'sys:initialized'; - /** * 同步标识。 * Synchronization flag. @@ -136,8 +127,6 @@ class zredis if(!$this->redis->ping()) helper::end('Can not connect to Redis server.'); - if(!$this->redis->exists(zredis::INITIALIZED)) $this->init(); - return $this; } catch(RedisException $e) @@ -146,32 +135,6 @@ class zredis } } - /** - * 初始化缓存。 - * Initialize cache. - * - * @access public - * @return void - */ - public function init(): void - { - if(empty($this->redis)) helper::end('Redis is not initialized.'); - - $this->redis->watch(zredis::INITIALIZED); - $this->redis->multi(); - - foreach(array_keys($this->config->redis->caches) as $table) - { - $this->table = $table; - $this->condition = ''; - $this->update(); - } - - $this->redis->set(zredis::INITIALIZED, date('Y-m-d H:i:s')); - - $this->redis->exec() ? $this->log('Redis cache initialization succeeded.') : $this->log('Redis cache initialization failed.'); - } - /** * 同步缓存前准备相关参数。 * Prepare related parameters before synchronizing cache. From 5dac796032b2c606009aef7e13d0626830c35872 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 17:22:14 +0800 Subject: [PATCH 088/300] Revert "* [perf] define table constant for cache." This reverts commit 13032535d753725d03768e3468f219f24559a072. --- config/redis.php | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/config/redis.php b/config/redis.php index 03a60f9fbe..bc57247ec2 100644 --- a/config/redis.php +++ b/config/redis.php @@ -7,23 +7,15 @@ $config->redis->timeout = 10; $config->redis->username = null; $config->redis->password = null; -define('CACHE_CONFIG', TABLE_CONFIG); -define('CACHE_BUILD', TABLE_BUILD); -define('CACHE_MODULE', TABLE_MODULE); -define('CACHE_PRODUCT', TABLE_PRODUCT); -define('CACHE_PROJECT', TABLE_PROJECT); -define('CACHE_RELEASE', TABLE_RELEASE); -define('CACHE_USER', TABLE_USER); - // Format : $config->redis->caches[TABLE_NAME] = 'KEY_FIELD'; // The TABLE_NAME is the name of the table in the database. // The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table. $config->redis->caches = []; -$config->redis->caches[CACHE_CONFIG] = 'id'; -$config->redis->caches[CACHE_BUILD] = 'id'; -$config->redis->caches[CACHE_MODULE] = 'id'; -$config->redis->caches[CACHE_PRODUCT] = 'id'; -$config->redis->caches[CACHE_PROJECT] = 'id'; -$config->redis->caches[CACHE_RELEASE] = 'id'; -$config->redis->caches[CACHE_USER] = 'account'; +$config->redis->caches[TABLE_CONFIG] = 'id'; +$config->redis->caches[TABLE_BUILD] = 'id'; +$config->redis->caches[TABLE_MODULE] = 'id'; +$config->redis->caches[TABLE_PRODUCT] = 'id'; +$config->redis->caches[TABLE_PROJECT] = 'id'; +$config->redis->caches[TABLE_RELEASE] = 'id'; +$config->redis->caches[TABLE_USER] = 'account'; From 914ba50a551efd850cdb131838e9a2c87bf79cea Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 17:37:45 +0800 Subject: [PATCH 089/300] * [refac] compatible with model.class.php. --- module/cache/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/cache/model.php b/module/cache/model.php index 9274257a52..2d91063088 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -106,7 +106,7 @@ class cacheModel extends model * @access public * @return cacheModel */ - public function __call(string $method, array $args): cacheModel + public function __call(string $method, array $args) { if(!isset($this->config->cache->keys[$this->module])) return $this->log('The module is not exist.'); From 1c79e4315e90e99fcc6b735169213766aa2971f7 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 18:04:19 +0800 Subject: [PATCH 090/300] - [refac] remove unused code and file. --- module/cache/config.php | 7 ---- module/cache/model.php | 86 +++++------------------------------------ 2 files changed, 9 insertions(+), 84 deletions(-) delete mode 100644 module/cache/config.php diff --git a/module/cache/config.php b/module/cache/config.php deleted file mode 100644 index 7a14fbf008..0000000000 --- a/module/cache/config.php +++ /dev/null @@ -1,7 +0,0 @@ -cache->keys = []; -$config->cache->keys['user'] = []; -$config->cache->keys['user']['getpairs'] = ['type' => 'array', 'filter' => 'encode']; - -$config->cache->keys['bug']['openedby'] = ['type' => 'array']; -$config->cache->keys['bug']['assignedto'] = ['type' => 'array']; diff --git a/module/cache/model.php b/module/cache/model.php index 2d91063088..08ef5f4032 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -82,8 +82,6 @@ class cacheModel extends model { $this->reset(); - if(!isset($this->config->cache->keys[$module])) return $this->log("The cache configuration of the module {$module} is not exist."); - $this->setModule($module); if(isset($this->$module)) return $this; @@ -108,8 +106,6 @@ class cacheModel extends model */ public function __call(string $method, array $args) { - if(!isset($this->config->cache->keys[$this->module])) return $this->log('The module is not exist.'); - $this->setMethod($method); $key = "res:{$this->module}:{$method}"; @@ -215,25 +211,7 @@ class cacheModel extends model */ public function value(int|string|object|array $value): cacheModel { - if(empty($this->config->cache->keys[$this->module][$this->method]['type'])) return $this->log("The cache configuration of [{$this->module}][{$this->method}] is not exist."); - - $config = $this->config->cache->keys[$this->module][$this->method]; - $type = $config['type']; - $filter = isset($config['filter']) ? $config['filter'] : ''; - - if($type == 'int' && !is_int($value)) return $this->log('The value must be int.'); - if($type == 'string' && !is_string($value)) return $this->log('The value must be string.'); - if($type == 'object' && !is_object($value)) return $this->log('The value must be object.'); - if($type == 'array') - { - if(!is_array($value)) return $this->log('The value must be array.'); - if($filter != 'encode' && !$this->isIndexedArray($array)) return $this->log('The value must be indexed array.'); - } - - if($type == 'object' || ($type == 'array' && $filter == 'encode')) $value = json_encode($value); - - $this->setValue($value); - + $this->setValue(json_encode($value)); return $this; } @@ -263,26 +241,9 @@ class cacheModel extends model */ public function fetch(): bool|int|string|object|array { - if(empty($this->config->cache->keys[$this->module][$this->method]['type'])) - { - $this->log("The cache configuration of [{$this->module}][{$this->method}] is not exist."); - return false; - } - - $config = $this->config->cache->keys[$this->module][$this->method]; - $type = $config['type']; - $filter = isset($config['filter']) ? $config['filter'] : ''; - - if($type == 'int') return (int)$this->app->redis->get($this->key); - if($type == 'string') return $this->app->redis->get($this->key); - if($type == 'object') return json_decode($this->app->redis->get($this->key)); - if($type == 'array') - { - if($filter == 'encode') return json_decode($this->app->redis->get($this->key)); - - return $this->app->redis->smembers($this->key); - } - return false; + $result = $this->app->redis->get($this->key); + if($result) return json_decode($result); + return $result; } /** @@ -297,43 +258,14 @@ class cacheModel extends model if(empty($this->value)) { $this->log('The value is empty.'); - return $this->result(false); - } - if(empty($this->config->cache->keys[$this->module][$this->method]['type'])) - { - $this->log("The cache configuration of [{$this->module}][{$this->method}] is not exist."); - return $this->result(false); + $this->reset(); + return false; } - $config = $this->config->cache->keys[$this->module][$this->method]; - $type = $config['type']; - $filter = isset($config['filter']) ? $config['filter'] : ''; - - if($type == 'int') return $this->result($this->app->redis->set($this->key, $this->value)); - if($type == 'string') return $this->result($this->app->redis->set($this->key, $this->value)); - if($type == 'object') return $this->result($this->app->redis->set($this->key, $this->value)); - if($type == 'array') - { - if($filter == 'encode') return $this->result($this->app->redis->set($this->key, $this->value)); - - return return $this->result($this->app->redis->sadd($this->key, ...$this->value)); - } - return $this->result(false); - } - - /** - * 返回结果。 - * Return the result. - * - * @param bool|int|string|object|array $result - * @access private - * @return bool|int|string|object|array - */ - private function result(bool|int|string|object|array $result): bool|int|string|object|array - { - if($result) $this->updateRelated(); + $this->app->redis->set($this->key, $this->value); + $this->updateRelatedKeys(); $this->reset(); - return $result; + return true; } /** From 28200cb33726d75c83f67fe18eb1650001144bea Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 24 Oct 2024 18:08:44 +0800 Subject: [PATCH 091/300] * [refac] fix error. --- module/cache/model.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/module/cache/model.php b/module/cache/model.php index 08ef5f4032..de330d6b5e 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -27,7 +27,7 @@ class cacheModel extends model * @access private * @var string */ - private $method = '' + private $method = ''; /** * 缓存的键。 @@ -63,7 +63,7 @@ class cacheModel extends model * @access public * @return void */ - public function __construct(): void + public function __construct() { parent::__construct(); @@ -104,17 +104,17 @@ class cacheModel extends model * @access public * @return cacheModel */ - public function __call(string $method, array $args) + public function __call($method, $args) { $this->setMethod($method); $key = "res:{$this->module}:{$method}"; $this->setKey($key); - if(empty($this->$module)) return $this; - if(!method_exists($this->$module, $method)) return $this->log("The method {$method} is not exist."); + if(empty($this->{$this->module})) return $this; + if(!method_exists($this->{$this->module}, $method)) return $this->log("The method {$method} is not exist."); - $key = call_user_func_array([$this->$module, $method], $args); + $key = call_user_func_array([$this->{$this->module}, $method], $args); $this->setKey($key); return $this; From 1b01378120296faf004aeebf1d1ec5a532af4d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Fri, 25 Oct 2024 01:48:44 +0800 Subject: [PATCH 092/300] * [perf] Fix error. --- framework/model.class.php | 2 +- lib/base/cache/cache.class.php | 86 ++++++++++++++++++++++++++++++++++ lib/zredis/zredis.class.php | 12 +++-- module/bug/model.php | 28 ++++++----- module/setting/model.php | 7 ++- 5 files changed, 116 insertions(+), 19 deletions(-) diff --git a/framework/model.class.php b/framework/model.class.php index 626b0d1678..2557e069b6 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -413,7 +413,7 @@ class model extends baseModel * @access public * @return mixed */ - public function __call($method, $arguments) + public function __call(string $method, array $arguments) { $moduleName = $this->getModuleName(); $taoClass = $moduleName . 'Tao'; diff --git a/lib/base/cache/cache.class.php b/lib/base/cache/cache.class.php index cdfe40c897..05b2516766 100644 --- a/lib/base/cache/cache.class.php +++ b/lib/base/cache/cache.class.php @@ -100,6 +100,24 @@ class baseCache */ public $conditionIsTrue; + /** + * 待处理的数据。 + * The data to be processed. + * + * @var array + * @access public + */ + public $data; + + /** + * 待处理数据的column,作为缓存的key。 + * The column as key. + * + * @var string + * @access public + */ + public $dataColumn; + /** * 构造方法。 * The construct method. @@ -429,6 +447,74 @@ class baseCache return $this; } + /** + * 选择处理的数据和column最为缓存的key。 + * Load data and select column as key. + * + * @param array $data + * @param string $field + * @access public + * @return static|cache. + */ + public function use($data, $column) + { + $this->data = $data; + $this->dataColumn = $column; + + return $this; + } + + /** + * data追加字段。 + * Append field to data. + * + * @param string $tableName + * @param string $fields + * @access public + * @return static|cache. + */ + public function append($tableName, $fields) + { + $fields = explode(',', $fields); + + $alias = []; + foreach($fields as $field) + { + $fieldInfo = explode(' ', trim($field)); + if(count($fieldInfo) == 1) + { + $alias[$fieldInfo[0]] = $fieldInfo[0]; + } + elseif(count($fieldInfo) == 2) + { + $alias[$fieldInfo[0]] = $fieldInfo[1]; + } + else + { + $alias[$fieldInfo[0]] = $fieldInfo[2]; + } + } + + $cacheKeys = []; + $key = $this->dataColumn; + foreach($this->data as $row) + { + if(!in_array($row->$key, $cacheKeys)) $cacheKeys[] = $row->$key; + } + $objects = $this->app->redis->fetchAll($tableName, $cacheKeys); + foreach($this->data as $row) + { + $object = $objects[$row->$key]; + foreach($alias as $aliasKey => $aliasValue) + { + $row->$aliasValue = $object->$aliasKey; + } + } + + return $this; + } + + //-------------------- Fetch相关方法(Fetch related methods) -------------------// /** diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index a118e91b1b..70159dd58b 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -261,13 +261,17 @@ class zredis if(!$keyList) $keyList = $this->redis->smembers("set:{$code}List"); foreach($keyList as $key) $keys[] = "raw:{$code}:{$key}"; - $objects = $this->redis->mget($keys); - foreach($objects as $key => $object) + $objects = $this->redis->mget($keys); + $cacheKey = $this->config->redis->caches[$table]; + + $result = []; + foreach($objects as $object) { - if($object) $objects[$key] = json_decode($object); + $object = json_decode($object); + if($object) $result[$object->$cacheKey] = $object; } - return $objects; + return $result; } /** diff --git a/module/bug/model.php b/module/bug/model.php index 7bf7725dd0..38db25bef0 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -856,27 +856,29 @@ class bugModel extends model $query = preg_replace('/`(\w+)`/', 't1.`$1`', $query); if($moduleName == 'contributeBug') $bugsAssignedByMe = $this->loadModel('my')->getAssignedByMe($account, null, $orderBy, 'bug'); - return $this->dao->select("t1.*, t2.name AS productName, t2.shadow, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) AS priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) AS severityOrder")->from(TABLE_BUG)->alias('t1') - ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id') - ->where('t1.deleted')->eq(0) - ->andWhere('t2.deleted')->eq('0') + + $bugs = $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) AS priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) AS severityOrder")->from(TABLE_BUG) + ->where('deleted')->eq(0) ->beginIF($type == 'bySearch')->andWhere($query)->fi() - ->beginIF($executionID)->andWhere('t1.execution')->eq($executionID)->fi() - ->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('t1.status')->ne('closed')->fi() - ->beginIF($type != 'all' and $type != 'bySearch')->andWhere("t1.`$type`")->eq($account)->fi() - ->beginIF($type == 'bySearch' and $moduleName == 'workBug')->andWhere("t1.assignedTo")->eq($account)->fi() - ->beginIF($type == 'assignedTo' and $moduleName == 'workBug')->andWhere('t1.status')->ne('closed')->fi() + ->beginIF($executionID)->andWhere('execution')->eq($executionID)->fi() + ->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('status')->ne('closed')->fi() + ->beginIF($type != 'all' and $type != 'bySearch')->andWhere("`$type`")->eq($account)->fi() + ->beginIF($type == 'bySearch' and $moduleName == 'workBug')->andWhere("assignedTo")->eq($account)->fi() + ->beginIF($type == 'assignedTo' and $moduleName == 'workBug')->andWhere('status')->ne('closed')->fi() ->beginIF($type == 'bySearch' and $moduleName == 'contributeBug') - ->andWhere('t1.openedBy', 1)->eq($account) - ->orWhere('t1.closedBy')->eq($account) - ->orWhere('t1.resolvedBy')->eq($account) - ->orWhere('t1.id')->in(!empty($bugsAssignedByMe) ? array_keys($bugsAssignedByMe) : array()) + ->andWhere('openedBy', 1)->eq($account) + ->orWhere('closedBy')->eq($account) + ->orWhere('resolvedBy')->eq($account) + ->orWhere('id')->in(!empty($bugsAssignedByMe) ? array_keys($bugsAssignedByMe) : array()) ->markRight(1) ->fi() ->orderBy($orderBy) ->beginIF($limit > 0)->limit($limit)->fi() ->page($pager) ->fetchAll(); + + $this->cache->cache->use($bugs, 'product')->append(TABLE_PRODUCT, 'name AS productName, shadow'); + return $bugs; } /** diff --git a/module/setting/model.php b/module/setting/model.php index a32c992d9b..a8bd3e6e02 100644 --- a/module/setting/model.php +++ b/module/setting/model.php @@ -244,7 +244,12 @@ class settingModel extends model */ public function getSysAndPersonalConfig(string $account = ''): array { - $records = $this->cache->setting->getSysAndPersonalConfig($account); + $owner = 'system,' . ($account ? $account : ''); + $records = $this->dao->select('*')->from(TABLE_CONFIG) + ->where('owner')->in($owner) + ->beginIF(!$this->app->upgrading)->andWhere('vision')->in(array('', $this->config->vision))->fi() + ->orderBy('id') + ->fetchAll('id'); if(!$records) return array(); $vision = $this->config->vision; From e530add9895b51547010c802830a09a87dd05124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Fri, 25 Oct 2024 02:07:55 +0800 Subject: [PATCH 093/300] * [perf] rename cache to mao --- framework/base/model.class.php | 8 +++--- framework/base/router.class.php | 27 +++---------------- .../cache.class.php => mao/mao.class.php} | 14 +++++----- .../cache.class.php => mao/mao.class.php} | 4 +-- module/bug/model.php | 2 +- module/cache/model.php | 1 - 6 files changed, 17 insertions(+), 39 deletions(-) rename lib/base/{cache/cache.class.php => mao/mao.class.php} (98%) rename lib/{cache/cache.class.php => mao/mao.class.php} (79%) diff --git a/framework/base/model.class.php b/framework/base/model.class.php index f2410395e5..7324bb064e 100644 --- a/framework/base/model.class.php +++ b/framework/base/model.class.php @@ -124,13 +124,13 @@ class baseModel public $global; /** - * $cache对象,用于访问缓存。 - * The $cache object, used to access the cache. + * $mao对象,用于访问缓存。 + * The $mao object, used to access the cache. * * @var object * @access public */ - public $cache; + public $mao; /** * 构造方法。 @@ -153,7 +153,7 @@ class baseModel $this->lang = $lang; $this->dbh = $dbh; $this->dao = $dao; - $this->cache = $app->cacheModel; + $this->mao = $app->mao; $this->appName = empty($appName) ? $this->app->getAppName() : $appName; $moduleName = $this->getModuleName(); diff --git a/framework/base/router.class.php b/framework/base/router.class.php index b0d3b02da6..742fab944f 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -1588,26 +1588,6 @@ class baseRouter //-------------------- 模块及扩展设置(Module and extension) --------------------// - /** - * 加载cache模块。 - * Load the cache module - * - * @access public - * @return void. - */ - public function loadCache(): void - { - if(!$this->checkInstalled()) return; - - $this->setModuleName('cache'); - $cacheModelFile = $this->setModelFile('cache'); - if(!file_exists($cacheModelFile)) return; - - helper::import($cacheModelFile); - - $this->cacheModel = new cacheModel(); - } - /** * 加载common模块。 * @@ -1626,8 +1606,6 @@ class baseRouter */ public function loadCommon(): object|bool { - $this->loadCache(); - $this->setModuleName('common'); $commonModelFile = $this->setModelFile('common'); if(!file_exists($commonModelFile)) return false; @@ -1656,8 +1634,6 @@ class baseRouter $this->setDebug(); - $common->cache = $this->cacheModel; - return $common; } @@ -3099,6 +3075,9 @@ class baseRouter $dao = new $driver($this); $this->dao = $dao; + + $this->loadClass('mao', true); + $this->mao = new mao($this); } /** diff --git a/lib/base/cache/cache.class.php b/lib/base/mao/mao.class.php similarity index 98% rename from lib/base/cache/cache.class.php rename to lib/base/mao/mao.class.php index 05b2516766..db14240ad5 100644 --- a/lib/base/cache/cache.class.php +++ b/lib/base/mao/mao.class.php @@ -1,7 +1,7 @@ app = $app; $this->config = $config; diff --git a/lib/cache/cache.class.php b/lib/mao/mao.class.php similarity index 79% rename from lib/cache/cache.class.php rename to lib/mao/mao.class.php index c3c7c85e1f..3c399fd4a3 100644 --- a/lib/cache/cache.class.php +++ b/lib/mao/mao.class.php @@ -9,8 +9,8 @@ * @link https://www.zentao.net */ -helper::import(dirname(__FILE__, 2) . '/base/cache/cache.class.php'); +helper::import(dirname(__FILE__, 2) . '/base/mao/mao.class.php'); -class cache extends baseCache +class mao extends baseMao { } diff --git a/module/bug/model.php b/module/bug/model.php index 38db25bef0..d6abd8e573 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -877,7 +877,7 @@ class bugModel extends model ->page($pager) ->fetchAll(); - $this->cache->cache->use($bugs, 'product')->append(TABLE_PRODUCT, 'name AS productName, shadow'); + $this->mao->use($bugs, 'product')->append(TABLE_PRODUCT, 'name AS productName, shadow'); return $bugs; } diff --git a/module/cache/model.php b/module/cache/model.php index de330d6b5e..60b210f89a 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -67,7 +67,6 @@ class cacheModel extends model { parent::__construct(); - $this->cache = $this->app->loadClass('cache'); } /** From 88d267c50eb27383e552a1a2529a2c81dd97abc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Fri, 25 Oct 2024 09:27:44 +0800 Subject: [PATCH 094/300] + [feat] Add getByKey of mao. --- lib/base/mao/mao.class.php | 110 ++++++++++++++++++++++++++---------- lib/zredis/zredis.class.php | 27 ++++++--- 2 files changed, 101 insertions(+), 36 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index db14240ad5..fdeccd4d7c 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -110,22 +110,41 @@ class baseMao public $data; /** - * 待处理数据的column,作为缓存的key。 - * The column as key. + * 待处理数据的column。 + * The column of data. * * @var string * @access public */ public $dataColumn; + /** + * The labels to get or set。 + * The labels to get or set. + * + * @var array + * @access public + */ + public $labels; + + /** + * 当前设置的label。 + * Current label. + * + * @var string + * @access public + */ + public $currentLabel; + /** * 构造方法。 * The construct method. * * @access public + * @param object $app * @return void */ - public function __construct($app) + public function __construct(object $app) { global $config; $this->app = $app; @@ -142,7 +161,7 @@ class baseMao * @access public * @return void */ - public function setTable($table) + public function setTable(string $table) { $this->table = $table; } @@ -155,7 +174,7 @@ class baseMao * @access public * @return void */ - public function setFields($fields) + public function setFields(string $fields) { $this->fields = $fields; } @@ -164,11 +183,11 @@ class baseMao * 设置条件。 * Set the $conditions property. * - * @param string $conditions + * @param array $conditions * @access public * @return void */ - public function setConditions($conditions) + public function setConditions(array $conditions) { $this->conditions = $conditions; $this->resetCondition(); @@ -245,7 +264,7 @@ class baseMao * @access public * @return static|sql|baseDAO the dao object self. */ - public function select($fields = '*') + public function select(string $fields = '*') { $this->setFields(explode(',', str_replace(' ', '', $fields))); return $this; @@ -255,13 +274,13 @@ class baseMao * 设置要操作的表。 * Set the from table. * - * @param string $table + * @param string $tableName * @access public * @return static|sql the dao object self. */ - public function from($table) + public function from(string $tableName) { - $this->setTable($table); + $this->setTable($tableName); return $this; } @@ -273,7 +292,7 @@ class baseMao * @access public * @return static|sql the sql object. */ - public function beginIF($conditionResult) + public function beginIF(bool $conditionResult) { $this->isConditionChecking = true; $this->conditionIsTrue = $conditionResult; @@ -311,7 +330,7 @@ class baseMao * @access public * @return static the dao object. */ - public function where($field) + public function where(string $field) { $this->resetCondition(); $this->condition['field'] = $field; @@ -326,7 +345,7 @@ class baseMao * @access public * @return static the dao object. */ - public function andWhere($field) + public function andWhere(string $field) { $this->resetCondition(); $this->condition['field'] = $field; @@ -341,7 +360,7 @@ class baseMao * @access public * @return static the dao object. */ - public function eq($value) + public function eq(string $value) { $this->condition['operator'] = 'eq'; $this->condition['value'] = $value; @@ -363,7 +382,7 @@ class baseMao * @access public * @return static the dao object. */ - public function ne($value) + public function ne(string $value) { $this->condition['operator'] = 'ne'; $this->condition['value'] = $value; @@ -381,11 +400,11 @@ class baseMao * 创建in部分。 * Create the in part. * - * @param string $value + * @param string|array $value * @access public * @return static the dao object. */ - public function in($value) + public function in(string|array $value) { $this->condition['operator'] = 'in'; $this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value; @@ -403,11 +422,11 @@ class baseMao * 创建notin部分。 * Create the in part. * - * @param string $value + * @param string|array $value * @access public * @return static the dao object. */ - public function notin($value) + public function notin(string|array $value) { $this->condition['operator'] = 'notin'; $this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value; @@ -429,7 +448,7 @@ class baseMao * @access public * @return static|sql the sql object. */ - public function orderBy($order) + public function orderBy(string $order) { return $this; } @@ -438,11 +457,11 @@ class baseMao * 创建LIMIT部分。 * Create the limit part. * - * @param string $limit + * @param int $limit * @access public * @return static|sql the sql object. */ - public function limit($limit) + public function limit(int $limit) { return $this; } @@ -456,7 +475,7 @@ class baseMao * @access public * @return static|cache. */ - public function use($data, $column) + public function use(array $data, string $column) { $this->data = $data; $this->dataColumn = $column; @@ -473,7 +492,7 @@ class baseMao * @access public * @return static|cache. */ - public function append($tableName, $fields) + public function append(string $tableName, string $fields) { $fields = explode(',', $fields); @@ -526,7 +545,7 @@ class baseMao * @access public * @return bool */ - private function isConditionMatched($object, $conditions) + private function isConditionMatched(object $object, array $conditions) { foreach($conditions as $condition) { @@ -565,7 +584,7 @@ class baseMao * @access public * @return object|mixed */ - public function fetch($field = '') + public function fetch(string $field = '') { $rawResult = $this->app->redis->fetchAll($this->table); if(empty($rawResult)) return ''; @@ -589,7 +608,7 @@ class baseMao * @access public * @return array the records */ - public function fetchAll($keyField = 0) + public function fetchAll(string $keyField = '') { $rawResult = $this->app->redis->fetchAll($this->table); if(empty($rawResult)) return []; @@ -635,7 +654,7 @@ class baseMao * @access public * @return array */ - public function fetchPairs($keyField = '', $valueField = '') + public function fetchPairs(string $keyField = '', string $valueField = '') { $pairs = []; @@ -652,4 +671,37 @@ class baseMao return $pairs; } + + /** + * 通过Key获取值 + * Get value by key. + * + * + * @param string $key + * @access public + * @return object + */ + public function getByKey(string $key): object + { + $this->labels[$this->currentLabel] = $key; + $this->lastLabel = ''; + + return $this->app->redis->get($key); + } + + /** + * 设置label. + * Set label. + * + * + * @param string $key + * @access public + * @return object + */ + public function label(string $label) + { + $this->currentLabel = $label; + + return $this; + } } diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 70159dd58b..19450ec61e 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -96,11 +96,11 @@ class zredis * 连接 Redis 服务器并初始化缓存。 * Connect to Redis server and initialize cache. * - * @param dao $dao + * @param object $dao * @access public * @return void */ - public function __construct($app) + public function __construct(object $app) { global $config; @@ -218,6 +218,22 @@ class zredis $this->reset(); } + /** + * 根据key获取值。 + * Get value by key. + * + * @param string $key + * @access public + * @return object + */ + public function get(string $key): object + { + $value = $this->redis->get($key); + if(!$value) return $value; + + return json_decode($value); + } + /** * 根据表名和键获取对象。 * Get object by table name and key. @@ -234,11 +250,8 @@ class zredis if(empty($key)) return $this->log('Key is required.'); if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); - $code = str_replace(['`', $this->config->db->prefix], '', $table); - $object = $this->redis->get("raw:{$code}:{$key}"); - if(!$object) return $object; - - return json_decode($object); + $code = str_replace(['`', $this->config->db->prefix], '', $table); + return $this->get("raw:{$code}:{$key}"); } /** From af6067100e3e218e7e3f703f0933a24e5658c035 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 13:32:11 +0800 Subject: [PATCH 095/300] + [refac] add method to set cache in redis. --- lib/zredis/zredis.class.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 19450ec61e..0ccd524e5e 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -234,6 +234,20 @@ class zredis return json_decode($value); } + /** + * 设置缓存。 + * Set cache. + * + * @param string $key + * @param mixed $value + * @access public + * @return void + */ + public function set(string $key, $value) + { + $this->redis->set($key, json_encode($value, JSON_UNESCAPED_UNICODE)); + } + /** * 根据表名和键获取对象。 * Get object by table name and key. From 26d504325e4ecff3b19e7bcb4f491eb18027c868 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 13:37:23 +0800 Subject: [PATCH 096/300] + [refac] add method to set cache. --- lib/base/mao/mao.class.php | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index fdeccd4d7c..2e49a050c6 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -136,6 +136,15 @@ class baseMao */ public $currentLabel; + /** + * 当前设置的 key。 + * Current key. + * + * @var string + * @access private + */ + private $currentKey; + /** * 构造方法。 * The construct method. @@ -683,6 +692,7 @@ class baseMao */ public function getByKey(string $key): object { + $this->currentKey = $key; $this->labels[$this->currentLabel] = $key; $this->lastLabel = ''; @@ -704,4 +714,37 @@ class baseMao return $this; } + + /** + * 根据 key 设置缓存。 + * Set cache by key. + * + * @param mixed $value + * @param string $key + * @access public + * @return void + */ + public function setByKey($value, string $key = '') + { + if(empty($key)) $key = $this->currentKey; + if(empty($key)) return false; + $this->app->redis->set($key, $value); + } + + /** + * 根据 label 设置缓存。 + * Set cache by label. + * + * @param mixed $value + * @param string $label + * @access public + * @return void + */ + public function setByLabel($value, string $label = '') + { + if(empty($label)) $label = $this->currentLabel; + if(empty($this->labels[$label])) return false; + + $this->app->redis->set($this->labels[$label], $value); + } } From 1e620c2c79e92c2bbf3071add997c694a8f7311b Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 13:38:35 +0800 Subject: [PATCH 097/300] * [perf] fix error of return type. --- lib/zredis/zredis.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 0ccd524e5e..1bd23d0e40 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -224,9 +224,9 @@ class zredis * * @param string $key * @access public - * @return object + * @return mixed */ - public function get(string $key): object + public function get(string $key) { $value = $this->redis->get($key); if(!$value) return $value; From 8bc5c90c865ed0633ed38f6bd3c33d10502c4bac Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 13:39:11 +0800 Subject: [PATCH 098/300] * [refac] fix error of return type. --- lib/base/mao/mao.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 2e49a050c6..c419a5815d 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -688,9 +688,9 @@ class baseMao * * @param string $key * @access public - * @return object + * @return mixed */ - public function getByKey(string $key): object + public function getByKey(string $key) { $this->currentKey = $key; $this->labels[$this->currentLabel] = $key; From 8671b4d179f050728df7cfc3606bd16e7bb7af31 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 15:50:12 +0800 Subject: [PATCH 099/300] * [refac] raname config->redis->caches to config->redis->cache->raw. --- config/redis.php | 19 ++++++++------- lib/zredis/zredis.class.php | 48 ++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/config/redis.php b/config/redis.php index bc57247ec2..b23d707f9e 100644 --- a/config/redis.php +++ b/config/redis.php @@ -7,15 +7,16 @@ $config->redis->timeout = 10; $config->redis->username = null; $config->redis->password = null; -// Format : $config->redis->caches[TABLE_NAME] = 'KEY_FIELD'; +// Format : $config->redis->cache->raw[TABLE_NAME] = 'KEY_FIELD'; // The TABLE_NAME is the name of the table in the database. // The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table. -$config->redis->caches = []; -$config->redis->caches[TABLE_CONFIG] = 'id'; -$config->redis->caches[TABLE_BUILD] = 'id'; -$config->redis->caches[TABLE_MODULE] = 'id'; -$config->redis->caches[TABLE_PRODUCT] = 'id'; -$config->redis->caches[TABLE_PROJECT] = 'id'; -$config->redis->caches[TABLE_RELEASE] = 'id'; -$config->redis->caches[TABLE_USER] = 'account'; +$config->redis->cache = new stdClass(); +$config->redis->cache->raw = []; +$config->redis->cache->raw[TABLE_CONFIG] = 'id'; +$config->redis->cache->raw[TABLE_BUILD] = 'id'; +$config->redis->cache->raw[TABLE_MODULE] = 'id'; +$config->redis->cache->raw[TABLE_PRODUCT] = 'id'; +$config->redis->cache->raw[TABLE_PROJECT] = 'id'; +$config->redis->cache->raw[TABLE_RELEASE] = 'id'; +$config->redis->cache->raw[TABLE_USER] = 'account'; diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 1bd23d0e40..60cb7701bb 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -147,11 +147,11 @@ class zredis */ public function prepare(string $table, string $event, string $sql) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($event)) return $this->log('Event type is required.'); - if(empty($sql)) return $this->log('SQL is required.'); - if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($event)) return $this->log('Event type is required.'); + if(empty($sql)) return $this->log('SQL is required.'); + if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); $this->table = $table; $this->event = $event; @@ -182,7 +182,7 @@ class zredis if($event == 'delete') { /* 执行删除操作后数据已经被删除,所以需要提前获取 ID 列表。*/ - $field = $this->config->redis->caches[$table]; + $field = $this->config->redis->cache->raw[$table]; $keyList = $this->dao->select($field)->from($table)->beginIF($condition)->where($condition)->fi()->fetchPairs(); } } @@ -200,10 +200,10 @@ class zredis */ public function sync() { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($this->table)) return $this->log('Table name is required.'); - if(empty($this->event)) return $this->log('Event type is required.'); - if(empty($this->config->redis->caches[$this->table])) return $this->log('No cache settings for table ' . $this->table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($this->table)) return $this->log('Table name is required.'); + if(empty($this->event)) return $this->log('Event type is required.'); + if(empty($this->config->redis->cache->raw[$this->table])) return $this->log('No cache settings for table ' . $this->table); $this->redis->multi(); @@ -259,10 +259,10 @@ class zredis */ public function fetch(string $table, int|string $key) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($key)) return $this->log('Key is required.'); - if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($key)) return $this->log('Key is required.'); + if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); $code = str_replace(['`', $this->config->db->prefix], '', $table); return $this->get("raw:{$code}:{$key}"); @@ -279,9 +279,9 @@ class zredis */ public function fetchAll(string $table, array $keyList = []) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); $keys = []; $code = str_replace(['`', $this->config->db->prefix], '', $table); @@ -289,7 +289,7 @@ class zredis foreach($keyList as $key) $keys[] = "raw:{$code}:{$key}"; $objects = $this->redis->mget($keys); - $cacheKey = $this->config->redis->caches[$table]; + $cacheKey = $this->config->redis->cache->raw[$table]; $result = []; foreach($objects as $object) @@ -314,11 +314,11 @@ class zredis */ public function fetchPairs(string $table, string $key, string $value, array $keyList = []) { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($key)) return $this->log('Key is required.'); - if(empty($value)) return $this->log('Value is required.'); - if(empty($this->config->redis->caches[$table])) return $this->log('No cache settings for table ' . $table); + if(empty($this->redis)) return $this->log('Redis is not initialized.'); + if(empty($table)) return $this->log('Table name is required.'); + if(empty($key)) return $this->log('Key is required.'); + if(empty($value)) return $this->log('Value is required.'); + if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); $objects = $this->fetchAll($table, $keyList); if(!$objects) return []; @@ -360,7 +360,7 @@ class zredis if(!$objects) return $this->log("Failed to fetch objects to update table {$this->table}.", $this->dao->get()); $code = str_replace(['`', $this->config->db->prefix], '', $this->table); - $key = $this->config->redis->caches[$this->table]; + $key = $this->config->redis->cache->raw[$this->table]; $pairs = []; foreach($objects as $object) $pairs["raw:{$code}:{$object->$key}"] = json_encode($object, JSON_UNESCAPED_UNICODE); From 6d98a6c719cfee8ebfc69deea6de2820a49bc11d Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 15:51:14 +0800 Subject: [PATCH 100/300] + [refac] add configuration items of redis cache. --- config/redis.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/config/redis.php b/config/redis.php index b23d707f9e..edc67668a7 100644 --- a/config/redis.php +++ b/config/redis.php @@ -20,3 +20,18 @@ $config->redis->cache->raw[TABLE_PRODUCT] = 'id'; $config->redis->cache->raw[TABLE_PROJECT] = 'id'; $config->redis->cache->raw[TABLE_RELEASE] = 'id'; $config->redis->cache->raw[TABLE_USER] = 'account'; + +$config->redis->cache->res = []; +$config->redis->cache->res['module'][] = ['key' => 'CACHE_MODULE_TREE', 'params' => ['type', 'root', 'branch']]; + +$config->redis->cache->res['user'][] = ['key' => 'CACHE_USER_PAIRS']; + +foreach($config->redis->cache->res as $module => $caches) +{ + foreach($caches as $cache) + { + $key = $cache['key']; + $value = str_replace('_', ':', strtolower($cache['key'])); + define($key, $value); + } +} From ad31506b83080dada855f510ee462ac6bda9b1b1 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 15:51:50 +0800 Subject: [PATCH 101/300] * [refac] refactor the method to get cache from redis. --- lib/base/mao/mao.class.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index c419a5815d..3107c28044 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -687,11 +687,17 @@ class baseMao * * * @param string $key + * @param mixed ...$arguments * @access public * @return mixed */ - public function getByKey(string $key) + public function getByKey(string $key, ...$arguments) { + if(!defined($key)) return false; + + $key = constant($key); + foreach($arguments as $argument) $key .= ':' . $argument; + $this->currentKey = $key; $this->labels[$this->currentLabel] = $key; $this->lastLabel = ''; @@ -703,7 +709,6 @@ class baseMao * 设置label. * Set label. * - * * @param string $key * @access public * @return object @@ -728,6 +733,7 @@ class baseMao { if(empty($key)) $key = $this->currentKey; if(empty($key)) return false; + $this->app->redis->set($key, $value); } From b97a8abe3b0aab4ef269d85251c8423712963530 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 15:54:06 +0800 Subject: [PATCH 102/300] * [refac] add example to get cache by mao object. --- module/product/zen.php | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/module/product/zen.php b/module/product/zen.php index 57d4987020..561a0a2b98 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -864,14 +864,26 @@ class productZen extends product $project = $projectID ? $this->loadModel('project')->getByID($projectID) : null; if(!empty($projectID) && !empty($project->hasProduct) && $this->app->rawModule == 'projectstory') { - return $this->tree->getProjectStoryTreeMenu($projectID, 0, array('treeModel', $createModuleLink), array('storyType' => $storyType)); + $moduleTree = $this->app->mao->label('projectModuleTree')->getByKey('CACHE_MODULE_TREE', 'project', $projectID, $storyType); + if(!$moduleTree) + { + $moduleTree = $this->tree->getProjectStoryTreeMenu($projectID, 0, array('treeModel', $createModuleLink), array('storyType' => $storyType)); + $this->app->mao->setByLabel($moduleTree, 'projectModuleTree'); + } + return $moduleTree; } /* Pre generate parameters. */ $userFunc = array('treeModel', $createModuleLink); $extra = array('projectID' => $projectID, 'productID' => $productID); - return $this->tree->getTreeMenu($productID, 'story', 0, $userFunc, $extra, $branch, "¶m=$param&storyType=$storyType"); + $moduleTree = $this->app->mao->label('productModuleTree')->getByKey('CACHE_MODULE_TREE', 'product', $productID, $branch, $storyType, $param); + if(!$moduleTree) + { + $moduleTree = $this->tree->getTreeMenu($productID, 'story', 0, $userFunc, $extra, $branch, "¶m=$param&storyType=$storyType"); + $this->app->mao->setByLabel($moduleTree, 'productModuleTree'); + } + return $moduleTree; } /** @@ -1440,10 +1452,20 @@ class productZen extends product $module = $this->app->tab == 'product' ? $storyType : $this->app->tab; $this->view->showGrades = !empty($this->config->{$module}->showGrades) ? $this->config->{$module}->showGrades : $this->story->getDefaultShowGrades($this->view->gradeMenu); + $users = $this->app->mao->getByKey('CACHE_USER_PAIRS'); + if($users) + { + $users = (array)$users; + } + else + { + $users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted'); + $this->app->mao->setByKey($users); + } $storyType = $isProjectStory ? 'all' : $storyType; $this->view->summary = $this->product->summary($stories, $storyType); $this->view->plans = $this->loadModel('productplan')->getPairs($productID, isset($projectProducts[$productID]) ? array(BRANCH_MAIN) + $projectProducts[$productID]->branches : (($branch === 'all' || empty($branch)) ? '' : $branch), 'unexpired,noclosed', true); - $this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted'); + $this->view->users = $users; $this->view->modules = $this->loadModel('tree')->getOptionMenu($productID, 'story', 0, $branchID); $this->view->storyTasks = $this->loadModel('task')->getStoryTaskCounts($storyIdList); $this->view->storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList); From 8bd0879e885fdd1dd45fe532fa504dcfacf34c2a Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 16:18:53 +0800 Subject: [PATCH 103/300] * [refac] resort methods. --- lib/base/mao/mao.class.php | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 3107c28044..e65854f839 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -681,11 +681,25 @@ class baseMao return $pairs; } + /** + * 设置label. + * Set label. + * + * @param string $key + * @access public + * @return object + */ + public function label(string $label) + { + $this->currentLabel = $label; + + return $this; + } + /** * 通过Key获取值 * Get value by key. * - * * @param string $key * @param mixed ...$arguments * @access public @@ -705,21 +719,6 @@ class baseMao return $this->app->redis->get($key); } - /** - * 设置label. - * Set label. - * - * @param string $key - * @access public - * @return object - */ - public function label(string $label) - { - $this->currentLabel = $label; - - return $this; - } - /** * 根据 key 设置缓存。 * Set cache by key. From dcf0eb4eff49a4a9aaf22d4dbf2b9bc47ef399c0 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 17:14:29 +0800 Subject: [PATCH 104/300] + [refac] add a method to set value of current key. --- lib/base/mao/mao.class.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index e65854f839..4e9030b462 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -719,6 +719,20 @@ class baseMao return $this->app->redis->get($key); } + /** + * 给当前 key 设置值。 + * Set value to current key. + * + * @param mixed $value + * @access public + * @return void + */ + public function set($value) + { + if(empty($this->currentKey)) return false; + $this->app->redis->set($this->currentKey, $value); + } + /** * 根据 key 设置缓存。 * Set cache by key. From 805702b0d1f64248ffeee26f07b7e3423c72b25f Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 25 Oct 2024 17:22:01 +0800 Subject: [PATCH 105/300] * [refac] resort the order of parameters. --- lib/base/mao/mao.class.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 4e9030b462..f17a4657fd 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -734,34 +734,33 @@ class baseMao } /** - * 根据 key 设置缓存。 + * 给指定 key 设置值。 * Set cache by key. * - * @param mixed $value * @param string $key + * @param mixed $value * @access public * @return void */ - public function setByKey($value, string $key = '') + public function setByKey(string $key, $value) { - if(empty($key)) $key = $this->currentKey; if(empty($key)) return false; $this->app->redis->set($key, $value); } /** - * 根据 label 设置缓存。 + * 给指定 label 设置值。 * Set cache by label. * - * @param mixed $value * @param string $label + * @param mixed $value * @access public * @return void */ - public function setByLabel($value, string $label = '') + public function setByLabel(string $label, $value) { - if(empty($label)) $label = $this->currentLabel; + if(empty($label)) return false; if(empty($this->labels[$label])) return false; $this->app->redis->set($this->labels[$label], $value); From 544879c8f176c74e87073d40d99f2f9458b7a7bb Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 28 Oct 2024 10:57:24 +0800 Subject: [PATCH 106/300] * [refac] rename set to save. --- lib/base/mao/mao.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index f17a4657fd..75a718a3eb 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -727,7 +727,7 @@ class baseMao * @access public * @return void */ - public function set($value) + public function save($value) { if(empty($this->currentKey)) return false; $this->app->redis->set($this->currentKey, $value); From 6f643638eaa121ddf5a4995cde57764c1ab624df Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 28 Oct 2024 13:08:26 +0800 Subject: [PATCH 107/300] + [refac] add $mao object to baseControl. --- framework/base/control.class.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/base/control.class.php b/framework/base/control.class.php index b18aa6d2ac..eb67021721 100644 --- a/framework/base/control.class.php +++ b/framework/base/control.class.php @@ -168,6 +168,15 @@ class baseControl */ public $devicePrefix; + /** + * $mao对象,用于访问缓存。 + * The $mao object, used to access the cache. + * + * @var object + * @access public + */ + public $mao; + /** * 构造方法。 * @@ -198,6 +207,7 @@ class baseControl $this->config = $config; $this->lang = $lang; $this->dbh = $dbh; + $this->mao = $app->mao; $this->viewType = $this->app->getViewType(); $this->appName = $appName ?: $this->app->getAppName(); From 9e8eef5ff7aed45e9976682ea94b5b8a3d6f7ae7 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 28 Oct 2024 17:31:36 +0800 Subject: [PATCH 108/300] + [refac] create key by arguments. --- lib/base/mao/mao.class.php | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 75a718a3eb..25ed8663e6 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -696,6 +696,55 @@ class baseMao return $this; } + /** + * 把数组转换成字符串。 + * Convert array to string. + * + * @param array $args + * @access private + * @return string + */ + private function recrusiveArgs(array $args): string + { + $result = []; + foreach($args as $key => $arg) + { + if(is_object($arg)) $arg = (array)$arg; + if(is_array($arg)) + { + if(array_values($arg) === $arg) + { + sort($arg); + } + else + { + ksort($arg); + } + $result[] = $key . '=' . $this->recrusiveArgs($arg); + } + else + { + $result[] = $key . '=' . $arg; + } + } + + return implode('&', $result); + } + + /** + * 创建缓存的key。 + * Create cache key. + * + * @param mixed ...$args + * @access public + * @return string + */ + public function createKey(...$args) + { + $args = $this->recrusiveArgs($args); + return substr(sha1($args), 0, 7); + } + /** * 通过Key获取值 * Get value by key. From 10ceb0081b076aa535cceab67bbce2d7a3e8cc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Mon, 28 Oct 2024 09:55:59 +0800 Subject: [PATCH 109/300] * [perf] Fix type of fields in mao. --- lib/base/mao/mao.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 25ed8663e6..c3aded9229 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -59,7 +59,7 @@ class baseMao * 查询的字段。 * The fields will be returned. * - * @var string + * @var array * @access public */ public $fields; @@ -179,11 +179,11 @@ class baseMao * 设置$fields属性。 * Set the $fields property. * - * @param string $fields + * @param array $fields * @access public * @return void */ - public function setFields(string $fields) + public function setFields(array $fields) { $this->fields = $fields; } @@ -236,7 +236,7 @@ class baseMao */ public function reset() { - $this->setFields(''); + $this->setFields(array()); $this->setTable(''); $this->setConditions([]); } From 95ec5bc55f7a43dcd04da4ae55d31bde03cfd339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Tue, 29 Oct 2024 09:14:06 +0800 Subject: [PATCH 110/300] * [perf] Fix condition of mao. --- lib/base/mao/mao.class.php | 8 +++++--- module/setting/model.php | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index c3aded9229..d1c0b15513 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -275,6 +275,8 @@ class baseMao */ public function select(string $fields = '*') { + $this->conditions = []; + $this->setFields(explode(',', str_replace(' ', '', $fields))); return $this; } @@ -297,14 +299,14 @@ class baseMao * 开始条件判断。 * Begin condition judge. * - * @param bool $condition + * @param bool|string $condition * @access public * @return static|sql the sql object. */ - public function beginIF(bool $conditionResult) + public function beginIF(bool|string $conditionResult) { $this->isConditionChecking = true; - $this->conditionIsTrue = $conditionResult; + $this->conditionIsTrue = (bool)$conditionResult; return $this; } diff --git a/module/setting/model.php b/module/setting/model.php index a8bd3e6e02..f422ba7572 100644 --- a/module/setting/model.php +++ b/module/setting/model.php @@ -226,7 +226,8 @@ class settingModel extends model $params['section'] = isset($params['section']) ? $params['section'] : ''; $params['key'] = isset($params['key']) ? $params['key'] : ''; - return $this->dao->$method('*')->from(TABLE_CONFIG)->where('1 = 1') + $access = $method == 'select' ? $this->mao : $this->dao; + return $access->$method('*')->from(TABLE_CONFIG)->where('1 = 1') ->beginIF($params['vision'])->andWhere('vision')->in($params['vision'])->fi() ->beginIF($params['owner'])->andWhere('owner')->in($params['owner'])->fi() ->beginIF($params['module'])->andWhere('module')->in($params['module'])->fi() @@ -245,7 +246,7 @@ class settingModel extends model public function getSysAndPersonalConfig(string $account = ''): array { $owner = 'system,' . ($account ? $account : ''); - $records = $this->dao->select('*')->from(TABLE_CONFIG) + $records = $this->mao->select('*')->from(TABLE_CONFIG) ->where('owner')->in($owner) ->beginIF(!$this->app->upgrading)->andWhere('vision')->in(array('', $this->config->vision))->fi() ->orderBy('id') From 8254d103e1eb4a8b3dbca43da930b91b0e5ad04a Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 29 Oct 2024 14:00:03 +0800 Subject: [PATCH 111/300] + [refac] return empty array if the key list is empty. --- lib/zredis/zredis.class.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php index 60cb7701bb..5a49f6df27 100644 --- a/lib/zredis/zredis.class.php +++ b/lib/zredis/zredis.class.php @@ -286,6 +286,8 @@ class zredis $keys = []; $code = str_replace(['`', $this->config->db->prefix], '', $table); if(!$keyList) $keyList = $this->redis->smembers("set:{$code}List"); + if(!$keyList) return []; + foreach($keyList as $key) $keys[] = "raw:{$code}:{$key}"; $objects = $this->redis->mget($keys); From 34575a5f902e6560aaa73b642203200124355a24 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 29 Oct 2024 14:33:39 +0800 Subject: [PATCH 112/300] * [refac] refactor configuration of result caches. --- config/redis.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/config/redis.php b/config/redis.php index edc67668a7..c05e1e865c 100644 --- a/config/redis.php +++ b/config/redis.php @@ -22,16 +22,16 @@ $config->redis->cache->raw[TABLE_RELEASE] = 'id'; $config->redis->cache->raw[TABLE_USER] = 'account'; $config->redis->cache->res = []; -$config->redis->cache->res['module'][] = ['key' => 'CACHE_MODULE_TREE', 'params' => ['type', 'root', 'branch']]; +$config->redis->cache->res[TABLE_MODULE][] = ['name' => 'CACHE_MODULE_TREE', 'fields' => ['type', 'root', 'branch']]; -$config->redis->cache->res['user'][] = ['key' => 'CACHE_USER_PAIRS']; - -foreach($config->redis->cache->res as $module => $caches) +$config->cache->keys = []; +foreach($config->redis->cache->res as $table => $caches) { foreach($caches as $cache) { - $key = $cache['key']; - $value = str_replace('_', ':', strtolower($cache['key'])); - define($key, $value); + $cache = (object)$cache; + $cache->table = $table; + $config->cache->keys[$cache->name] = $cache; + define($cache->name, $cache->name); } } From 4ccb6bf1b799d2986ec891739dd0272b06cdf663 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 29 Oct 2024 16:30:51 +0800 Subject: [PATCH 113/300] + [refac] create cache key based on the configuration. --- lib/base/mao/mao.class.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index d1c0b15513..b1d4d75810 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -759,8 +759,25 @@ class baseMao public function getByKey(string $key, ...$arguments) { if(!defined($key)) return false; + $cache = $this->config->cache->keys[$key]; + if(!empty($cache->fields) && !empty($arguments)) + { + $tableFields = $this->app->dao->descTable($cache->table); + foreach($cache->fields as $index => $field) + { + if(!isset($tableFields[$field])) return $this->app->triggerError("The {$field} field does not exist in table {$cache->table}", __FILE__, __LINE__, true); + if(!isset($arguments[$index])) continue; + + $tableField = $tableFields[$field]; + if(stripos($tableField->type, 'int') !== false) $arguments[$index] = (int) $arguments[$index]; + if(stripos($tableField->type, 'float') !== false) $arguments[$index] = (float)$arguments[$index]; + if(stripos($tableField->type, 'decimal') !== false) $arguments[$index] = (float)$arguments[$index]; + if(stripos($tableField->type, 'double') !== false) $arguments[$index] = (float)$arguments[$index]; + } + } $key = constant($key); + $key = str_replace(['cache', '_'], ['res', ':'], strtolower($key)); foreach($arguments as $argument) $key .= ':' . $argument; $this->currentKey = $key; From 9b2c70dcc94e4f1144ed0359c2d8fd4900f26d39 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 29 Oct 2024 16:31:51 +0800 Subject: [PATCH 114/300] * [refac] trigger error if the key or label is wrong. --- lib/base/mao/mao.class.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index b1d4d75810..5ec89674f3 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -758,7 +758,8 @@ class baseMao */ public function getByKey(string $key, ...$arguments) { - if(!defined($key)) return false; + if(empty($this->config->cache->keys[$key])) return $this->app->triggerError("The {$key} key is not defined", __FILE__, __LINE__, true); + $cache = $this->config->cache->keys[$key]; if(!empty($cache->fields) && !empty($arguments)) { @@ -797,7 +798,7 @@ class baseMao */ public function save($value) { - if(empty($this->currentKey)) return false; + if(empty($this->currentKey)) return $this->app->triggerError('The current key is empty', __FILE__, __LINE__, true); $this->app->redis->set($this->currentKey, $value); } @@ -812,7 +813,7 @@ class baseMao */ public function setByKey(string $key, $value) { - if(empty($key)) return false; + if(empty($key)) return $this->app->triggerError('The key is empty', __FILE__, __LINE__, true); $this->app->redis->set($key, $value); } @@ -828,8 +829,8 @@ class baseMao */ public function setByLabel(string $label, $value) { - if(empty($label)) return false; - if(empty($this->labels[$label])) return false; + if(empty($label)) return $this->app->triggerError('The label is empty', __FILE__, __LINE__, true); + if(empty($this->labels[$label])) return $this->app->triggerError("The {$label} label is not set", __FILE__, __LINE__, true); $this->app->redis->set($this->labels[$label], $value); } From e0a7e784b18e9b51e2e0fd7cb3dbbdf582d0d508 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 31 Oct 2024 16:59:13 +0800 Subject: [PATCH 115/300] * [refac] refactor configuration items of cache. --- config/cache.php | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/config/cache.php b/config/cache.php index 3024c8700a..dd6cbd2afc 100644 --- a/config/cache.php +++ b/config/cache.php @@ -1,10 +1,40 @@ cache = new stdclass(); -$config->cache->enable = false; // 是否开启缓存。Enable cache or not. -$config->cache->lifetime = 5 * 60; // 缓存生存时间。The lifetime of cache. -$config->cache->driver = 'File'; // 缓存驱动。 The driver of cache. can be File|Yac|Apcu. +$config->cache->enable = false; // 是否开启缓存。Enable cache or not. +$config->cache->driver = ''; // 缓存驱动。 The driver of cache. Can be File|Yac|Apcu|Redis. +$config->cache->lifetime = 2592000; // 缓存生存时间。The lifetime of cache. The default value is 30 days. + +$config->cache->client = new stdClass(); +$config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not. + +// Format : $config->cache->raw[TABLE_NAME] = 'KEY_FIELD'; +// The TABLE_NAME is the name of the table in the database. +// The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table. + +$config->cache->raw = []; +$config->cache->raw[TABLE_CONFIG] = 'id'; +$config->cache->raw[TABLE_BUILD] = 'id'; +$config->cache->raw[TABLE_MODULE] = 'id'; +$config->cache->raw[TABLE_PRODUCT] = 'id'; +$config->cache->raw[TABLE_PROJECT] = 'id'; +$config->cache->raw[TABLE_RELEASE] = 'id'; +$config->cache->raw[TABLE_USER] = 'account'; + +$config->cache->res = []; +$config->cache->res[TABLE_MODULE][] = ['name' => 'CACHE_MODULE_TREE', 'fields' => ['type', 'root', 'branch']]; + +$config->cache->keys = []; +foreach($config->cache->res as $table => $caches) +{ + foreach($caches as $cache) + { + $cache = (object)$cache; + $cache->table = $table; + $config->cache->keys[$cache->name] = $cache; + define($cache->name, $cache->name); + } +} $config->cache->enableFullPage = false; // 是否开启整页缓存。Enable full page cache or not. $config->cache->fullPageLifetime = 5 * 60; @@ -31,11 +61,3 @@ $config->cacheKeys->execution->ajaxGetDropMenuExecutions = 'ajaxDropMenuExecutio $config->cacheKeys->bug = new stdclass(); $config->cacheKeys->bug->browse = 'bugBrowse%s'; - -$config->cache->dao = new stdClass(); -$config->cache->dao->enable = false; // 是否开启 dao 缓存。Enable dao cache or not. -$config->cache->dao->lifetime = 60 * 60 * 24 * 30; // 缓存生存时间,默认为 30 天。The lifetime of cache, default is 30 days. -$config->cache->dao->driver = 'Apcu'; // 缓存驱动,可以为 File|Yac|Apcu。The driver of cache. can be File|Yac|Apcu. - -$config->cache->client = new stdClass(); -$config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not. From 9867af1c2073dadc2f55b9018468b9b96306d89c Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 31 Oct 2024 16:59:59 +0800 Subject: [PATCH 116/300] * [refac] modify the order in which files are loaded. --- config/config.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/config/config.php b/config/config.php index 84d12df1f7..2c16fe3680 100644 --- a/config/config.php +++ b/config/config.php @@ -205,10 +205,6 @@ if(file_exists($filterConfig)) include $filterConfig; $dbConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'db.php'; if(file_exists($dbConfig)) include $dbConfig; -/* 引用缓存的配置。 Include the cache config file. */ -$cacheConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cache.php'; -if(file_exists($cacheConfig)) include $cacheConfig; - /* 读取环境变量的配置。 Read the env config. */ if($config->inContainer || $config->inQuickon) { @@ -229,11 +225,6 @@ if($config->inContainer || $config->inQuickon) $config->default->lang = getenv('ZT_DEFAULT_LANG'); } -/* 引用自定义的配置。 Include the custom config file. */ -$myConfigRoot = (defined('RUN_MODE') and in_array(RUN_MODE, array('test', 'uitest'))) ? dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR . 'config' : dirname(__FILE__); -$myConfig = $myConfigRoot . DIRECTORY_SEPARATOR . 'my.php'; -if(file_exists($myConfig)) include $myConfig; - /* 禅道配置文件。zentaopms settings. */ $zentaopmsConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'zentaopms.php'; if(file_exists($zentaopmsConfig)) include $zentaopmsConfig; @@ -246,14 +237,19 @@ if(file_exists($actionsMapConfig)) include $actionsMapConfig; $routesConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'routes.php'; if(file_exists($routesConfig)) include $routesConfig; -/* 引用 Redis 的配置。 Include the redis config file. */ -$redisConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'redis.php'; -if(file_exists($redisConfig)) include $redisConfig; +/* 引用缓存的配置。 Include the cache config file. */ +$cacheConfig = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cache.php'; +if(file_exists($cacheConfig)) include $cacheConfig; /* Include extension config files. */ $extConfigFiles = glob(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ext/*.php'); if($extConfigFiles) foreach($extConfigFiles as $extConfigFile) include $extConfigFile; +/* 引用自定义的配置。 Include the custom config file. */ +$myConfigRoot = (defined('RUN_MODE') and in_array(RUN_MODE, array('test', 'uitest'))) ? dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR . 'config' : dirname(__FILE__); +$myConfig = $myConfigRoot . DIRECTORY_SEPARATOR . 'my.php'; +if(file_exists($myConfig)) include $myConfig; + /* Set version. */ if($config->edition != 'open') { From 9ac773badec19e50536435c13b82d095f449f105 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 31 Oct 2024 17:01:23 +0800 Subject: [PATCH 117/300] + [refac] add cache class to manage cache. --- lib/cache/cache.class.php | 716 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 716 insertions(+) create mode 100644 lib/cache/cache.class.php diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php new file mode 100644 index 0000000000..47097ca3c8 --- /dev/null +++ b/lib/cache/cache.class.php @@ -0,0 +1,716 @@ + + * @package cache + * @link https://www.zentao.net + */ +helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheInterface.php'); +helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheException.php'); +helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'InvalidArgumentException.php'); +helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'ApcuDriver.php'); +helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'RedisDriver.php'); +helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php'); +helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'FileDriver.php'); + +use ZenTao\Cache\SimpleCache\InvalidArgumentException; + +class cache +{ + const DRIVER_APCU = 'Apcu'; + + const DRIVER_FILE = 'File'; + + const DRIVER_YAC = 'Yac'; + + const DRIVER_REDIS = 'Redis'; + + /** + * 全局应用程序对象。 + * Global application object. + * + * @access private + * @var object + */ + private $app; + + /** + * 全局数据库操作对象。 + * Global database operation object. + * + * @access private + * @var object + */ + private $dao; + + /** + * 全局配置对象。 + * Global configuration object. + * + * @access private + * @var object + */ + private $config; + + /** + * 全局缓存对象。 + * Global cache object. + * + * @access private + * @var object + */ + private $cache; + + /** + * 缓存键。 + * Cache key. + * + * @access private + * @var string + */ + private $key = ''; + + /** + * 缓存标签。 + * Cache label. + * + * @access private + * @var array + */ + private $labels = []; + + /** + * 影响缓存的表名。 + * Table name that affects cache. + * + * @access private + * @var string + */ + private $table = ''; + + /** + * 影响缓存的事件。 + * Event that affects cache. + * + * @access private + * @var string + */ + private $event = ''; + + /** + * 影响缓存的 WHERE 子句。 + * WHERE clause that affects cache. + * + * @access private + * @var string + */ + private $where = ''; + + /** + * 受影响的对象列表。 + * Affected object list. + * + * @access private + * @var array + */ + private $objects = []; + + /** + * 构造函数,根据配置文件初始化缓存对象。 + * Constructor, initialize cache object according to the configuration file. + * + * @param object $app 全局应用程序对象。 + * @access public + * @return void + */ + public function __construct(object $app) + { + $this->app = $app; + $this->dao = $app->dao; + $this->config = $app->config; + + if(empty($this->config->cache->enable)) return $this->log('The cache is not enabled', __FILE__, __LINE__); + + $driver = ucfirst(strtolower($this->config->cache->driver)); + switch($driver) + { + case self::DRIVER_APCU: + $className = 'ZenTao\Cache\Driver\ApcuDriver'; + break; + case self::DRIVER_YAC: + $className = 'ZenTao\Cache\Driver\YacDriver'; + break; + case self::DRIVER_FILE: + $className = 'ZenTao\Cache\Driver\FileDriver'; + break; + case self::DRIVER_REDIS: + $className = 'ZenTao\Cache\Driver\RedisDriver'; + break; + default: + return $this->log("Driver {$driver} is not supported.", __FILE__, __LINE__); + } + + if($driver != self::DRIVER_FILE && !extension_loaded($driver)) return $this->log("Driver ext-{$driver} is not loaded.", __FILE__, __LINE__); + + $this->cache = new $className($this->config->db->name, $this->config->cache->lifetime, $app->getCacheRoot()); + } + + /** + * 设置缓存键。 + * Set cache key. + * + * @param string $key 缓存键。 + * @access private + * @return void + */ + private function setKey(string $key) + { + $this->key = $key; + } + + /** + * 设置影响缓存的表名。 + * Set the table name that affects the cache. + * + * @param string $table 表名。 + * @access private + * @return void + */ + private function setTable(string $table) + { + $this->table = $table; + } + + /** + * 设置影响缓存的事件。 + * Set the event that affects the cache. + * + * @param string $event 事件。 + * @access private + * @return void + */ + private function setEvent(string $event) + { + $this->event = $event; + } + + /** + * 设置影响缓存的 WHERE 子句。 + * Set the WHERE clause that affects the cache. + * + * @param string $where WHERE 子句。 + * @access private + * @return void + */ + private function setWhere(string $where) + { + $this->where = $where; + } + + /** + * 设置受影响的对象列表。 + * Set the list of affected objects. + * + * @param array $objects 对象列表。 + * @access private + * @return void + */ + private function setObjects(array $objects) + { + $this->objects = $objects; + } + + /** + * 获取表的主键字段。 + * Get the primary key field of the table. + * + * @access private + * @return string + */ + private function getTableField(): string + { + return $this->config->cache->raw[$this->table]; + } + + /** + * 获取表的缓存代号。 + * Get the cache code of the table. + * + * @access private + * @return string + */ + private function getTableCode(): string + { + return str_replace(['`', $this->config->db->prefix], '', $this->table); + } + + /** + * 获取缓存键。 + * Get cache key. + * + * @param string $key 缓存键。 + * @access private + * @return string + */ + private function getCacheKey(string $key): string + { + return str_replace(['cache', '_'], ['res', ':'], strtolower($key)); + } + + /** + * 新增数据时更新缓存。 + * Update cache when adding data. + * + * @access private + * @return void + */ + private function create() + { + $objectID = $this->dao->lastInsertID(); + if(!$objectID) return $this->log('Failed to fetch last insert id.', __FILE__, __LINE__); + + $field = $this->getTableField(); + $code = $this->getTableCode(); + + /* 获取新增的数据。Get the new data. */ + $object = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->fetch(); + if(!$object) return $this->log('Failed to fetch the new object. The sql is: ' . $this->dao->get(), __FILE__, __LINE__); + + /* 把新增的数据保存到缓存中。Save the new data to cache. */ + $this->cache->set("raw:{$code}:{$object->$field}", $object); + + /* 把新增的数据的 id 保存到缓存中。Save the id of the new data to cache. */ + $objectIdList = $this->cache->get("set:{$code}List"); + $this->cache->set("set:{$code}List", $objectIdList ? array_merge($objectIdList, [$object->$field]) : [$object->$field]); + + if(empty($this->config->cache->res[$this->table])) return; + + /* 删除受影响的缓存。Delete the affected cache. */ + $this->deleteAffectedCache([$object]); + } + + /** + * 更新数据时更新缓存。 + * Update cache when updating data. + * + * @access private + * @return void + */ + private function update() + { + if(empty($this->objects)) return $this->log('No objects to update.', __FILE__, __LINE__); + + $field = $this->getTableField(); + $code = $this->getTableCode(); + + /* 获取被更新数据的 id 列表。Get the id list of the updated data. */ + $objectIdList = array_map(function($object) use ($field) { return $object->$field; }, $this->objects); + + /* 获取更新后的数据。Get the updated data. */ + $objects = $this->dao->select('*')->from($this->table)->where($field)->in($objectIdList)->fetchAll($field); + if(!$objects) return $this->log('Failed to fetch updated objects. The sql is: ' . $this->dao->get(), __FILE__, __LINE__); + + /* 把更新后的数据保存到缓存中。Save the updated data to cache. */ + $values = []; + foreach($objects as $object) $values["raw:{$code}:{$object->$field}"] = $object; + $this->cache->setMultiple($values); + + if(empty($this->config->cache->res[$this->table])) return; + + /* 获取受影响的数据。Get the affected data. */ + foreach($objectIdList as $objectID) + { + if(isset($this->objects[$objectID]) && isset($objects[$objectID]) && $this->objects[$objectID] == $objects[$objectID]) unset($this->objects[$objectID], $objects[$objectID]); + } + $affectedObjects = array_merge($this->objects, $objects); + + /* 删除受影响的缓存。Delete the affected cache. */ + $this->deleteAffectedCache($affectedObjects); + } + + /** + * 删除数据时更新缓存。 + * Update cache when deleting data. + * + * @access private + * @return void + */ + private function delete() + { + if(empty($this->objects)) return $this->log('No objects to delete.', __FILE__, __LINE__); + + $field = $this->getTableField(); + $code = $this->getTableCode(); + + /* 把被删除的数据从缓存中删除。Delete the deleted data from cache. */ + $keys = []; + foreach($this->objects as $object) $keys[] = "raw:{$code}:{$object->key}"; + $this->cache->deleteMultiple($keys); + + /* 把被删除的数据的 id 从缓存中删除。Delete the id of the deleted data from cache. */ + $objectIdList = $this->cache->get("set:{$code}List"); + $this->cache->set("set:{$code}List", array_diff($objectIdList, array_map(function($object) use ($field) { return $object->$field; }, $this->objects))); + + if(empty($this->config->cache->res[$this->table])) return; + + /* 删除受影响的缓存。Delete the affected cache. */ + $this->deleteAffectedCache($this->objects); + } + + /** + * 删除受影响的缓存。 + * Delete the affected cache. + * + * @param array $objects 受影响的对象列表。 + * @access private + * @return void + */ + private function deleteAffectedCache(array $objects) + { + /* 根据受影响的数据查找受影响的缓存。Find the affected cache by the affected data. */ + $keys = []; + foreach($this->config->cache->res[$this->table] as $res) + { + $res = (object)$res; + + /* 如果没有设置关联字段则整个缓存都受影响。If no associated fields are set, the entire cache is affected. */ + if(empty($res->fields)) + { + $keys[] = $this->getCacheKey($res->name); + continue; + } + + /* 根据关联字段查找受影响的缓存。Find the affected cache by the associated fields. */ + foreach($objects as $object) + { + $key = $this->getCacheKey($res->name); + foreach($res->fields as $field) + { + if(!isset($object->$field)) return $this->log("The {$field} field does not exist in table {$this->table}.", __FILE__, __LINE__); + + $key .= ':' . $object->$field; + } + $keys[] = $key; + } + } + + /* 删除受影响的缓存。Delete the affected cache. */ + $this->cache->deleteMultiple($keys); + } + + /** + * 检查表是否有缓存设置。 + * Check if the table has cache settings. + * + * @param string $table 表名。 + * @access private + * @return bool + */ + private function checkTable(string $table = ''): bool + { + if(empty($table)) $table = $this->table; + return !empty($this->config->cache->raw[$table]); + } + + /** + * 记录日志信息。 + * Record log information. + * + * @param string $message 日志信息。 + * @param string $file 文件名。 + * @param string $line 行号。 + * @access private + * @return false + */ + private function log(string $message, string $file, string $line): bool + { + if(!$this->config->debug) return false; + + $runMode = PHP_SAPI == 'cli' ? '_cli' : ''; + $logFile = $this->app->getLogRoot() . 'redis' . $runMode . '.' . date('Ymd') . '.log.php'; + if(!file_exists($logFile)) file_put_contents($logFile, '\n"); + + $content = date('Ymd H:i:s') . ': ' . $this->getURI() . "\nError: {$message} in $file on line $line\n"; + file_put_contents($logFile, $content, FILE_APPEND); + + if($this->config->debug >= 2) $this->app->triggerError($message, __FILE__, __LINE__, true); + + return false; + } + + /** + * 获取 URI。 + * Get URI. + * + * @access private + * @return string + */ + private function getURI(): string + { + $uri = $this->app->getURI(); + if($uri) return $uri; + + if($this->config->requestType == 'GET') return $_SERVER['REQUEST_URI']; + + if($this->config->requestType == 'PATH_INFO' || $this->config->requestType == 'PATH_INFO2') + { + $pathInfo = $this->app->getPathInfo(); + if(empty($pathInfo)) return ''; + + $dotPos = strrpos($pathInfo, '.'); + if($dotPos) return substr($pathInfo, 0, $dotPos); + return $pathInfo; + } + + return ''; + } + + /** + * 从缓存中获取指定表的所有数据。 + * Get all data of the specified table from the cache. + * + * @param string $table 表名。 + * @access public + * @return array + */ + public function fetchAll(string $table): array + { + if(!$this->checkTable($table)) return []; + + if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__); + + $this->setTable($table); + + $code = $this->getTableCode(); + $keys = $this->cache->get("set:{$code}List"); + if(!$keys) return []; + + $objects = $this->cache->getMultiple($keys); + if(!$objects) return []; + + $result = []; + $field = $this->getTableField(); + foreach($objects as $object) $result[$object->$field] = $object; + + return $result; + } + + /** + * 设置缓存键。 + * Set cache key. + * + * @param string $key 缓存键。 + * @param mixed ...$args 缓存键的参数。 + * @access public + * @return object + */ + public function key($key, ...$args) + { + if(empty($this->config->cache->keys[$key])) return $this->log("The {$key} key is not defined", __FILE__, __LINE__); + + $cache = $this->config->cache->keys[$key]; + if(!empty($cache->fields) && !empty($args)) + { + $tableFields = $this->dao->descTable($cache->table); + foreach($cache->fields as $index => $field) + { + if(!isset($tableFields[$field])) return $this->log("The {$field} field does not exist in table {$cache->table}", __FILE__, __LINE__); + if(!isset($args[$index])) continue; + + $tableField = $tableFields[$field]; + if(stripos($tableField->type, 'int') !== false) $args[$index] = (int) $args[$index]; + if(stripos($tableField->type, 'float') !== false) $args[$index] = (float)$args[$index]; + if(stripos($tableField->type, 'decimal') !== false) $args[$index] = (float)$args[$index]; + if(stripos($tableField->type, 'double') !== false) $args[$index] = (float)$args[$index]; + } + } + + $key = $this->getCacheKey(constant($key)); + foreach($args as $arg) $key .= ':' . $arg; + + $this->setKey($key); + return $this; + } + + /** + * 设置缓存标签。 + * Set cache label. + * + * @param string $label 缓存标签。 + * @access public + * @return object + */ + public function label(string $label) + { + if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__); + if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__); + $this->labels[$label] = $this->key; + return $this; + } + + /** + * 根据当前缓存键获取缓存。 + * Get cache according to the current cache key. + * + * @access public + * @return mixed + */ + public function get() + { + if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__); + return $this->cache->get($this->key); + } + + /** + * 根据指定缓存键获取缓存。 + * Get cache according to the specified cache key. + * + * @param string $key 缓存键。 + * @access public + * @return mixed + */ + public function getByKey(string $key) + { + if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); + return $this->cache->get($key); + } + + /** + * 根据当前缓存键保存缓存。 + * Save cache according to the current cache key. + * + * @param mixed $value 缓存值。 + * @access public + * @return bool + */ + public function save($value) + { + if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__); + return $this->cache->set($this->key, $value); + } + + /** + * 根据指定缓存建保存缓存。 + * Save cache according to the specified cache key. + * + * @param string $key 缓存键。 + * @param mixed $value 缓存值。 + * @access public + * @return bool + */ + public function saveByKey(string $key, $value) + { + if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); + return $this->cache->set($key, $value); + } + + /** + * 根据指定缓存标签保存缓存。 + * Save cache according to the specified cache label. + * + * @param string $label 缓存标签。 + * @param mixed $value 缓存值。 + * @access public + * @return bool + */ + public function saveByLabel(string $label, $value) + { + if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__); + if(empty($this->labels[$label])) return $this->log("The {$label} label is not set", __FILE__, __LINE__); + + return $this->cache->set($this->labels[$label], $value); + } + + /** + * 重置缓存相关设置项。 + * Reset cache related settings. + * + * @access public + * @return void + */ + public function reset() + { + $this->setKey(''); + $this->setTable(''); + $this->setEvent(''); + $this->setObjects([]); + } + + /** + * 执行数据库操作前准备更新缓存需要的设置项。 + * Prepare the settings required to update the cache before executing the database operation. + * + * @param string $table 表名。 + * @param string $event 事件。 + * @param string $sql SQL 语句。 + * @access public + * @return void + */ + public function prepare(string $table, string $event, string $sql) + { + $this->reset(); + + if(!$this->checkTable($table)) return; + + if(empty($table)) return $this->log('Table name is required.', __FILE__, __LINE__); + if(empty($event)) return $this->log('Event type is required.', __FILE__, __LINE__); + if(empty($sql)) return $this->log('SQL statement is required.', __FILE__, __LINE__); + + $this->setTable($table); + $this->setEvent($event); + + if($event == 'update' || $event == 'delete') + { + /* 获取 WHERE 子句的内容。Get the content of WHERE clause. */ + $whereLen = strlen(DAO::WHERE); + $wherePOS = strrpos($sql, DAO::WHERE); + $groupPOS = strrpos($sql, DAO::GROUPBY); + $havingPOS = strrpos($sql, DAO::HAVING); + $orderPOS = strrpos($sql, DAO::ORDERBY); + $limitPOS = strrpos($sql, DAO::LIMIT); + $splitPOS = $orderPOS ? $orderPOS : $limitPOS; + $splitPOS = $havingPOS ? $havingPOS : $splitPOS; + $splitPOS = $groupPOS ? $groupPOS : $splitPOS; + if($splitPOS) + { + $where = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); + } + else + { + $where = substr($sql, $wherePOS + $whereLen); + } + + /* 执行操作后数据已经被修改,所以需要提前获取被影响的数据。*/ + $field = $this->getTableField(); + $objects = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->fetchAll($field); + if(!$objects) return $this->log('Failed to fetch affected objects. The sql is: ' . $this->dao->get(), __FILE__, __LINE__); + + $this->setWhere($where); + $this->setObjects($objects); + } + } + + /** + * 执行数据库操作后更新缓存。 + * Update the cache after executing the database operation. + * + * @access public + * @return void + */ + public function sync() + { + if(!$this->checkTable()) return; + + if(empty($this->table)) return $this->log('Table name is required.', __FILE__, __LINE__); + if(empty($this->event)) return $this->log('Event type is required.', __FILE__, __LINE__); + + if($this->event == 'insert') $this->create(); + if($this->event == 'update') $this->update(); + if($this->event == 'delete') $this->delete(); + + $this->reset(); + } +} From bc1e918af3ed9383b6dc233e2e6a80bd960d2a27 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 31 Oct 2024 17:02:06 +0800 Subject: [PATCH 118/300] + [refac] add method to load cache. --- framework/base/router.class.php | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 742fab944f..78e7d41ece 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -306,6 +306,15 @@ class baseRouter */ public $lang; + /** + * 全局缓存对象,用于操作缓存。 + * The global cache object, used to operate cache. + * + * @var object + * @access public + */ + public $cache = null; + /** * 全局$dbh对象,数据库连接句柄。 * The global $dbh object, the database connection handler. @@ -494,12 +503,7 @@ class baseRouter $this->setTimezone(); if($this->config->framework->autoConnectDB) $this->connectDB(); - - if($this->config->redis->enable) - { - $this->loadClass('zredis', true); - $this->redis = new zredis($this); - } + if($this->config->cache->enable) $this->loadCache(); $this->setupProfiling(); $this->setupXhprof(); @@ -3080,6 +3084,23 @@ class baseRouter $this->mao = new mao($this); } + /** + * 加载缓存类,初始化全局缓存对象。 + * Load the cache class and init the global cache object. + * + * @access public + * @return void + */ + private function loadCache() + { + $this->loadClass('cache', true); + $this->cache = new cache($this); + + /* 为 dao 和 mao 设置访问缓存的对象。 Set the cache object for dao and mao. */ + $this->dao->cache = $this->cache; + $this->mao->cache = $this->cache; + } + /** * Init config of slave db. * From 125e75249ff3f2a3709e2ee21f2ec017bf6182d3 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 31 Oct 2024 17:02:27 +0800 Subject: [PATCH 119/300] * [refac] add method to connect redis server. --- lib/cache/driver/RedisDriver.php | 41 +++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index e6cb7ffc08..151169f283 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -31,8 +31,43 @@ class RedisDriver implements CacheInterface $this->namespace = $namespace; $this->defaultLifetime = $defaultLifetime; - global $app; - $this->redis = $app->redis; + $this->connectRedis(); + } + + /** + * 连接 Redis 服务器。 + * Connect to the Redis server. + * + * @access private + * @return object + */ + private function connectRedis() + { + global $config; + + if(empty($config->redis)) \helper::end('Redis is not enabled in the configuration file.'); + + try + { + $this->redis = new \Redis(); + + $version = phpversion('redis'); + if(version_compare($version, '5.3.0', 'ge')) + { + $this->redis->connect($config->redis->host , $config->redis->port, $config->redis->timeout, '', 0, 0, ['auth' => [$config->redis->username, $config->redis->password]]); + } + else + { + $this->redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); + $this->redis->auth(['pass' => $this->config->redis->password]); + } + + if(!$this->redis->ping()) \helper::end('Can not connect to Redis server.'); + } + catch(RedisException $e) + { + \helper::end('Can not connect to Redis server. The error message is: ' . $e->getMessage()); + } } /** @@ -224,7 +259,7 @@ class RedisDriver implements CacheInterface */ private function buildKeyName($key) { - return $this->namespace . $key; + return $this->namespace . ':' . $key; } /** From 8d3314f9ae3e8f5fe68b3cbfdf48c476640e999b Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 31 Oct 2024 17:03:07 +0800 Subject: [PATCH 120/300] * [refac] calling cache object to access cache. --- lib/base/dao/dao.class.php | 32 ++---- lib/base/mao/mao.class.php | 194 ++----------------------------------- 2 files changed, 17 insertions(+), 209 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index c274304c34..684a11a0c8 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -215,21 +215,9 @@ class baseDAO $this->config = $config; $this->lang = $lang; $this->dbh = $dbh; + $this->cache = $app->cache; $this->slaveDBH = $slaveDBH ? $slaveDBH : false; - if($config->cache->dao->enable) - { - $this->app->loadClass('cache', true); - try - { - $this->cache = new cache($config->cache->dao->driver, $config->db->name . '-dao-', $config->cache->dao->lifetime); - } - catch (Exception $e) - { - die($e->getMessage()); - } - } - $this->reset(); } @@ -344,7 +332,7 @@ class baseDAO { if(!$this->app->isServing() || empty($this->cache)) return self::CACHE_MISS; - $cache = $this->cache->get($key); + $cache = $this->cache->getByKey($key); if($cache === null) return self::CACHE_MISS; /* 解析缓存的更新时间和值到变量中。 */ @@ -362,7 +350,7 @@ class baseDAO { if(strpos($table, 'boardlayer') !== false) return self::CACHE_MISS; - $tableCache = $this->cache->get($table); + $tableCache = $this->cache->getByKey($table); if($tableCache === null) continue; if($tableCache[0] > $cachedTime) return self::CACHE_MISS; @@ -390,7 +378,7 @@ class baseDAO $this->app->useClientCache = false; - $this->cache->set($key, array(microtime(true), $value)); + $this->cache->saveByKey($key, array(microtime(true), $value)); } /** @@ -1054,14 +1042,14 @@ class baseDAO /* Force to query from master db, if db has been changed. */ $this->slaveDBH = false; - $this->app->redis->prepare($table, $method, $sql); + $this->cache->prepare($table, $method, $sql); $result = $this->dbh->exec($sql); /* See: https://www.php.net/manual/en/pdo.lastinsertid.php .*/ if($method == 'insert') $this->_lastInsertID = $this->dbh->lastInsertID(); - $result ? $this->app->redis->sync() : $this->app->redis->reset(); + $result ? $this->cache->sync() : $this->cache->reset(); $this->setTableCache($sql); @@ -1099,7 +1087,7 @@ class baseDAO public function fetch($field = '') { $sql = $this->processSQL(); - $key = 'fetch-' . md5($sql); + $key = 'dao:fetch:' . md5($sql); $result = $this->getCache($key, $sql); if($result === self::CACHE_MISS) @@ -1125,7 +1113,7 @@ class baseDAO public function fetchAll($keyField = '') { $sql = $this->processSQL(); - $key = 'fetchAll-' . md5($sql); + $key = 'dao:fetchAll:' . md5($sql); $rows = $this->getCache($key, $sql); if($rows === self::CACHE_MISS) @@ -1154,7 +1142,7 @@ class baseDAO { $sql = $this->processSQL(); $table = $this->table; - $key = 'fetchGroup-' . md5($sql); + $key = 'dao:fetchAll:' . md5($sql); $rows = $this->getCache($key, $sql); if($rows === self::CACHE_MISS) @@ -1186,7 +1174,7 @@ class baseDAO public function fetchPairs($keyField = '', $valueField = '') { $sql = $this->processSQL(); - $key = 'fetchPairs-' . md5($sql); + $key = 'dao:fetchAll:' . md5($sql); $rows = $this->getCache($key, $sql); if($rows === self::CACHE_MISS) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 5ec89674f3..c45ead3b8c 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -37,15 +37,6 @@ class baseMao */ public $config; - /** - * 缓存类型。 - * The cache driver. - * - * @var bool - * @access public - */ - public $driver = 'redis'; - /** * 正在使用的表。 * The table of current query. @@ -118,33 +109,6 @@ class baseMao */ public $dataColumn; - /** - * The labels to get or set。 - * The labels to get or set. - * - * @var array - * @access public - */ - public $labels; - - /** - * 当前设置的label。 - * Current label. - * - * @var string - * @access public - */ - public $currentLabel; - - /** - * 当前设置的 key。 - * Current key. - * - * @var string - * @access private - */ - private $currentKey; - /** * 构造方法。 * The construct method. @@ -158,6 +122,7 @@ class baseMao global $config; $this->app = $app; $this->config = $config; + $this->cache = $app->cache; $this->reset(); } @@ -531,7 +496,7 @@ class baseMao { if(!in_array($row->$key, $cacheKeys)) $cacheKeys[] = $row->$key; } - $objects = $this->app->redis->fetchAll($tableName, $cacheKeys); + $objects = $this->cache->fetchAll($tableName, $cacheKeys); foreach($this->data as $row) { $object = $objects[$row->$key]; @@ -597,7 +562,7 @@ class baseMao */ public function fetch(string $field = '') { - $rawResult = $this->app->redis->fetchAll($this->table); + $rawResult = $this->cache->fetchAll($this->table); if(empty($rawResult)) return ''; foreach($rawResult as $row) @@ -621,7 +586,7 @@ class baseMao */ public function fetchAll(string $keyField = '') { - $rawResult = $this->app->redis->fetchAll($this->table); + $rawResult = $this->cache->fetchAll($this->table); if(empty($rawResult)) return []; $result = []; @@ -683,155 +648,10 @@ class baseMao return $pairs; } - /** - * 设置label. - * Set label. - * - * @param string $key - * @access public - * @return object - */ - public function label(string $label) + public function __call(string $method, array $args) { - $this->currentLabel = $label; + if(method_exists($this->cache, $method)) return call_user_func_array([$this->cache, $method], $args); - return $this; - } - - /** - * 把数组转换成字符串。 - * Convert array to string. - * - * @param array $args - * @access private - * @return string - */ - private function recrusiveArgs(array $args): string - { - $result = []; - foreach($args as $key => $arg) - { - if(is_object($arg)) $arg = (array)$arg; - if(is_array($arg)) - { - if(array_values($arg) === $arg) - { - sort($arg); - } - else - { - ksort($arg); - } - $result[] = $key . '=' . $this->recrusiveArgs($arg); - } - else - { - $result[] = $key . '=' . $arg; - } - } - - return implode('&', $result); - } - - /** - * 创建缓存的key。 - * Create cache key. - * - * @param mixed ...$args - * @access public - * @return string - */ - public function createKey(...$args) - { - $args = $this->recrusiveArgs($args); - return substr(sha1($args), 0, 7); - } - - /** - * 通过Key获取值 - * Get value by key. - * - * @param string $key - * @param mixed ...$arguments - * @access public - * @return mixed - */ - public function getByKey(string $key, ...$arguments) - { - if(empty($this->config->cache->keys[$key])) return $this->app->triggerError("The {$key} key is not defined", __FILE__, __LINE__, true); - - $cache = $this->config->cache->keys[$key]; - if(!empty($cache->fields) && !empty($arguments)) - { - $tableFields = $this->app->dao->descTable($cache->table); - foreach($cache->fields as $index => $field) - { - if(!isset($tableFields[$field])) return $this->app->triggerError("The {$field} field does not exist in table {$cache->table}", __FILE__, __LINE__, true); - if(!isset($arguments[$index])) continue; - - $tableField = $tableFields[$field]; - if(stripos($tableField->type, 'int') !== false) $arguments[$index] = (int) $arguments[$index]; - if(stripos($tableField->type, 'float') !== false) $arguments[$index] = (float)$arguments[$index]; - if(stripos($tableField->type, 'decimal') !== false) $arguments[$index] = (float)$arguments[$index]; - if(stripos($tableField->type, 'double') !== false) $arguments[$index] = (float)$arguments[$index]; - } - } - - $key = constant($key); - $key = str_replace(['cache', '_'], ['res', ':'], strtolower($key)); - foreach($arguments as $argument) $key .= ':' . $argument; - - $this->currentKey = $key; - $this->labels[$this->currentLabel] = $key; - $this->lastLabel = ''; - - return $this->app->redis->get($key); - } - - /** - * 给当前 key 设置值。 - * Set value to current key. - * - * @param mixed $value - * @access public - * @return void - */ - public function save($value) - { - if(empty($this->currentKey)) return $this->app->triggerError('The current key is empty', __FILE__, __LINE__, true); - $this->app->redis->set($this->currentKey, $value); - } - - /** - * 给指定 key 设置值。 - * Set cache by key. - * - * @param string $key - * @param mixed $value - * @access public - * @return void - */ - public function setByKey(string $key, $value) - { - if(empty($key)) return $this->app->triggerError('The key is empty', __FILE__, __LINE__, true); - - $this->app->redis->set($key, $value); - } - - /** - * 给指定 label 设置值。 - * Set cache by label. - * - * @param string $label - * @param mixed $value - * @access public - * @return void - */ - public function setByLabel(string $label, $value) - { - if(empty($label)) return $this->app->triggerError('The label is empty', __FILE__, __LINE__, true); - if(empty($this->labels[$label])) return $this->app->triggerError("The {$label} label is not set", __FILE__, __LINE__, true); - - $this->app->redis->set($this->labels[$label], $value); + $this->app->triggerError("Method $method not found in class baseMao.", __FILE__, __LINE__, true); } } From 2d596a6e19f3dfeb091b8189d9b60551c4fe0d8c Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 31 Oct 2024 17:07:17 +0800 Subject: [PATCH 121/300] * [refac] revert code of calling mao to access cache. --- module/product/zen.php | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/module/product/zen.php b/module/product/zen.php index 561a0a2b98..57d4987020 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -864,26 +864,14 @@ class productZen extends product $project = $projectID ? $this->loadModel('project')->getByID($projectID) : null; if(!empty($projectID) && !empty($project->hasProduct) && $this->app->rawModule == 'projectstory') { - $moduleTree = $this->app->mao->label('projectModuleTree')->getByKey('CACHE_MODULE_TREE', 'project', $projectID, $storyType); - if(!$moduleTree) - { - $moduleTree = $this->tree->getProjectStoryTreeMenu($projectID, 0, array('treeModel', $createModuleLink), array('storyType' => $storyType)); - $this->app->mao->setByLabel($moduleTree, 'projectModuleTree'); - } - return $moduleTree; + return $this->tree->getProjectStoryTreeMenu($projectID, 0, array('treeModel', $createModuleLink), array('storyType' => $storyType)); } /* Pre generate parameters. */ $userFunc = array('treeModel', $createModuleLink); $extra = array('projectID' => $projectID, 'productID' => $productID); - $moduleTree = $this->app->mao->label('productModuleTree')->getByKey('CACHE_MODULE_TREE', 'product', $productID, $branch, $storyType, $param); - if(!$moduleTree) - { - $moduleTree = $this->tree->getTreeMenu($productID, 'story', 0, $userFunc, $extra, $branch, "¶m=$param&storyType=$storyType"); - $this->app->mao->setByLabel($moduleTree, 'productModuleTree'); - } - return $moduleTree; + return $this->tree->getTreeMenu($productID, 'story', 0, $userFunc, $extra, $branch, "¶m=$param&storyType=$storyType"); } /** @@ -1452,20 +1440,10 @@ class productZen extends product $module = $this->app->tab == 'product' ? $storyType : $this->app->tab; $this->view->showGrades = !empty($this->config->{$module}->showGrades) ? $this->config->{$module}->showGrades : $this->story->getDefaultShowGrades($this->view->gradeMenu); - $users = $this->app->mao->getByKey('CACHE_USER_PAIRS'); - if($users) - { - $users = (array)$users; - } - else - { - $users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted'); - $this->app->mao->setByKey($users); - } $storyType = $isProjectStory ? 'all' : $storyType; $this->view->summary = $this->product->summary($stories, $storyType); $this->view->plans = $this->loadModel('productplan')->getPairs($productID, isset($projectProducts[$productID]) ? array(BRANCH_MAIN) + $projectProducts[$productID]->branches : (($branch === 'all' || empty($branch)) ? '' : $branch), 'unexpired,noclosed', true); - $this->view->users = $users; + $this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted'); $this->view->modules = $this->loadModel('tree')->getOptionMenu($productID, 'story', 0, $branchID); $this->view->storyTasks = $this->loadModel('task')->getStoryTaskCounts($storyIdList); $this->view->storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList); From 732e342ca63ae522aa8fc222391487470dcf3bae Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 09:25:48 +0800 Subject: [PATCH 122/300] + [refac] initialize the cache of the specified table. --- lib/cache/cache.class.php | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 47097ca3c8..66ddc24bd5 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -468,6 +468,29 @@ class cache return ''; } + /** + * 初始化指定表的缓存。 + * Initialize the cache of the specified table. + * + * @access private + * @return array + */ + private function initTableCache(): array + { + $field = $this->getTableField(); + $objects = $this->dao->select('*')->from($this->table)->fetchAll($field); + if(!$objects) return []; + + $values = []; + $code = $this->getTableCode(); + foreach($objects as $key => $object) $values["raw:{$code}:{$key}"] = $object; + + $this->cache->setMultiple($values); + $this->cache->set("set:{$code}List", array_keys($objects)); + + return $objects; + } + /** * 从缓存中获取指定表的所有数据。 * Get all data of the specified table from the cache. @@ -484,9 +507,12 @@ class cache $this->setTable($table); - $code = $this->getTableCode(); - $keys = $this->cache->get("set:{$code}List"); - if(!$keys) return []; + $code = $this->getTableCode(); + $objectIdList = $this->cache->get("set:{$code}List"); + if(!$objectIdList) return $this->initTableCache(); + + $keys = []; + foreach($objectIdList as $objectID) $keys[] = "raw:{$code}:{$objectID}"; $objects = $this->cache->getMultiple($keys); if(!$objects) return []; From 2bf184edff79d01c3f4d13faabbbe46738bc7030 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 09:48:02 +0800 Subject: [PATCH 123/300] * [refac] add method to get cache key. --- lib/cache/cache.class.php | 79 +++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 66ddc24bd5..ea2ef01036 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -248,14 +248,41 @@ class cache } /** - * 获取缓存键。 - * Get cache key. + * 获取原始数据类型缓存的键。该缓存用于保存表的原始数据。 + * Get the key of the raw data type cache. This cache is used to save the original data of the table. + * + * @param string $code 缓存代号。 + * @param int|string $id 主键值。 + * @access private + * @return string + */ + private function getRawCacheKey(string $code, int|string $id): string + { + return 'raw:' . $code . ':' . $id; + } + + /** + * 获取集合类型缓存的键。该缓存用于保存表的主键字段。 + * Get the key of the set type cache. This cache is used to save the primary key field of the table. + * + * @param string $code 缓存代号。 + * @access private + * @return string + */ + private function getSetCacheKey(string $code): string + { + return 'set:' . $code . ':list'; + } + + /** + * 获取结果类型缓存的键。该缓存用于保存表的计算结果。 + * Get the key of the result type cache. This cache is used to save the calculation results of the table. * * @param string $key 缓存键。 * @access private * @return string */ - private function getCacheKey(string $key): string + private function getResCacheKey(string $key): string { return str_replace(['cache', '_'], ['res', ':'], strtolower($key)); } @@ -280,11 +307,13 @@ class cache if(!$object) return $this->log('Failed to fetch the new object. The sql is: ' . $this->dao->get(), __FILE__, __LINE__); /* 把新增的数据保存到缓存中。Save the new data to cache. */ - $this->cache->set("raw:{$code}:{$object->$field}", $object); + $rawCacheKey = $this->getRawCacheKey($code, $object->$field); + $this->cache->set($rawCacheKey, $object); /* 把新增的数据的 id 保存到缓存中。Save the id of the new data to cache. */ - $objectIdList = $this->cache->get("set:{$code}List"); - $this->cache->set("set:{$code}List", $objectIdList ? array_merge($objectIdList, [$object->$field]) : [$object->$field]); + $setCacheKey = $this->getSetCacheKey($code); + $objectIdList = $this->cache->get($setCacheKey); + $this->cache->set($setCacheKey, $objectIdList ? array_merge($objectIdList, [$object->$field]) : [$object->$field]); if(empty($this->config->cache->res[$this->table])) return; @@ -315,7 +344,11 @@ class cache /* 把更新后的数据保存到缓存中。Save the updated data to cache. */ $values = []; - foreach($objects as $object) $values["raw:{$code}:{$object->$field}"] = $object; + foreach($objects as $object) + { + $rawCacheKey = $this->getRawCacheKey($code, $object->$field); + $values[$rawCacheKey] = $object; + } $this->cache->setMultiple($values); if(empty($this->config->cache->res[$this->table])) return; @@ -346,13 +379,14 @@ class cache $code = $this->getTableCode(); /* 把被删除的数据从缓存中删除。Delete the deleted data from cache. */ - $keys = []; - foreach($this->objects as $object) $keys[] = "raw:{$code}:{$object->key}"; - $this->cache->deleteMultiple($keys); + $affectedKeys = []; + foreach($this->objects as $object) $affectedKeys = $this->getRawCacheKey($code, $object->$field); + $this->cache->deleteMultiple($affectedKeys); /* 把被删除的数据的 id 从缓存中删除。Delete the id of the deleted data from cache. */ - $objectIdList = $this->cache->get("set:{$code}List"); - $this->cache->set("set:{$code}List", array_diff($objectIdList, array_map(function($object) use ($field) { return $object->$field; }, $this->objects))); + $setCacheKey = $this->getSetCacheKey($code); + $objectIdList = $this->cache->get($setCacheKey); + $this->cache->set($setCacheKey, array_diff($objectIdList, array_map(function($object) use ($field) { return $object->$field; }, $this->objects))); if(empty($this->config->cache->res[$this->table])) return; @@ -379,14 +413,14 @@ class cache /* 如果没有设置关联字段则整个缓存都受影响。If no associated fields are set, the entire cache is affected. */ if(empty($res->fields)) { - $keys[] = $this->getCacheKey($res->name); + $keys[] = $this->getResCacheKey($res->name); continue; } /* 根据关联字段查找受影响的缓存。Find the affected cache by the associated fields. */ foreach($objects as $object) { - $key = $this->getCacheKey($res->name); + $key = $this->getResCacheKey($res->name); foreach($res->fields as $field) { if(!isset($object->$field)) return $this->log("The {$field} field does not exist in table {$this->table}.", __FILE__, __LINE__); @@ -483,10 +517,16 @@ class cache $values = []; $code = $this->getTableCode(); - foreach($objects as $key => $object) $values["raw:{$code}:{$key}"] = $object; + foreach($objects as $key => $object) + { + $rawCacheKey = $this->getRawCacheKey($code, $key); + $values[$rawCacheKey] = $object; + } $this->cache->setMultiple($values); - $this->cache->set("set:{$code}List", array_keys($objects)); + + $setCacheKey = $this->getSetCacheKey($code); + $this->cache->set($setCacheKey, array_keys($objects)); return $objects; } @@ -508,11 +548,12 @@ class cache $this->setTable($table); $code = $this->getTableCode(); - $objectIdList = $this->cache->get("set:{$code}List"); + $setCacheKey = $this->getSetCacheKey($code); + $objectIdList = $this->cache->get($setCacheKey); if(!$objectIdList) return $this->initTableCache(); $keys = []; - foreach($objectIdList as $objectID) $keys[] = "raw:{$code}:{$objectID}"; + foreach($objectIdList as $objectID) $keys[] = $this->getRawCacheKey($code, $objectID); $objects = $this->cache->getMultiple($keys); if(!$objects) return []; @@ -554,7 +595,7 @@ class cache } } - $key = $this->getCacheKey(constant($key)); + $key = $this->getResCacheKey(constant($key)); foreach($args as $arg) $key .= ':' . $arg; $this->setKey($key); From a3910b7afd0952164f9e0b78febf34850e8d25aa Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 10:03:55 +0800 Subject: [PATCH 124/300] + [refac] add method to get the data of the specified table and id from the cache. --- lib/cache/cache.class.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index ea2ef01036..47f41251f5 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -531,6 +531,33 @@ class cache return $objects; } + /** + * 从缓存中获取指定表指定 id 的数据。 + * Get the data of the specified table and id from the cache. + * + * @param string $table 表名。 + * @param int|string $id 主键值。 + * @access public + * @return object|false + */ + public function fetch(string $table, int|string $id): object|bool + { + if(!$this->checkTable($table)) return $this->log("The {$table} table is not set in the cache configuration", __FILE__, __LINE__); + + if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__); + if(empty($id)) return $this->log('The id is empty', __FILE__, __LINE__); + + $this->setTable($table); + + $code = $this->getTableCode(); + $key = $this->getRawCacheKey($code, $id); + $object = $this->cache->get($key); + if($object) return $object; + + $objects = $this->initTableCache(); + return isset($objects[$id]) ? $objects[$id] : false; + } + /** * 从缓存中获取指定表的所有数据。 * Get all data of the specified table from the cache. From 551a93b1c2a98fcc181ea1fd3b270f35d191c23e Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 14:10:30 +0800 Subject: [PATCH 125/300] + [refac] convert the method findByXXX to where condition. --- lib/base/mao/mao.class.php | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index c45ead3b8c..e10c02d5f9 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -648,8 +648,45 @@ class baseMao return $pairs; } + /** + * 把名为 findByXXX 的方法转换为 where 条件。 + * Convert the method findByXXX to where condition. + * + * @param string $method + * @param array $args + * @access private + * @return object the mao object. + */ + private function findBy(string $method, array $args) + { + $field = str_replace('findby', '', $method); + if(count($args) == 1) + { + $operator = 'eq'; + $value = $args[0]; + } + else + { + $operator = $args[0]; + $value = $args[1]; + } + + $this->setFields(['*']); + $this->conditions = [['field' => $field, 'operator' => $operator, 'value' => $value]]; + + return $this; + } + public function __call(string $method, array $args) { + $method = strtolower($method); + + /* + * 如果是findByxxx,转换为where条件语句。 + * findByxxx, xxx as will be in the where. + **/ + if(strpos($method, 'findby') !== false) return $this->findBy($method, $args); + if(method_exists($this->cache, $method)) return call_user_func_array([$this->cache, $method], $args); $this->app->triggerError("Method $method not found in class baseMao.", __FILE__, __LINE__, true); From 1bea086a9b61df2bc8bd851f39fc94a968f9968d Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 14:10:48 +0800 Subject: [PATCH 126/300] * [refac] fix error the findBy method cann't get matched record. --- lib/base/mao/mao.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index e10c02d5f9..50597775be 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -528,11 +528,11 @@ class baseMao $value = $object->{$condition['field']}; if($condition['operator'] == 'eq') { - if($condition['value'] !== $value) return false; + if($condition['value'] != $value) return false; } elseif($condition['operator'] == 'ne') { - if($condition['value'] === $value) return false; + if($condition['value'] == $value) return false; } elseif($condition['operator'] == 'in') { From 06f71d73765cbfeafa2e6d492178661938015a68 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 15:13:48 +0800 Subject: [PATCH 127/300] * [refac] fetch data of specified id list from cache. --- lib/cache/cache.class.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 47f41251f5..a39a75c15a 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -566,7 +566,7 @@ class cache * @access public * @return array */ - public function fetchAll(string $table): array + public function fetchAll(string $table, array $objectIdList = []): array { if(!$this->checkTable($table)) return []; @@ -574,10 +574,14 @@ class cache $this->setTable($table); - $code = $this->getTableCode(); - $setCacheKey = $this->getSetCacheKey($code); - $objectIdList = $this->cache->get($setCacheKey); - if(!$objectIdList) return $this->initTableCache(); + $code = $this->getTableCode(); + + if(!$objectIdList) + { + $setCacheKey = $this->getSetCacheKey($code); + $objectIdList = $this->cache->get($setCacheKey); + if(!$objectIdList) return $this->initTableCache(); + } $keys = []; foreach($objectIdList as $objectID) $keys[] = $this->getRawCacheKey($code, $objectID); From 1a499c690160e42d8b26e1b2a1e74d6438822653 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 15:38:38 +0800 Subject: [PATCH 128/300] * [refac] fetch data from database if the table isn't set in the cache configuration. --- lib/base/mao/mao.class.php | 42 ++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 50597775be..1f2a64431d 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -122,6 +122,7 @@ class baseMao global $config; $this->app = $app; $this->config = $config; + $this->dao = $app->dao; $this->cache = $app->cache; $this->reset(); @@ -555,13 +556,15 @@ class baseMao * 获取一个记录。 * Fetch one record. * - * @param string $field 如果已经设置获取的字段,则只返回这个字段的值,否则返回这个记录。 + * @param string $keyField 如果已经设置获取的字段,则只返回这个字段的值,否则返回这个记录。 * if the field is set, only return the value of this field, else return this record * @access public * @return object|mixed */ - public function fetch(string $field = '') + public function fetch(string $keyField = '') { + if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetch', $keyField); + $rawResult = $this->cache->fetchAll($this->table); if(empty($rawResult)) return ''; @@ -569,7 +572,7 @@ class baseMao { if(!$this->isConditionMatched($row, $this->conditions)) continue; - return $field ? $row->$field : $row; + return $keyField ? $row->$keyField : $row; } return ''; @@ -584,9 +587,10 @@ class baseMao * @access public * @return array the records */ - public function fetchAll(string $keyField = '') + public function fetchAll(string $keyField = ''): array { - $rawResult = $this->cache->fetchAll($this->table); + if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchAll', $keyField); + if(empty($rawResult)) return []; $result = []; @@ -632,12 +636,14 @@ class baseMao */ public function fetchPairs(string $keyField = '', string $valueField = '') { + if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchPairs', $keyField, $valueField); + $pairs = []; $rows = $this->fetchAll(); if(empty($rows)) return []; - if(empty($keyField)) $keyField = $this->fields[0]; + if(empty($keyField)) $keyField = $this->fields[0]; if(empty($valueField)) $valueField = $this->fields[1]; foreach($rows as $row) @@ -648,6 +654,30 @@ class baseMao return $pairs; } + /** + * 从数据库中获取数据。 + * Fetch data from database. + * + * @param string $fetchFunc fetch|fetchAll|fetchPairs + * @param string $keyField + * @param string $valueField + * @access private + * @return mixed + */ + private function fetchFromDB(string $fetchFunc, string $keyField, string $valueField = '') + { + $fields = implode(',', $this->fields); + $this->dao->select($fields)->from($this->table)->where('1=1'); + + foreach($this->conditions as $index => $condition) + { + $func = $condition['operator']; + $this->dao->andWhere($condition['field'])->$func($condition['value']); + } + + return $this->dao->$fetchFunc($keyField, $valueField); + } + /** * 把名为 findByXXX 的方法转换为 where 条件。 * Convert the method findByXXX to where condition. From e4ed70a0ba0251d89ca63b0e7bfc94f7da4e175a Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 15:42:19 +0800 Subject: [PATCH 129/300] * [refac] if the primary key field is in the condition, try to get from the cache by the primary key field. --- lib/base/mao/mao.class.php | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 1f2a64431d..2b660631e7 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -565,7 +565,20 @@ class baseMao { if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetch', $keyField); - $rawResult = $this->cache->fetchAll($this->table); + $rawResult = []; + + /* 如果条件中有主键字段,则尝试通过主键字段从缓存中获取。If the primary key field is in the condition, try to get from the cache by the primary key field. */ + $field = $this->config->cache->raw[$this->table]; + foreach($this->conditions as $condition) + { + if($condition['field'] == $field && $condition['operator'] == 'eq') + { + $rawResult[] = $this->cache->fetch($this->table, $condition['value']); + break; + } + } + + if(empty($rawResult)) $rawResult = $this->cache->fetchAll($this->table); if(empty($rawResult)) return ''; foreach($rawResult as $row) @@ -591,6 +604,26 @@ class baseMao { if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchAll', $keyField); + $rawResult = []; + + /* 如果条件中有主键字段,则尝试通过主键字段从缓存中获取。If the primary key field is in the condition, try to get from the cache by the primary key field. */ + $field = $this->config->cache->raw[$this->table]; + foreach($this->conditions as $condition) + { + if($condition['field'] == $field && $condition['operator'] == 'in') + { + $value = $condition['value']; + if(is_numeric($value)) $value = [$value]; + if(is_string($value)) $value = explode(',', str_replace(' ', '', $value)); + if(is_array($value)) + { + $rawResult = $this->cache->fetchAll($this->table, $value); + break; + } + } + } + + if(empty($rawResult)) $rawResult = $this->cache->fetchAll($this->table); if(empty($rawResult)) return []; $result = []; @@ -658,7 +691,7 @@ class baseMao * 从数据库中获取数据。 * Fetch data from database. * - * @param string $fetchFunc fetch|fetchAll|fetchPairs + * @param string $fetchFunc fetch|fetchAll * @param string $keyField * @param string $valueField * @access private From 6ca5416baab7d570ac2ad30220f6331320cb8965 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 15:44:57 +0800 Subject: [PATCH 130/300] * [refac] remove unused variable. --- lib/base/mao/mao.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 2b660631e7..732a1464ed 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -702,7 +702,7 @@ class baseMao $fields = implode(',', $this->fields); $this->dao->select($fields)->from($this->table)->where('1=1'); - foreach($this->conditions as $index => $condition) + foreach($this->conditions as $condition) { $func = $condition['operator']; $this->dao->andWhere($condition['field'])->$func($condition['value']); From 61c1ad11ba2b15ee9c9fb2b9202f645b8454d0bf Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 16:11:14 +0800 Subject: [PATCH 131/300] * [refac] rename redis to cache. --- lib/cache/cache.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index a39a75c15a..625b629168 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -464,7 +464,7 @@ class cache if(!$this->config->debug) return false; $runMode = PHP_SAPI == 'cli' ? '_cli' : ''; - $logFile = $this->app->getLogRoot() . 'redis' . $runMode . '.' . date('Ymd') . '.log.php'; + $logFile = $this->app->getLogRoot() . 'cache' . $runMode . '.' . date('Ymd') . '.log.php'; if(!file_exists($logFile)) file_put_contents($logFile, '\n"); $content = date('Ymd H:i:s') . ': ' . $this->getURI() . "\nError: {$message} in $file on line $line\n"; From b6cdac814baeb4a0215d1dca727f6d9c63952357 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 16:11:40 +0800 Subject: [PATCH 132/300] * [refac] if the cache object is empty or the cache result is empty fetch data from database. --- lib/base/mao/mao.class.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 732a1464ed..f1091442d3 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -563,7 +563,7 @@ class baseMao */ public function fetch(string $keyField = '') { - if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetch', $keyField); + if(empty($this->cache) || empty($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetch', $keyField); $rawResult = []; @@ -579,7 +579,7 @@ class baseMao } if(empty($rawResult)) $rawResult = $this->cache->fetchAll($this->table); - if(empty($rawResult)) return ''; + if(empty($rawResult)) return $this->fetchFromDB('fetch', $keyField); foreach($rawResult as $row) { @@ -602,7 +602,7 @@ class baseMao */ public function fetchAll(string $keyField = ''): array { - if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchAll', $keyField); + if(empty($this->cache) || empty($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchAll', $keyField); $rawResult = []; @@ -624,7 +624,7 @@ class baseMao } if(empty($rawResult)) $rawResult = $this->cache->fetchAll($this->table); - if(empty($rawResult)) return []; + if(empty($rawResult)) return $this->fetchFromDB('fetchAll', $keyField); $result = []; foreach($rawResult as $row) @@ -669,9 +669,7 @@ class baseMao */ public function fetchPairs(string $keyField = '', string $valueField = '') { - if(!isset($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchPairs', $keyField, $valueField); - - $pairs = []; + if(empty($this->cache) || empty($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchPairs', $keyField, $valueField); $rows = $this->fetchAll(); if(empty($rows)) return []; @@ -679,6 +677,7 @@ class baseMao if(empty($keyField)) $keyField = $this->fields[0]; if(empty($valueField)) $valueField = $this->fields[1]; + $pairs = []; foreach($rows as $row) { $pairs[$row->$keyField] = $row->$valueField; @@ -750,8 +749,10 @@ class baseMao **/ if(strpos($method, 'findby') !== false) return $this->findBy($method, $args); + if(empty($this->cache)) return false; + if(method_exists($this->cache, $method)) return call_user_func_array([$this->cache, $method], $args); - $this->app->triggerError("Method $method not found in class baseMao.", __FILE__, __LINE__, true); + $this->app->triggerError("Method $method not found in class baseMao.", __FILE__, __LINE__, $this->config->debug >= 2); } } From c8bd87698c7ece2745cffabc7b553626f211205b Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 1 Nov 2024 19:40:11 +0800 Subject: [PATCH 133/300] * [refac] replace dao with mao. --- framework/model.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/model.class.php b/framework/model.class.php index 2557e069b6..dae146ed32 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -90,7 +90,7 @@ class model extends baseModel $table = zget($this->config->objectTables, $moduleName, ''); if(empty($table)) return false; - return $this->dao->findById($objectID)->from($table)->fetch(); + return $this->mao->findById($objectID)->from($table)->fetch(); } /** From 4c47a182e65930699affbe642cd84985c6452d86 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 4 Nov 2024 08:53:18 +0800 Subject: [PATCH 134/300] - [refac] remove unused file. --- lib/zredis/zredis.class.php | 476 ------------------------------- module/cache/handler/bug.php | 26 -- module/cache/handler/setting.php | 34 --- module/cache/model.php | 326 --------------------- 4 files changed, 862 deletions(-) delete mode 100644 lib/zredis/zredis.class.php delete mode 100644 module/cache/handler/bug.php delete mode 100644 module/cache/handler/setting.php delete mode 100644 module/cache/model.php diff --git a/lib/zredis/zredis.class.php b/lib/zredis/zredis.class.php deleted file mode 100644 index 5a49f6df27..0000000000 --- a/lib/zredis/zredis.class.php +++ /dev/null @@ -1,476 +0,0 @@ - - * @package zredis - * @link https://www.zentao.net - */ -class zredis -{ - /** - * 同步标识。 - * Synchronization flag. - * - * @var string - * @access public - */ - const SYNCHRONIZED = 'sys:synchronized'; - - /** - * 全局对象 $app。 - * The global $app object. - * - * @var object - * @access private - */ - private $app; - - /** - * 全局 $config 对象。 - * The global $config object. - * - * @var object - * @access private - */ - private $config; - - /** - * $dao 对象,用于访问或者更新数据库。 - * The $dao object, used to access or update database. - * - * @var dao - * @access private - */ - private $dao; - - /** - * $redis 对象,用于访问或者更新 Redis。 - * The $redis object, used to access or update database. - * - * @var redis - * @access private - */ - private $redis = null; - - /** - * 触发缓存更新的表名。 - * The table name to trigger cache update. - * - * @var string - * @access private - */ - private $table = ''; - - /** - * 触发缓存更新的事件类型,包括 insert, update, delete。 - * The event type to trigger cache update, including insert, update, delete. - * - * @var string - * @access private - */ - private $event = ''; - - /** - * 触发缓存更新的条件。 - * The condition to trigger cache update. - * - * @var string - * @access private - */ - private $condition = ''; - - /** - * 触发缓存删除的键列表。实际存储的值是根据缓存的 key 字段来决定的。 - * The ID list to trigger cache delete. The actual stored value is determined by the key field of the cache. - * - * @var array - * @access private - */ - private $keyList = []; - - /** - * 连接 Redis 服务器并初始化缓存。 - * Connect to Redis server and initialize cache. - * - * @param object $dao - * @access public - * @return void - */ - public function __construct(object $app) - { - global $config; - - $this->app = $app; - $this->config = $config; - $this->dao = $app->dao; - - if(empty($this->config->redis->enable)) helper::end('Redis is not enabled in the configuration file.'); - - try - { - $this->redis = new Redis(); - - $version = phpversion('redis'); - if(version_compare($version, '5.3.0', 'ge')) - { - $this->redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0, ['auth' => [$this->config->redis->username, $this->config->redis->password]]); - } - else - { - $this->redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); - $this->redis->auth(['pass' => $this->config->redis->password]); - } - - if(!$this->redis->ping()) helper::end('Can not connect to Redis server.'); - - return $this; - } - catch(RedisException $e) - { - $this->triggerError($e->getMessage(), __FILE__, __LINE__, true); - } - } - - /** - * 同步缓存前准备相关参数。 - * Prepare related parameters before synchronizing cache. - * - * @param string $table - * @param string $event - * @param string $sql - * @access public - * @return void - */ - public function prepare(string $table, string $event, string $sql) - { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($event)) return $this->log('Event type is required.'); - if(empty($sql)) return $this->log('SQL is required.'); - if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); - - $this->table = $table; - $this->event = $event; - - $condition = ''; - $keyList = []; - if($event == 'update' || $event == 'delete') - { - /* 获取 WHERE 子句的内容。Get the content of WHERE clause. */ - $whereLen = strlen(DAO::WHERE); - $wherePOS = strrpos($sql, DAO::WHERE); - $groupPOS = strrpos($sql, DAO::GROUPBY); - $havingPOS = strrpos($sql, DAO::HAVING); - $orderPOS = strrpos($sql, DAO::ORDERBY); - $limitPOS = strrpos($sql, DAO::LIMIT); - $splitPOS = $orderPOS ? $orderPOS : $limitPOS; - $splitPOS = $havingPOS ? $havingPOS : $splitPOS; - $splitPOS = $groupPOS ? $groupPOS : $splitPOS; - if($splitPOS) - { - $condition = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); - } - else - { - $condition = substr($sql, $wherePOS + $whereLen); - } - - if($event == 'delete') - { - /* 执行删除操作后数据已经被删除,所以需要提前获取 ID 列表。*/ - $field = $this->config->redis->cache->raw[$table]; - $keyList = $this->dao->select($field)->from($table)->beginIF($condition)->where($condition)->fi()->fetchPairs(); - } - } - - $this->condition = $condition; - $this->keyList = array_values($keyList); - } - - /** - * 同步缓存。 - * Sync cache. - * - * @access public - * @return void - */ - public function sync() - { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($this->table)) return $this->log('Table name is required.'); - if(empty($this->event)) return $this->log('Event type is required.'); - if(empty($this->config->redis->cache->raw[$this->table])) return $this->log('No cache settings for table ' . $this->table); - - $this->redis->multi(); - - if($this->event == 'insert') $this->create(); - if($this->event == 'update') $this->update(); - if($this->event == 'delete') $this->delete(); - - $this->redis->set(zredis::SYNCHRONIZED, date('Y-m-d H:i:s')); - - $this->redis->exec(); - - $this->reset(); - } - - /** - * 根据key获取值。 - * Get value by key. - * - * @param string $key - * @access public - * @return mixed - */ - public function get(string $key) - { - $value = $this->redis->get($key); - if(!$value) return $value; - - return json_decode($value); - } - - /** - * 设置缓存。 - * Set cache. - * - * @param string $key - * @param mixed $value - * @access public - * @return void - */ - public function set(string $key, $value) - { - $this->redis->set($key, json_encode($value, JSON_UNESCAPED_UNICODE)); - } - - /** - * 根据表名和键获取对象。 - * Get object by table name and key. - * - * @param string $table - * @param int|string $key - * @access public - * @return object - */ - public function fetch(string $table, int|string $key) - { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($key)) return $this->log('Key is required.'); - if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); - - $code = str_replace(['`', $this->config->db->prefix], '', $table); - return $this->get("raw:{$code}:{$key}"); - } - - /** - * 根据表名获取对象列表。 - * Get object list by table name. - * - * @param string $table - * @param array $keyList - * @access public - * @return array - */ - public function fetchAll(string $table, array $keyList = []) - { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); - - $keys = []; - $code = str_replace(['`', $this->config->db->prefix], '', $table); - if(!$keyList) $keyList = $this->redis->smembers("set:{$code}List"); - if(!$keyList) return []; - - foreach($keyList as $key) $keys[] = "raw:{$code}:{$key}"; - - $objects = $this->redis->mget($keys); - $cacheKey = $this->config->redis->cache->raw[$table]; - - $result = []; - foreach($objects as $object) - { - $object = json_decode($object); - if($object) $result[$object->$cacheKey] = $object; - } - - return $result; - } - - /** - * 根据表名和键获取键值对。 - * Get key-value pairs by table name and key. - * - * @param string $table - * @param string $key - * @param string $value - * @param array $keyList - * @access public - * @return array - */ - public function fetchPairs(string $table, string $key, string $value, array $keyList = []) - { - if(empty($this->redis)) return $this->log('Redis is not initialized.'); - if(empty($table)) return $this->log('Table name is required.'); - if(empty($key)) return $this->log('Key is required.'); - if(empty($value)) return $this->log('Value is required.'); - if(empty($this->config->redis->cache->raw[$table])) return $this->log('No cache settings for table ' . $table); - - $objects = $this->fetchAll($table, $keyList); - if(!$objects) return []; - - $paris = []; - foreach($objects as $object) $pairs[$object->$key] = $object->$value; - return $pairs; - } - - /** - * 创建缓存。 - * Create cache. - * - * @access private - * @return void - */ - private function create() - { - $objectID = $this->dao->lastInsertID(); - if(!$objectID) return $this->log('Failed to fetch last insert id.'); - - $this->condition = "`id` = '{$objectID}'"; - - $this->update(); - } - - /** - * 更新缓存。 - * Update cache. - * - * @access private - * @return void - */ - private function update() - { - $objects = $this->dao->select('*')->from($this->table) - ->beginIF($this->condition)->where($this->condition)->fi() - ->fetchAll(); - if(!$objects) return $this->log("Failed to fetch objects to update table {$this->table}.", $this->dao->get()); - - $code = str_replace(['`', $this->config->db->prefix], '', $this->table); - $key = $this->config->redis->cache->raw[$this->table]; - - $pairs = []; - foreach($objects as $object) $pairs["raw:{$code}:{$object->$key}"] = json_encode($object, JSON_UNESCAPED_UNICODE); - $this->redis->mset($pairs); - - $members = []; - foreach($objects as $object) $members[] = $object->$key; - $this->redis->sadd("set:{$code}List", ...$members); - } - - /** - * 删除缓存。 - * Delete cache. - * - * @access private - * @return void - */ - private function delete() - { - if(empty($this->keyList)) return $this->log('Failed to fetch key list to delete.'); - - $keyList = array_values($this->keyList); - - $keys = []; - $code = str_replace(['`', $this->config->db->prefix], '', $this->table); - foreach($keyList as $key) $keys[] = "raw:{$code}:{$key}"; - - $this->redis->del($keys); - $this->redis->srem("set:{$code}List", ...$keyList); - } - - /** - * 重置参数。 - * Reset parameters. - * - * @access public - * @return void - */ - public function reset(): void - { - $this->table = ''; - $this->event = ''; - $this->condition = ''; - $this->keyList = []; - } - - /** - * 记录日志。 - * Log. - * - * @param string $log - * @param string $sql - * @access private - * @return void - */ - private function log(string $log, string $sql = ''): void - { - $runMode = PHP_SAPI == 'cli' ? '_cli' : ''; - $logFile = $this->app->getLogRoot() . 'redis' . $runMode . '.' . date('Ymd') . '.log.php'; - if(!file_exists($logFile)) file_put_contents($logFile, '\n"); - - $content = date('Ymd H:i:s') . ': ' . $this->getURI() . "\n{$log}\n"; - - if($sql) $content .= "The sql is: {$sql}\n"; - - file_put_contents($logFile, $content, FILE_APPEND); - } - - /** - * 获取 URI。 - * Get URI. - * - * @access private - * @return string - */ - private function getURI(): string - { - $uri = $this->app->getURI(); - if($uri) return $uri; - - if($this->config->requestType == 'GET') return $_SERVER['REQUEST_URI']; - - if($this->config->requestType == 'PATH_INFO' || $this->config->requestType == 'PATH_INFO2') - { - $pathInfo = $this->app->getPathInfo(); - if(empty($pathInfo)) return ''; - - $dotPos = strrpos($pathInfo, '.'); - if($dotPos) return substr($pathInfo, 0, $dotPos); - return $pathInfo; - } - - return ''; - } - - /** - * 调用 Redis 对象的方法。 - * Call the method of Redis object. - * - * @param string $method - * @param array $args - * @access public - * @return void - */ - public function __call(string $method, array $args) - { - if(empty($this->redis)) helper::end('Redis is not initialized.'); - - return $this->redis->$method(...$args); - } -} diff --git a/module/cache/handler/bug.php b/module/cache/handler/bug.php deleted file mode 100644 index 5c72c6f536..0000000000 --- a/module/cache/handler/bug.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @package cache - * @link https://www.zentao.net - */ -class bugHandler -{ - /** - * 获取指派给某人的bug的缓存键名。 - * Get the cache key name of bugs assigned to someone. - * - * @param string $account - * @access public - * @return string - */ - public function assignedTo(string $account): string - { - return 'res:bug:assignedTo:' . $account; - } -} diff --git a/module/cache/handler/setting.php b/module/cache/handler/setting.php deleted file mode 100644 index 3423c99cf2..0000000000 --- a/module/cache/handler/setting.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @package action - * @version $Id$ - * @link https://www.zentao.net - */ -class settingHandler -{ - /** - * 获取系统及用户的配置信息。 - * Get system and personal config. - * - * @param string $account - * @access public - * @return array - */ - public function getSysAndPersonalConfig(string $account) - { - $owner = 'system,' . ($account ? $account : ''); - $records = $this->cache->select('*')->from(TABLE_CONFIG) - ->where('owner')->in($owner) - ->beginIF(!$this->app->upgrading)->andWhere('vision')->in(array('', $this->config->vision))->fi() - ->orderBy('id') - ->fetchAll('id'); - - return $records; - } -} diff --git a/module/cache/model.php b/module/cache/model.php deleted file mode 100644 index 60b210f89a..0000000000 --- a/module/cache/model.php +++ /dev/null @@ -1,326 +0,0 @@ - - * @package cache - * @link https://www.zentao.net - */ -class cacheModel extends model -{ - /** - * 缓存的模块。 - * The module of cache. - * - * @access private - * @var string - */ - private $module = ''; - - /** - * 缓存的方法。 - * The method of cache. - * - * @access private - * @var string - */ - private $method = ''; - - /** - * 缓存的键。 - * The key of cache. - * - * @access private - * @var string - */ - private $key = ''; - - /** - * 缓存的值。 - * The value of cache. - * - * @access private - * @var int|string|object|array - */ - private $value = ''; - - /** - * 缓存相关的键。 - * The related keys of cache. - * - * @access private - * @var array - */ - private $relatedKeys = []; - - /** - * 构造方法。 - * Constructor. - * - * @access public - * @return void - */ - public function __construct() - { - parent::__construct(); - - } - - /** - * 魔术方法,加载子类。 - * Magic get method. - * - * @param string $module - * @access public - * @return cacheModel - */ - public function __get(string $module): cacheModel - { - $this->reset(); - - $this->setModule($module); - - if(isset($this->$module)) return $this; - - $file = dirname(__FILE__) . DS . 'handler' . DS . strtolower($module) . '.php'; - if(!helper::import($file)) return $this->log("The handler file {$file} is not exist."); - - $class = $module . 'Handler'; - $this->$module = new $class(); - - return $this; - } - - /** - * 魔术方法,调用子类方法。 - * Magic call method. - * - * @param string $method - * @param array $args - * @access public - * @return cacheModel - */ - public function __call($method, $args) - { - $this->setMethod($method); - - $key = "res:{$this->module}:{$method}"; - $this->setKey($key); - - if(empty($this->{$this->module})) return $this; - if(!method_exists($this->{$this->module}, $method)) return $this->log("The method {$method} is not exist."); - - $key = call_user_func_array([$this->{$this->module}, $method], $args); - $this->setKey($key); - - return $this; - } - - /** - * 设置缓存的模块。 - * Set the module of cache. - * - * @param string $module - * @access private - * @return void - */ - private function setModule(string $module): void - { - $this->module = $module; - } - - /** - * 设置缓存的方法。 - * Set the method of cache. - * - * @param string $method - * @access private - * @return void - */ - private function setMethod(string $method): void - { - $this->method = $method; - } - - /** - * 设置缓存的键。 - * Set the key of cache. - * - * @param string $key - * @access private - * @return void - */ - private function setKey(string $key): void - { - $this->key = $key; - } - - /** - * 设置缓存的值。 - * Set the value of cache. - * - * @param int|string|object|array $value - * @access private - * @return void - */ - private function setValue(int|string|object|array $value): void - { - $this->value = $value; - } - - /** - * 设置缓存相关的键。 - * Set the related keys of cache. - * - * @param array $relatedKeys - * @access private - * @return void - */ - private function setRelatedKeys(array $relatedKeys): void - { - $this->relatedKeys = $relatedKeys; - } - - /** - * 重置缓存相关设置。 - * Reset the cache settings. - * - * @access private - * @return void - */ - private function reset(): void - { - $this->setModule(''); - $this->setMethod(''); - $this->setKey(''); - $this->setValue(''); - $this->setRelatedKeys([]); - } - - /** - * 设置缓存的值。 - * Set the value of cache. - * - * @param int|string|object|array $value - * @access public - * @return cacheModel - */ - public function value(int|string|object|array $value): cacheModel - { - $this->setValue(json_encode($value)); - return $this; - } - - /** - * 设置缓存相关的键。 - * Set the related keys of cache. - * - * @param array $relatedKeys - * @access public - * @return cacheModel - */ - public function relatedKeys(array $relatedKeys): cacheModel - { - if(!is_array($relatedKeys)) return $this->log('The relatedKeys must be array.'); - if(!$this->isIndexedArray($relatedKeys)) return $this->log('The relatedKeys must be indexed array.'); - - $this->setRelatedKeys($relatedKeys); - return $this; - } - - /** - * 获取缓存。 - * Fetch the cache. - * - * @access public - * @return false|int|string|object|array - */ - public function fetch(): bool|int|string|object|array - { - $result = $this->app->redis->get($this->key); - if($result) return json_decode($result); - return $result; - } - - /** - * 保存缓存。 - * Save the cache. - * - * @access public - * @return false|int|string|object|array - */ - public function save(): bool|int|string|object|array - { - if(empty($this->value)) - { - $this->log('The value is empty.'); - $this->reset(); - return false; - } - - $this->app->redis->set($this->key, $this->value); - $this->updateRelatedKeys(); - $this->reset(); - return true; - } - - /** - * 更新相关的缓存。 - * Update the related cache. - * - * @access private - * @return bool - */ - private function updateRelatedKeys(): bool - { - if(empty($this->config->objectTables[$this->module])) return false; - - $pairs = []; - $table = $this->config->objectTables[$this->module]; - $code = str_replace(['`', $this->config->db->prefix], '', $table); - $object = $this->app->redis->fetchAll($table, $this->relatedKeys); - $key = $this->config->redis->caches[$table]; - foreach($objects as $object) - { - $relatedKeys = isset($object->_relatedKeys) ? $object->_relatedKeys : []; - $relatedKeys[] = $this->key; - $object->_relatedKeys = array_unique($relatedKeys); - $pairs["raw:{$code}:{$object->$key}"] = json_encode($object, JSON_UNESCAPED_UNICODE); - } - - $this->app->redis->mset($pairs); - - return true; - } - - /** - * 检查是否为索引数组。 - * Check if it is an indexed array. - * - * @param array $array - * @access private - * @return bool - */ - private function isIndexedArray(array $array): bool - { - if(function_exists('array_is_list')) return array_is_list($value); - return $value === array_values($value); - } - - /** - * 记录日志。 - * Save log. - * - * @param string $message - * @access private - * @return object - */ - private function log(string $message) - { - if($this->config->debug) $this->app->triggerError($message, __FILE__, __LINE__, $this->config->debug >= 2); - return $this; - } -} From 37b1e21cb301caf687e21fcc7bb8ce5f6c86e4af Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 4 Nov 2024 13:28:15 +0800 Subject: [PATCH 135/300] * [refac] don't call property of none object. --- lib/base/dao/dao.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 684a11a0c8..54514a6cfa 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -1042,14 +1042,14 @@ class baseDAO /* Force to query from master db, if db has been changed. */ $this->slaveDBH = false; - $this->cache->prepare($table, $method, $sql); + if($this->cache) $this->cache->prepare($table, $method, $sql); $result = $this->dbh->exec($sql); /* See: https://www.php.net/manual/en/pdo.lastinsertid.php .*/ if($method == 'insert') $this->_lastInsertID = $this->dbh->lastInsertID(); - $result ? $this->cache->sync() : $this->cache->reset(); + if($this->cache) $result ? $this->cache->sync() : $this->cache->reset(); $this->setTableCache($sql); From f3c6734ae65cbb668aa0f77d717c5b4fd75b5917 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 4 Nov 2024 14:39:23 +0800 Subject: [PATCH 136/300] * [refac] set serializer of php redis. --- lib/cache/driver/RedisDriver.php | 45 +++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index 151169f283..8319956ea9 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -17,23 +17,58 @@ use ZenTao\Cache\SimpleCache\InvalidArgumentException; class RedisDriver implements CacheInterface { /** + * 缓存命名空间,用来区分不同的缓存。 + * The cache namespace, used to distinguish different caches. + * * @var string */ private $namespace; /** + * 缓存过期时间,单位为秒。 + * The cache expiration time, in seconds. + * * @var int */ private $defaultLifetime; + /** + * 缓存序列化器,默认使用 PHP 内置的序列化器。 + * The cache serializer, the default is the PHP built-in serializer. + * + * @var int + */ + private $serializer = \Redis::SERIALIZER_PHP; + public function __construct($namespace = '', $defaultLifetime = 0) { $this->namespace = $namespace; $this->defaultLifetime = $defaultLifetime; + $this->setSerializer(); + $this->connectRedis(); } + /** + * 设置序列化器。 + * Set the serializer. + * + * @access private + * @return void + */ + private function setSerializer() + { + if(function_exists('igbinary_serialize')) + { + $this->serializer = \Redis::SERIALIZER_IGBINARY; + } + elseif(function_exists('msgpack_pack')) + { + $this->serializer = \Redis::SERIALIZER_MSGPACK; + } + } + /** * 连接 Redis 服务器。 * Connect to the Redis server. @@ -63,6 +98,8 @@ class RedisDriver implements CacheInterface } if(!$this->redis->ping()) \helper::end('Can not connect to Redis server.'); + + $this->redis->setOption(\Redis::OPT_SERIALIZER, $this->serializer); } catch(RedisException $e) { @@ -85,7 +122,7 @@ class RedisDriver implements CacheInterface $value = $this->redis->get($key); - return $value ? unserialize($value) : $default; + return $value ? $value : $default; } /** @@ -103,7 +140,7 @@ class RedisDriver implements CacheInterface $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; - return $this->redis->set($key, serialize($value), (int)$ttl); + return $this->redis->set($key, $value, (int)$ttl); } /** @@ -178,7 +215,7 @@ class RedisDriver implements CacheInterface { $key = preg_replace("/^$this->namespace/", '', $key); - $mappedResult[$key] = unserialize($value); + $mappedResult[$key] = $value; } return $mappedResult; @@ -200,7 +237,7 @@ class RedisDriver implements CacheInterface foreach($values as $key => $value) { - $mappedByNamespaceValues[$this->buildKeyName($key)] = serialize($value); + $mappedByNamespaceValues[$this->buildKeyName($key)] = $value; } $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; From 9394c4e9695fc87e99ab7e2fb511de75db9fb71f Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 4 Nov 2024 17:30:51 +0800 Subject: [PATCH 137/300] - [refac] remove unused file. --- config/redis.php | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 config/redis.php diff --git a/config/redis.php b/config/redis.php deleted file mode 100644 index c05e1e865c..0000000000 --- a/config/redis.php +++ /dev/null @@ -1,37 +0,0 @@ -redis = new stdClass(); -$config->redis->enable = true; -$config->redis->host = '127.0.0.1'; -$config->redis->port = 6379; -$config->redis->timeout = 10; -$config->redis->username = null; -$config->redis->password = null; - -// Format : $config->redis->cache->raw[TABLE_NAME] = 'KEY_FIELD'; -// The TABLE_NAME is the name of the table in the database. -// The KEY_FIELD is the field of the table which is used to generate the key of the cache. It must be unique in the table. - -$config->redis->cache = new stdClass(); -$config->redis->cache->raw = []; -$config->redis->cache->raw[TABLE_CONFIG] = 'id'; -$config->redis->cache->raw[TABLE_BUILD] = 'id'; -$config->redis->cache->raw[TABLE_MODULE] = 'id'; -$config->redis->cache->raw[TABLE_PRODUCT] = 'id'; -$config->redis->cache->raw[TABLE_PROJECT] = 'id'; -$config->redis->cache->raw[TABLE_RELEASE] = 'id'; -$config->redis->cache->raw[TABLE_USER] = 'account'; - -$config->redis->cache->res = []; -$config->redis->cache->res[TABLE_MODULE][] = ['name' => 'CACHE_MODULE_TREE', 'fields' => ['type', 'root', 'branch']]; - -$config->cache->keys = []; -foreach($config->redis->cache->res as $table => $caches) -{ - foreach($caches as $cache) - { - $cache = (object)$cache; - $cache->table = $table; - $config->cache->keys[$cache->name] = $cache; - define($cache->name, $cache->name); - } -} From 16980124c0b4c3b10ebf4755d39641203b12d5f3 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 4 Nov 2024 17:33:37 +0800 Subject: [PATCH 138/300] * [refac] add namespace of cache. --- config/cache.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/config/cache.php b/config/cache.php index dd6cbd2afc..ec8d225857 100644 --- a/config/cache.php +++ b/config/cache.php @@ -1,9 +1,10 @@ cache = new stdclass(); -$config->cache->enable = false; // 是否开启缓存。Enable cache or not. -$config->cache->driver = ''; // 缓存驱动。 The driver of cache. Can be File|Yac|Apcu|Redis. -$config->cache->lifetime = 2592000; // 缓存生存时间。The lifetime of cache. The default value is 30 days. +$config->cache->enable = false; // 是否开启缓存。Enable cache or not. +$config->cache->driver = ''; // 缓存驱动。 The driver of cache. Can be File|Yac|Apcu|Redis. +$config->cache->namespace = ''; // 缓存命名空间。The namespace of cache. +$config->cache->lifetime = 2592000; // 缓存生存时间。The lifetime of cache. The default value is 30 days. $config->cache->client = new stdClass(); $config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not. From ca997cd1c427cded50cd9f443e1b9947facb0992 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 5 Nov 2024 10:26:42 +0800 Subject: [PATCH 139/300] * [refac] add namespace and connector. --- lib/cache/cache.class.php | 66 +++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 625b629168..3dfb2289d8 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -64,6 +64,24 @@ class cache */ private $cache; + /** + * 缓存命名空间。 + * Cache namespace. + * + * @access private + * @var string + */ + private $namespace; + + /** + * 缓存键连接符。 + * Cache key connector. + * + * @access private + * @var string + */ + private $connector; + /** * 缓存键。 * Cache key. @@ -138,15 +156,19 @@ class cache switch($driver) { case self::DRIVER_APCU: + $this->setConnector('-'); $className = 'ZenTao\Cache\Driver\ApcuDriver'; break; case self::DRIVER_YAC: + $this->setConnector('-'); $className = 'ZenTao\Cache\Driver\YacDriver'; break; case self::DRIVER_FILE: + $this->setConnector('_'); $className = 'ZenTao\Cache\Driver\FileDriver'; break; case self::DRIVER_REDIS: + $this->setConnector(':'); $className = 'ZenTao\Cache\Driver\RedisDriver'; break; default: @@ -155,7 +177,35 @@ class cache if($driver != self::DRIVER_FILE && !extension_loaded($driver)) return $this->log("Driver ext-{$driver} is not loaded.", __FILE__, __LINE__); - $this->cache = new $className($this->config->db->name, $this->config->cache->lifetime, $app->getCacheRoot()); + $this->cache = new $className($this->config->cache->namespace, $this->config->cache->lifetime, $app->getCacheRoot()); + + $this->setNamespace($this->config->cache->namespace); + } + + /** + * 设置缓存命名空间。 + * Set cache namespace. + * + * @param string $namespace 缓存命名空间。 + * @access private + * @return void + */ + private function setNamespace(string $namespace) + { + $this->namespace = $namespace; + } + + /** + * 设置缓存键连接符。 + * Set cache key connector. + * + * @param string $connector 缓存键连接符。 + * @access private + * @return void + */ + private function setConnector(string $connector) + { + $this->connector = $connector; } /** @@ -258,7 +308,7 @@ class cache */ private function getRawCacheKey(string $code, int|string $id): string { - return 'raw:' . $code . ':' . $id; + return $this->namespace . $this->connector . 'raw' . $this->connector . $code . $this->connector . $id; } /** @@ -271,7 +321,7 @@ class cache */ private function getSetCacheKey(string $code): string { - return 'set:' . $code . ':list'; + return $this->namespace . $this->connector . 'set' . $this->connector . $code . $this->connector . 'list'; } /** @@ -284,7 +334,7 @@ class cache */ private function getResCacheKey(string $key): string { - return str_replace(['cache', '_'], ['res', ':'], strtolower($key)); + return $this->namespace . $this->connector . str_replace(['cache', '_'], ['res', $this->connector], strtolower($key)); } /** @@ -425,7 +475,7 @@ class cache { if(!isset($object->$field)) return $this->log("The {$field} field does not exist in table {$this->table}.", __FILE__, __LINE__); - $key .= ':' . $object->$field; + $key .= $this->connector . $object->$field; } $keys[] = $key; } @@ -627,7 +677,7 @@ class cache } $key = $this->getResCacheKey(constant($key)); - foreach($args as $arg) $key .= ':' . $arg; + foreach($args as $arg) $key .= $this->connector . $arg; $this->setKey($key); return $this; @@ -673,6 +723,8 @@ class cache public function getByKey(string $key) { if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); + + $key = $this->namespace . $this->connector . $key; return $this->cache->get($key); } @@ -702,6 +754,8 @@ class cache public function saveByKey(string $key, $value) { if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); + + $key = $this->namespace . $this->connector . $key; return $this->cache->set($key, $value); } From 4a01690b1e704a3120588b5a2fdd7053c5a414b7 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 5 Nov 2024 10:27:20 +0800 Subject: [PATCH 140/300] * [refac] simplify apcu driver class and redis driver class. --- lib/cache/driver/ApcuDriver.php | 93 +---------------------- lib/cache/driver/RedisDriver.php | 122 +++---------------------------- 2 files changed, 14 insertions(+), 201 deletions(-) diff --git a/lib/cache/driver/ApcuDriver.php b/lib/cache/driver/ApcuDriver.php index 53409fddc1..7adeda2b41 100644 --- a/lib/cache/driver/ApcuDriver.php +++ b/lib/cache/driver/ApcuDriver.php @@ -34,9 +34,6 @@ class ApcuDriver implements CacheInterface public function get($key, $default = null) { - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - $value = apcu_fetch($key, $success); return $success === false ? $default : $value; @@ -44,9 +41,6 @@ class ApcuDriver implements CacheInterface public function set($key, $value, $ttl = null) { - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; return apcu_store($key, $value, (int) $ttl); @@ -54,9 +48,6 @@ class ApcuDriver implements CacheInterface public function delete($key) { - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - return apcu_delete($key); } @@ -67,52 +58,20 @@ class ApcuDriver implements CacheInterface public function getMultiple($keys, $default = null) { - $this->assertKeyNames($keys); - $keys = $this->buildKeyNames($keys); - - $result = apcu_fetch($keys); - - if(!is_null($default) && is_array($result) && count($keys) > count($result)) - { - $notFoundKeys = array_diff($keys, array_keys($result)); - $result = array_merge($result, array_fill_keys($notFoundKeys, $default)); - } - - $mappedResult = array(); - - foreach($result as $key => $value) - { - $key = preg_replace("/^$this->namespace/", '', $key); - - $mappedResult[$key] = $value; - } - - return $mappedResult; + return apcu_fetch($keys); } public function setMultiple($values, $ttl = null) { - $this->assertKeyNames(array_keys($values)); - - $mappedByNamespaceValues = array(); - - foreach($values as $key => $value) - { - $mappedByNamespaceValues[$this->buildKeyName($key)] = $value; - } - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; - $result = apcu_store($mappedByNamespaceValues, (int) $ttl); + $result = apcu_store($values, (int) $ttl); return $result === true ? true : (is_array($result) && count($result) == 0 ? true : false); } public function deleteMultiple($keys) { - $this->assertKeyNames($keys); - $keys = $this->buildKeyNames($keys); - $result = apcu_delete($keys); return count($result) === count($keys) ? false : true; @@ -120,54 +79,6 @@ class ApcuDriver implements CacheInterface public function has($key) { - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - return (bool) apcu_exists($key); } - - /** - * @param string $key - * - * @return string - */ - private function buildKeyName($key) - { - return $this->namespace . $key; - } - - /** - * @param string[] $keys - * - * @return string[] - */ - private function buildKeyNames(array $keys) - { - return array_map(function($key) { - return $this->buildKeyName($key); - }, $keys); - - } - - /** - * @param mixed $key - * - * @throws InvalidArgumentException - */ - private function assertKeyName($key) - { - if(!is_scalar($key) || is_bool($key)) throw new InvalidArgumentException(); - } - - /** - * @param string[] $keys - * - * @throws InvalidArgumentException - */ - private function assertKeyNames(array $keys) - { - array_map(function ($value) { - $this->assertKeyName($value); - }, $keys); - } } diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index 8319956ea9..1297c71959 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -117,9 +117,6 @@ class RedisDriver implements CacheInterface */ public function get($key, $default = null) { - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - $value = $this->redis->get($key); return $value ? $value : $default; @@ -135,9 +132,6 @@ class RedisDriver implements CacheInterface */ public function set($key, $value, $ttl = null) { - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; return $this->redis->set($key, $value, (int)$ttl); @@ -148,16 +142,10 @@ class RedisDriver implements CacheInterface * * @link https://github.com/phpredis/phpredis?tab=readme-ov-file#del-delete-unlink * @param mixed $key - * @param bool $prefix * @return int */ - public function delete($key, $prefix = true) + public function delete($key) { - if(!$prefix) return $this->redis->del($key); - - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - return $this->redis->del($key); } @@ -169,24 +157,18 @@ class RedisDriver implements CacheInterface */ public function clear() { - global $config; - /* With Redis::SCAN_RETRY enabled */ $this->redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY); - $it = NULL; - while($cachedKeys = $this->redis->scan($it)) + $it = null; + $keys = []; + + while($cachedKeys = $this->redis->scan($it, $this->namespace . ':*')) { - foreach ($cachedKeys as $key) - { - if(strpos($key, $config->db->name) !== false) - { - $this->delete($key, false); - } - } + $keys = array_merge($keys, $cachedKeys); } - return true; + return $this->deleteMultiple($keys); } /** @@ -198,27 +180,7 @@ class RedisDriver implements CacheInterface */ public function getMultiple($keys, $default = null) { - $this->assertKeyNames($keys); - $keys = $this->buildKeyNames($keys); - - $result = $this->redis->mget($keys); - - if(!is_null($default) && is_array($result) && count($keys) > count($result)) - { - $notFoundKeys = array_diff($keys, array_keys($result)); - $result = array_merge($result, array_fill_keys($notFoundKeys, $default)); - } - - $mappedResult = array(); - - foreach($result as $key => $value) - { - $key = preg_replace("/^$this->namespace/", '', $key); - - $mappedResult[$key] = $value; - } - - return $mappedResult; + return $this->redis->mget($keys); } /** @@ -231,26 +193,17 @@ class RedisDriver implements CacheInterface */ public function setMultiple($values, $ttl = null) { - $this->assertKeyNames(array_keys($values)); - - $mappedByNamespaceValues = array(); - - foreach($values as $key => $value) - { - $mappedByNamespaceValues[$this->buildKeyName($key)] = $value; - } - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; if(!empty($ttl)) { - foreach($mappedByNamespaceValues as $key => $value) + foreach($values as $key => $value) { $this->redis->set($key, $value, $ttl); } } - return $this->redis->mset($mappedByNamespaceValues); + return $this->redis->mset($values); } /** @@ -261,17 +214,9 @@ class RedisDriver implements CacheInterface */ public function deleteMultiple($keys) { - $this->assertKeyNames($keys); - $keys = $this->buildKeyNames($keys); + $result = $this->redis->del($keys); - $result = array(); - foreach($keys as $key) - { - $isDeleted = $this->delete($key); - if($isDeleted) $result[] = $isDeleted; - } - - return count($result) === count($keys) ? true : false; + return count($result) === count($keys); } /** @@ -283,49 +228,6 @@ class RedisDriver implements CacheInterface */ public function has($key) { - $this->assertKeyName($key); - $key = $this->buildKeyName($key); - return (bool) $this->redis->exists($key); } - - /** - * @param string $key - * - * @return string - */ - private function buildKeyName($key) - { - return $this->namespace . ':' . $key; - } - - /** - * @param string[] $keys - * - * @return string[] - */ - private function buildKeyNames(array $keys) - { - return array_map(function($key){return $this->buildKeyName($key);}, $keys); - } - - /** - * @param mixed $key - * - * @throws InvalidArgumentException - */ - private function assertKeyName($key) - { - if(!is_scalar($key) || is_bool($key)) throw new InvalidArgumentException(); - } - - /** - * @param string[] $keys - * - * @throws InvalidArgumentException - */ - private function assertKeyNames(array $keys) - { - array_map(function ($value){$this->assertKeyName($value);}, $keys); - } } From 0637346f85a7d4ff92ee9c5dd16be3ebe87b3fb9 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 6 Nov 2024 09:15:08 +0800 Subject: [PATCH 141/300] + [refac] add method to clear cache. --- lib/cache/cache.class.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 3dfb2289d8..93b65bba7b 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -865,4 +865,16 @@ class cache $this->reset(); } + + /** + * 清空缓存。 + * Clear cache. + * + * @access public + * @return void + */ + public function clear + { + $this->cache->clear(); + } } From 56174d03da932dd52bca362f4aab1d7a06ff8849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Tue, 5 Nov 2024 08:51:05 +0800 Subject: [PATCH 142/300] * [refac] Append field to data. --- lib/base/mao/mao.class.php | 77 +++++++++++++++++++++++++++++++++++--- module/bug/model.php | 2 +- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index f1091442d3..196a23d9c3 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -243,7 +243,28 @@ class baseMao { $this->conditions = []; - $this->setFields(explode(',', str_replace(' ', '', $fields))); + $fields = explode(',', $fields); + + $alias = []; + foreach($fields as $field) + { + $fieldInfo = explode(' ', trim($field)); + if(count($fieldInfo) == 1) + { + $alias[$fieldInfo[0]] = $fieldInfo[0]; + } + elseif(count($fieldInfo) == 2) + { + $alias[$fieldInfo[0]] = $fieldInfo[1]; + } + else + { + $alias[$fieldInfo[0]] = $fieldInfo[2]; + } + } + + $this->setFields($alias); + return $this; } @@ -310,7 +331,7 @@ class baseMao public function where(string $field) { $this->resetCondition(); - $this->condition['field'] = $field; + $this->condition['field'] = trim($field, '`'); return $this; } @@ -325,7 +346,7 @@ class baseMao public function andWhere(string $field) { $this->resetCondition(); - $this->condition['field'] = $field; + $this->condition['field'] = trim($field, '`'); return $this; } @@ -585,7 +606,10 @@ class baseMao { if(!$this->isConditionMatched($row, $this->conditions)) continue; - return $keyField ? $row->$keyField : $row; + if(!$field) return $row; + + $field = trim($field, '`'); + return $row->$field; } return ''; @@ -632,14 +656,14 @@ class baseMao if(!$this->isConditionMatched($row, $this->conditions)) continue; $data = new stdclass(); - foreach($this->fields as $field) + foreach($this->fields as $field => $alias) { if($field == '*') { $data = $row; break; } - $data->$field = $row->$field; + $data->$alias = $row->$field; } if($keyField) @@ -739,6 +763,47 @@ class baseMao return $this; } + /** + * 获取缓存数据拼接到已有数据。 + * Append cache fields to data. + * + * @param array $data + * @param string $primaryKey + * @access public + * @return void + */ + public function into(array $data, $primaryKey) + { + if(empty($data)) return; + + /* Get data keys as conditions. */ + $keyList = []; + foreach($data as $row) $keyList[$row->$primaryKey] = $row->$primaryKey; + + $rawResult = $this->cache->fetchAll($this->table, $keyList); + + $result = []; + foreach($rawResult as $row) + { + $data = new stdclass(); + foreach($this->fields as $field => $alias) + { + $data->$alias = $row->$field; + } + + if($keyField) + { + $result[$row->$keyField] = $data; + } + else + { + $result[] = $data; + } + } + + return $result; + } + public function __call(string $method, array $args) { $method = strtolower($method); diff --git a/module/bug/model.php b/module/bug/model.php index d6abd8e573..5c7ed9a9e5 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -877,7 +877,7 @@ class bugModel extends model ->page($pager) ->fetchAll(); - $this->mao->use($bugs, 'product')->append(TABLE_PRODUCT, 'name AS productName, shadow'); + $this->mao->select('name AS productName, shadow')->from(TABLE_PRODUCT)->into($bugs, 'product'); return $bugs; } From aafac9c06385ca0e6dd420cd0f13dc4873e917ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Tue, 5 Nov 2024 10:40:33 +0800 Subject: [PATCH 143/300] * [misc] Fix field error. --- lib/base/mao/mao.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 196a23d9c3..96e2994d81 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -606,10 +606,10 @@ class baseMao { if(!$this->isConditionMatched($row, $this->conditions)) continue; - if(!$field) return $row; + if(!$keyField) return $row; - $field = trim($field, '`'); - return $row->$field; + $keyField = trim($keyField, '`'); + return $row->$keyField; } return ''; From f1bb5c856f27ccaa92bf3b078a41e234491af840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Wed, 6 Nov 2024 11:13:54 +0800 Subject: [PATCH 144/300] * [misc] Select into for cache. --- lib/base/mao/mao.class.php | 25 +++++------------- lib/cache/cache.class.php | 54 +++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 46 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 96e2994d81..f09e34cc11 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -778,30 +778,17 @@ class baseMao /* Get data keys as conditions. */ $keyList = []; - foreach($data as $row) $keyList[$row->$primaryKey] = $row->$primaryKey; + foreach($data as $index => $row) $keyList[$index] = $row->$primaryKey; - $rawResult = $this->cache->fetchAll($this->table, $keyList); + $cacheResult = $this->cache->fetchAll($this->table, array_unique($keyList)); - $result = []; - foreach($rawResult as $row) + foreach($data as $index=> $row) { - $data = new stdclass(); - foreach($this->fields as $field => $alias) - { - $data->$alias = $row->$field; - } + $key = $keyList[$index]; + $cacheRow = $cacheResult[$key]; - if($keyField) - { - $result[$row->$keyField] = $data; - } - else - { - $result[] = $data; - } + foreach($this->fields as $field => $alias) $row->$alias = $cacheRow->$field; } - - return $result; } public function __call(string $method, array $args) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 93b65bba7b..83dbbeae85 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -626,22 +626,34 @@ class cache $code = $this->getTableCode(); - if(!$objectIdList) - { - $setCacheKey = $this->getSetCacheKey($code); - $objectIdList = $this->cache->get($setCacheKey); - if(!$objectIdList) return $this->initTableCache(); - } - - $keys = []; - foreach($objectIdList as $objectID) $keys[] = $this->getRawCacheKey($code, $objectID); - - $objects = $this->cache->getMultiple($keys); - if(!$objects) return []; - $result = []; - $field = $this->getTableField(); - foreach($objects as $object) $result[$object->$field] = $object; + + /* Try get all objectIdList. */ + $setCacheKey = $this->getSetCacheKey($code); + $allObjectIdList = $this->cache->get($setCacheKey); + + /* No data in cache, init table cache. */ + if(!$allObjectIdList) + { + $allData = $this->initTableCache(); + if(empty($objectIdList)) return $allData; + + foreach($allData as $objectId => $object) + { + if(in_array($objectId, $objectIdList)) $result[$objectId] = $object; + } + } + else + { + $keys = []; + foreach($objectIdList as $objectID) $keys[] = $this->getRawCacheKey($code, $objectID); + + $objects = $this->cache->getMultiple($keys); + if(!$objects) return []; + + $field = $this->getTableField(); + foreach($objects as $object) $result[$object->$field] = $object; + } return $result; } @@ -865,16 +877,4 @@ class cache $this->reset(); } - - /** - * 清空缓存。 - * Clear cache. - * - * @access public - * @return void - */ - public function clear - { - $this->cache->clear(); - } } From c36e3e807c192bbdf92af308e57bf25e0a206475 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 13:37:11 +0800 Subject: [PATCH 145/300] * [refac] add method to clear cache. --- lib/cache/cache.class.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 83dbbeae85..03f1c17d70 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -877,4 +877,16 @@ class cache $this->reset(); } + + /** + * 清空缓存。 + * Clear cache. + * + * @access public + * @return void + */ + public function clear() + { + $this->cache->clear(); + } } From 3f81e771e0abd29d2903595a428db2579333d6a4 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:01:26 +0800 Subject: [PATCH 146/300] - [refac] remove code of cache from admin module. --- module/admin/config/form.php | 3 --- module/admin/control.php | 46 ------------------------------------ module/admin/lang/de.php | 12 ---------- module/admin/lang/en.php | 12 ---------- module/admin/lang/fr.php | 12 ---------- module/admin/lang/zh-cn.php | 12 ---------- module/admin/zen.php | 21 ---------------- 7 files changed, 118 deletions(-) diff --git a/module/admin/config/form.php b/module/admin/config/form.php index 6fa1ddc479..f862d6abc2 100644 --- a/module/admin/config/form.php +++ b/module/admin/config/form.php @@ -24,6 +24,3 @@ $config->admin->form->log['days'] = array('type' => 'int', 'required' => true, ' $config->admin->form->resetpwdsetting = array(); $config->admin->form->resetpwdsetting['resetPWDByMail'] = array('type' => 'int', 'required' => false, 'default' => 0); - -$config->admin->form->cache = array(); -$config->admin->form->cache['dao'] = array('type' => 'array', 'default' => array('enable' => 0)); diff --git a/module/admin/control.php b/module/admin/control.php index dc2f65192d..4b82e00a35 100755 --- a/module/admin/control.php +++ b/module/admin/control.php @@ -590,50 +590,4 @@ class admin extends control echo 'success'; } - - /** - * 设置是否启用缓存。 - * Set cache enable. - * - * @access public - * @return void - */ - public function cache() - { - if($_POST) - { - if(!extension_loaded('apcu')) return $this->send(array('result' => 'fail', 'message' => $this->lang->admin->apcuNotLoaded)); - if(!ini_get('apc.enabled')) return $this->send(array('result' => 'fail', 'message' => $this->lang->admin->apcuNotEnabled)); - - $cache = form::data()->get(); - $this->loadModel('setting')->setItem('system.common.global.cache', json_encode($cache)); - - if($cache->dao['enable'] != $this->config->cache->dao->enable) $this->dao->clearCache(); - - return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true)); - } - - if(helper::isAPCuEnabled()) - { - $this->view->rate = $this->adminZen->getAPCuMemory('rate'); - $this->view->used = $this->adminZen->getAPCuMemory('used'); - $this->view->total = $this->adminZen->getAPCuMemory('total'); - } - - $this->view->title = $this->lang->admin->cache; - $this->display(); - } - - /** - * 清除数据缓存。 - * Clear data cache. - * - * @access public - * @return void - */ - public function ajaxClearCache() - { - $this->dao->clearCache(); - return $this->send(array('result' => 'success', 'message' => $this->lang->admin->clearSuccess, 'load' => true)); - } } diff --git a/module/admin/lang/de.php b/module/admin/lang/de.php index a43d945734..a551501bb1 100755 --- a/module/admin/lang/de.php +++ b/module/admin/lang/de.php @@ -53,18 +53,6 @@ $lang->admin->engineInfo = "The %s table engine is $lang->admin->engineSummary['hasMyISAM'] = "There are %s tables that are not InnoDB engines"; $lang->admin->engineSummary['allInnoDB'] = "All tables are InnoDB engines"; -$lang->admin->daoCache = 'Data Cache'; -$lang->admin->clearCache = 'Clear'; -$lang->admin->clearSuccess = 'Cleared'; -$lang->admin->memory = 'Memory'; -$lang->admin->usedMemory = 'Total %s, %s used'; -$lang->admin->apcuNotice = 'Data cache is based on the PHP-APCu extension, which needs to be loaded before enabling.'; -$lang->admin->apcuNotLoaded = 'Please load the APCu extension before enabling DAO cache'; -$lang->admin->apcuNotEnabled = 'Please enable the apc.enabled option before enabling DAO cache'; - -$lang->admin->cacheStatusList[1] = 'On'; -$lang->admin->cacheStatusList[0] = 'Off'; - $lang->admin->info = new stdclass(); $lang->admin->info->version = 'Aktuelle Version ist %s. '; $lang->admin->info->links = 'You can visit links below'; diff --git a/module/admin/lang/en.php b/module/admin/lang/en.php index 5a1a8f43a2..38a3ade9fe 100755 --- a/module/admin/lang/en.php +++ b/module/admin/lang/en.php @@ -53,18 +53,6 @@ $lang->admin->engineInfo = "The %s table engine $lang->admin->engineSummary['hasMyISAM'] = "There are %s tables that are not InnoDB engines"; $lang->admin->engineSummary['allInnoDB'] = "All tables are InnoDB engines"; -$lang->admin->daoCache = 'Data Cache'; -$lang->admin->clearCache = 'Clear'; -$lang->admin->clearSuccess = 'Cleared'; -$lang->admin->memory = 'Memory'; -$lang->admin->usedMemory = 'Total %s, %s used'; -$lang->admin->apcuNotice = 'Data cache is based on the PHP-APCu extension, which needs to be loaded before enabling.'; -$lang->admin->apcuNotLoaded = 'Please load the APCu extension before enabling DAO cache'; -$lang->admin->apcuNotEnabled = 'Please enable the apc.enabled option before enabling DAO cache'; - -$lang->admin->cacheStatusList[1] = 'On'; -$lang->admin->cacheStatusList[0] = 'Off'; - $lang->admin->info = new stdclass(); $lang->admin->info->version = 'Current Version is %s. '; $lang->admin->info->links = 'You can visit links below'; diff --git a/module/admin/lang/fr.php b/module/admin/lang/fr.php index ae1935b8e4..3e99ea8b84 100755 --- a/module/admin/lang/fr.php +++ b/module/admin/lang/fr.php @@ -53,18 +53,6 @@ $lang->admin->engineInfo = "The %s table engine is $lang->admin->engineSummary['hasMyISAM'] = "There are %s tables that are not InnoDB engines"; $lang->admin->engineSummary['allInnoDB'] = "All tables are InnoDB engines"; -$lang->admin->daoCache = 'Data Cache'; -$lang->admin->clearCache = 'Clear'; -$lang->admin->clearSuccess = 'Cleared'; -$lang->admin->memory = 'Memory'; -$lang->admin->usedMemory = 'Total %s, %s used'; -$lang->admin->apcuNotice = 'Data cache is based on the PHP-APCu extension, which needs to be loaded before enabling.'; -$lang->admin->apcuNotLoaded = 'Please load the APCu extension before enabling DAO cache'; -$lang->admin->apcuNotEnabled = 'Please enable the apc.enabled option before enabling DAO cache'; - -$lang->admin->cacheStatusList[1] = 'On'; -$lang->admin->cacheStatusList[0] = 'Off'; - $lang->admin->info = new stdclass(); $lang->admin->info->version = 'La version actuelle est %s customisée. '; $lang->admin->info->links = 'Vous pouvez visiter les liens ci-dessous'; diff --git a/module/admin/lang/zh-cn.php b/module/admin/lang/zh-cn.php index 1014eff174..d224905e6c 100755 --- a/module/admin/lang/zh-cn.php +++ b/module/admin/lang/zh-cn.php @@ -53,18 +53,6 @@ $lang->admin->engineInfo = "表%s的引擎是admin->engineSummary['hasMyISAM'] = "有%s个表不是InnoDB引擎"; $lang->admin->engineSummary['allInnoDB'] = "所有的表都是InnoDB引擎了"; -$lang->admin->daoCache = '数据缓存'; -$lang->admin->clearCache = '清除缓存'; -$lang->admin->clearSuccess = '清除成功'; -$lang->admin->memory = '内存使用'; -$lang->admin->usedMemory = '总计 %s,已使用 %s'; -$lang->admin->apcuNotice = '数据缓存基于 PHP-APCu 扩展实现,开启前需要先加载 APCu 扩展。'; -$lang->admin->apcuNotLoaded = '请加载 APCu 扩展后再开启数据缓存'; -$lang->admin->apcuNotEnabled = '请启用 apc.enabled 选项后再开启数据缓存'; - -$lang->admin->cacheStatusList[1] = '开启'; -$lang->admin->cacheStatusList[0] = '关闭'; - $lang->admin->info = new stdclass(); $lang->admin->info->version = '当前系统的版本是%s,'; $lang->admin->info->links = '您可以访问以下链接:'; diff --git a/module/admin/zen.php b/module/admin/zen.php index 4132b07d97..18a1309e55 100644 --- a/module/admin/zen.php +++ b/module/admin/zen.php @@ -302,25 +302,4 @@ class adminZen extends admin return $data; } - - /** - * 获取APCu内存使用数据。 - * Get APCu memory data. - * - * @param string $type total|free|used - * @access protected - * @return string|float - */ - public function getAPCuMemory(string $type = 'total'): string|float - { - if(!helper::isAPCuEnabled()) return ''; - - $info = apcu_sma_info(true); - - if($type == 'total') return helper::formatKB($info['seg_size']); - if($type == 'free') return helper::formatKB($info['avail_mem']); - if($type == 'used') return helper::formatKB($info['seg_size'] - $info['avail_mem']); - if($type == 'rate') return round(($info['seg_size'] - $info['avail_mem']) / $info['seg_size'] * 100, 2); - return ''; - } } From e4aee0c87dd41b42ddabe11c663162262cbaff11 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:35:51 +0800 Subject: [PATCH 147/300] + [refac] add cache module to manage configuration of cache. --- module/cache/config/form.php | 7 ++ module/cache/control.php | 107 ++++++++++++++++++ module/cache/js/setting.ui.js | 16 +++ module/cache/lang/de.php | 38 +++++++ module/cache/lang/en.php | 38 +++++++ module/cache/lang/fr.php | 38 +++++++ module/cache/lang/zh-cn.php | 38 +++++++ module/cache/model.php | 29 +++++ module/cache/ui/setting.html.php | 182 +++++++++++++++++++++++++++++++ 9 files changed, 493 insertions(+) create mode 100644 module/cache/config/form.php create mode 100644 module/cache/control.php create mode 100644 module/cache/js/setting.ui.js create mode 100644 module/cache/lang/de.php create mode 100644 module/cache/lang/en.php create mode 100644 module/cache/lang/fr.php create mode 100644 module/cache/lang/zh-cn.php create mode 100644 module/cache/model.php create mode 100644 module/cache/ui/setting.html.php diff --git a/module/cache/config/form.php b/module/cache/config/form.php new file mode 100644 index 0000000000..90d0502b68 --- /dev/null +++ b/module/cache/config/form.php @@ -0,0 +1,7 @@ +cache->form = new stdClass(); +$config->cache->form->setting = array(); +$config->cache->form->setting['enable'] = array('type' => 'int', 'default' => 0); +$config->cache->form->setting['driver'] = array('type' => 'string', 'default' => ''); +$config->cache->form->setting['namespace'] = array('type' => 'string', 'default' => ''); +$config->cache->form->setting['redis'] = array('type' => 'array', 'default' => array('host' => '', 'port' => 0, 'username' => '', 'password' => '', 'serializer' => '')); diff --git a/module/cache/control.php b/module/cache/control.php new file mode 100644 index 0000000000..f4bb2b39ee --- /dev/null +++ b/module/cache/control.php @@ -0,0 +1,107 @@ + + * @package cache + * @link https://www.zentao.net + */ +class cache extends control +{ + /** + * 设置是否启用缓存。 + * Set cache enable. + * + * @access public + * @return void + */ + public function setting() + { + if($_POST) + { + $redis = null; + $cache = form::data()->get(); + if(isset($cache->redis)) + { + $redis = (object)$cache->redis; + unset($cache->redis); + } + if(!isset($redis->host)) $redis->host = ''; + if(!isset($redis->port)) $redis->port = ''; + if(!isset($redis->username)) $redis->username = ''; + if(!isset($redis->password)) $redis->password = ''; + if(!isset($redis->serializer)) $redis->serializer = ''; + + if($cache->enable) + { + if($cache->driver == 'apcu') + { + /* 检查是否加载了 APCu 扩展。Check if the APCu extension is loaded. */ + if(!extension_loaded('apcu')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->apcu->notLoaded)); + if(!ini_get('apc.enabled')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->apcu->notEnabled)); + } + if($cache->driver == 'redis') + { + /* 检查是否加载了 Redis 扩展。Check if the Redis extension is loaded. */ + if(!extension_loaded('redis')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->notLoaded)); + if(!extension_loaded('igbinary') && $redis->serializer == 'igbinary') return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->igbinaryNotLoaded)); + + /* 检查 Redis 配置是否正确。Check if the Redis configuration is correct. */ + $errors = []; + if(empty($redis->host)) $errors['redis[host]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->host); + if(empty($redis->port)) $errors['redis[port]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->port); + if($errors) return $this->send(array('result' => 'fail', 'message' => $errors)); + + /* 检查 Redis 连接是否正常。Check if the Redis connection is normal. */ + try + { + helper::connectRedis($redis); + } + catch(Exception $e) + { + return $this->send(array('result' => 'fail', 'message' => $e->getMessage())); + } + } + } + + /* 如果缓存配置发生变化,清空缓存。If the cache configuration changes, clear the cache. */ + if($cache->enable != $this->config->cache->enable + || $cache->driver != $this->config->cache->driver + || $cache->namespace != $this->config->cache->namespace + || $redis->serializer != $this->config->redis->serializer) + { + $this->mao->clear(); + } + + $this->loadModel('setting')->setItems('system.common.cache', $cache); + if($cache->driver == 'redis') $this->setting->setItems('system.common.redis', $redis); + + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true)); + } + + if($this->config->cache->enable) + { + $this->view->rate = $this->mao->memory('rate'); + $this->view->used = $this->mao->memory('used'); + $this->view->total = $this->mao->memory('total'); + } + + $this->view->title = $this->lang->cache->common; + $this->display(); + } + + /** + * 清除数据缓存。 + * Clear data cache. + * + * @access public + * @return void + */ + public function ajaxClear() + { + $this->cache->clear(); + return $this->send(array('result' => 'success', 'message' => $this->lang->cache->clearSuccess, 'load' => true)); + } +} diff --git a/module/cache/js/setting.ui.js b/module/cache/js/setting.ui.js new file mode 100644 index 0000000000..c5ae216b0b --- /dev/null +++ b/module/cache/js/setting.ui.js @@ -0,0 +1,16 @@ +function toggleCache() +{ + $('.cache').toggleClass('hidden', $(this).val() != '1'); +} + +function toggleDriver() +{ + $('.apcu').toggleClass('hidden', $(this).val() != 'apcu'); + $('.redis').toggleClass('hidden', $(this).val() != 'redis'); +} + +function toggleSerializer() +{ + $('.serialize').toggleClass('hidden', $(this).val() != 'serialize'); + $('.igbinary').toggleClass('hidden', $(this).val() != 'igbinary'); +} diff --git a/module/cache/lang/de.php b/module/cache/lang/de.php new file mode 100644 index 0000000000..eda04d2ccc --- /dev/null +++ b/module/cache/lang/de.php @@ -0,0 +1,38 @@ +cache->clear = 'Clear Cache'; +$lang->cache->clearSuccess = 'Cache cleared successfully.'; +$lang->cache->type = 'Cache Type'; +$lang->cache->namespace = 'Namespace'; +$lang->cache->memory = 'Memory'; +$lang->cache->usedMemory = 'Total %s, used %s'; + +$lang->cache->statusList[1] = 'On'; +$lang->cache->statusList[0] = 'Off'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; +$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; +$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis Host'; +$lang->cache->redis->port = 'Redis Port'; +$lang->cache->redis->username = 'Redis User'; +$lang->cache->redis->password = 'Redis Password'; +$lang->cache->redis->serializer = 'Redis Serializer'; +$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.'; +$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; +$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; diff --git a/module/cache/lang/en.php b/module/cache/lang/en.php new file mode 100644 index 0000000000..eda04d2ccc --- /dev/null +++ b/module/cache/lang/en.php @@ -0,0 +1,38 @@ +cache->clear = 'Clear Cache'; +$lang->cache->clearSuccess = 'Cache cleared successfully.'; +$lang->cache->type = 'Cache Type'; +$lang->cache->namespace = 'Namespace'; +$lang->cache->memory = 'Memory'; +$lang->cache->usedMemory = 'Total %s, used %s'; + +$lang->cache->statusList[1] = 'On'; +$lang->cache->statusList[0] = 'Off'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; +$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; +$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis Host'; +$lang->cache->redis->port = 'Redis Port'; +$lang->cache->redis->username = 'Redis User'; +$lang->cache->redis->password = 'Redis Password'; +$lang->cache->redis->serializer = 'Redis Serializer'; +$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.'; +$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; +$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; diff --git a/module/cache/lang/fr.php b/module/cache/lang/fr.php new file mode 100644 index 0000000000..eda04d2ccc --- /dev/null +++ b/module/cache/lang/fr.php @@ -0,0 +1,38 @@ +cache->clear = 'Clear Cache'; +$lang->cache->clearSuccess = 'Cache cleared successfully.'; +$lang->cache->type = 'Cache Type'; +$lang->cache->namespace = 'Namespace'; +$lang->cache->memory = 'Memory'; +$lang->cache->usedMemory = 'Total %s, used %s'; + +$lang->cache->statusList[1] = 'On'; +$lang->cache->statusList[0] = 'Off'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; +$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; +$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis Host'; +$lang->cache->redis->port = 'Redis Port'; +$lang->cache->redis->username = 'Redis User'; +$lang->cache->redis->password = 'Redis Password'; +$lang->cache->redis->serializer = 'Redis Serializer'; +$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.'; +$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; +$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; diff --git a/module/cache/lang/zh-cn.php b/module/cache/lang/zh-cn.php new file mode 100644 index 0000000000..643adcdaa9 --- /dev/null +++ b/module/cache/lang/zh-cn.php @@ -0,0 +1,38 @@ +cache->clear = '清除缓存'; +$lang->cache->clearSuccess = '清除成功'; +$lang->cache->type = '缓存类型'; +$lang->cache->namespace = '缓存命名空间'; +$lang->cache->memory = '内存使用'; +$lang->cache->usedMemory = '总计 %s,已使用 %s'; + +$lang->cache->statusList[1] = '开启'; +$lang->cache->statusList[0] = '关闭'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = '使用 APCu 缓存需要先加载 APCu 扩展。'; +$lang->cache->apcu->notLoaded = '请加载 APCu 扩展后再开启数据缓存'; +$lang->cache->apcu->notEnabled = '请启用 apc.enabled 选项后再开启数据缓存'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis 主机'; +$lang->cache->redis->port = 'Redis 端口'; +$lang->cache->redis->username = 'Redis 用户名'; +$lang->cache->redis->password = 'Redis 密码'; +$lang->cache->redis->serializer = 'Redis 序列化器'; +$lang->cache->redis->notice = '使用 Redis 缓存需要先加载 Redis 扩展。'; +$lang->cache->redis->notLoaded = '请加载 Redis 扩展后再开启数据缓存。'; +$lang->cache->redis->igbinaryNotLoaded = '请加载 igbinary 扩展后再开启数据缓存。'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP 内置序列化器'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = '填写域名或 IP 地址,无需填写协议和端口号。'; +$lang->cache->redis->tips->serializer = '数据需要序列化后缓存。更改序列化器会清空缓存数据。'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = '命名空间用来防止不同应用间缓存数据冲突。更改命名空间会清空缓存数据。'; diff --git a/module/cache/model.php b/module/cache/model.php new file mode 100644 index 0000000000..692d25b7ef --- /dev/null +++ b/module/cache/model.php @@ -0,0 +1,29 @@ + + * @package cache + * @link https://www.zentao.net + */ +class cacheModel extends model +{ + /** + * 清空缓存。 + * Clear the cache. + * + * @access public + * @return void + */ + public function clear() + { + /* Redis 采用遍历删除的方式,所以需要先关闭缓存,清空之后再打开。Redis uses the method of traversing deletion, so you need to turn off the cache first, clear it, and then turn it on. */ + $needStop = $this->config->cache->driver == 'redis'; + if($needStop) $this->loadModel('setting')->setItem('system.common.cache.enable', 0); + $this->mao->clear(); + if($needStop) $this->setting->setItem('system.common.cache.enable', 1); + } +} diff --git a/module/cache/ui/setting.html.php b/module/cache/ui/setting.html.php new file mode 100644 index 0000000000..6601c8839d --- /dev/null +++ b/module/cache/ui/setting.html.php @@ -0,0 +1,182 @@ + + * @package cache + * @link https://www.zentao.net + */ +namespace zin; + +$driver = !empty($config->cache->driver) ? $config->cache->driver : ''; +$namespace = !empty($config->cache->namespace) ? $config->cache->namespace : $config->db->name; +$serializer = !empty($config->redis->serializer) ? $config->redis->serializer : 'serialize'; +$hiddenCache = $config->cache->enable ? '' : ' hidden'; +$hiddenApcu = $driver == 'apcu' ? '' : ' hidden'; +$hiddenRedis = $driver == 'redis' ? '' : ' hidden'; + +formPanel +( + set::actions + ([ + 'submit', + $config->cache->enable ? ['text' => $lang->cache->clear, 'url' => inlink('ajaxClear'), 'class' => 'secondary ajax-submit'] : null, + 'cancel' + ]), + on::change('input[name=enable]', 'toggleCache'), + on::change('input[name=driver]', 'toggleDriver'), + on::change('input[name=redis\\\[serializer\\\]]', 'toggleSerializer'), + formGroup + ( + set::label($lang->cache->common), + radioList + ( + set::name('enable'), + set::items($lang->cache->statusList), + set::value($config->cache->enable), + set::inline(true) + ), + set::required() + ), + formGroup + ( + setClass('cache' . $hiddenCache), + set::label($lang->cache->type), + radioList + ( + setClass('w-1/3'), + set::name('driver'), + set::items($lang->cache->driverList), + set::value($driver), + set::inline(true) + ), + span + ( + setClass('apcu ml-4 mt-1.5' . $hiddenApcu), + icon('info text-warning mr-2'), + $lang->cache->apcu->notice + ), + span + ( + setClass('redis ml-4 mt-1.5' . $hiddenRedis), + icon('info text-warning mr-2'), + $lang->cache->redis->notice + ), + set::required() + ), + formGroup + ( + setClass('cache' . $hiddenCache), + set::label($lang->cache->namespace), + set::required(), + input + ( + setClass('w-1/3'), + set::name('namespace'), + set::value($namespace) + ), + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->tips->namespace + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->host), + set::required(), + input + ( + setClass('w-1/3'), + set::name('redis[host]'), + set::value($config->redis->host) + ), + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->redis->tips->host + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->port), + set::required(), + input + ( + setClass('w-1/3'), + set::name('redis[port]'), + set::value($config->redis->port) + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->username), + input + ( + setClass('w-1/3'), + set::name('redis[username]'), + set::value($config->redis->username) + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->password), + input + ( + setClass('w-1/3'), + set::name('redis[password]'), + set::type('password'), + set::value($config->redis->password) + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->serializer), + radioList + ( + setClass('w-1/3'), + set::name('redis[serializer]'), + set::items($lang->cache->redis->serializerList), + set::value($serializer), + set::inline(true) + ), + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->redis->tips->serializer + ) + ), + $config->cache->enable ? formGroup + ( + setStyle(array('align-items' => 'center')), + set::label($lang->cache->memory), + div + ( + setClass('w-1/3'), + progressBar + ( + set::percent($rate), + set::width('100%'), + set::color('rgb(var(--color-' . ($rate <= 50 ? 'success' : ($rate <= 80 ? 'warning' : 'danger')) . '-500-rgb))') + ) + ), + div + ( + setClass('flex ml-4 gap-4'), + span($rate . '%'), + span(sprintf($lang->cache->usedMemory, $total, $used)) + ) + ) : null +); + +render(); From d827ef5fcc0798299aea4d312a4a68877ffdc5f2 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:39:49 +0800 Subject: [PATCH 148/300] + [refac] add default configuration of redis. --- config/cache.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config/cache.php b/config/cache.php index ec8d225857..7be4a90357 100644 --- a/config/cache.php +++ b/config/cache.php @@ -62,3 +62,10 @@ $config->cacheKeys->execution->ajaxGetDropMenuExecutions = 'ajaxDropMenuExecutio $config->cacheKeys->bug = new stdclass(); $config->cacheKeys->bug->browse = 'bugBrowse%s'; + +$config->redis = new stdClass(); +$config->redis->host = ''; +$config->redis->port = ''; +$config->redis->username = ''; +$config->redis->password = ''; +$config->redis->serializer = 'serialize'; // serialize|igbinary From 0f5466864b45598657c0a29599dbaf0492e57be0 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:40:34 +0800 Subject: [PATCH 149/300] + [refac] initialize lang items of cache module. --- module/common/lang/common.php | 1 + module/common/lang/de.php | 1 + module/common/lang/en.php | 1 + module/common/lang/fr.php | 1 + module/common/lang/menu.php | 1 + module/common/lang/zh-cn.php | 1 + 6 files changed, 6 insertions(+) diff --git a/module/common/lang/common.php b/module/common/lang/common.php index a379dec1b9..2f3245e02e 100644 --- a/module/common/lang/common.php +++ b/module/common/lang/common.php @@ -1,5 +1,6 @@ common = new stdclass(); +$lang->cache = new stdclass(); $lang->index = new stdclass(); $lang->my = new stdclass(); $lang->todo = new stdclass(); diff --git a/module/common/lang/de.php b/module/common/lang/de.php index 06ab2dbc02..b75f7fe748 100644 --- a/module/common/lang/de.php +++ b/module/common/lang/de.php @@ -204,6 +204,7 @@ $lang->contactUs->wechat = 'Wechat'; $lang->common->common = 'Standard Module'; $lang->common->story = 'Story'; +$lang->cache->common = 'Cache'; $lang->my->common = 'My'; $lang->todo->common = 'Todo'; $lang->block->common = 'InfoBlock'; diff --git a/module/common/lang/en.php b/module/common/lang/en.php index aab249c7d2..79e6e22c79 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -204,6 +204,7 @@ $lang->contactUs->wechat = 'Wechat'; $lang->common->common = 'Common Module'; $lang->common->story = 'Story'; +$lang->cache->common = 'Cache'; $lang->my->common = 'My'; $lang->todo->common = 'Todo'; $lang->block->common = 'Block'; diff --git a/module/common/lang/fr.php b/module/common/lang/fr.php index 0b1ef4b6f8..ddf10a2e14 100644 --- a/module/common/lang/fr.php +++ b/module/common/lang/fr.php @@ -204,6 +204,7 @@ $lang->contactUs->wechat = 'Wechat'; $lang->common->common = 'Module Commun'; $lang->common->story = 'Story'; +$lang->cache->common = 'Cache'; $lang->my->common = 'My'; $lang->todo->common = 'Agenda'; $lang->block->common = 'Bloc'; diff --git a/module/common/lang/menu.php b/module/common/lang/menu.php index 2fed65fe52..8ec26c3ced 100644 --- a/module/common/lang/menu.php +++ b/module/common/lang/menu.php @@ -784,6 +784,7 @@ $lang->navGroup->account = 'admin'; $lang->navGroup->ops = 'admin'; $lang->navGroup->service = 'admin'; $lang->navGroup->domain = 'admin'; +$lang->navGroup->cache = 'admin'; $lang->navGroup->aiapp = 'aiapp'; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index f1b0a528e2..bed42a18c6 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -204,6 +204,7 @@ $lang->contactUs->wechat = '微信'; $lang->common->common = '公有模块'; $lang->common->story = '需求'; +$lang->cache->common = '缓存'; $lang->my->common = '地盘'; $lang->todo->common = '待办'; $lang->block->common = '区块'; From 943835ef7dff3e3fa09451297dc16301f2cb948c Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:41:28 +0800 Subject: [PATCH 150/300] - [refac] remove unused lang items. --- module/common/lang/de.php | 1 - module/common/lang/en.php | 1 - module/common/lang/fr.php | 1 - module/common/lang/zh-cn.php | 1 - 4 files changed, 4 deletions(-) diff --git a/module/common/lang/de.php b/module/common/lang/de.php index b75f7fe748..d612f8d88c 100644 --- a/module/common/lang/de.php +++ b/module/common/lang/de.php @@ -441,7 +441,6 @@ $lang->admin->data = 'Data'; $lang->admin->cron = 'Cron'; $lang->admin->buildIndex = 'Full Text Search'; $lang->admin->tableEngine = 'Table Engine'; -$lang->admin->cache = 'Cache'; $lang->convert->importJira = 'Import Jira'; diff --git a/module/common/lang/en.php b/module/common/lang/en.php index 79e6e22c79..f789ba3e3b 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -441,7 +441,6 @@ $lang->admin->data = 'Data'; $lang->admin->cron = 'Cron'; $lang->admin->buildIndex = 'Full Text Search'; $lang->admin->tableEngine = 'Table Engine'; -$lang->admin->cache = 'Cache'; $lang->convert->importJira = 'Import Jira'; diff --git a/module/common/lang/fr.php b/module/common/lang/fr.php index ddf10a2e14..0521da8d88 100644 --- a/module/common/lang/fr.php +++ b/module/common/lang/fr.php @@ -441,7 +441,6 @@ $lang->admin->data = 'Data'; $lang->admin->cron = 'Cron'; $lang->admin->buildIndex = 'Full Text Search'; $lang->admin->tableEngine = 'Table Engine'; -$lang->admin->cache = 'Cache'; $lang->convert->importJira = 'Import Jira'; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index bed42a18c6..022c75717d 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -441,7 +441,6 @@ $lang->admin->data = '数据'; $lang->admin->cron = '定时'; $lang->admin->buildIndex = '重建索引'; $lang->admin->tableEngine = '表引擎'; -$lang->admin->cache = '缓存'; $lang->convert->importJira = '导入Jira数据'; From 7e40454e94a28acf4688e8d3c23597fb311a81a0 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:42:04 +0800 Subject: [PATCH 151/300] * [refac] change admin-cache to cache-setting. --- module/admin/config.php | 2 +- module/admin/lang/menu.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/admin/config.php b/module/admin/config.php index cd78c03143..446cefcf52 100755 --- a/module/admin/config.php +++ b/module/admin/config.php @@ -5,7 +5,7 @@ $config->admin->log->saveDays = 30; if(!isset($config->safe)) $config->safe = new stdclass(); if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,12345678,qwerty,123456789,1234,1234567,abc123,111111,123123'; -$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'admin|cache', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup'); +$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup'); $config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial'); $config->admin->menuGroup['switch'] = array('admin|setmodule'); $config->admin->menuGroup['model'] = array('auditcl', 'stage', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project'); diff --git a/module/admin/lang/menu.php b/module/admin/lang/menu.php index 367b4e94b8..d0d1f0f34d 100644 --- a/module/admin/lang/menu.php +++ b/module/admin/lang/menu.php @@ -47,7 +47,7 @@ $lang->admin->menuList->convert['order'] = 50; $lang->admin->menuList->system['subMenu']['mode'] = array('link' => "{$lang->custom->mode}|custom|mode|"); $lang->admin->menuList->system['subMenu']['trash'] = array('link' => "{$lang->action->trash}|action|trash|"); $lang->admin->menuList->system['subMenu']['safe'] = array('link' => "{$lang->security}|admin|safe|", 'alias' => 'checkweak,resetpwdsetting', 'links' => array('admin|resetpwdsetting|', 'admin|checkweak|')); -$lang->admin->menuList->system['subMenu']['cache'] = array('link' => "{$lang->admin->cache}|admin|cache|"); +$lang->admin->menuList->system['subMenu']['cache'] = array('link' => "{$lang->cache->common}|cache|setting|"); $lang->admin->menuList->system['subMenu']['cron'] = array('link' => "{$lang->admin->cron}|cron|index|", 'subModule' => 'cron'); $lang->admin->menuList->system['subMenu']['timezone'] = array('link' => "{$lang->timezone}|custom|timezone|"); $lang->admin->menuList->system['subMenu']['buildindex'] = array('link' => "{$lang->admin->buildIndex}|search|buildindex|"); From ece01c01ae9ab7b6e20f8400e84a4da48476cb33 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:43:43 +0800 Subject: [PATCH 152/300] + [refac] add method to connect redis server. --- framework/helper.class.php | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/framework/helper.class.php b/framework/helper.class.php index 47fff7cde1..379490e381 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -433,6 +433,43 @@ class helper extends baseHelper $checkFunc = 'check' . $operator; return validater::$checkFunc($value1, $value2); } + + /** + * 连接 Redis 服务器。 + * Connect to Redis server. + * + * @param object $setting + * @static + * @access public + * @return object + */ + public static function connectRedis(object $setting) + { + if(!class_exists('Redis')) throw new Exception('The Redis extension is not installed.'); + try + { + $redis = new Redis(); + + $version = phpversion('redis'); + if(version_compare($version, '5.3.0', 'ge')) + { + $redis->connect($setting->host, (int)$setting->port, 1, null, 0, 0, ['auth' => [$setting->username ?: null, $setting->password ?: null]]); + } + else + { + $redis->connect($setting->host, (int)$setting->port, 1, null, 0, 0); + $redis->auth(['pass' => $setting->password ?: null]); + } + + if(!$redis->ping()) throw new Exception('Can not connect to Redis server.'); + + return $redis; + } + catch(RedisException $e) + { + throw new Exception('Can not connect to Redis server. The error message is: ' . $e->getMessage()); + } + } } /** From b0e0090809dfe30593f8eeede191461214f43caa Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:47:15 +0800 Subject: [PATCH 153/300] + [refac] add method to get memory usage. --- lib/cache/cache.class.php | 12 ++++++++++++ lib/cache/driver/ApcuDriver.php | 18 ++++++++++++++++++ lib/cache/driver/RedisDriver.php | 18 ++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 03f1c17d70..841debf0d9 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -889,4 +889,16 @@ class cache { $this->cache->clear(); } + + /** + * 获取内存使用情况。 + * Get memory usage. + * + * @param string $type + * @return string + */ + public function memory(string $type) + { + return $this->cache->memory($type); + } } diff --git a/lib/cache/driver/ApcuDriver.php b/lib/cache/driver/ApcuDriver.php index 7adeda2b41..6e712c16ec 100644 --- a/lib/cache/driver/ApcuDriver.php +++ b/lib/cache/driver/ApcuDriver.php @@ -81,4 +81,22 @@ class ApcuDriver implements CacheInterface { return (bool) apcu_exists($key); } + + /** + * 获取内存使用情况。 + * Get memory usage. + * + * @param string $type + * @return string + */ + public function memory($type) + { + $info = apcu_sma_info(true); + + if($type == 'total') return \helper::formatKB($info['seg_size']); + if($type == 'free') return \helper::formatKB($info['avail_mem']); + if($type == 'used') return \helper::formatKB($info['seg_size'] - $info['avail_mem']); + if($type == 'rate') return round(($info['seg_size'] - $info['avail_mem']) / $info['seg_size'] * 100, 2); + return ''; + } } diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index 1297c71959..ecb6cdf776 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -230,4 +230,22 @@ class RedisDriver implements CacheInterface { return (bool) $this->redis->exists($key); } + + /** + * 获取内存使用情况。 + * Get memory usage. + * + * @param string $type + * @return string + */ + public function memory($type) + { + $info = $this->redis->info(); + + if($type == 'total') return $info['total_system_memory_human']; + if($type == 'free') return \helper::formatKB($info['total_system_memory'] - $info['used_memory']); + if($type == 'used') return $info['used_memory_human']; + if($type == 'rate') return round(($info['used_memory'] / $info['total_system_memory']) * 100, 2); + return ''; + } } From b6fe5f93188a270c9a28571497e45b4b294da6f3 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:50:23 +0800 Subject: [PATCH 154/300] * [refac] calling method in helper class to connect redis server. --- lib/cache/driver/RedisDriver.php | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index ecb6cdf776..f08e80d047 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -80,30 +80,14 @@ class RedisDriver implements CacheInterface { global $config; - if(empty($config->redis)) \helper::end('Redis is not enabled in the configuration file.'); - try { - $this->redis = new \Redis(); - - $version = phpversion('redis'); - if(version_compare($version, '5.3.0', 'ge')) - { - $this->redis->connect($config->redis->host , $config->redis->port, $config->redis->timeout, '', 0, 0, ['auth' => [$config->redis->username, $config->redis->password]]); - } - else - { - $this->redis->connect($this->config->redis->host , $this->config->redis->port, $this->config->redis->timeout, '', 0, 0); - $this->redis->auth(['pass' => $this->config->redis->password]); - } - - if(!$this->redis->ping()) \helper::end('Can not connect to Redis server.'); - + $this->redis = \helper::connectRedis($config->redis); $this->redis->setOption(\Redis::OPT_SERIALIZER, $this->serializer); } - catch(RedisException $e) + catch(Exception $e) { - \helper::end('Can not connect to Redis server. The error message is: ' . $e->getMessage()); + \helper::end($e->getMessage()); } } From 871637c2affa74a9852bdfcf7b2239fba87741c9 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:50:50 +0800 Subject: [PATCH 155/300] * [refac] fix error. --- lib/cache/driver/RedisDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index f08e80d047..d2402f88d8 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -200,7 +200,7 @@ class RedisDriver implements CacheInterface { $result = $this->redis->del($keys); - return count($result) === count($keys); + return $result === count($keys); } /** From 4ff19d7ef301e88488d25edab0a17a1b6ebe5c01 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:52:46 +0800 Subject: [PATCH 156/300] * [refac] add namespace to resolve class name conflicts. --- framework/base/router.class.php | 2 +- lib/cache/cache.class.php | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 78e7d41ece..dfa38a5a20 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3094,7 +3094,7 @@ class baseRouter private function loadCache() { $this->loadClass('cache', true); - $this->cache = new cache($this); + $this->cache = new Zentao\Cache\cache($this); /* 为 dao 和 mao 设置访问缓存的对象。 Set the cache object for dao and mao. */ $this->dao->cache = $this->cache; diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 841debf0d9..b4c3236d6a 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -8,13 +8,15 @@ * @package cache * @link https://www.zentao.net */ -helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheInterface.php'); -helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheException.php'); -helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'InvalidArgumentException.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'ApcuDriver.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'RedisDriver.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php'); -helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'FileDriver.php'); +namespace Zentao\Cache; + +\helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheInterface.php'); +\helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheException.php'); +\helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'InvalidArgumentException.php'); +\helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'ApcuDriver.php'); +\helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'RedisDriver.php'); +\helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php'); +\helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'FileDriver.php'); use ZenTao\Cache\SimpleCache\InvalidArgumentException; From 62f313c0d6e1c9d33d2f87719a56dc30344557d5 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:55:35 +0800 Subject: [PATCH 157/300] * [refac] load cache configuration from database before initialize cache access object. --- framework/base/router.class.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index dfa38a5a20..e3e8df8759 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -503,7 +503,7 @@ class baseRouter $this->setTimezone(); if($this->config->framework->autoConnectDB) $this->connectDB(); - if($this->config->cache->enable) $this->loadCache(); + $this->loadCache(); $this->setupProfiling(); $this->setupXhprof(); @@ -3093,6 +3093,16 @@ class baseRouter */ private function loadCache() { + $cacheConfig = $this->dao->select('`key`, value')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('cache')->fetchPairs(); + foreach($cacheConfig as $key => $value) $this->config->cache->$key = $value; + if(!$this->config->cache->enable) return; + + if($this->config->cache->driver == 'redis') + { + $redisConfig = $this->dao->select('`key`, value')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('redis')->fetchPairs(); + foreach($redisConfig as $key => $value) $this->config->redis->$key = $value; + } + $this->loadClass('cache', true); $this->cache = new Zentao\Cache\cache($this); From 14a0088b9798566cece3a15574ab176e785e7100 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:58:41 +0800 Subject: [PATCH 158/300] * [refac] return result of the method who cleared the cache. --- lib/cache/cache.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index b4c3236d6a..b8d5e7d121 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -889,7 +889,7 @@ class cache */ public function clear() { - $this->cache->clear(); + return $this->cache->clear(); } /** From 5fea836c6e80500aff4049540111409a0ac10fe2 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 09:09:06 +0800 Subject: [PATCH 159/300] * [refac] fix class not found error. --- lib/cache/cache.class.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index b8d5e7d121..7454cc8831 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -831,12 +831,12 @@ class cache if($event == 'update' || $event == 'delete') { /* 获取 WHERE 子句的内容。Get the content of WHERE clause. */ - $whereLen = strlen(DAO::WHERE); - $wherePOS = strrpos($sql, DAO::WHERE); - $groupPOS = strrpos($sql, DAO::GROUPBY); - $havingPOS = strrpos($sql, DAO::HAVING); - $orderPOS = strrpos($sql, DAO::ORDERBY); - $limitPOS = strrpos($sql, DAO::LIMIT); + $whereLen = strlen(\DAO::WHERE); + $wherePOS = strrpos($sql, \DAO::WHERE); + $groupPOS = strrpos($sql, \DAO::GROUPBY); + $havingPOS = strrpos($sql, \DAO::HAVING); + $orderPOS = strrpos($sql, \DAO::ORDERBY); + $limitPOS = strrpos($sql, \DAO::LIMIT); $splitPOS = $orderPOS ? $orderPOS : $limitPOS; $splitPOS = $havingPOS ? $havingPOS : $splitPOS; $splitPOS = $groupPOS ? $groupPOS : $splitPOS; From a5be1563081340a692dd34491d778aa192d93eae Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 09:23:59 +0800 Subject: [PATCH 160/300] * [refac] optimize error message. --- lib/cache/cache.class.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 7454cc8831..1898a0a273 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -356,7 +356,7 @@ class cache /* 获取新增的数据。Get the new data. */ $object = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->fetch(); - if(!$object) return $this->log('Failed to fetch the new object. The sql is: ' . $this->dao->get(), __FILE__, __LINE__); + if(!$object) return $this->log('Failed to fetch new object. The sql is: ' . $this->dao->get(), __FILE__, __LINE__); /* 把新增的数据保存到缓存中。Save the new data to cache. */ $rawCacheKey = $this->getRawCacheKey($code, $object->$field); @@ -475,7 +475,7 @@ class cache $key = $this->getResCacheKey($res->name); foreach($res->fields as $field) { - if(!isset($object->$field)) return $this->log("The {$field} field does not exist in table {$this->table}.", __FILE__, __LINE__); + if(!isset($object->$field)) return $this->log("Field {$field} does not exist in table {$this->table}.", __FILE__, __LINE__); $key .= $this->connector . $object->$field; } @@ -594,7 +594,7 @@ class cache */ public function fetch(string $table, int|string $id): object|bool { - if(!$this->checkTable($table)) return $this->log("The {$table} table is not set in the cache configuration", __FILE__, __LINE__); + if(!$this->checkTable($table)) return $this->log("Table {$table} is not set in the cache configuration", __FILE__, __LINE__); if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__); if(empty($id)) return $this->log('The id is empty', __FILE__, __LINE__); @@ -671,7 +671,7 @@ class cache */ public function key($key, ...$args) { - if(empty($this->config->cache->keys[$key])) return $this->log("The {$key} key is not defined", __FILE__, __LINE__); + if(empty($this->config->cache->keys[$key])) return $this->log("Key {$key} is not defined", __FILE__, __LINE__); $cache = $this->config->cache->keys[$key]; if(!empty($cache->fields) && !empty($args)) @@ -679,7 +679,7 @@ class cache $tableFields = $this->dao->descTable($cache->table); foreach($cache->fields as $index => $field) { - if(!isset($tableFields[$field])) return $this->log("The {$field} field does not exist in table {$cache->table}", __FILE__, __LINE__); + if(!isset($tableFields[$field])) return $this->log("Field {$field} does not exist in table {$cache->table}", __FILE__, __LINE__); if(!isset($args[$index])) continue; $tableField = $tableFields[$field]; @@ -785,7 +785,7 @@ class cache public function saveByLabel(string $label, $value) { if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__); - if(empty($this->labels[$label])) return $this->log("The {$label} label is not set", __FILE__, __LINE__); + if(empty($this->labels[$label])) return $this->log("Label {$label} does not exist", __FILE__, __LINE__); return $this->cache->set($this->labels[$label], $value); } From ccc5d3a888ea3d47f919e7aebb020dfb1dd6c410 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 09:24:15 +0800 Subject: [PATCH 161/300] + [refac] check if the label already used. --- lib/cache/cache.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 1898a0a273..b35fdaf0a0 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -709,6 +709,7 @@ class cache { if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__); if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__); + if(isset($this->labels[$label])) return $this->log("Label {$label} already used", __FILE__, __LINE__); $this->labels[$label] = $this->key; return $this; } From 2e20324fe60cce4cd68a13e39c22f95fb98c2eb2 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 10:56:56 +0800 Subject: [PATCH 162/300] + [refac] close cache connection when program is shutdown. --- framework/base/router.class.php | 3 +++ lib/cache/cache.class.php | 12 ++++++++++++ lib/cache/driver/RedisDriver.php | 12 ++++++++++++ 3 files changed, 27 insertions(+) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index e3e8df8759..c7ed4ca26c 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3226,6 +3226,9 @@ class baseRouter */ public function shutdown() { + /* 如果开启了缓存则关闭缓存连接,主要用于 Redis 等缓存服务。Close the cache connection if it's open. */ + if(!empty($this->cache)) $this->cache->close(); + /* 如果debug模式开启,保存sql语句(If debug on, save sql queries) */ if(!empty($this->config->debug)) $this->saveSQL(); diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index b35fdaf0a0..1ea276bd7f 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -893,6 +893,18 @@ class cache return $this->cache->clear(); } + /** + * 关闭缓存连接。 + * Close cache connection. + * + * @access public + * @return void + */ + public function close() + { + if(method_exists($this->cache, 'close')) $this->cache->close(); + } + /** * 获取内存使用情况。 * Get memory usage. diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index d2402f88d8..3622485e5f 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -215,6 +215,18 @@ class RedisDriver implements CacheInterface return (bool) $this->redis->exists($key); } + /** + * 关闭 Redis 连接。 + * Close the Redis connection. + * + * @access public + * @return bool + */ + public function close() + { + return $this->redis->close(); + } + /** * 获取内存使用情况。 * Get memory usage. From 94ac8198f9fadac36d6d7002f526eee0cc462dab Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 11:22:59 +0800 Subject: [PATCH 163/300] + [refac] add cache property. --- lib/base/dao/dao.class.php | 9 +++++++++ lib/base/mao/mao.class.php | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 54514a6cfa..af945e9324 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -89,6 +89,15 @@ class baseDAO */ public $slaveDBH; + /** + * 全局对象$cache。 + * The global cache object. + * + * @var object + * @access public + */ + public $cache = null; + /** * sql对象,用于生成sql语句。 * The sql object, used to create the query sql. diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index f09e34cc11..20c10dda9a 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -37,6 +37,24 @@ class baseMao */ public $config; + /** + * 全局对象$dao + * The global dao object. + * + * @var object + * @access protected + */ + protected $dao; + + /** + * 全局对象$cache + * The global cache object. + * + * @var object + * @access public + */ + public $cache = null; + /** * 正在使用的表。 * The table of current query. From 9376b57f2f7adbc5ea7a2da9e1a2da32a2e3b7f7 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 13:17:21 +0800 Subject: [PATCH 164/300] - [refac] remove unused method. --- lib/base/mao/mao.class.php | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 20c10dda9a..116017b926 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -225,30 +225,6 @@ class baseMao $this->setConditions([]); } - /** - * 开始事务。 - * Begin Transaction - * - * @access public - * @return void - */ - public function begin() - { - $this->app->redis->multi(); - } - - /** - * 提交事务。 - * Commits a transaction. - * - * @access public - * @return void - */ - public function commit() - { - $this->app->redis->exec(); - } - /** * select方法,调用sql::select()。 * The select method, call sql::select(). From 4c78c021b36e6f67b2f01db93cafecf13194838c Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 13:48:03 +0800 Subject: [PATCH 165/300] - [refac] remove unused append method. --- lib/base/mao/mao.class.php | 55 +------------------------------------- 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 116017b926..ef085380ce 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -475,59 +475,6 @@ class baseMao return $this; } - /** - * data追加字段。 - * Append field to data. - * - * @param string $tableName - * @param string $fields - * @access public - * @return static|cache. - */ - public function append(string $tableName, string $fields) - { - $fields = explode(',', $fields); - - $alias = []; - foreach($fields as $field) - { - $fieldInfo = explode(' ', trim($field)); - if(count($fieldInfo) == 1) - { - $alias[$fieldInfo[0]] = $fieldInfo[0]; - } - elseif(count($fieldInfo) == 2) - { - $alias[$fieldInfo[0]] = $fieldInfo[1]; - } - else - { - $alias[$fieldInfo[0]] = $fieldInfo[2]; - } - } - - $cacheKeys = []; - $key = $this->dataColumn; - foreach($this->data as $row) - { - if(!in_array($row->$key, $cacheKeys)) $cacheKeys[] = $row->$key; - } - $objects = $this->cache->fetchAll($tableName, $cacheKeys); - foreach($this->data as $row) - { - $object = $objects[$row->$key]; - foreach($alias as $aliasKey => $aliasValue) - { - $row->$aliasValue = $object->$aliasKey; - } - } - - return $this; - } - - - //-------------------- Fetch相关方法(Fetch related methods) -------------------// - /** * 判断匹配条件。 * Check condition is matched. @@ -776,7 +723,7 @@ class baseMao $cacheResult = $this->cache->fetchAll($this->table, array_unique($keyList)); - foreach($data as $index=> $row) + foreach($data as $index => $row) { $key = $keyList[$index]; $cacheRow = $cacheResult[$key]; From 4e728fb4593cc8659a22726677b5be2dd07ad89a Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 13:57:08 +0800 Subject: [PATCH 166/300] + [refac] add method comment. --- lib/base/mao/mao.class.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index ef085380ce..dbfc66aa01 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -732,6 +732,19 @@ class baseMao } } + /** + * 魔术方法。 + * 1. 转换 findByxxx 为 where 条件。 + * 2. 调用cache对象的方法。 + * Magic method. + * 1. Convert findByxxx to where condition. + * 2. Call the cache object method. + * + * @param string $method + * @param array $args + * @access public + * @return mixed + */ public function __call(string $method, array $args) { $method = strtolower($method); From ad2c087ee6027f16b9aa2a62d507a9055a4dcc59 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 15:06:39 +0800 Subject: [PATCH 167/300] * [refac] remove parameter type. --- lib/base/mao/mao.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index dbfc66aa01..4e26daf7ec 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -352,7 +352,7 @@ class baseMao * @access public * @return static the dao object. */ - public function eq(string $value) + public function eq($value) { $this->condition['operator'] = 'eq'; $this->condition['value'] = $value; @@ -374,7 +374,7 @@ class baseMao * @access public * @return static the dao object. */ - public function ne(string $value) + public function ne($value) { $this->condition['operator'] = 'ne'; $this->condition['value'] = $value; From 82eda5c887ae666898a548c6a9a1a30bdf2cd515 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 17:11:36 +0800 Subject: [PATCH 168/300] + [refac] don't load cache configuration if isn't installed. --- framework/base/router.class.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index c7ed4ca26c..9cab59b7cd 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3093,6 +3093,8 @@ class baseRouter */ private function loadCache() { + if(!$this->checkInstalled()) return false; + $cacheConfig = $this->dao->select('`key`, value')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('cache')->fetchPairs(); foreach($cacheConfig as $key => $value) $this->config->cache->$key = $value; if(!$this->config->cache->enable) return; From 8c69fccd2a96205eca30074d12992932ce0c7c20 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 8 Nov 2024 17:15:13 +0800 Subject: [PATCH 169/300] - [refac] remove unused method. --- lib/base/mao/mao.class.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 4e26daf7ec..f5f12c29f1 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -458,23 +458,6 @@ class baseMao return $this; } - /** - * 选择处理的数据和column最为缓存的key。 - * Load data and select column as key. - * - * @param array $data - * @param string $field - * @access public - * @return static|cache. - */ - public function use(array $data, string $column) - { - $this->data = $data; - $this->dataColumn = $column; - - return $this; - } - /** * 判断匹配条件。 * Check condition is matched. From fe16031fd1dcefcd4f4cec96b76a4e257d1fc493 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 11 Nov 2024 15:06:56 +0800 Subject: [PATCH 170/300] + [refac] add default $mao property of router class. --- framework/base/router.class.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 9cab59b7cd..8ad62956e3 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -333,6 +333,15 @@ class baseRouter */ public $dao; + /** + * $mao对象,用于访问或者更新缓存。 + * The $mao object, used to access or update cache. + * + * @var mao + * @access public + */ + public $mao; + /** * 从数据库的句柄。 * The slave database handler. From 7c362f7350fd7ed24e57f071560134f25e1dbe11 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 12 Nov 2024 16:46:41 +0800 Subject: [PATCH 171/300] * [refac] add cache scope. --- config/cache.php | 1 + module/cache/config/form.php | 1 + module/cache/lang/zh-cn.php | 24 +++++++++++++++--------- module/cache/ui/setting.html.php | 28 ++++++++++++++++++++++++---- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/config/cache.php b/config/cache.php index 7be4a90357..e4230ee6f0 100644 --- a/config/cache.php +++ b/config/cache.php @@ -3,6 +3,7 @@ $config->cache = new stdclass(); $config->cache->enable = false; // 是否开启缓存。Enable cache or not. $config->cache->driver = ''; // 缓存驱动。 The driver of cache. Can be File|Yac|Apcu|Redis. +$config->cache->scope = ''; // 缓存服务范围。The scope of cache. Can be private|shared. $config->cache->namespace = ''; // 缓存命名空间。The namespace of cache. $config->cache->lifetime = 2592000; // 缓存生存时间。The lifetime of cache. The default value is 30 days. diff --git a/module/cache/config/form.php b/module/cache/config/form.php index 90d0502b68..b61efeaab4 100644 --- a/module/cache/config/form.php +++ b/module/cache/config/form.php @@ -3,5 +3,6 @@ $config->cache->form = new stdClass(); $config->cache->form->setting = array(); $config->cache->form->setting['enable'] = array('type' => 'int', 'default' => 0); $config->cache->form->setting['driver'] = array('type' => 'string', 'default' => ''); +$config->cache->form->setting['scope'] = array('type' => 'string', 'default' => ''); $config->cache->form->setting['namespace'] = array('type' => 'string', 'default' => ''); $config->cache->form->setting['redis'] = array('type' => 'array', 'default' => array('host' => '', 'port' => 0, 'username' => '', 'password' => '', 'serializer' => '')); diff --git a/module/cache/lang/zh-cn.php b/module/cache/lang/zh-cn.php index 643adcdaa9..117d5a83ea 100644 --- a/module/cache/lang/zh-cn.php +++ b/module/cache/lang/zh-cn.php @@ -1,8 +1,10 @@ cache->clear = '清除缓存'; $lang->cache->clearSuccess = '清除成功'; -$lang->cache->type = '缓存类型'; -$lang->cache->namespace = '缓存命名空间'; +$lang->cache->status = '状态'; +$lang->cache->type = '服务'; +$lang->cache->namespace = '命名空间'; +$lang->cache->scope = '服务范围'; $lang->cache->memory = '内存使用'; $lang->cache->usedMemory = '总计 %s,已使用 %s'; @@ -12,19 +14,22 @@ $lang->cache->statusList[0] = '关闭'; $lang->cache->driverList['apcu'] = 'APCu'; $lang->cache->driverList['redis'] = 'Redis'; +$lang->cache->scopeList['private'] = '本应用独享'; +$lang->cache->scopeList['shared'] = '多应用共享'; + $lang->cache->apcu = new stdClass(); $lang->cache->apcu->notice = '使用 APCu 缓存需要先加载 APCu 扩展。'; $lang->cache->apcu->notLoaded = '请加载 APCu 扩展后再开启数据缓存'; $lang->cache->apcu->notEnabled = '请启用 apc.enabled 选项后再开启数据缓存'; $lang->cache->redis = new stdClass(); -$lang->cache->redis->host = 'Redis 主机'; -$lang->cache->redis->port = 'Redis 端口'; -$lang->cache->redis->username = 'Redis 用户名'; -$lang->cache->redis->password = 'Redis 密码'; -$lang->cache->redis->serializer = 'Redis 序列化器'; -$lang->cache->redis->notice = '使用 Redis 缓存需要先加载 Redis 扩展。'; -$lang->cache->redis->notLoaded = '请加载 Redis 扩展后再开启数据缓存。'; +$lang->cache->redis->host = 'Redis 主机'; +$lang->cache->redis->port = 'Redis 端口'; +$lang->cache->redis->username = 'Redis 用户名'; +$lang->cache->redis->password = 'Redis 密码'; +$lang->cache->redis->serializer = 'Redis 序列化器'; +$lang->cache->redis->notice = '使用 Redis 缓存需要先加载 Redis 扩展。'; +$lang->cache->redis->notLoaded = '请加载 Redis 扩展后再开启数据缓存。'; $lang->cache->redis->igbinaryNotLoaded = '请加载 igbinary 扩展后再开启数据缓存。'; $lang->cache->redis->serializerList['serialize'] = 'PHP 内置序列化器'; @@ -36,3 +41,4 @@ $lang->cache->redis->tips->serializer = '数据需要序列化后缓存。更改 $lang->cache->tips = new stdClass(); $lang->cache->tips->namespace = '命名空间用来防止不同应用间缓存数据冲突。更改命名空间会清空缓存数据。'; +$lang->cache->tips->scope = '如果缓存服务只有本应用使用请选择『本应用独享』,否则选择『多应用共享』。'; diff --git a/module/cache/ui/setting.html.php b/module/cache/ui/setting.html.php index 6601c8839d..96ee727aeb 100644 --- a/module/cache/ui/setting.html.php +++ b/module/cache/ui/setting.html.php @@ -30,20 +30,21 @@ formPanel on::change('input[name=redis\\\[serializer\\\]]', 'toggleSerializer'), formGroup ( - set::label($lang->cache->common), + set::label($lang->cache->status), + set::required(), radioList ( set::name('enable'), set::items($lang->cache->statusList), set::value($config->cache->enable), set::inline(true) - ), - set::required() + ) ), formGroup ( setClass('cache' . $hiddenCache), set::label($lang->cache->type), + set::required(), radioList ( setClass('w-1/3'), @@ -63,8 +64,27 @@ formPanel setClass('redis ml-4 mt-1.5' . $hiddenRedis), icon('info text-warning mr-2'), $lang->cache->redis->notice + ) + ), + formGroup + ( + setClass('cache' . $hiddenCache), + set::label($lang->cache->scope), + set::required(), + radioList + ( + setClass('w-1/3'), + set::name('scope'), + set::items($lang->cache->scopeList), + set::value($config->cache->scope), + set::inline(true) ), - set::required() + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->tips->scope + ) ), formGroup ( From 8af8f18d38da0c5facbaff79b5c27a640cc73a5f Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 12 Nov 2024 17:25:02 +0800 Subject: [PATCH 172/300] * [refac] modify lang items. --- module/cache/lang/de.php | 10 ++++++++-- module/cache/lang/en.php | 10 ++++++++-- module/cache/lang/fr.php | 10 ++++++++-- module/cache/lang/zh-cn.php | 6 +++--- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/module/cache/lang/de.php b/module/cache/lang/de.php index eda04d2ccc..8e4546a3e9 100644 --- a/module/cache/lang/de.php +++ b/module/cache/lang/de.php @@ -1,8 +1,10 @@ cache->clear = 'Clear Cache'; $lang->cache->clearSuccess = 'Cache cleared successfully.'; -$lang->cache->type = 'Cache Type'; +$lang->cache->status = 'Status'; +$lang->cache->driver = 'Cache Type'; $lang->cache->namespace = 'Namespace'; +$lang->cache->scope = 'Scope'; $lang->cache->memory = 'Memory'; $lang->cache->usedMemory = 'Total %s, used %s'; @@ -12,6 +14,9 @@ $lang->cache->statusList[0] = 'Off'; $lang->cache->driverList['apcu'] = 'APCu'; $lang->cache->driverList['redis'] = 'Redis'; +$lang->cache->scopeList['private'] = 'Exclusively for this application'; +$lang->cache->scopeList['shared'] = 'Shared by multiple applications'; + $lang->cache->apcu = new stdClass(); $lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; $lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; @@ -35,4 +40,5 @@ $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, $lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; $lang->cache->tips = new stdClass(); -$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace after caching is enabled will clear the cache data.'; +$lang->cache->tips->scope = 'If the cache service is only used by this application, please select "Exclusively for this application", otherwise select "Shared by multiple applications".'; diff --git a/module/cache/lang/en.php b/module/cache/lang/en.php index eda04d2ccc..8e4546a3e9 100644 --- a/module/cache/lang/en.php +++ b/module/cache/lang/en.php @@ -1,8 +1,10 @@ cache->clear = 'Clear Cache'; $lang->cache->clearSuccess = 'Cache cleared successfully.'; -$lang->cache->type = 'Cache Type'; +$lang->cache->status = 'Status'; +$lang->cache->driver = 'Cache Type'; $lang->cache->namespace = 'Namespace'; +$lang->cache->scope = 'Scope'; $lang->cache->memory = 'Memory'; $lang->cache->usedMemory = 'Total %s, used %s'; @@ -12,6 +14,9 @@ $lang->cache->statusList[0] = 'Off'; $lang->cache->driverList['apcu'] = 'APCu'; $lang->cache->driverList['redis'] = 'Redis'; +$lang->cache->scopeList['private'] = 'Exclusively for this application'; +$lang->cache->scopeList['shared'] = 'Shared by multiple applications'; + $lang->cache->apcu = new stdClass(); $lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; $lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; @@ -35,4 +40,5 @@ $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, $lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; $lang->cache->tips = new stdClass(); -$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace after caching is enabled will clear the cache data.'; +$lang->cache->tips->scope = 'If the cache service is only used by this application, please select "Exclusively for this application", otherwise select "Shared by multiple applications".'; diff --git a/module/cache/lang/fr.php b/module/cache/lang/fr.php index eda04d2ccc..8e4546a3e9 100644 --- a/module/cache/lang/fr.php +++ b/module/cache/lang/fr.php @@ -1,8 +1,10 @@ cache->clear = 'Clear Cache'; $lang->cache->clearSuccess = 'Cache cleared successfully.'; -$lang->cache->type = 'Cache Type'; +$lang->cache->status = 'Status'; +$lang->cache->driver = 'Cache Type'; $lang->cache->namespace = 'Namespace'; +$lang->cache->scope = 'Scope'; $lang->cache->memory = 'Memory'; $lang->cache->usedMemory = 'Total %s, used %s'; @@ -12,6 +14,9 @@ $lang->cache->statusList[0] = 'Off'; $lang->cache->driverList['apcu'] = 'APCu'; $lang->cache->driverList['redis'] = 'Redis'; +$lang->cache->scopeList['private'] = 'Exclusively for this application'; +$lang->cache->scopeList['shared'] = 'Shared by multiple applications'; + $lang->cache->apcu = new stdClass(); $lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; $lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; @@ -35,4 +40,5 @@ $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, $lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; $lang->cache->tips = new stdClass(); -$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace after caching is enabled will clear the cache data.'; +$lang->cache->tips->scope = 'If the cache service is only used by this application, please select "Exclusively for this application", otherwise select "Shared by multiple applications".'; diff --git a/module/cache/lang/zh-cn.php b/module/cache/lang/zh-cn.php index 117d5a83ea..71e485e5ac 100644 --- a/module/cache/lang/zh-cn.php +++ b/module/cache/lang/zh-cn.php @@ -1,8 +1,8 @@ cache->clear = '清除缓存'; $lang->cache->clearSuccess = '清除成功'; -$lang->cache->status = '状态'; -$lang->cache->type = '服务'; +$lang->cache->status = '缓存状态'; +$lang->cache->driver = '缓存服务'; $lang->cache->namespace = '命名空间'; $lang->cache->scope = '服务范围'; $lang->cache->memory = '内存使用'; @@ -40,5 +40,5 @@ $lang->cache->redis->tips->host = '填写域名或 IP 地址,无需填 $lang->cache->redis->tips->serializer = '数据需要序列化后缓存。更改序列化器会清空缓存数据。'; $lang->cache->tips = new stdClass(); -$lang->cache->tips->namespace = '命名空间用来防止不同应用间缓存数据冲突。更改命名空间会清空缓存数据。'; +$lang->cache->tips->namespace = '命名空间用来防止不同应用间缓存数据冲突。启用缓存后更改命名空间会清空缓存数据。'; $lang->cache->tips->scope = '如果缓存服务只有本应用使用请选择『本应用独享』,否则选择『多应用共享』。'; From 967fabd44915ae13d7c5b7de23f89660f5ac8292 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 12 Nov 2024 17:26:05 +0800 Subject: [PATCH 173/300] * [refac] set default cache driver to redis. --- config/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cache.php b/config/cache.php index e4230ee6f0..9440c18981 100644 --- a/config/cache.php +++ b/config/cache.php @@ -2,7 +2,7 @@ /* 缓存设置。Cache settings. */ $config->cache = new stdclass(); $config->cache->enable = false; // 是否开启缓存。Enable cache or not. -$config->cache->driver = ''; // 缓存驱动。 The driver of cache. Can be File|Yac|Apcu|Redis. +$config->cache->driver = 'redis'; // 缓存驱动。 The driver of cache. Can be file|yac|apcu|redis. $config->cache->scope = ''; // 缓存服务范围。The scope of cache. Can be private|shared. $config->cache->namespace = ''; // 缓存命名空间。The namespace of cache. $config->cache->lifetime = 2592000; // 缓存生存时间。The lifetime of cache. The default value is 30 days. From 3f16552bd32ed45c71abe30b5c9ee3af38e56aae Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 12 Nov 2024 17:27:10 +0800 Subject: [PATCH 174/300] - [refac] remove unused code. --- module/cache/js/setting.ui.js | 6 ------ module/cache/ui/setting.html.php | 1 - 2 files changed, 7 deletions(-) diff --git a/module/cache/js/setting.ui.js b/module/cache/js/setting.ui.js index c5ae216b0b..ae6ab7168e 100644 --- a/module/cache/js/setting.ui.js +++ b/module/cache/js/setting.ui.js @@ -8,9 +8,3 @@ function toggleDriver() $('.apcu').toggleClass('hidden', $(this).val() != 'apcu'); $('.redis').toggleClass('hidden', $(this).val() != 'redis'); } - -function toggleSerializer() -{ - $('.serialize').toggleClass('hidden', $(this).val() != 'serialize'); - $('.igbinary').toggleClass('hidden', $(this).val() != 'igbinary'); -} diff --git a/module/cache/ui/setting.html.php b/module/cache/ui/setting.html.php index 96ee727aeb..85d3c3196b 100644 --- a/module/cache/ui/setting.html.php +++ b/module/cache/ui/setting.html.php @@ -27,7 +27,6 @@ formPanel ]), on::change('input[name=enable]', 'toggleCache'), on::change('input[name=driver]', 'toggleDriver'), - on::change('input[name=redis\\\[serializer\\\]]', 'toggleSerializer'), formGroup ( set::label($lang->cache->status), From 9ea4c9a0f707627b0182ce78bc67e4bc0f472c36 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 12 Nov 2024 17:27:53 +0800 Subject: [PATCH 175/300] * [refac] check required form fields. --- module/cache/control.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/module/cache/control.php b/module/cache/control.php index f4bb2b39ee..ce3dace0ce 100644 --- a/module/cache/control.php +++ b/module/cache/control.php @@ -36,6 +36,14 @@ class cache extends control if($cache->enable) { + $errors = []; + if(empty($cache->driver)) $errors['driver'] = sprintf($this->lang->error->notempty, $this->lang->cache->driver); + if(empty($cache->scope)) $errors['scope'] = sprintf($this->lang->error->notempty, $this->lang->cache->scope); + if(empty($cache->namespace)) $errors['namespace'] = sprintf($this->lang->error->notempty, $this->lang->cache->namespace); + if($cache->driver == 'redis' && empty($redis->host)) $errors['redis[host]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->host); + if($cache->driver == 'redis' && empty($redis->port)) $errors['redis[port]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->port); + if($errors) return $this->send(array('result' => 'fail', 'message' => $errors)); + if($cache->driver == 'apcu') { /* 检查是否加载了 APCu 扩展。Check if the APCu extension is loaded. */ @@ -48,12 +56,6 @@ class cache extends control if(!extension_loaded('redis')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->notLoaded)); if(!extension_loaded('igbinary') && $redis->serializer == 'igbinary') return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->igbinaryNotLoaded)); - /* 检查 Redis 配置是否正确。Check if the Redis configuration is correct. */ - $errors = []; - if(empty($redis->host)) $errors['redis[host]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->host); - if(empty($redis->port)) $errors['redis[port]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->port); - if($errors) return $this->send(array('result' => 'fail', 'message' => $errors)); - /* 检查 Redis 连接是否正常。Check if the Redis connection is normal. */ try { From 29fa9fdd5808a33fbcbbba11b9ca2c647d635ad7 Mon Sep 17 00:00:00 2001 From: liugang Date: Tue, 12 Nov 2024 17:28:20 +0800 Subject: [PATCH 176/300] * [refac] fix the error of form control linkage display. --- module/cache/js/setting.ui.js | 1 + module/cache/ui/setting.html.php | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/module/cache/js/setting.ui.js b/module/cache/js/setting.ui.js index ae6ab7168e..6a1ba523c8 100644 --- a/module/cache/js/setting.ui.js +++ b/module/cache/js/setting.ui.js @@ -1,6 +1,7 @@ function toggleCache() { $('.cache').toggleClass('hidden', $(this).val() != '1'); + toggleDriver.call($('[name=driver]:checked')); } function toggleDriver() diff --git a/module/cache/ui/setting.html.php b/module/cache/ui/setting.html.php index 85d3c3196b..00447e50d3 100644 --- a/module/cache/ui/setting.html.php +++ b/module/cache/ui/setting.html.php @@ -14,8 +14,8 @@ $driver = !empty($config->cache->driver) ? $config->cache->driver : $namespace = !empty($config->cache->namespace) ? $config->cache->namespace : $config->db->name; $serializer = !empty($config->redis->serializer) ? $config->redis->serializer : 'serialize'; $hiddenCache = $config->cache->enable ? '' : ' hidden'; -$hiddenApcu = $driver == 'apcu' ? '' : ' hidden'; -$hiddenRedis = $driver == 'redis' ? '' : ' hidden'; +$hiddenApcu = !$hiddenCache && $driver == 'apcu' ? '' : ' hidden'; +$hiddenRedis = !$hiddenCache && $driver == 'redis' ? '' : ' hidden'; formPanel ( @@ -42,7 +42,7 @@ formPanel formGroup ( setClass('cache' . $hiddenCache), - set::label($lang->cache->type), + set::label($lang->cache->driver), set::required(), radioList ( From f6996e21f731816ee458f73dc425ef46eb5c3d7b Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 11:03:33 +0800 Subject: [PATCH 177/300] * [refac] modify default lifetime of cache to no expiration. --- config/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cache.php b/config/cache.php index 9440c18981..2d30595f39 100644 --- a/config/cache.php +++ b/config/cache.php @@ -5,7 +5,7 @@ $config->cache->enable = false; // 是否开启缓存。Enable cache or $config->cache->driver = 'redis'; // 缓存驱动。 The driver of cache. Can be file|yac|apcu|redis. $config->cache->scope = ''; // 缓存服务范围。The scope of cache. Can be private|shared. $config->cache->namespace = ''; // 缓存命名空间。The namespace of cache. -$config->cache->lifetime = 2592000; // 缓存生存时间。The lifetime of cache. The default value is 30 days. +$config->cache->lifetime = 0; // 缓存生存时间,默认永不过期。The lifetime of cache. Default is no expiration. $config->cache->client = new stdClass(); $config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not. From 736a438d22b07492c82a7e4fa29dbf41b603dacf Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 11:04:55 +0800 Subject: [PATCH 178/300] * [refac] modify default serializer of redis to igbinary. --- config/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cache.php b/config/cache.php index 2d30595f39..1156128271 100644 --- a/config/cache.php +++ b/config/cache.php @@ -69,4 +69,4 @@ $config->redis->host = ''; $config->redis->port = ''; $config->redis->username = ''; $config->redis->password = ''; -$config->redis->serializer = 'serialize'; // serialize|igbinary +$config->redis->serializer = 'igbinary'; // serialize|igbinary From 8a52a817df2762a21dea207ca58083a45d2cabd8 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 11:05:53 +0800 Subject: [PATCH 179/300] + [refac] add configuration of dao cache. --- config/cache.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/cache.php b/config/cache.php index 1156128271..5e84cf3830 100644 --- a/config/cache.php +++ b/config/cache.php @@ -7,6 +7,10 @@ $config->cache->scope = ''; // 缓存服务范围。The scope of ca $config->cache->namespace = ''; // 缓存命名空间。The namespace of cache. $config->cache->lifetime = 0; // 缓存生存时间,默认永不过期。The lifetime of cache. Default is no expiration. +$config->cache->dao = new stdClass(); +$config->cache->dao->enable = true; // 是否开启 DAO 缓存。Enable DAO cache or not. +$config->cache->dao->lifetime = 2592000; // DAO 缓存生存时间,默认为 30 天。The lifetime of DAO cache. Default is 30 days. + $config->cache->client = new stdClass(); $config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not. From 77a2984c3844cff4665e784c6a9356551e6ffa20 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 11:06:21 +0800 Subject: [PATCH 180/300] * [refac] user can set expiration of cache. --- lib/cache/cache.class.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 1ea276bd7f..3939f223be 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -763,15 +763,16 @@ class cache * * @param string $key 缓存键。 * @param mixed $value 缓存值。 + * @param int $ttl 缓存时间。 * @access public * @return bool */ - public function saveByKey(string $key, $value) + public function saveByKey(string $key, $value, int $ttl = 0) { if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); $key = $this->namespace . $this->connector . $key; - return $this->cache->set($key, $value); + return $this->cache->set($key, $value, $ttl); } /** From 6cc8baf961e29eeba9003ccd1cc85c0a581cc1de Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 11:07:41 +0800 Subject: [PATCH 181/300] * [refac] remove extra spaces. --- lib/base/dao/dao.class.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index af945e9324..66e36c0e40 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -220,12 +220,12 @@ class baseDAO public function __construct($app) { global $config, $lang, $dbh, $slaveDBH; - $this->app = $app; - $this->config = $config; - $this->lang = $lang; - $this->dbh = $dbh; - $this->cache = $app->cache; - $this->slaveDBH = $slaveDBH ? $slaveDBH : false; + $this->app = $app; + $this->config = $config; + $this->lang = $lang; + $this->dbh = $dbh; + $this->cache = $app->cache; + $this->slaveDBH = $slaveDBH ? $slaveDBH : false; $this->reset(); } From 28c27c06efc3d5ba845e8355eb45c076e65623d3 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 11:08:16 +0800 Subject: [PATCH 182/300] * [refac] set expiration of dao cache. --- lib/base/dao/dao.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 66e36c0e40..9ce14d2a19 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -387,7 +387,7 @@ class baseDAO $this->app->useClientCache = false; - $this->cache->saveByKey($key, array(microtime(true), $value)); + $this->cache->saveByKey($key, array(microtime(true), $value), $this->config->cache->dao->lifetime); } /** From e55458ac98d086c1cce21e98de4e69be3ecc5607 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 13:34:50 +0800 Subject: [PATCH 183/300] * [refac] optimize ttl parameter. --- lib/cache/driver/ApcuDriver.php | 8 ++++---- lib/cache/driver/RedisDriver.php | 16 +++++----------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/cache/driver/ApcuDriver.php b/lib/cache/driver/ApcuDriver.php index 6e712c16ec..01339e1ca1 100644 --- a/lib/cache/driver/ApcuDriver.php +++ b/lib/cache/driver/ApcuDriver.php @@ -41,9 +41,9 @@ class ApcuDriver implements CacheInterface public function set($key, $value, $ttl = null) { - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + $ttl = (int)($ttl ?: $this->defaultLifetime); - return apcu_store($key, $value, (int) $ttl); + return apcu_store($key, $value, $ttl); } public function delete($key) @@ -63,9 +63,9 @@ class ApcuDriver implements CacheInterface public function setMultiple($values, $ttl = null) { - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + $ttl = (int)($ttl ?: $this->defaultLifetime); - $result = apcu_store($values, (int) $ttl); + $result = apcu_store($values, $ttl); return $result === true ? true : (is_array($result) && count($result) == 0 ? true : false); } diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index 3622485e5f..a078816990 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -116,9 +116,9 @@ class RedisDriver implements CacheInterface */ public function set($key, $value, $ttl = null) { - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + $ttl = (int)($ttl ?: $this->defaultLifetime); - return $this->redis->set($key, $value, (int)$ttl); + return $this->redis->set($key, $value, $ttl ?: null); } /** @@ -177,17 +177,11 @@ class RedisDriver implements CacheInterface */ public function setMultiple($values, $ttl = null) { - $ttl = is_null($ttl) ? $this->defaultLifetime : $ttl; + $ttl = (int)($ttl ?: $this->defaultLifetime); - if(!empty($ttl)) - { - foreach($values as $key => $value) - { - $this->redis->set($key, $value, $ttl); - } - } + if(!$ttl) return $this->redis->mset($values); - return $this->redis->mset($values); + foreach($values as $key => $value) $this->redis->set($key, $value, $ttl); } /** From 35b0b699aada2dd6c991a9b1d061c31a78d32085 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 13:43:13 +0800 Subject: [PATCH 184/300] * [refac] add method to create cache key. --- lib/cache/cache.class.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index 3939f223be..c92c648105 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -310,7 +310,7 @@ class cache */ private function getRawCacheKey(string $code, int|string $id): string { - return $this->namespace . $this->connector . 'raw' . $this->connector . $code . $this->connector . $id; + return $this->createKey('raw', $code, $id); } /** @@ -323,7 +323,7 @@ class cache */ private function getSetCacheKey(string $code): string { - return $this->namespace . $this->connector . 'set' . $this->connector . $code . $this->connector . 'list'; + return $this->createKey('set', $code, 'list'); } /** @@ -336,7 +336,8 @@ class cache */ private function getResCacheKey(string $key): string { - return $this->namespace . $this->connector . str_replace(['cache', '_'], ['res', $this->connector], strtolower($key)); + $args = explode('_', str_replace('cache', 'res', strtolower($key))); + return $this->createKey(...$args); } /** @@ -583,6 +584,19 @@ class cache return $objects; } + /** + * 生成缓存键。 + * Generate cache key. + * + * @param mixed ...$args 缓存键的参数。 + * @access public + * @return string + */ + public function createKey(...$args): string + { + return $this->namespace . $this->connector . implode($this->connector, $args); + } + /** * 从缓存中获取指定表指定 id 的数据。 * Get the data of the specified table and id from the cache. @@ -739,7 +753,6 @@ class cache { if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); - $key = $this->namespace . $this->connector . $key; return $this->cache->get($key); } @@ -771,7 +784,6 @@ class cache { if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); - $key = $this->namespace . $this->connector . $key; return $this->cache->set($key, $value, $ttl); } From 3665ecea176678f15bcd9b38c7b2d78ce1f458de Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 13:57:29 +0800 Subject: [PATCH 185/300] * [refac] remove unused code. --- lib/base/dao/dao.class.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 9ce14d2a19..70d305b1d6 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -1149,9 +1149,8 @@ class baseDAO */ public function fetchGroup($groupField, $keyField = '') { - $sql = $this->processSQL(); - $table = $this->table; - $key = 'dao:fetchAll:' . md5($sql); + $sql = $this->processSQL(); + $key = 'dao:fetchAll:' . md5($sql); $rows = $this->getCache($key, $sql); if($rows === self::CACHE_MISS) From 80265530647e68aa603698f942e14b3e4504bde8 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 14:37:58 +0800 Subject: [PATCH 186/300] * [refac] decrease default lifetime of dao cache to 7 days for apcu doesn't have LRU. --- config/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cache.php b/config/cache.php index 5e84cf3830..56bed9735b 100644 --- a/config/cache.php +++ b/config/cache.php @@ -9,7 +9,7 @@ $config->cache->lifetime = 0; // 缓存生存时间,默认永不过 $config->cache->dao = new stdClass(); $config->cache->dao->enable = true; // 是否开启 DAO 缓存。Enable DAO cache or not. -$config->cache->dao->lifetime = 2592000; // DAO 缓存生存时间,默认为 30 天。The lifetime of DAO cache. Default is 30 days. +$config->cache->dao->lifetime = 604800; // DAO 缓存生存时间,默认为 7 天。The lifetime of DAO cache. Default is 7 days. $config->cache->client = new stdClass(); $config->cache->client->enable = false; // 是否开启客户端缓存。Enable client cache or not. From 1a8060020367167d0967149758d1ddda39eb73ca Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 14:40:58 +0800 Subject: [PATCH 187/300] * [refac] add method to create dao cache key. --- lib/base/dao/dao.class.php | 39 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 70d305b1d6..71043557af 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -328,6 +328,21 @@ class baseDAO $this->method = $method; } + /** + * 生成缓存的 key。 + * Create the cache key. + * + * @param mixed $args + * @access private + * @return string + */ + private function createCacheKey(...$args): string + { + if(empty($this->cache)) return implode('-', $args); + + return $this->cache->createKey('dao', ...$args); + } + /** * 获取缓存。 * Get the cache. @@ -359,7 +374,8 @@ class baseDAO { if(strpos($table, 'boardlayer') !== false) return self::CACHE_MISS; - $tableCache = $this->cache->getByKey($table); + $tableKey = $this->createCacheKey('table', $table); + $tableCache = $this->cache->getByKey($tableKey); if($tableCache === null) continue; if($tableCache[0] > $cachedTime) return self::CACHE_MISS; @@ -410,7 +426,8 @@ class baseDAO /* 更新表的缓存时间。*/ /* Update the table cache time. */ $table = str_replace(array('`', '"'), '', $table); - $this->setCache($table); + $key = $this->createCacheKey('table', $table); + $this->setCache($key); } } @@ -1095,9 +1112,8 @@ class baseDAO */ public function fetch($field = '') { - $sql = $this->processSQL(); - $key = 'dao:fetch:' . md5($sql); - + $sql = $this->processSQL(); + $key = $this->createCacheKey('fetch', md5($sql)); $result = $this->getCache($key, $sql); if($result === self::CACHE_MISS) { @@ -1121,9 +1137,8 @@ class baseDAO */ public function fetchAll($keyField = '') { - $sql = $this->processSQL(); - $key = 'dao:fetchAll:' . md5($sql); - + $sql = $this->processSQL(); + $key = $this->createCacheKey('fetchAll', md5($sql)); $rows = $this->getCache($key, $sql); if($rows === self::CACHE_MISS) { @@ -1150,8 +1165,7 @@ class baseDAO public function fetchGroup($groupField, $keyField = '') { $sql = $this->processSQL(); - $key = 'dao:fetchAll:' . md5($sql); - + $key = $this->createCacheKey('fetchAll', md5($sql)); $rows = $this->getCache($key, $sql); if($rows === self::CACHE_MISS) { @@ -1181,9 +1195,8 @@ class baseDAO */ public function fetchPairs($keyField = '', $valueField = '') { - $sql = $this->processSQL(); - $key = 'dao:fetchAll:' . md5($sql); - + $sql = $this->processSQL(); + $key = $this->createCacheKey('fetchAll', md5($sql)); $rows = $this->getCache($key, $sql); if($rows === self::CACHE_MISS) { From e89e7fa5725bb1e3cd23b1b17ab8d0d4872717ef Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 15:27:50 +0800 Subject: [PATCH 188/300] * [refac] the table updated time in dao cache won't expire. --- lib/base/dao/dao.class.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index 71043557af..97229a0d09 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -394,16 +394,17 @@ class baseDAO * * @param string $key * @param mixed $value + * @param int $ttl * @access public * @return void */ - public function setCache($key, $value = null) + public function setCache($key, $value = null, int $ttl = null) { if(!$this->app->isServing() || empty($this->cache)) return false; $this->app->useClientCache = false; - $this->cache->saveByKey($key, array(microtime(true), $value), $this->config->cache->dao->lifetime); + $this->cache->saveByKey($key, array(microtime(true), $value), $ttl ?? $this->config->cache->dao->lifetime); } /** @@ -427,7 +428,7 @@ class baseDAO /* Update the table cache time. */ $table = str_replace(array('`', '"'), '', $table); $key = $this->createCacheKey('table', $table); - $this->setCache($key); + $this->setCache($key, $table, 0); } } From 7cf6a7b0ba5034ef6384f3adea30c092004b1944 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 16:06:28 +0800 Subject: [PATCH 189/300] - [refac] remove unused cache.html.php in admin module. --- module/admin/ui/cache.html.php | 57 ---------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 module/admin/ui/cache.html.php diff --git a/module/admin/ui/cache.html.php b/module/admin/ui/cache.html.php deleted file mode 100644 index 0bea985dca..0000000000 --- a/module/admin/ui/cache.html.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @package admin - * @link https://www.zentao.net - */ -namespace zin; - -formPanel -( - set::actions - ([ - 'submit', - $config->cache->dao->enable ? ['text' => $lang->admin->clearCache, 'url' => inlink('ajaxClearCache'), 'class' => 'secondary ajax-submit'] : null, - 'cancel' - ]), - formGroup - ( - set::label($lang->admin->daoCache), - radioList - ( - set::name('dao[enable]'), - set::items($lang->admin->cacheStatusList), - set::value($config->cache->dao->enable), - set::inline(true) - ), - span(setClass('ml-4 mt-1.5'), icon('info text-warning mr-2'), $lang->admin->apcuNotice) - ), - helper::isAPCuEnabled() && $this->config->cache->dao->driver == 'Apcu' ? formRow - ( - formGroup - ( - setClass('w-1/2'), - setStyle(array('align-items' => 'center')), - set::label($lang->admin->memory), - progressBar - ( - set::percent($rate), - set::width('100%'), - set::color('rgb(var(--color-' . ($rate <= 50 ? 'success' : ($rate <= 80 ? 'warning' : 'danger')) . '-500-rgb))') - ) - ), - formGroup - ( - setClass('w-1/2 ml-4 gap-4'), - setStyle(array('align-items' => 'center')), - span($rate . '%'), - span(sprintf($lang->admin->usedMemory, $total, $used)) - ) - ) : null -); - -render(); From 4d07bea8351b28635b96ba34630c3b7917e4e3fd Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 17:11:37 +0800 Subject: [PATCH 190/300] + [refac] add method to clear cache. --- lib/base/mao/mao.class.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index f5f12c29f1..b2ab74cc54 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -715,6 +715,18 @@ class baseMao } } + /** + * 清除缓存。 + * Clear cache. + * + * @access public + * @return void + */ + public function clearCache() + { + $this->cache->clear(); + } + /** * 魔术方法。 * 1. 转换 findByxxx 为 where 条件。 From adcf3b0caa84c5490b74bda3087636e2db84e8aa Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 13 Nov 2024 17:12:07 +0800 Subject: [PATCH 191/300] * [refac] refactor method to enable cache by apcu after installed. --- module/install/control.php | 12 +----------- module/install/model.php | 23 +++++++++++++---------- module/upgrade/model.php | 2 +- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/module/install/control.php b/module/install/control.php index 13f48c904b..5a4d53d3c1 100644 --- a/module/install/control.php +++ b/module/install/control.php @@ -381,17 +381,7 @@ class install extends control $this->install->importBIData(); - $this->install->enableDaoCache(); - - /** - * 安装完成后清除缓存。 - * 通过 dao 的 exec 方法更新数据库会自动更新缓存。 - * 通过 dbh 执行 sql 语句的方式更新数据库不会自动更新缓存,应该在清除缓存之前执行,否则可能导致缓存命中但数据已过期。 - * Clear the cache after the installation is complete. - * Update the database through the exec method of dao will automatically update the cache. - * Update the database by executing sql statements through dbh will not automatically update the cache, should be executed before clearing the cache, otherwise it may cause cache hits but the data has expired. - */ - $this->dao->clearCache(); + $this->install->enableCache(); $skipApp = (string)getenv('ZT_SKIP_DEVOPS_INIT'); $link = ($this->config->inQuickon && (!$skipApp || $skipApp == 'false')) ? inlink('app') : inlink('step6'); diff --git a/module/install/model.php b/module/install/model.php index 6d9aa92db6..111e1c022d 100644 --- a/module/install/model.php +++ b/module/install/model.php @@ -423,24 +423,27 @@ class installModel extends model } /** - * 开启 dao 缓存。 - * Enable dao cache. + * 开启缓存。 + * Enable cache. * * @access public * @return bool */ - public function enableDaoCache(): bool + public function enableCache(): bool { if(!helper::isAPCuEnabled()) return false; $cache = new stdclass(); - $cache->owner = 'system'; - $cache->module = 'common'; - $cache->section = 'global'; - $cache->key = 'cache'; - $cache->value = '{"dao":{"enable":"1"}}'; - $this->dao->replace(TABLE_CONFIG)->data($cache)->exec(); + $cache->status = true; + $cache->driver = 'apcu'; + $cache->scope = 'shared'; + $cache->namespace = $this->config->db->name; - return !dao::isError(); + $this->loadModel('setting')->setItems('system.common.cache', $cache); + if(dao::isError()) return false; + + $this->mao->clearCache(); + + return true; } } diff --git a/module/upgrade/model.php b/module/upgrade/model.php index a1b842ae0c..469adaec5a 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -9214,7 +9214,7 @@ class upgradeModel extends model */ public function openCacheByAPCu(): bool { - return $this->loadModel('install')->enableDaoCache(); + return $this->loadModel('install')->enableCache(); } /** From a53291f9f44be924779ce97696b50308f8362911 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 14 Nov 2024 15:38:03 +0800 Subject: [PATCH 192/300] * [refac] modify default cache driver to apcu. --- config/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cache.php b/config/cache.php index 56bed9735b..c33013cd1a 100644 --- a/config/cache.php +++ b/config/cache.php @@ -2,7 +2,7 @@ /* 缓存设置。Cache settings. */ $config->cache = new stdclass(); $config->cache->enable = false; // 是否开启缓存。Enable cache or not. -$config->cache->driver = 'redis'; // 缓存驱动。 The driver of cache. Can be file|yac|apcu|redis. +$config->cache->driver = 'apcu'; // 缓存驱动。 The driver of cache. Can be file|yac|apcu|redis. $config->cache->scope = ''; // 缓存服务范围。The scope of cache. Can be private|shared. $config->cache->namespace = ''; // 缓存命名空间。The namespace of cache. $config->cache->lifetime = 0; // 缓存生存时间,默认永不过期。The lifetime of cache. Default is no expiration. From 8c17984ce19e381d4e132930c4aef21dd790b559 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 14 Nov 2024 15:38:31 +0800 Subject: [PATCH 193/300] * [refac] modify php serialize's key to php. --- config/cache.php | 2 +- module/cache/lang/de.php | 4 ++-- module/cache/lang/en.php | 4 ++-- module/cache/lang/fr.php | 4 ++-- module/cache/lang/zh-cn.php | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config/cache.php b/config/cache.php index c33013cd1a..6460dda84a 100644 --- a/config/cache.php +++ b/config/cache.php @@ -73,4 +73,4 @@ $config->redis->host = ''; $config->redis->port = ''; $config->redis->username = ''; $config->redis->password = ''; -$config->redis->serializer = 'igbinary'; // serialize|igbinary +$config->redis->serializer = 'igbinary'; // php|igbinary diff --git a/module/cache/lang/de.php b/module/cache/lang/de.php index 8e4546a3e9..da7ddbb371 100644 --- a/module/cache/lang/de.php +++ b/module/cache/lang/de.php @@ -32,8 +32,8 @@ $lang->cache->redis->notice = 'To use Redis cache, you need to load $lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; $lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; -$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; -$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; +$lang->cache->redis->serializerList['php'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; diff --git a/module/cache/lang/en.php b/module/cache/lang/en.php index 8e4546a3e9..da7ddbb371 100644 --- a/module/cache/lang/en.php +++ b/module/cache/lang/en.php @@ -32,8 +32,8 @@ $lang->cache->redis->notice = 'To use Redis cache, you need to load $lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; $lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; -$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; -$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; +$lang->cache->redis->serializerList['php'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; diff --git a/module/cache/lang/fr.php b/module/cache/lang/fr.php index 8e4546a3e9..da7ddbb371 100644 --- a/module/cache/lang/fr.php +++ b/module/cache/lang/fr.php @@ -32,8 +32,8 @@ $lang->cache->redis->notice = 'To use Redis cache, you need to load $lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; $lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; -$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; -$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; +$lang->cache->redis->serializerList['php'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; diff --git a/module/cache/lang/zh-cn.php b/module/cache/lang/zh-cn.php index 71e485e5ac..58661cf6dd 100644 --- a/module/cache/lang/zh-cn.php +++ b/module/cache/lang/zh-cn.php @@ -32,8 +32,8 @@ $lang->cache->redis->notice = '使用 Redis 缓存需要先加载 Re $lang->cache->redis->notLoaded = '请加载 Redis 扩展后再开启数据缓存。'; $lang->cache->redis->igbinaryNotLoaded = '请加载 igbinary 扩展后再开启数据缓存。'; -$lang->cache->redis->serializerList['serialize'] = 'PHP 内置序列化器'; -$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; +$lang->cache->redis->serializerList['php'] = 'PHP 内置序列化器'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = '填写域名或 IP 地址,无需填写协议和端口号。'; From 9c100fa04ddb74f2c1d15bd3bd6349296b44f395 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 14 Nov 2024 15:39:39 +0800 Subject: [PATCH 194/300] * [refac] refactor cache library. --- lib/cache/cache.class.php | 41 +++++++---------- lib/cache/driver/ApcuDriver.php | 45 ++++++++++++++++-- lib/cache/driver/RedisDriver.php | 78 ++++++++++++++++++-------------- 3 files changed, 103 insertions(+), 61 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index c92c648105..de3acc7374 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -30,6 +30,8 @@ class cache const DRIVER_REDIS = 'Redis'; + const DRIVER_LIST = [self::DRIVER_APCU, self::DRIVER_FILE, self::DRIVER_YAC, self::DRIVER_REDIS]; + /** * 全局应用程序对象。 * Global application object. @@ -155,33 +157,24 @@ class cache if(empty($this->config->cache->enable)) return $this->log('The cache is not enabled', __FILE__, __LINE__); $driver = ucfirst(strtolower($this->config->cache->driver)); - switch($driver) - { - case self::DRIVER_APCU: - $this->setConnector('-'); - $className = 'ZenTao\Cache\Driver\ApcuDriver'; - break; - case self::DRIVER_YAC: - $this->setConnector('-'); - $className = 'ZenTao\Cache\Driver\YacDriver'; - break; - case self::DRIVER_FILE: - $this->setConnector('_'); - $className = 'ZenTao\Cache\Driver\FileDriver'; - break; - case self::DRIVER_REDIS: - $this->setConnector(':'); - $className = 'ZenTao\Cache\Driver\RedisDriver'; - break; - default: - return $this->log("Driver {$driver} is not supported.", __FILE__, __LINE__); - } + if(!in_array($driver, self::DRIVER_LIST)) return $this->log("Driver {$driver} is not supported.", __FILE__, __LINE__); if($driver != self::DRIVER_FILE && !extension_loaded($driver)) return $this->log("Driver ext-{$driver} is not loaded.", __FILE__, __LINE__); - $this->cache = new $className($this->config->cache->namespace, $this->config->cache->lifetime, $app->getCacheRoot()); + $connector = $driver == self::DRIVER_REDIS ? ':' : '-'; + $className = "ZenTao\Cache\Driver\\{$driver}Driver"; + $scope = $this->config->cache->scope; + $namespace = $this->config->cache->namespace; + $lifetime = $this->config->cache->lifetime; + $redis = $this->config->redis; - $this->setNamespace($this->config->cache->namespace); + $this->setNamespace($namespace); + $this->setConnector($connector); + + if($driver == self::DRIVER_APCU) return $this->cache = new $className($namespace, $lifetime, $scope, $connector); + if($driver == self::DRIVER_REDIS) return $this->cache = new $className($namespace, $lifetime, $scope, $connector, $redis); + if($driver == self::DRIVER_YAC) return $this->cache = new $className($namespace, $lifetime); + if($driver == self::DRIVER_FILE) return $this->cache = new $className($namespace, $lifetime, $app->getCacheRoot()); } /** @@ -466,7 +459,7 @@ class cache /* 如果没有设置关联字段则整个缓存都受影响。If no associated fields are set, the entire cache is affected. */ if(empty($res->fields)) { - $keys[] = $this->getResCacheKey($res->name); + $keys = $this->getResCacheKey($res->name); continue; } diff --git a/lib/cache/driver/ApcuDriver.php b/lib/cache/driver/ApcuDriver.php index 01339e1ca1..66760d7ec9 100644 --- a/lib/cache/driver/ApcuDriver.php +++ b/lib/cache/driver/ApcuDriver.php @@ -17,19 +17,47 @@ use ZenTao\Cache\SimpleCache\InvalidArgumentException; class ApcuDriver implements CacheInterface { /** + * 缓存命名空间。 + * The cache namespace. + * + * @access private * @var string */ private $namespace; /** + * 缓存默认生命周期。 + * The cache default lifetime. + * + * @access private * @var int */ private $defaultLifetime; - public function __construct($namespace = '', $defaultLifetime = 0) + /** + * 缓存服务范围。private 独享|public 共享。 + * The cache scope. + * + * @access private + * @var string + */ + private $scope; + + /** + * 缓存键连接符。 + * Cache key connector. + * + * @access private + * @var string + */ + private $connector; + + public function __construct($namespace = '', $defaultLifetime = 0, $scope = '', $connector = '') { - $this->namespace = $namespace; + $this->namespace = $namespace; $this->defaultLifetime = $defaultLifetime; + $this->scope = $scope; + $this->connector = $connector; } public function get($key, $default = null) @@ -53,7 +81,18 @@ class ApcuDriver implements CacheInterface public function clear() { - return apcu_clear_cache(); + if($this->scope == 'private') return apcu_clear_cache(); + + $keys = []; + $info = apcu_cache_info(); + $cacheList = $info['cache_list']; + foreach($cacheList as $cache) + { + if(strpos($cache['info'], $this->namespace . $this->connector) === 0) $keys[] = $cache['info']; + } + if(!$keys) return true; + + return $this->deleteMultiple($keys); } public function getMultiple($keys, $default = null) diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index a078816990..bd54fc0274 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -33,57 +33,49 @@ class RedisDriver implements CacheInterface private $defaultLifetime; /** - * 缓存序列化器,默认使用 PHP 内置的序列化器。 - * The cache serializer, the default is the PHP built-in serializer. - * - * @var int - */ - private $serializer = \Redis::SERIALIZER_PHP; - - public function __construct($namespace = '', $defaultLifetime = 0) - { - $this->namespace = $namespace; - $this->defaultLifetime = $defaultLifetime; - - $this->setSerializer(); - - $this->connectRedis(); - } - - /** - * 设置序列化器。 - * Set the serializer. + * 缓存服务范围。private 独享|public 共享。 + * The cache scope. * * @access private - * @return void + * @var string */ - private function setSerializer() + private $scope; + + /** + * 缓存键连接符。 + * Cache key connector. + * + * @access private + * @var string + */ + private $connector; + + public function __construct($namespace = '', $defaultLifetime = 0, $scope = '', $connector = '', $setting = null) { - if(function_exists('igbinary_serialize')) - { - $this->serializer = \Redis::SERIALIZER_IGBINARY; - } - elseif(function_exists('msgpack_pack')) - { - $this->serializer = \Redis::SERIALIZER_MSGPACK; - } + $this->namespace = $namespace; + $this->defaultLifetime = $defaultLifetime; + $this->scope = $scope; + $this->connector = $connector; + + $this->connectRedis($setting); } /** * 连接 Redis 服务器。 * Connect to the Redis server. * + * @param object $setting * @access private * @return object */ - private function connectRedis() + private function connectRedis($setting) { global $config; try { - $this->redis = \helper::connectRedis($config->redis); - $this->redis->setOption(\Redis::OPT_SERIALIZER, $this->serializer); + $this->redis = \helper::connectRedis($setting); + $this->redis->setOption(\Redis::OPT_SERIALIZER, $this->getSerializer($setting->serializer)); } catch(Exception $e) { @@ -91,6 +83,22 @@ class RedisDriver implements CacheInterface } } + /** + * 设置序列化器。 + * Set the serializer. + * + * @param string $serializer + * @access private + * @return void + */ + private function getSerializer($serializer) + { + if($serializer == 'igbinary') return \Redis::SERIALIZER_IGBINARY; + if($serializer == 'php') return \Redis::SERIALIZER_PHP; + if($serializer == 'msgpack') return \Redis::SERIALIZER_MSGPACK; + if($serializer == 'json') return \Redis::SERIALIZER_JSON; + } + /** * Get the value related to the specified key. * @@ -141,13 +149,15 @@ class RedisDriver implements CacheInterface */ public function clear() { + if($this->scope == 'private') return $this->redis->flushDB(); + /* With Redis::SCAN_RETRY enabled */ $this->redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY); $it = null; $keys = []; - while($cachedKeys = $this->redis->scan($it, $this->namespace . ':*')) + while($cachedKeys = $this->redis->scan($it, $this->namespace . $this->connector . '*')) { $keys = array_merge($keys, $cachedKeys); } From 335d4e23bf57fd41c45b8b43b238d5998d9a0f4c Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 10:34:52 +0800 Subject: [PATCH 195/300] * [refac] if not all data in cache, get the missing data from the database. --- lib/cache/cache.class.php | 49 +++++++++++++++++++++----------- lib/cache/driver/ApcuDriver.php | 5 +++- lib/cache/driver/RedisDriver.php | 2 +- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index de3acc7374..c26c66f9bc 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -622,12 +622,13 @@ class cache * Get all data of the specified table from the cache. * * @param string $table 表名。 + * @param array $objectIdList 主键值列表。 * @access public * @return array */ public function fetchAll(string $table, array $objectIdList = []): array { - if(!$this->checkTable($table)) return []; + if(!$this->checkTable($table)) return $this->log("Table {$table} is not set in the cache configuration", __FILE__, __LINE__); if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__); @@ -635,35 +636,51 @@ class cache $code = $this->getTableCode(); - $result = []; - - /* Try get all objectIdList. */ + /* 尝试获取指定表的所有主键字段的值。Try to get the values of all primary key fields of the specified table. */ $setCacheKey = $this->getSetCacheKey($code); $allObjectIdList = $this->cache->get($setCacheKey); - /* No data in cache, init table cache. */ + /* 如果主键字段的值为空,则初始化指定表的缓存。If the value of the primary key field is empty, initialize the cache of the specified table. */ if(!$allObjectIdList) { $allData = $this->initTableCache(); if(empty($objectIdList)) return $allData; - foreach($allData as $objectId => $object) - { - if(in_array($objectId, $objectIdList)) $result[$objectId] = $object; - } + return array_intersect_key($allData, $objectIdList); } - else + + /* 如果主键字段的值不为空,则从缓存中获取数据。If the value of the primary key field is not empty, get the data from the cache. */ + if(empty($objectIdList)) $objectIdList = $allObjectIdList; + + $keys = []; + foreach($objectIdList as $objectID) $keys[$objectID] = $this->getRawCacheKey($code, $objectID); + + $objects = $this->cache->getMultiple(array_values($keys)); + + /* 如果缓存中没有全部的数据,则从数据库中获取缺失的数据。If not all data in cache, get the missing data from the database. */ + if(count($keys) > count($objects)) { - $keys = []; - foreach($objectIdList as $objectID) $keys[] = $this->getRawCacheKey($code, $objectID); + $lostObjects = []; + $diffIdList = array_keys(array_diff($keys, array_keys($objects))); + $diffObjects = $this->dao->select('*')->from($table)->where('id')->in($diffIdList)->fetchAll(); + foreach($diffObjects as $object) + { + $rawCacheKey = $this->getRawCacheKey($code, $object->id); + $lostObjects[$rawCacheKey] = $object; + } - $objects = $this->cache->getMultiple($keys); - if(!$objects) return []; + /* 把缺失的数据保存到缓存中。Save the missing data to cache. */ + $this->cache->setMultiple($lostObjects); - $field = $this->getTableField(); - foreach($objects as $object) $result[$object->$field] = $object; + $objects += $lostObjects; } + if(!$objects) return []; + + $result = []; + $field = $this->getTableField(); + foreach($objects as $object) $result[$object->$field] = $object; + return $result; } diff --git a/lib/cache/driver/ApcuDriver.php b/lib/cache/driver/ApcuDriver.php index 66760d7ec9..04d0efc20c 100644 --- a/lib/cache/driver/ApcuDriver.php +++ b/lib/cache/driver/ApcuDriver.php @@ -97,7 +97,10 @@ class ApcuDriver implements CacheInterface public function getMultiple($keys, $default = null) { - return apcu_fetch($keys); + $values = apcu_fetch($keys); + if($values === false) return []; + + return $values; } public function setMultiple($values, $ttl = null) diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index bd54fc0274..c9314c171f 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -174,7 +174,7 @@ class RedisDriver implements CacheInterface */ public function getMultiple($keys, $default = null) { - return $this->redis->mget($keys); + return array_filter(array_combine($keys, $this->redis->mget($keys))); } /** From 19cb0049d1d578e422e9436f838c74d8a88d2feb Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 13:24:54 +0800 Subject: [PATCH 196/300] * [refac] modify replace to insert. --- module/install/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/install/model.php b/module/install/model.php index 111e1c022d..77c68e1b91 100644 --- a/module/install/model.php +++ b/module/install/model.php @@ -372,7 +372,7 @@ class installModel extends model $config->section = 'global'; $config->key = 'showDemoUsers'; $config->value = '1'; - $this->dao->replace(TABLE_CONFIG)->data($config)->exec(); + $this->dao->insert(TABLE_CONFIG)->data($config)->exec(); return true; } From b2322ec0acfa7a331991ce0c6ca676deadbc4402 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 14:23:15 +0800 Subject: [PATCH 197/300] + [refac] add default database of redis. --- config/cache.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/cache.php b/config/cache.php index 6460dda84a..27619c88b9 100644 --- a/config/cache.php +++ b/config/cache.php @@ -73,4 +73,5 @@ $config->redis->host = ''; $config->redis->port = ''; $config->redis->username = ''; $config->redis->password = ''; +$config->redis->database = 0; $config->redis->serializer = 'igbinary'; // php|igbinary From 64e7c831d049e85b90e8202794a990be9a2aed61 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 14:23:47 +0800 Subject: [PATCH 198/300] * [refac] user can set database of redis. --- module/cache/config/form.php | 2 +- module/cache/control.php | 2 ++ module/cache/lang/de.php | 1 + module/cache/lang/en.php | 1 + module/cache/lang/fr.php | 1 + module/cache/lang/zh-cn.php | 2 ++ module/cache/ui/setting.html.php | 34 ++++++++++++++++++++++++-------- 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/module/cache/config/form.php b/module/cache/config/form.php index b61efeaab4..f7c52123e5 100644 --- a/module/cache/config/form.php +++ b/module/cache/config/form.php @@ -5,4 +5,4 @@ $config->cache->form->setting['enable'] = array('type' => 'int', 'default' $config->cache->form->setting['driver'] = array('type' => 'string', 'default' => ''); $config->cache->form->setting['scope'] = array('type' => 'string', 'default' => ''); $config->cache->form->setting['namespace'] = array('type' => 'string', 'default' => ''); -$config->cache->form->setting['redis'] = array('type' => 'array', 'default' => array('host' => '', 'port' => 0, 'username' => '', 'password' => '', 'serializer' => '')); +$config->cache->form->setting['redis'] = array('type' => 'array', 'default' => array('host' => '', 'port' => 0, 'username' => '', 'password' => '', 'database' => 0, 'serializer' => 'igbinary')); diff --git a/module/cache/control.php b/module/cache/control.php index ce3dace0ce..c0edf01fa7 100644 --- a/module/cache/control.php +++ b/module/cache/control.php @@ -32,6 +32,7 @@ class cache extends control if(!isset($redis->port)) $redis->port = ''; if(!isset($redis->username)) $redis->username = ''; if(!isset($redis->password)) $redis->password = ''; + if(!isset($redis->database)) $redis->database = ''; if(!isset($redis->serializer)) $redis->serializer = ''; if($cache->enable) @@ -42,6 +43,7 @@ class cache extends control if(empty($cache->namespace)) $errors['namespace'] = sprintf($this->lang->error->notempty, $this->lang->cache->namespace); if($cache->driver == 'redis' && empty($redis->host)) $errors['redis[host]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->host); if($cache->driver == 'redis' && empty($redis->port)) $errors['redis[port]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->port); + if($cache->driver == 'redis' && empty($redis->database) && $redis->database !== '0') $errors['redis[database]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->database); if($errors) return $this->send(array('result' => 'fail', 'message' => $errors)); if($cache->driver == 'apcu') diff --git a/module/cache/lang/de.php b/module/cache/lang/de.php index da7ddbb371..c7435ad974 100644 --- a/module/cache/lang/de.php +++ b/module/cache/lang/de.php @@ -37,6 +37,7 @@ $lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->database = 'Fill in the number of the Redis database, the default is 0.'; $lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; $lang->cache->tips = new stdClass(); diff --git a/module/cache/lang/en.php b/module/cache/lang/en.php index da7ddbb371..c7435ad974 100644 --- a/module/cache/lang/en.php +++ b/module/cache/lang/en.php @@ -37,6 +37,7 @@ $lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->database = 'Fill in the number of the Redis database, the default is 0.'; $lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; $lang->cache->tips = new stdClass(); diff --git a/module/cache/lang/fr.php b/module/cache/lang/fr.php index da7ddbb371..c7435ad974 100644 --- a/module/cache/lang/fr.php +++ b/module/cache/lang/fr.php @@ -37,6 +37,7 @@ $lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->database = 'Fill in the number of the Redis database, the default is 0.'; $lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; $lang->cache->tips = new stdClass(); diff --git a/module/cache/lang/zh-cn.php b/module/cache/lang/zh-cn.php index 58661cf6dd..5000ad8b57 100644 --- a/module/cache/lang/zh-cn.php +++ b/module/cache/lang/zh-cn.php @@ -27,6 +27,7 @@ $lang->cache->redis->host = 'Redis 主机'; $lang->cache->redis->port = 'Redis 端口'; $lang->cache->redis->username = 'Redis 用户名'; $lang->cache->redis->password = 'Redis 密码'; +$lang->cache->redis->database = 'Redis 数据库'; $lang->cache->redis->serializer = 'Redis 序列化器'; $lang->cache->redis->notice = '使用 Redis 缓存需要先加载 Redis 扩展。'; $lang->cache->redis->notLoaded = '请加载 Redis 扩展后再开启数据缓存。'; @@ -37,6 +38,7 @@ $lang->cache->redis->serializerList['igbinary'] = 'igbinary'; $lang->cache->redis->tips = new stdClass(); $lang->cache->redis->tips->host = '填写域名或 IP 地址,无需填写协议和端口号。'; +$lang->cache->redis->tips->database = '填写 Redis 数据库的编号,默认为 0。'; $lang->cache->redis->tips->serializer = '数据需要序列化后缓存。更改序列化器会清空缓存数据。'; $lang->cache->tips = new stdClass(); diff --git a/module/cache/ui/setting.html.php b/module/cache/ui/setting.html.php index 00447e50d3..177742146d 100644 --- a/module/cache/ui/setting.html.php +++ b/module/cache/ui/setting.html.php @@ -10,12 +10,9 @@ declare(strict_types=1); */ namespace zin; -$driver = !empty($config->cache->driver) ? $config->cache->driver : ''; -$namespace = !empty($config->cache->namespace) ? $config->cache->namespace : $config->db->name; -$serializer = !empty($config->redis->serializer) ? $config->redis->serializer : 'serialize'; $hiddenCache = $config->cache->enable ? '' : ' hidden'; -$hiddenApcu = !$hiddenCache && $driver == 'apcu' ? '' : ' hidden'; -$hiddenRedis = !$hiddenCache && $driver == 'redis' ? '' : ' hidden'; +$hiddenApcu = !$hiddenCache && $config->cache->driver == 'apcu' ? '' : ' hidden'; +$hiddenRedis = !$hiddenCache && $config->cache->driver == 'redis' ? '' : ' hidden'; formPanel ( @@ -49,7 +46,7 @@ formPanel setClass('w-1/3'), set::name('driver'), set::items($lang->cache->driverList), - set::value($driver), + set::value($config->cache->driver), set::inline(true) ), span @@ -94,7 +91,7 @@ formPanel ( setClass('w-1/3'), set::name('namespace'), - set::value($namespace) + set::value($config->cache->namespace ?: $config->db->name) ), span ( @@ -157,6 +154,27 @@ formPanel ) ), formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->database), + set::required(), + input + ( + setClass('w-1/3'), + set::type('number'), + set::min(0), + set::step(1), + set::name('redis[database]'), + set::value($config->redis->database) + ), + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->redis->tips->database + ) + ), + formGroup ( setClass('redis' . $hiddenRedis), set::label($lang->cache->redis->serializer), @@ -165,7 +183,7 @@ formPanel setClass('w-1/3'), set::name('redis[serializer]'), set::items($lang->cache->redis->serializerList), - set::value($serializer), + set::value($config->redis->serializer), set::inline(true) ), span From c496b00364911804e58eceb3634d04b2ba67270f Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 14:24:13 +0800 Subject: [PATCH 199/300] + [refac] check redis server's max database number. --- framework/helper.class.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/helper.class.php b/framework/helper.class.php index 379490e381..9da64a705c 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -446,6 +446,7 @@ class helper extends baseHelper public static function connectRedis(object $setting) { if(!class_exists('Redis')) throw new Exception('The Redis extension is not installed.'); + try { $redis = new Redis(); @@ -463,6 +464,9 @@ class helper extends baseHelper if(!$redis->ping()) throw new Exception('Can not connect to Redis server.'); + $databases = $redis->config('GET', 'databases'); + if($setting->database >= $databases['databases']) throw new Exception("The database number is out of range. Your Redis server's max database number is " . $databases['databases'] - 1 . '.'); + return $redis; } catch(RedisException $e) From 37d7879cf60c22f2d7ed829af4f2c1950d292189 Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 14:24:38 +0800 Subject: [PATCH 200/300] + [refac] select database after connected to redis server. --- lib/cache/driver/RedisDriver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/cache/driver/RedisDriver.php b/lib/cache/driver/RedisDriver.php index c9314c171f..d94a6b2022 100644 --- a/lib/cache/driver/RedisDriver.php +++ b/lib/cache/driver/RedisDriver.php @@ -76,6 +76,7 @@ class RedisDriver implements CacheInterface { $this->redis = \helper::connectRedis($setting); $this->redis->setOption(\Redis::OPT_SERIALIZER, $this->getSerializer($setting->serializer)); + $this->redis->select($setting->database); } catch(Exception $e) { From 9b6c0038f1adde6aa89213a3edc7088f02a6c8ec Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 15:34:04 +0800 Subject: [PATCH 201/300] + [misc] ignore blocksuite. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b95020cc62..a1c346edde 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ config/ext/xuan*.php framework/xuanxuan.class.php lib/phpaes/ www/x.php +www/js/zui3/editor/blocksuite/* From 788449167f9ac3e453623fa14216b0fc3259839c Mon Sep 17 00:00:00 2001 From: liugang Date: Fri, 15 Nov 2024 15:51:44 +0800 Subject: [PATCH 202/300] Revert "+ [misc] ignore blocksuite." This reverts commit 776d16ad9b23173b6eacd433da800fffda291d50. --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index a1c346edde..b95020cc62 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,3 @@ config/ext/xuan*.php framework/xuanxuan.class.php lib/phpaes/ www/x.php -www/js/zui3/editor/blocksuite/* From f5fa3f54cd5c9d4618b7216c64f7c0ad34b7a9e0 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 09:53:10 +0800 Subject: [PATCH 203/300] * [refac] add cache module to open modules. --- module/upgrade/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/upgrade/config.php b/module/upgrade/config.php index e204945d45..216a10400e 100644 --- a/module/upgrade/config.php +++ b/module/upgrade/config.php @@ -1409,7 +1409,7 @@ $config->delete['20_9'][] = 'extension/max/deploy/js/finish.ui.js'; $config->delete['20_9'][] = 'extension/ipd/deploy/js/finish.ui.js'; $config->delete['20_9'][] = 'module/account/'; -$config->upgrade->openModules = array('action', 'admin', 'ai', 'bi', 'aiapp', 'api', 'automation', 'backup', 'block', 'branch', 'budget', 'bug', 'build', 'caselib', 'chart', 'ci', 'client', 'common', 'company', 'compile', 'convert', 'cron', 'custom', 'datatable', 'dataview', 'dept', 'design', 'dev', 'dimension', 'doc', 'durationestimation', 'entry', 'execution', 'extension', 'file', 'git', 'gitlab', 'group', 'holiday', 'im', 'index', 'index.html', 'install', 'issue', 'jenkins', 'job', 'kanban', 'license', 'mail', 'message', 'metric', 'misc', 'mr', 'my', 'personnel', 'pipeline', 'product', 'productplan', 'productset', 'program', 'programplan', 'project', 'projectbuild', 'projectplan', 'projectrelease', 'projectstory', 'pivot', 'qa', 'release', 'repo', 'report', 'risk', 'score', 'screen', 'search', 'setting', 'sonarqube', 'sso', 'stage', 'stakeholder', 'story', 'subject', 'svn', 'task', 'testcase', 'testreport', 'testsuite', 'testtask', 'todo', 'tree', 'tutorial', 'upgrade', 'user', 'webhook', 'weekly', 'workestimation', 'gitea', 'gogs', 'transfer', 'zahost', 'zanode', 'editor', 'charter', 'roadmap', 'account', 'cne', 'host', 'instance', 'ops', 'serverroom', 'space', 'store', 'system', 'solution', 'demand', 'gitfox', 'epic', 'requirement'); +$config->upgrade->openModules = array('action', 'admin', 'ai', 'bi', 'aiapp', 'api', 'automation', 'backup', 'block', 'branch', 'budget', 'bug', 'build', 'cache', 'caselib', 'chart', 'ci', 'client', 'common', 'company', 'compile', 'convert', 'cron', 'custom', 'datatable', 'dataview', 'dept', 'design', 'dev', 'dimension', 'doc', 'durationestimation', 'entry', 'execution', 'extension', 'file', 'git', 'gitlab', 'group', 'holiday', 'im', 'index', 'index.html', 'install', 'issue', 'jenkins', 'job', 'kanban', 'license', 'mail', 'message', 'metric', 'misc', 'mr', 'my', 'personnel', 'pipeline', 'product', 'productplan', 'productset', 'program', 'programplan', 'project', 'projectbuild', 'projectplan', 'projectrelease', 'projectstory', 'pivot', 'qa', 'release', 'repo', 'report', 'risk', 'score', 'screen', 'search', 'setting', 'sonarqube', 'sso', 'stage', 'stakeholder', 'story', 'subject', 'svn', 'task', 'testcase', 'testreport', 'testsuite', 'testtask', 'todo', 'tree', 'tutorial', 'upgrade', 'user', 'webhook', 'weekly', 'workestimation', 'gitea', 'gogs', 'transfer', 'zahost', 'zanode', 'editor', 'charter', 'roadmap', 'account', 'cne', 'host', 'instance', 'ops', 'serverroom', 'space', 'store', 'system', 'solution', 'demand', 'gitfox', 'epic', 'requirement'); $config->upgrade->unsetModules = array('design', 'program', 'programplan', 'projectbuild', 'projectrelease', 'stage', 'stakeholder', 'product', 'branch', 'productplan', 'release', 'build', 'qa', 'bug', 'testcase', 'testtask', 'testreport', 'testsuite', 'caselib', 'automation', 'repo', 'ci', 'compile', 'jenkins', 'job', 'svn', 'gitlab', 'sonarqube', 'mr', 'git', 'report', 'sqlbuilder', 'feedback', 'faq', 'attend', 'holiday', 'leave', 'makeup', 'overtime', 'lieu', 'ops', 'host', 'serverroom', 'account', 'domain', 'service', 'deploy', 'conference', 'traincourse', 'pssp', 'baseline', 'classify', 'cm', 'cmcl', 'auditcl', 'reviewcl', 'process', 'activity', 'zoutput', 'auditplan', 'nc', 'subject', 'weekly', 'workestimation', 'issue', 'durationestimation', 'risk', 'opportunity', 'trainplan', 'gapanalysis', 'researchplan', 'researchreport', 'meeting', 'meetingroom', 'budget', 'reviewissue', 'reviewsetting', 'review', 'milestone', 'measurement', 'measrecord', 'assetlib', 'setting', 'im', 'client', 'ldap', 'dev', 'api', 'gitea', 'gogs', 'zanode', 'zahost'); global $lang; From 3dcd4cde2ecafd7204463783619ea634a945f94c Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 09:54:04 +0800 Subject: [PATCH 204/300] + [refac] reset where condition to empty. --- lib/cache/cache.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index c26c66f9bc..da995a5075 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -826,6 +826,7 @@ class cache $this->setKey(''); $this->setTable(''); $this->setEvent(''); + $this->setWhere(''); $this->setObjects([]); } From 7954ef14e4a27b491d34cc2191a2a3147d48feec Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 09:54:56 +0800 Subject: [PATCH 205/300] * [refac] fix error when the sql doesn't have where condition. --- lib/cache/cache.class.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index da995a5075..b87085369a 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -865,13 +865,18 @@ class cache $splitPOS = $orderPOS ? $orderPOS : $limitPOS; $splitPOS = $havingPOS ? $havingPOS : $splitPOS; $splitPOS = $groupPOS ? $groupPOS : $splitPOS; - if($splitPOS) + + $where = ''; + if($wherePOS) { - $where = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); - } - else - { - $where = substr($sql, $wherePOS + $whereLen); + if($splitPOS) + { + $where = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); + } + else + { + $where = substr($sql, $wherePOS + $whereLen); + } } /* 执行操作后数据已经被修改,所以需要提前获取被影响的数据。*/ From 8d5b8a28da1725c2909ab2975be661b1383bca2d Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 09:55:51 +0800 Subject: [PATCH 206/300] - [refac] the affected object could be empty. --- lib/cache/cache.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index b87085369a..b56ea0fc2a 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -882,7 +882,6 @@ class cache /* 执行操作后数据已经被修改,所以需要提前获取被影响的数据。*/ $field = $this->getTableField(); $objects = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->fetchAll($field); - if(!$objects) return $this->log('Failed to fetch affected objects. The sql is: ' . $this->dao->get(), __FILE__, __LINE__); $this->setWhere($where); $this->setObjects($objects); From 70b40dab3d9d37e093536797008531952ae1314d Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 10:37:10 +0800 Subject: [PATCH 207/300] * [refac] fix error generate affected keys. --- lib/cache/cache.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cache/cache.class.php b/lib/cache/cache.class.php index b56ea0fc2a..8aeea8791f 100644 --- a/lib/cache/cache.class.php +++ b/lib/cache/cache.class.php @@ -426,7 +426,7 @@ class cache /* 把被删除的数据从缓存中删除。Delete the deleted data from cache. */ $affectedKeys = []; - foreach($this->objects as $object) $affectedKeys = $this->getRawCacheKey($code, $object->$field); + foreach($this->objects as $object) $affectedKeys[] = $this->getRawCacheKey($code, $object->$field); $this->cache->deleteMultiple($affectedKeys); /* 把被删除的数据的 id 从缓存中删除。Delete the id of the deleted data from cache. */ From 1fe9bfe01d9fcd97db97c1ecdd9493e41b8f56b8 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 10:37:48 +0800 Subject: [PATCH 208/300] * [refac] use delete and insert operations instead of replace operation. --- module/setting/model.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/module/setting/model.php b/module/setting/model.php index f422ba7572..83295c8ed1 100644 --- a/module/setting/model.php +++ b/module/setting/model.php @@ -53,8 +53,17 @@ class settingModel extends model $item = $this->parseItemPath($path); if(empty($item)) return false; + $vision = zget($item, 'vision', ''); + $item->value = strval($value); - $this->dao->replace(TABLE_CONFIG)->data($item)->exec(); + $this->dao->delete()->from(TABLE_CONFIG) + ->where('owner')->eq($item->owner) + ->andWhere('module')->eq($item->module) + ->andWhere('section')->eq($item->section) + ->andWhere('`key`')->eq($item->key) + ->beginIF($vision)->andWhere('vision')->eq($vision)->fi() + ->exec(); + $this->dao->insert(TABLE_CONFIG)->data($item)->exec(); return !dao::isError(); } From eb251860ba236a2a2837c28c726051e166a4d7e6 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 13:12:47 +0800 Subject: [PATCH 209/300] * [refac] use insert operation instead of replace operation. --- module/upgrade/model.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 469adaec5a..c01169e18f 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -3327,16 +3327,16 @@ class upgradeModel extends model $setting->key = 'storyRequirement'; $setting->value = '0'; - $this->dao->replace(TABLE_CONFIG)->data($setting)->exec(); + $this->dao->insert(TABLE_CONFIG)->data($setting)->exec(); $setting->key = 'hourPoint'; $setting->value = '0'; - $this->dao->replace(TABLE_CONFIG)->data($setting)->exec(); + $this->dao->insert(TABLE_CONFIG)->data($setting)->exec(); $setting->module = 'common'; $setting->key = 'conceptSetted'; $setting->value = '1'; - $this->dao->replace(TABLE_CONFIG)->data($setting)->exec(); + $this->dao->insert(TABLE_CONFIG)->data($setting)->exec(); } return true; @@ -8836,9 +8836,9 @@ class upgradeModel extends model { unset($createField->id); $createField->key = 'createWaterfallFields'; //瀑布模型 - $this->dao->replace(TABLE_CONFIG)->data($createField)->exec(); + $this->dao->insert(TABLE_CONFIG)->data($createField)->exec(); $createField->key = 'createWaterfallplusFields'; //融合瀑布模型 - $this->dao->replace(TABLE_CONFIG)->data($createField)->exec(); + $this->dao->insert(TABLE_CONFIG)->data($createField)->exec(); } /* 删除旧的config。*/ From 5aefa58096e12e9b144fca2c4f2d5fd4ac8e2194 Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 14:57:38 +0800 Subject: [PATCH 210/300] + [refac] clear cache when redis database changed. --- module/cache/control.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/cache/control.php b/module/cache/control.php index c0edf01fa7..7cfcf7afd8 100644 --- a/module/cache/control.php +++ b/module/cache/control.php @@ -74,6 +74,7 @@ class cache extends control if($cache->enable != $this->config->cache->enable || $cache->driver != $this->config->cache->driver || $cache->namespace != $this->config->cache->namespace + || $redis->database != $this->config->redis->database || $redis->serializer != $this->config->redis->serializer) { $this->mao->clear(); From 16cc6278d2dfc58e6bffaa5d8c174b53ceef270a Mon Sep 17 00:00:00 2001 From: liugang Date: Mon, 18 Nov 2024 14:58:06 +0800 Subject: [PATCH 211/300] * [refac] calling clearCache method of mao class. --- module/cache/control.php | 2 +- module/cache/model.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/cache/control.php b/module/cache/control.php index 7cfcf7afd8..33e382c80b 100644 --- a/module/cache/control.php +++ b/module/cache/control.php @@ -77,7 +77,7 @@ class cache extends control || $redis->database != $this->config->redis->database || $redis->serializer != $this->config->redis->serializer) { - $this->mao->clear(); + $this->cache->clear(); } $this->loadModel('setting')->setItems('system.common.cache', $cache); diff --git a/module/cache/model.php b/module/cache/model.php index 692d25b7ef..4606c39ba8 100644 --- a/module/cache/model.php +++ b/module/cache/model.php @@ -23,7 +23,7 @@ class cacheModel extends model /* Redis 采用遍历删除的方式,所以需要先关闭缓存,清空之后再打开。Redis uses the method of traversing deletion, so you need to turn off the cache first, clear it, and then turn it on. */ $needStop = $this->config->cache->driver == 'redis'; if($needStop) $this->loadModel('setting')->setItem('system.common.cache.enable', 0); - $this->mao->clear(); + $this->mao->clearCache(); if($needStop) $this->setting->setItem('system.common.cache.enable', 1); } } From 4fc58a3d0b8d177b6f4fe562b9a0828bca10ec75 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 20 Nov 2024 09:13:10 +0800 Subject: [PATCH 212/300] * [refac] fix error of calling method on null. --- lib/base/mao/mao.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index b2ab74cc54..83abdd083d 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -724,7 +724,7 @@ class baseMao */ public function clearCache() { - $this->cache->clear(); + if(!empty($this->cache)) $this->cache->clear(); } /** From 6de2f0835eb87778152501bd95fc54eb11ca3b68 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 15:51:46 +0800 Subject: [PATCH 213/300] * [uitest] add exception config. --- test/config/config.php | 1 + test/lib/ui.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/config/config.php b/test/config/config.php index 9ce7b2fc18..6f0c1253de 100644 --- a/test/config/config.php +++ b/test/config/config.php @@ -43,6 +43,7 @@ $config->uitest->chrome->options[] = '--ignore-certificate-errors'; $config->uitest->chrome->options[] = '--test-type'; $config->uitest->chrome->options[] = '--start-maximized'; +$config->uitest->setExceptionHandler = true; $config->uitest->exitBrowserOnException = true; $extConfigFiles = glob(dirname(__FILE__) . DS . 'ext' . DS . '*.php'); if($extConfigFiles) foreach($extConfigFiles as $extConfigFile) include $extConfigFile; diff --git a/test/lib/ui.php b/test/lib/ui.php index 59a7f77415..f8cde9c806 100644 --- a/test/lib/ui.php +++ b/test/lib/ui.php @@ -154,13 +154,13 @@ class tester extends result $this->cookieFile = '/tmp/cookie'; // 设置异常处理函数 - set_exception_handler(array($this, 'exceptionHandler')); + if($this->config->uitest->setExceptionHandler) set_exception_handler(array($this, 'exceptionHandler')); } /** * Exception handler. * - * @param int $e + * @param object $e * @access public * @return void */ From 7b0df7ccb743a70be8bed2f06522ab2ef0cedaa3 Mon Sep 17 00:00:00 2001 From: huairong Date: Wed, 20 Nov 2024 16:12:09 +0800 Subject: [PATCH 214/300] + [task#133029,doing,0.5h,1.5h] Add close bug function of project. --- module/project/test/lib/bug.ui.class.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/project/test/lib/bug.ui.class.php b/module/project/test/lib/bug.ui.class.php index 763c3de027..cbe2d8da95 100644 --- a/module/project/test/lib/bug.ui.class.php +++ b/module/project/test/lib/bug.ui.class.php @@ -129,4 +129,24 @@ class bugTester extends tester if($form->dom->status->getText() == '已解决') return $this->success('解决Bug成功'); return $this->failed('解决Bug失败'); } + + /** + * 关闭bug。 + * Close bug. + * + * @access public + * @return object + */ + public function closeBug() + { + $form = $this->initForm('project', 'bug', array('project' => 1), 'appIframe-project'); + $form->dom->closeBtn->click(); + $title = $form->dom->closeTitle->getText(); + $form->dom->close->click(); + $form->wait(1); + $form->dom->search(array("{$this->lang->bug->name},=,{$title}")); + $form->wait(1); + if($form->dom->status->getText() == '已关闭') return $this->success('关闭Bug成功'); + return $this->failed('关闭Bug失败'); + } } From dc95398909e4fad9639f9adc327029f7351d32dd Mon Sep 17 00:00:00 2001 From: huairong Date: Wed, 20 Nov 2024 16:15:22 +0800 Subject: [PATCH 215/300] + [task#133029,doing,0.2h,1.3h] Add element of close bug page. --- module/project/test/ui/page/bug.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/module/project/test/ui/page/bug.php b/module/project/test/ui/page/bug.php index 5159cc7a06..4dfd8093b0 100644 --- a/module/project/test/ui/page/bug.php +++ b/module/project/test/ui/page/bug.php @@ -20,8 +20,16 @@ class bugPage extends page 'assignToAdmin' => "//*[@class='popover show fade dropdown in']/menu/menu/li[1]/a/div/div", 'confirmBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[1]/div/nav/a[1]", 'firstConfirm' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[6]/div", + 'firstAssign' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[7]/div/a/span", + 'secondCheckbox' => "//*[@id='table-project-bug']/div[2]/div[1]/div/div[3]/div/div/label", + 'batchAssignTo' => "//*[@id='table-project-bug']/div[3]/nav[1]/button/span[2]", + 'assignToAdmin' => "//*[@class='popover show fade dropdown in']/menu/menu/li[1]/a/div/div", + 'confirmBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[1]/div/nav/a[1]", + 'firstConfirm' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[6]/div", 'resolveBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[2]/div/nav/a[1]/i", 'secondStatus' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[11]/div/span", + 'closeBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[3]/div/nav/a[1]/i", + 'activeBtn' => "//*[@id='table-project-bug']/div[2]/div[3]/div/div[4]/div/nav/a[1]/i", /* 指派页面 */ 'assignTo' => "//*[@id='assignedTo']/div/input", 'submitBtn' => "//*[@class='form load-indicator form-ajax no-morph form-horz']/div[4]/div/button/span", @@ -32,9 +40,13 @@ class bugPage extends page 'resolution' => "//*[@name='resolution']", 'build' => "//*[@name='resolvedBuild']", 'resolve' => "//*[@class='toolbar form-actions form-group no-label']/button/span", - 'resolveTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]", + 'resolveTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]", /* 搜索列表 */ 'status' => "//*[@id='table-project-bug']/div[2]/div[2]/div/div[3]/div/span", + 'resolveTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]", + /* 关闭页面 */ + 'close' => "//*[@class='form load-indicator form-ajax no-morph form-horz']/div[2]/div/button", + 'closeTitle' => "//*[@class='modal modal-async load-indicator modal-trans show in']/div/div/div[1]/div/div[2]/span[1]", ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); } From a6b0522ef732fcd0c2db00d501849a3d26639b1d Mon Sep 17 00:00:00 2001 From: huairong Date: Wed, 20 Nov 2024 16:17:27 +0800 Subject: [PATCH 216/300] + [task#133029,doing,0.2h,1.1h] Add comment of ui test file. --- module/project/test/ui/closebug.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 module/project/test/ui/closebug.php diff --git a/module/project/test/ui/closebug.php b/module/project/test/ui/closebug.php new file mode 100755 index 0000000000..e71f20c313 --- /dev/null +++ b/module/project/test/ui/closebug.php @@ -0,0 +1,14 @@ +#!/usr/bin/env php + Date: Wed, 20 Nov 2024 16:19:27 +0800 Subject: [PATCH 217/300] + [task#133029,doing,0.1h,1.0h] Add product testdata --- module/project/test/ui/closebug.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/module/project/test/ui/closebug.php b/module/project/test/ui/closebug.php index e71f20c313..db31054b11 100755 --- a/module/project/test/ui/closebug.php +++ b/module/project/test/ui/closebug.php @@ -12,3 +12,12 @@ cid=1 - 测试结果 @关闭Bug成功 */ + +chdir(__DIR__); +include '../lib/bug.ui.class.php'; + +$product = zenData('product'); +$product->id->range('1-2'); +$product->name->range('产品1, 产品2'); +$product->type->range('normal'); +$product->gen(2); From 4aeb1d6acf7ecc503aa4acefad2096dee33847c2 Mon Sep 17 00:00:00 2001 From: huairong Date: Wed, 20 Nov 2024 16:21:38 +0800 Subject: [PATCH 218/300] + [task#133029,doing,0.3h,0.7h] Add project and projectproduct testdata --- module/project/test/ui/closebug.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/project/test/ui/closebug.php b/module/project/test/ui/closebug.php index db31054b11..bfe8eef8c0 100755 --- a/module/project/test/ui/closebug.php +++ b/module/project/test/ui/closebug.php @@ -21,3 +21,23 @@ $product->id->range('1-2'); $product->name->range('产品1, 产品2'); $product->type->range('normal'); $product->gen(2); + +$project = zenData('project'); +$project->id->range('1'); +$project->project->range('0'); +$project->model->range('scrum'); +$project->type->range('project'); +$project->auth->range('extend'); +$project->storytype->range('`story,epic,requirement`'); +$project->path->range('`,1,`'); +$project->grade->range('1'); +$project->name->range('敏捷项目1'); +$project->hasProduct->range('1'); +$project->status->range('wait'); +$project->acl->range('open'); +$project->gen(1); + +$projectProduct = zenData('projectproduct'); +$projectProduct->project->range('1'); +$projectProduct->product->range('1{1}, 2{1}'); +$projectProduct->gen(2); From b05315e4e876a02a56d0cdd7555e74bd6df26a4a Mon Sep 17 00:00:00 2001 From: huairong Date: Wed, 20 Nov 2024 16:23:14 +0800 Subject: [PATCH 219/300] + [task#133029,doing,0.3h,0.4h] Add bug and team testdata --- module/project/test/ui/closebug.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/module/project/test/ui/closebug.php b/module/project/test/ui/closebug.php index bfe8eef8c0..4175791c15 100755 --- a/module/project/test/ui/closebug.php +++ b/module/project/test/ui/closebug.php @@ -41,3 +41,21 @@ $projectProduct = zenData('projectproduct'); $projectProduct->project->range('1'); $projectProduct->product->range('1{1}, 2{1}'); $projectProduct->gen(2); + +$bug = zenData('bug'); +$bug->id->range('1-10'); +$bug->project->range('1'); +$bug->product->range('1{2}, 2{2}'); +$bug->execution->range('0'); +$bug->title->range('Bug1, Bug2, Bug3, Bug4'); +$bug->status->range('active{2}, resolved{2}'); +$bug->assignedTo->range('[]'); +$bug->gen(4); + +$team = zendata('team'); +$team->id->range('1'); +$team->root->range('1'); +$team->type->range('project'); +$team->account->range('admin'); +$team->join->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD'); +$team->gen(1); From ba5f3c0368d2e714175c3cf991eebd148bb9e141 Mon Sep 17 00:00:00 2001 From: huairong Date: Wed, 20 Nov 2024 16:26:10 +0800 Subject: [PATCH 220/300] + [task#133029,done,0.2h] Add testcase of close bug ui test file --- module/project/test/ui/closebug.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/module/project/test/ui/closebug.php b/module/project/test/ui/closebug.php index 4175791c15..540d83d8b0 100755 --- a/module/project/test/ui/closebug.php +++ b/module/project/test/ui/closebug.php @@ -59,3 +59,10 @@ $team->type->range('project'); $team->account->range('admin'); $team->join->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD'); $team->gen(1); + +$tester = new bugTester(); +$tester->login(); + +r($tester->closeBug()) && p('status,message') && e('SUCCESS,关闭Bug成功'); + +$tester->closeBrowser(); From 6bf3e1720a33eda36a627ab0120b3a18e8dad6a7 Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:37:26 +0800 Subject: [PATCH 221/300] +[task#133129,doing,0.5h,3.5h] Add ztf notes. --- module/projectstory/test/ui/checktab.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 module/projectstory/test/ui/checktab.php diff --git a/module/projectstory/test/ui/checktab.php b/module/projectstory/test/ui/checktab.php new file mode 100755 index 0000000000..33089582f0 --- /dev/null +++ b/module/projectstory/test/ui/checktab.php @@ -0,0 +1,20 @@ +#!/usr/bin/env php + Date: Wed, 20 Nov 2024 16:38:05 +0800 Subject: [PATCH 222/300] +[task#133129,doing,1h,3h] Add ztf notes. --- module/projectstory/test/ui/checktab.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/projectstory/test/ui/checktab.php b/module/projectstory/test/ui/checktab.php index 33089582f0..1f9895fb1e 100755 --- a/module/projectstory/test/ui/checktab.php +++ b/module/projectstory/test/ui/checktab.php @@ -18,3 +18,23 @@ cid=1 - 最终测试状态 @SUCCESS - 检查评审中标签下显示条数 - 测试结果 @reviewingTab下显示条数正确 + - 最终测试状态 @SUCCESS +- 检查变更中标签下显示条数 + - 测试结果 @changingTab下显示条数正确 + - 最终测试状态 @SUCCESS +- 检查已关闭标签下显示条数 + - 测试结果 @closedTab下显示条数正确 + - 最终测试状态 @SUCCESS +- 检查已关联执行标签下显示条数 + - 测试结果 @linkedExecutionTab下显示条数正确 + - 最终测试状态 @SUCCESS +- 检查未关联执行标签下显示条数 + - 测试结果 @unlinkedExecutionTab下显示条数正确 + - 最终测试状态 @SUCCESS + +*/ + +chdir(__DIR__); +include '../lib/checktab.ui.class.php'; + +$product = zenData('product'); From 253115b750eb737997c272a32675d181b56d230b Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:38:56 +0800 Subject: [PATCH 223/300] *[task#133129,doing,1.5h,2.5h] Add project testdata. --- module/projectstory/test/ui/checktab.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/module/projectstory/test/ui/checktab.php b/module/projectstory/test/ui/checktab.php index 1f9895fb1e..a4cf03a971 100755 --- a/module/projectstory/test/ui/checktab.php +++ b/module/projectstory/test/ui/checktab.php @@ -38,3 +38,21 @@ chdir(__DIR__); include '../lib/checktab.ui.class.php'; $product = zenData('product'); +$product->id->range('1'); +$product->name->range('产品1'); +$product->type->range('normal'); +$product->gen(1); + +$project = zenData('project'); +$project->id->range('1-2'); +$project->project->range('0,1'); +$project->model->range('scrum'); +$project->type->range('project,sprint'); +$project->auth->range('extend,[]'); +$project->storytype->range('`story,epic,requirement`,`story`'); +$project->path->range('`,1,`, `,1,2,`'); +$project->grade->range('1'); +$project->name->range('项目1,执行1'); +$project->hasProduct->range('1'); +$project->status->range('wait'); +$project->acl->range('open'); From 6c0df9b0a520ca0816be0dfd81e4daa47252dd88 Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:39:50 +0800 Subject: [PATCH 224/300] *[task#133129,doing,1.5h,2.5h] Add story testdata. --- module/projectstory/test/ui/checktab.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/projectstory/test/ui/checktab.php b/module/projectstory/test/ui/checktab.php index a4cf03a971..20c8a10a06 100755 --- a/module/projectstory/test/ui/checktab.php +++ b/module/projectstory/test/ui/checktab.php @@ -56,3 +56,23 @@ $project->name->range('项目1,执行1'); $project->hasProduct->range('1'); $project->status->range('wait'); $project->acl->range('open'); +$project->gen(2); + +$projectProduct = zenData('projectproduct'); +$projectProduct->project->range('1'); +$projectProduct->product->range('1'); +$projectProduct->gen(1); + +$story = zenData('story'); +$story->id->range('1-10'); +$story->parent->range('0'); +$story->isParent->range('0'); +$story->root->range('1-10'); +$story->path->range('`,1,`, `,2,`, `,3,`, `,4,`, `,5,`, `,6,`, `,7,`, `,8,`, `,9,`, `,10,`'); +$story->grade->range('1'); +$story->product->range('1'); +$story->module->range('0'); +$story->plan->range('0'); +$story->title->range('需求001,需求002,需求003,需求004,需求005,需求006,需求007,需求008,需求009,需求010'); +$story->type->range('story'); +$story->estimate->range('0'); From f0a64a71018fdb64173f3aa72dbbe28e44963fb1 Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:40:16 +0800 Subject: [PATCH 225/300] *[task#133129,doing,2h,1.5h] Add story testdata. --- module/projectstory/test/ui/checktab.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/module/projectstory/test/ui/checktab.php b/module/projectstory/test/ui/checktab.php index 20c8a10a06..48bdad536b 100755 --- a/module/projectstory/test/ui/checktab.php +++ b/module/projectstory/test/ui/checktab.php @@ -76,3 +76,22 @@ $story->plan->range('0'); $story->title->range('需求001,需求002,需求003,需求004,需求005,需求006,需求007,需求008,需求009,需求010'); $story->type->range('story'); $story->estimate->range('0'); +$story->status->range('active{3}, closed{1}, reviewing{2}, draft{1}, changing{3}'); +$story->stage->range('projected'); +$story->assignedTo->range('[]'); +$story->version->range('1'); +$story->gen(10); + +$storySpec = zenData('storyspec'); +$storySpec->story->range('1-10'); +$storySpec->version->range('1'); +$storySpec->title->range('1-10'); +$storySpec->gen(10); + +$projectStory = zenData('projectstory'); +$projectStory->project->range('1{10},2{1}'); +$projectStory->product->range('1'); +$projectStory->branch->range('0'); +$projectStory->story->range('1-10,3'); +$projectStory->version->range('1'); +$projectStory->order->range('1{10},2{1}'); From 296e016035c1487402073259e69bc863762e3b24 Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:41:14 +0800 Subject: [PATCH 226/300] *[task#133129,doing,2h,1.5h] Add ui test file of peojectstory checktab. --- module/projectstory/test/ui/checktab.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/module/projectstory/test/ui/checktab.php b/module/projectstory/test/ui/checktab.php index 48bdad536b..a94961f53e 100755 --- a/module/projectstory/test/ui/checktab.php +++ b/module/projectstory/test/ui/checktab.php @@ -95,3 +95,19 @@ $projectStory->branch->range('0'); $projectStory->story->range('1-10,3'); $projectStory->version->range('1'); $projectStory->order->range('1{10},2{1}'); +$projectStory->gen(11); + +$tester = new checkTabTester(); +$tester->login(); + +/* 标签统计 */ +r($tester->checkTab('allTab', '10')) && p('message,status') && e('allTab下显示条数正确,SUCCESS'); //检查全部标签下显示条数 +r($tester->checkTab('unclosedTab', '9')) && p('message,status') && e('unclosedTab下显示条数正确,SUCCESS'); //检查未关闭标签下显示条数 +r($tester->checkTab('draftTab', '1')) && p('message,status') && e('draftTab下显示条数正确,SUCCESS'); //检查草稿标签下显示条数 +r($tester->checkTab('reviewingTab', '2')) && p('message,status') && e('reviewingTab下显示条数正确,SUCCESS'); //检查评审中标签下显示条数 +r($tester->checkTab('changingTab', '3')) && p('message,status') && e('changingTab下显示条数正确,SUCCESS'); //检查变更中标签下显示条数 +r($tester->checkTab('closedTab', '1')) && p('message,status') && e('closedTab下显示条数正确,SUCCESS'); //检查已关闭标签下显示条数 +r($tester->checkTab('linkedExecutionTab', '1')) && p('message,status') && e('linkedExecutionTab下显示条数正确,SUCCESS'); //检查已关联执行标签下显示条数 +r($tester->checkTab('unlinkedExecutionTab', '9')) && p('message,status') && e('unlinkedExecutionTab下显示条数正确,SUCCESS'); //检查未关联执行标签下显示条数 + +$tester->closeBrowser(); From 77be3e739cefc5e90596248e158411c3375b7f0e Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:42:32 +0800 Subject: [PATCH 227/300] +[task#133129,doing,2.5h,1.5h] Add element of peojectstory checktab. --- module/projectstory/test/ui/page/story.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100755 module/projectstory/test/ui/page/story.php diff --git a/module/projectstory/test/ui/page/story.php b/module/projectstory/test/ui/page/story.php new file mode 100755 index 0000000000..937c91de58 --- /dev/null +++ b/module/projectstory/test/ui/page/story.php @@ -0,0 +1,19 @@ + "//*[@id='main']/div/div[1]/div[1]/menu/li[1]/a/span[1]", + 'unclosedTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[2]/a/span[1]", + 'draftTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[3]/a/span[1]", + 'reviewingTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[4]/a/span[1]", + 'changingTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[5]/a/span[1]", + 'moreTab' => "//*[@id='main']/div/div[1]/div[1]/menu/li[6]/a/span[1]", + 'closedTab' => "//*[@id='more']/menu/menu/li[1]/a/div/div", + 'linkedExecutionTab' => "//*[@id='more']/menu/menu/li[2]/a/div/div", + 'unlinkedExecutionTab' => "//*[@id='more']/menu/menu/li[3]/a/div/div", + 'num' => "//*[@id='stories_table']/div/div[3]/div[2]/strong[1]" + ); From 7ca9f07f11481b3ea759c9e9c5f43310154e6786 Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:42:50 +0800 Subject: [PATCH 228/300] +[task#133129,doing,3h,1h] Add element of peojectstory checktab. --- module/projectstory/test/ui/page/story.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/module/projectstory/test/ui/page/story.php b/module/projectstory/test/ui/page/story.php index 937c91de58..65bd65aa0e 100755 --- a/module/projectstory/test/ui/page/story.php +++ b/module/projectstory/test/ui/page/story.php @@ -17,3 +17,6 @@ class storyPage extends page 'unlinkedExecutionTab' => "//*[@id='more']/menu/menu/li[3]/a/div/div", 'num' => "//*[@id='stories_table']/div/div[3]/div[2]/strong[1]" ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} From 4041a1d5205837a7ba5bce18fc77c924d777e694 Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:44:20 +0800 Subject: [PATCH 229/300] +[task#133129,doing,3.5h,0.5h] Add testui function of projectstory checktab. --- .../test/lib/checktab.ui.class.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 module/projectstory/test/lib/checktab.ui.class.php diff --git a/module/projectstory/test/lib/checktab.ui.class.php b/module/projectstory/test/lib/checktab.ui.class.php new file mode 100755 index 0000000000..ac3130d694 --- /dev/null +++ b/module/projectstory/test/lib/checktab.ui.class.php @@ -0,0 +1,20 @@ +initForm('projectstory', 'story', array('project' => '1'), 'appIframe-project'); + $tabs = array('allTab', 'unclosedTab', 'draftTab', 'reviewingTab', 'changingTab'); + if(!in_array($tab, $tabs)) $form->dom->moreTab->click(); + $form->dom->$tab->click(); + $form->wait(2); From 984f2b4a171cb9c1c89797c84fafeafcacb41d24 Mon Sep 17 00:00:00 2001 From: lihuifang Date: Wed, 20 Nov 2024 16:44:27 +0800 Subject: [PATCH 230/300] +[task#133129,doing,3.5h,0.5h] Add testui function of projectstory checktab. --- module/projectstory/test/lib/checktab.ui.class.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/module/projectstory/test/lib/checktab.ui.class.php b/module/projectstory/test/lib/checktab.ui.class.php index ac3130d694..e7bd2d8b70 100755 --- a/module/projectstory/test/lib/checktab.ui.class.php +++ b/module/projectstory/test/lib/checktab.ui.class.php @@ -18,3 +18,7 @@ class checkTabTester extends tester if(!in_array($tab, $tabs)) $form->dom->moreTab->click(); $form->dom->$tab->click(); $form->wait(2); + if($form->dom->num->getText() == $expectNum) return $this->success($tab . '下显示条数正确'); + return $this->failed($tab . '下显示条数不正确'); + } +} From b1f263c9ae36084fcd4cc38bb892aca4e19bf2ac Mon Sep 17 00:00:00 2001 From: zhaixiaojian Date: Wed, 20 Nov 2024 15:48:12 +0800 Subject: [PATCH 231/300] * [task#133126,doing,3h,1h] Add backup and delete language items --- module/action/lang/de.php | 2 ++ module/action/lang/en.php | 2 ++ module/action/lang/fr.php | 2 ++ module/action/lang/zh-cn.php | 11 ++++++----- module/instance/lang/de.php | 3 ++- module/instance/lang/en.php | 5 +++-- module/instance/lang/fr.php | 3 ++- module/instance/lang/zh-cn.php | 3 ++- 8 files changed, 21 insertions(+), 10 deletions(-) diff --git a/module/action/lang/de.php b/module/action/lang/de.php index 3dc28a5fad..46dabd7d46 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -360,6 +360,7 @@ $lang->action->desc->closeautobackup = '$date, Disable automatic backup by < $lang->action->desc->autodeletebackups = '$date, Automatically clean up backups by $actor.' . "\n"; $lang->action->desc->savebackupsettings = '$date, Save backup settings backups by $actor.' . "\n"; $lang->action->desc->deleteexpiredbackup = '$date, Clean up expired backups by $actor.' . "\n"; +$lang->action->desc->manualdeletebackup = '$date, Manually clean up backup by $actor.' . "\n"; /* Used to display dynamic information. */ $lang->action->label = new stdclass(); @@ -568,6 +569,7 @@ $lang->action->label->closeautobackup = 'Disable automatic backup'; $lang->action->label->autodeletebackups = 'Automatically clean up backups'; $lang->action->label->savebackupsettings = 'Save backup settings'; $lang->action->label->deleteexpiredbackup = 'Clean up expired backups'; +$lang->action->label->manualdeletebackup = 'Manually clean up backup'; /* Dynamic information is grouped by object. */ $lang->action->dynamicAction = new stdclass; diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 97c12d471d..d4c870c58a 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -360,6 +360,7 @@ $lang->action->desc->closeautobackup = '$date, Disable automatic backup by < $lang->action->desc->autodeletebackups = '$date, Automatically clean up backups by $actor.' . "\n"; $lang->action->desc->savebackupsettings = '$date, Save backup settings backups by $actor.' . "\n"; $lang->action->desc->deleteexpiredbackup = '$date, Clean up expired backups by $actor.' . "\n"; +$lang->action->desc->manualdeletebackup = '$date, Manually clean up backup by $actor.' . "\n"; /* Used to display dynamic information. */ $lang->action->label = new stdclass(); @@ -568,6 +569,7 @@ $lang->action->label->closeautobackup = 'Disable automatic backup'; $lang->action->label->autodeletebackups = 'Automatically clean up backups'; $lang->action->label->savebackupsettings = 'Save backup settings'; $lang->action->label->deleteexpiredbackup = 'Clean up expired backups'; +$lang->action->label->manualdeletebackup = 'Manually clean up backup'; /* Dynamic information is grouped by object. */ $lang->action->dynamicAction = new stdclass; diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index bb1421e968..5f6634ce89 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -360,6 +360,7 @@ $lang->action->desc->closeautobackup = '$date, Disable automatic backup by < $lang->action->desc->autodeletebackups = '$date, Automatically clean up backups by $actor.' . "\n"; $lang->action->desc->savebackupsettings = '$date, Save backup settings backups by $actor.' . "\n"; $lang->action->desc->deleteexpiredbackup = '$date, Clean up expired backups by $actor.' . "\n"; +$lang->action->desc->manualdeletebackup = '$date, Manually clean up backup by $actor.' . "\n"; /* Used to display dynamic information. */ $lang->action->label = new stdclass(); @@ -568,6 +569,7 @@ $lang->action->label->closeautobackup = 'Disable automatic backup'; $lang->action->label->autodeletebackups = 'Automatically clean up backups'; $lang->action->label->savebackupsettings = 'Save backup settings'; $lang->action->label->deleteexpiredbackup = 'Clean up expired backups'; +$lang->action->label->manualdeletebackup = 'Manually clean up backup'; /* Dynamic information is grouped by object. */ $lang->action->dynamicAction = new stdclass(); diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 2454d5a0e0..72fc59fdf1 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -357,9 +357,10 @@ $lang->action->desc->manualbackup = '$date, 由 $actor $lang->action->desc->autobackup = '$date, 由 $actor 自动备份。' . "\n"; $lang->action->desc->openautobackup = '$date, 由 $actor 开启自动备份。' . "\n"; $lang->action->desc->closeautobackup = '$date, 由 $actor 关闭自动备份。' . "\n"; -$lang->action->desc->autodeletebackups = '$date, 由 $actor 自动清理备份。' . "\n"; +$lang->action->desc->autodeletebackups = '$date, 由 $actor 自动删除备份。' . "\n"; $lang->action->desc->savebackupsettings = '$date, 由 $actor 保存备份设置。' . "\n"; -$lang->action->desc->deleteexpiredbackup = '$date, 由 $actor 清理过期备份。' . "\n"; +$lang->action->desc->deleteexpiredbackup = '$date, 由 $actor 删除过期备份。' . "\n"; +$lang->action->desc->manualdeletebackup = '$date, 由 $actor 手动删除备份。' . "\n"; /* 用来显示动态信息。*/ $lang->action->label = new stdclass(); @@ -565,10 +566,10 @@ $lang->action->label->manualbackup = '手动备份了'; $lang->action->label->autobackup = '自动备份了'; $lang->action->label->openautobackup = '开启自动备份'; $lang->action->label->closeautobackup = '关闭自动备份'; -$lang->action->label->autodeletebackups = '自动清理备份'; +$lang->action->label->autodeletebackups = '自动删除备份'; $lang->action->label->savebackupsettings = '保存备份设置'; -$lang->action->label->deleteexpiredbackup = '清理过期备份'; - +$lang->action->label->deleteexpiredbackup = '删除过期备份'; +$lang->action->label->manualdeletebackup = '手动删除备份'; /* 动态信息按照对象分组 */ diff --git a/module/instance/lang/de.php b/module/instance/lang/de.php index ebd34a6ae0..fa85304569 100644 --- a/module/instance/lang/de.php +++ b/module/instance/lang/de.php @@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = 'The system will only delete expi $lang->instance->backup->backupSize = 'Size'; $lang->instance->backup->confirmTip = 'Are you sure you want to back up?'; $lang->instance->backup->cronRemark = 'DevOps service automatic backup task'; -$lang->instance->backup->backupSettingsTips = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.'; +$lang->instance->backup->backupSettingsTip = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.'; +$lang->instance->backup->confirmDeleteTip = 'Are you sure you want to delete the backup?'; $lang->instance->backup->cycleList[1] = 'Daily'; diff --git a/module/instance/lang/en.php b/module/instance/lang/en.php index ebd34a6ae0..fc4815035c 100644 --- a/module/instance/lang/en.php +++ b/module/instance/lang/en.php @@ -117,7 +117,7 @@ $lang->instance->backup->volSpentSeconds = 'Time taken (seconds)'; $lang->instance->backup->volSize = 'Size'; $lang->instance->backup->lastRestore = 'Last rollback'; $lang->instance->backup->restoreDate = 'Restore time'; -$lang->instance->backup->latestBackupAt = 'Last backup time'; +$lang->instance->backup->latestBackupAt = 'Last backup time'; $lang->instance->backup->backupBeforeRestore = 'We recommend that you backup before rolling back!'; $lang->instance->backup->enableAutoBackup = 'Enable automatic backup'; $lang->instance->backup->autoBackup = 'Automatic backup'; @@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = 'The system will only delete expi $lang->instance->backup->backupSize = 'Size'; $lang->instance->backup->confirmTip = 'Are you sure you want to back up?'; $lang->instance->backup->cronRemark = 'DevOps service automatic backup task'; -$lang->instance->backup->backupSettingsTips = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.'; +$lang->instance->backup->backupSettingsTip = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.'; +$lang->instance->backup->confirmDeleteTip = 'Are you sure you want to delete the backup?'; $lang->instance->backup->cycleList[1] = 'Daily'; diff --git a/module/instance/lang/fr.php b/module/instance/lang/fr.php index ebd34a6ae0..fa85304569 100644 --- a/module/instance/lang/fr.php +++ b/module/instance/lang/fr.php @@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = 'The system will only delete expi $lang->instance->backup->backupSize = 'Size'; $lang->instance->backup->confirmTip = 'Are you sure you want to back up?'; $lang->instance->backup->cronRemark = 'DevOps service automatic backup task'; -$lang->instance->backup->backupSettingsTips = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.'; +$lang->instance->backup->backupSettingsTip = 'The system will delete backups that exceed the set number of days, but will retain at least one valid backup.'; +$lang->instance->backup->confirmDeleteTip = 'Are you sure you want to delete the backup?'; $lang->instance->backup->cycleList[1] = 'Daily'; diff --git a/module/instance/lang/zh-cn.php b/module/instance/lang/zh-cn.php index e40e9d8865..fdcad8fde8 100644 --- a/module/instance/lang/zh-cn.php +++ b/module/instance/lang/zh-cn.php @@ -132,7 +132,8 @@ $lang->instance->backup->keepBackupBySystem = '备份数据超过1条时系统 $lang->instance->backup->backupSize = '大小'; $lang->instance->backup->confirmTip = '确认要备份吗?'; $lang->instance->backup->cronRemark = 'Devops服务自动备份任务'; -$lang->instance->backup->backupSettingsTips = '系统会删除超过设置天数的备份,但会保留至少一个有效备份。'; +$lang->instance->backup->backupSettingsTip = '系统会删除超过设置天数的备份,但会保留至少一个有效备份。'; +$lang->instance->backup->confirmDeleteTip = '确认要删除备份吗?'; $lang->instance->backup->cycleList[1] = '每日'; From 10daf60fccfdda24372d7d491fb2cb3f1d1b3dc4 Mon Sep 17 00:00:00 2001 From: zhaixiaojian Date: Wed, 20 Nov 2024 15:49:23 +0800 Subject: [PATCH 232/300] * [task#133126,doing,3h,1h] Add backup and deletion function. --- module/instance/config/dtable.php | 8 +++++++- module/instance/control.php | 20 ++++++++++++++++++++ module/instance/model.php | 13 +++++++++++++ module/instance/ui/backupsettings.html.php | 2 +- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/module/instance/config/dtable.php b/module/instance/config/dtable.php index ac793d7c71..fff4d9edac 100644 --- a/module/instance/config/dtable.php +++ b/module/instance/config/dtable.php @@ -50,10 +50,16 @@ if(!isset($config->backup)) $config->backup->dtable->fieldList['actions']['type'] = 'actions'; $config->backup->dtable->fieldList['actions']['width'] = '100'; - $config->backup->dtable->fieldList['actions']['menu'] = array('restore'); + $config->backup->dtable->fieldList['actions']['menu'] = array('restore', 'delete'); $config->backup->dtable->fieldList['actions']['list']['restore']['icon'] = 'icon-restart'; $config->backup->dtable->fieldList['actions']['list']['restore']['hint'] = $lang->instance->restore->common; $config->backup->dtable->fieldList['actions']['list']['restore']['url'] = array('module' => 'instance', 'method' => 'ajaxRestore', 'params' => 'instanceID={instanceId}&backupName={name}'); $config->backup->dtable->fieldList['actions']['list']['restore']['className'] = 'ajax-submit'; + + $config->backup->dtable->fieldList['actions']['list']['delete']['icon'] = 'icon-trash'; + $config->backup->dtable->fieldList['actions']['list']['delete']['hint'] = $lang->instance->backup->delete; + $config->backup->dtable->fieldList['actions']['list']['delete']['url'] = array('module' => 'instance', 'method' => 'ajaxDeleteBackup', 'params' => 'instanceID={instanceId}&backupName={name}'); + $config->backup->dtable->fieldList['actions']['list']['delete']['className'] = 'ajax-submit'; + $config->backup->dtable->fieldList['actions']['list']['delete']['data-confirm'] = array('message' => $lang->instance->backup->confirmDeleteTip, 'icon' => 'icon-exclamation-sign', 'iconClass' => 'warning-pale rounded-full icon-2x'); } diff --git a/module/instance/control.php b/module/instance/control.php index db51cbd2cc..4f698f65a0 100644 --- a/module/instance/control.php +++ b/module/instance/control.php @@ -652,4 +652,24 @@ class instance extends control } return $this->send(array('result' => 'success', 'message' => zget($this->lang->instance->notices, 'backupSuccess'))); } + + /** + * Delete backup by ajax. + * 删除备份。 + * @param int $backupID + * @access public + * @return void + */ + public function ajaxDeleteBackup(string $instanceID, string $backupName) + { + $instance = $this->instance->getByID((int)$instanceID); + if(empty($instance)) $this->send(array('result' => 'success', 'message' => $this->lang->instance->instanceNotExists)); + + $success = $this->instance->deleteBackup($instance, $backupName); + if(!$success) return $this->send(array('result' => 'fail', 'message' => zget($this->lang->instance->notices, 'deleteFail'))); + + $this->action->create('instance', $instance->id, 'manualdeletebackup', '', json_encode(array('result' => 'success'))); + $locate = $this->createLink('instance', 'view', 'id=' . $instanceID); + return $this->send(array('result' => 'success', 'load' => array('alert' => zget($this->lang->instance->notices, 'deleteSuccess'), 'locate' => $locate, 'closeModal' => true))); + } } diff --git a/module/instance/model.php b/module/instance/model.php index 2e583a843f..f6199d9d8e 100644 --- a/module/instance/model.php +++ b/module/instance/model.php @@ -1379,4 +1379,17 @@ class instanceModel extends model if(count($deleteData) > 0) $this->action->create('instance', $instance->id, 'deleteexpiredbackup', '', json_encode(array('result' => 'success', 'data' =>$deleteData))); return true; } + + /** + * Delete backup. + * 删除备份。 + * @param object $instance + * @param string $backupName + * @access public + * @return bool + */ + public function deleteBackup($instance, $backupName) + { + return $this->cne->deleteBackup($instance, $backupName); + } } diff --git a/module/instance/ui/backupsettings.html.php b/module/instance/ui/backupsettings.html.php index 50d5ab52e0..428aeb0dd5 100644 --- a/module/instance/ui/backupsettings.html.php +++ b/module/instance/ui/backupsettings.html.php @@ -28,7 +28,7 @@ formPanel set::label($this->lang->instance->backup->keepDays), set::control('input'), set::value((int)$instance->backupKeepDays), - span($this->lang->instance->backup->backupSettingsTips, set::className('text-warning inline-block mt-2')) + span($this->lang->instance->backup->backupSettingsTip, set::className('text-warning inline-block mt-2')) ), ), formRow From 1936340faa4856fdcab9159a7eca86140dd7fe7a Mon Sep 17 00:00:00 2001 From: lijie Date: Wed, 20 Nov 2024 14:57:47 +0800 Subject: [PATCH 233/300] [task#133035, 0.5h] add epic view elment xpath. --- module/epic/test/ui/page/view.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 module/epic/test/ui/page/view.php diff --git a/module/epic/test/ui/page/view.php b/module/epic/test/ui/page/view.php new file mode 100755 index 0000000000..35f2a8cbb3 --- /dev/null +++ b/module/epic/test/ui/page/view.php @@ -0,0 +1,16 @@ + "//*[@class='tab-content']/div/div/div[6]/div[2]/span", + /*激活弹窗的激活按钮*/ + 'activate' => "//*[@type='submit']" + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} From fa29af322df1ebdb0c40f438e974c8e48c2a824c Mon Sep 17 00:00:00 2001 From: lijie Date: Wed, 20 Nov 2024 15:01:46 +0800 Subject: [PATCH 234/300] [task#133035, 0.5h] add activete epic ui function. --- .../story/test/lib/activatestory.ui.class.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/module/story/test/lib/activatestory.ui.class.php b/module/story/test/lib/activatestory.ui.class.php index bd4db7ff27..382abc45f2 100644 --- a/module/story/test/lib/activatestory.ui.class.php +++ b/module/story/test/lib/activatestory.ui.class.php @@ -33,6 +33,25 @@ class activateStoryTester extends tester $viewPage = $this->loadPage('story', 'view'); //进入需求详情页查看状态是否与关闭前一致 if($viewPage->dom->status->getText() != $status) return $this->failed('激活需求后状态不正确'); - return $this->success('激活需求成功'); + return $this->success('激活研发需求成功'); + } + * @param string $storyID + * @access public + * @return object + */ + public function activateEpic($storyID, $status) + { + $form = $this->initform('epic', 'view', array('id' => $storyID), 'appIframe-product'); //进入业务需求详情页 + $form->dom->btn($this->lang->story->activate)->click(); //点击激活需求按钮 + $form->wait(1); + + $form->dom->assignedTo->picker('admin'); //选择指派人 + $form->dom->activate->click(); //点击激活按钮 + $form->wait(1); + + $viewPage = $this->loadPage('epic', 'view'); //进入需求详情页查看状态是否与关闭前一致 + if($viewPage->dom->status->getText() != $status) return $this->failed('激活需求后状态不正确'); + + return $this->success('激活业务需求成功'); } } From 1c4cd584a6e0b7e72f720cbec59dd7c2063e196a Mon Sep 17 00:00:00 2001 From: lijie Date: Wed, 20 Nov 2024 15:05:31 +0800 Subject: [PATCH 235/300] [task#133035, 0.1h] modify activate epic function. --- module/story/test/lib/activatestory.ui.class.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/module/story/test/lib/activatestory.ui.class.php b/module/story/test/lib/activatestory.ui.class.php index 382abc45f2..76339d2333 100644 --- a/module/story/test/lib/activatestory.ui.class.php +++ b/module/story/test/lib/activatestory.ui.class.php @@ -35,6 +35,9 @@ class activateStoryTester extends tester return $this->success('激活研发需求成功'); } + + /** + * check the information after activate an epic * @param string $storyID * @access public * @return object From 1460e2798f9a35460e7f810cbc423dac6a230e2f Mon Sep 17 00:00:00 2001 From: lijie Date: Wed, 20 Nov 2024 15:07:57 +0800 Subject: [PATCH 236/300] [task#133035, 0.5h] add activate epic ui steps. --- module/story/test/ui/activatestory.php | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/module/story/test/ui/activatestory.php b/module/story/test/ui/activatestory.php index e58c26b455..83b0830c97 100755 --- a/module/story/test/ui/activatestory.php +++ b/module/story/test/ui/activatestory.php @@ -24,42 +24,44 @@ $product->vision->range('rnd'); $product->gen(1); $story = zenData('story'); -$story->id->range('1-2'); -$story->root->range('1-2'); -$story->path->range('`,1,`, `,2,`'); +$story->id->range('1-6'); +$story->root->range('1-6'); +$story->path->range('`,1,`, `,2,`, `,3,`, `,4,`, `,5,`, `,6,`'); $story->grade->range('1'); $story->product->range('1'); $story->module->range('0'); -$story->title->range('激活研发需求, 草稿研发需求'); -$story->type->range('story'); +$story->title->range('激活研发需求, 草稿研发需求, 激活用户需求, 草稿用户需求, 激活业务需求, 草稿业务需求'); +$story->type->range('story{2}, requirement{2}, epic{2}'); $story->stage->range('closed'); $story->status->range('closed'); $story->openedBy->range('admin'); $story->version->range('1'); -$story->gen(2); +$story->gen(6); $storyspec = zenData('storyspec'); -$storyspec->story->range('1-2'); +$storyspec->story->range('1-6'); $storyspec->version->range('1'); -$storyspec->title->range('激活研发需求, 草稿研发需求'); -$storyspec->gen(2); +$storyspec->title->range('激活研发需求, 草稿研发需求, 激活用户需求, 草稿用户需求, 激活业务需求, 草稿业务需求'); +$storyspec->gen(6); $action = zenData('action'); -$action->id->range('1-2'); +$action->id->range('1-6'); $action->objectType->range('story'); -$action->objectID->range('1-2'); +$action->objectID->range('1-6'); $action->product->range('`,1`'); $action->actor->range('admin'); $action->action->range('closed'); $action->date->range('(-2D)-(-D):60m')->type('timestamp')->format('YY/MM/DD hh:mm:ss'); $action->extra->range('Done|active, Done|draft'); -$action->gen(2); +$action->gen(6); $tester = new activateStoryTester(); $tester->login(); $stutus = array('激活', '草稿'); -r($tester->activateStory(1, $stutus[0])) && p('message') && e('激活需求成功'); -r($tester->activateStory(2, $stutus[1])) && p('message') && e('激活需求成功'); +r($tester->activateStory(1, $stutus[0])) && p('message') && e('激活研发需求成功'); +r($tester->activateStory(2, $stutus[1])) && p('message') && e('激活研发需求成功'); +r($tester->activateEpic(5, $stutus[0])) && p('message') && e('激活业务需求成功'); +r($tester->activateEpic(6, $stutus[1])) && p('message') && e('激活业务需求成功'); $tester->closeBrowser(); From a24eb7e58c437158ae4e5614821b79e2f8b79229 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 16:48:58 +0800 Subject: [PATCH 237/300] * [task#132857] add create app action. --- module/action/config.php | 1 + module/action/lang/de.php | 2 +- module/action/lang/en.php | 2 +- module/action/lang/fr.php | 2 +- module/action/lang/zh-cn.php | 5 +++-- module/action/model.php | 2 ++ 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/module/action/config.php b/module/action/config.php index 5eedf8abf1..7c10b45013 100755 --- a/module/action/config.php +++ b/module/action/config.php @@ -70,6 +70,7 @@ $config->action->objectNameFields['solution'] = 'name'; $config->action->objectNameFields['prompt'] = 'name'; $config->action->objectNameFields['miniprogram'] = 'name'; $config->action->objectNameFields['holiday'] = 'name'; +$config->action->objectNameFields['system'] = 'name'; $config->action->commonImgSize = 870; diff --git a/module/action/lang/de.php b/module/action/lang/de.php index 46dabd7d46..58c9456f37 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -561,7 +561,7 @@ $lang->action->label->createbackup = 'Created system backup'; $lang->action->label->deletebackup = 'Deleted system backup'; $lang->action->label->restorebackup = 'Restored system backup'; $lang->action->label->upgradesystem = 'Executed system upgrade'; -$lang->action->label->system = ''; +$lang->action->label->system = 'Application'; $lang->action->label->manualbackup = 'Manual backup'; $lang->action->label->autobackup = 'Automatic backup'; $lang->action->label->openautobackup = 'Enable automatic backup'; diff --git a/module/action/lang/en.php b/module/action/lang/en.php index d4c870c58a..1fef3262c8 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -561,7 +561,7 @@ $lang->action->label->createbackup = 'Created system backup'; $lang->action->label->deletebackup = 'Deleted system backup'; $lang->action->label->restorebackup = 'Restored system backup'; $lang->action->label->upgradesystem = 'Executed system upgrade'; -$lang->action->label->system = ''; +$lang->action->label->system = 'Application'; $lang->action->label->manualbackup = 'Manual backup'; $lang->action->label->autobackup = 'Automatic backup'; $lang->action->label->openautobackup = 'Enable automatic backup'; diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index 5f6634ce89..8371f5b53c 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -561,7 +561,7 @@ $lang->action->label->createbackup = 'Created system backup'; $lang->action->label->deletebackup = 'Deleted system backup'; $lang->action->label->restorebackup = 'Restored system backup'; $lang->action->label->upgradesystem = 'Executed system upgrade'; -$lang->action->label->system = ''; +$lang->action->label->system = 'Application'; $lang->action->label->manualbackup = 'Manual backup'; $lang->action->label->autobackup = 'Automatic backup'; $lang->action->label->openautobackup = 'Enable automatic backup'; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 72fc59fdf1..4dbd978234 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -188,6 +188,7 @@ $lang->action->objectTypes['projectbuild'] = '版本'; $lang->action->objectTypes['board'] = '白板'; $lang->action->objectTypes['boardspace'] = '白板空间'; $lang->action->objectTypes['productline'] = '产品线'; +$lang->action->objectTypes['system'] = '应用'; /* 用来描述操作历史记录。*/ $lang->action->desc = new stdclass(); @@ -561,7 +562,7 @@ $lang->action->label->createbackup = '创建了系统备份'; $lang->action->label->deletebackup = '删除了系统备份'; $lang->action->label->restorebackup = '还原了系统备份'; $lang->action->label->upgradesystem = '执行了系统升级'; -$lang->action->label->system = ''; +$lang->action->label->system = '应用'; $lang->action->label->manualbackup = '手动备份了'; $lang->action->label->autobackup = '自动备份了'; $lang->action->label->openautobackup = '开启自动备份'; @@ -924,7 +925,7 @@ $lang->action->label->chartgroup = '分组'; $lang->action->label->serverroom = '机房|serverroom|browse|'; $lang->action->label->host = '主机|host|view|id=%s'; $lang->action->label->account = "账号|account|view|id=%s"; -$lang->action->label->instance = '应用|instance|view|id=%s'; +$lang->action->label->instance = '服务|instance|view|id=%s'; $lang->action->label->prompt = '提词|ai|promptview|id=%s'; $lang->action->label->miniprogram = '小程序|aiapp|browseminiprogram|id=%s'; $lang->action->label->holiday = '节假日|holiday|browse|'; diff --git a/module/action/model.php b/module/action/model.php index 3fa929fe38..5c0e8b9023 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -1200,6 +1200,8 @@ class actionModel extends model $actionObjectLabel = $this->lang->doc->menuTitle; } } + + if($objectType == 'system' && strpos(strtolower($actionType), 'backup') !== false) $actionObjectLabel = ''; } if(in_array($this->config->edition, array('max', 'ipd')) && $objectType == 'assetlib') From 085dc78efeb0182370e2a67934cbe3e2ac941e02 Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 07:29:14 +0000 Subject: [PATCH 238/300] * [story#69519,0.5h] add lang. --- module/pivot/lang/de.php | 1 + module/pivot/lang/en.php | 1 + module/pivot/lang/fr.php | 1 + module/pivot/lang/zh-cn.php | 1 + 4 files changed, 4 insertions(+) diff --git a/module/pivot/lang/de.php b/module/pivot/lang/de.php index 7ee11f89b3..1224b80162 100644 --- a/module/pivot/lang/de.php +++ b/module/pivot/lang/de.php @@ -26,6 +26,7 @@ $lang->pivot->exportRange = 'Export Range'; $lang->pivot->story = 'Story'; $lang->pivot->clear = 'Clear'; $lang->pivot->keep = 'Keep'; +$lang->pivot->new = 'New'; $lang->pivot->accessDenied = 'You do not have access to this pivot'; $lang->pivot->acl = 'Access Control'; diff --git a/module/pivot/lang/en.php b/module/pivot/lang/en.php index 02fcf9cf61..3debc727f3 100644 --- a/module/pivot/lang/en.php +++ b/module/pivot/lang/en.php @@ -26,6 +26,7 @@ $lang->pivot->exportRange = 'Export Range'; $lang->pivot->story = 'Story'; $lang->pivot->clear = 'Clear'; $lang->pivot->keep = 'Keep'; +$lang->pivot->new = 'New'; $lang->pivot->accessDenied = 'You do not have access to this pivot'; $lang->pivot->acl = 'Access Control'; diff --git a/module/pivot/lang/fr.php b/module/pivot/lang/fr.php index b8014fd9e3..e4248a4dbf 100644 --- a/module/pivot/lang/fr.php +++ b/module/pivot/lang/fr.php @@ -26,6 +26,7 @@ $lang->pivot->exportRange = 'Export Range'; $lang->pivot->story = 'Story'; $lang->pivot->clear = 'Clear'; $lang->pivot->keep = 'Keep'; +$lang->pivot->new = 'New'; $lang->pivot->accessDenied = 'You do not have access to this pivot'; $lang->pivot->acl = 'Access Control'; diff --git a/module/pivot/lang/zh-cn.php b/module/pivot/lang/zh-cn.php index b796089257..627f924793 100644 --- a/module/pivot/lang/zh-cn.php +++ b/module/pivot/lang/zh-cn.php @@ -26,6 +26,7 @@ $lang->pivot->exportRange = '导出范围'; $lang->pivot->story = '需求'; $lang->pivot->clear = '清空'; $lang->pivot->keep = '保留设计'; +$lang->pivot->new = '新'; $lang->pivot->accessDenied = '您无权访问该透视表'; $lang->pivot->acl = '访问控制'; From 1599cc2730d40f74c42420031913683f9ca5c1a9 Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 08:42:05 +0000 Subject: [PATCH 239/300] * [story#69519,0.5h] add getAccountFirstAction. --- module/action/model.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/module/action/model.php b/module/action/model.php index 5c0e8b9023..61ce919f6e 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -366,6 +366,19 @@ class actionModel extends model return $action; } + /** + * 获取用户第一条操作。 + * Get user first action. + * + * @param string $account + * @access public + * @return object + */ + public function getAccountFirstAction(string $account): object + { + return $this->dao->select('*')->from(TABLE_ACTION)->where('actor')->eq($account)->orderBy('id')->limit(1)->fetch(); + } + /** * 获取已删除的对象。 * Get deleted objects. From 81fb4e6b63c8e0559f383ca8a3b6e18b245a4fd3 Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 08:43:08 +0000 Subject: [PATCH 240/300] * [story#69519,0.5h] add mark. --- module/mark/model.php | 85 +++++++++++++++++++++++++++++++++++++++++++ module/pivot/zen.php | 18 +++++++++ 2 files changed, 103 insertions(+) create mode 100644 module/mark/model.php diff --git a/module/mark/model.php b/module/mark/model.php new file mode 100644 index 0000000000..be99d0b00f --- /dev/null +++ b/module/mark/model.php @@ -0,0 +1,85 @@ + + * @package mail + * @link https://www.zentao.net + */ +?> +dao->select('objectID, version')->from(TABLE_MARK) + ->where('objectType')->eq($objectType) + ->andWhere('objectID')->in($objectIDs) + ->beginIF($version != 'all')->andWhere('version')->eq($version)->fi() + ->andWhere('account')->eq($this->app->user->account) + ->andWhere('mark')->eq($mark) + ->fetchAll(); + } + + public function getMarks(array $objects, string $objectType, string $mark): array + { + $objectIDs = array_column($objects, 'id'); + $marks = $this->getNeededMarks($objectIDs, $objectType, 'all', $mark); + + foreach($objects as $object) + { + $objectMarks = array_filter($marks, function($mark) use($object) { + return $mark->objectID == $object->id && $mark->version == $object->version; + }); + + $object->mark = !empty($objectMarks); + } + + return $objects; + } + + /** + * 设置对象的标记。 + * Set object marks. + * + * @param array $objectIDs + * @param string $objectType + * @param string $version + * @param string $mark + * @param string $extra + * @access public + * @return bool + */ + public function setMark(array $objectIDs, string $objectType, string $version, string $mark, string $extra = ''): bool + { + $data = new stdclass(); + $data->objectType = $objectType; + $data->version = $version; + $data->account = $this->app->user->account; + $data->mark = $mark; + $data->extra = $extra; + $data->date = helper::now(); + + foreach($objectIDs as $objectID) + { + $data->objectID = $objectID; + $this->dao->insert(TABLE_MARK)->data($data)->autocheck()->exec(); + } + + return dao::isError(); + } +} diff --git a/module/pivot/zen.php b/module/pivot/zen.php index 5ede438925..db7b1ca587 100644 --- a/module/pivot/zen.php +++ b/module/pivot/zen.php @@ -80,6 +80,7 @@ class pivotZen extends pivot $groups = $this->pivot->getGroupsByDimensionAndPath($dimensionID, $currentGroup->path); if(!$groups) return array(); + $firstAction = $this->loadModel('action')->getAccountFirstAction($this->app->user->account); $menus = array(); foreach($groups as $group) { @@ -87,6 +88,7 @@ class pivotZen extends pivot $pivots = $this->pivot->getAllPivotByGroupID($group->id); $pivots = $this->pivot->filterInvisiblePivot($pivots); + $pivots = $this->loadModel('mark')->getMarks($pivots, 'pivot', 'view'); if(empty($pivots)) continue; if($group->grade > 1) $menus[] = (object)array('id' => $group->id, 'parent' => 0, 'name' => $group->name); @@ -95,6 +97,7 @@ class pivotZen extends pivot foreach($pivots as $pivot) { + $this->setNewMark($pivot, $firstAction); $params = helper::safe64Encode("groupID={$group->id}&pivotID={$pivot->id}"); $url = inlink('preview', "dimension={$dimensionID}&group={$currentGroup->id}&method=show¶ms={$params}"); $menus[] = (object)array('id' => $group->id . '_' . $pivot->id, 'parent' => $group->grade > 1 ? $group->id : 0, 'name' => $pivot->name, 'url' => $url); @@ -108,6 +111,21 @@ class pivotZen extends pivot return array_merge($menus, $builtinMenus); } + /** + * 设置 “新” 标签。 + * Set new mark. + * + * @param object $pivot + * @param object $firstAction + * @access protected + * @return void + */ + protected function setNewMark(object $pivot, object $firstAction): void + { + if(!$pivot->mark && $pivot->createdDate < $firstAction->date) $pivot->mark = true; + if(!$pivot->mark) $pivot->name = array('html' => $pivot->name . ' ' . $this->lang->pivot->new . ''); + } + /** * 在第一个维度上显示内置透视表。 * Display the built-in pivots in the first dimension. From c2c61447f5f6b801c81d9ca2d714c3b59c1105e1 Mon Sep 17 00:00:00 2001 From: qixinzhi Date: Wed, 20 Nov 2024 08:46:08 +0000 Subject: [PATCH 241/300] * [story#69519,0.5h] fix sonar. --- module/mark/model.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/mark/model.php b/module/mark/model.php index be99d0b00f..d70dd22e00 100644 --- a/module/mark/model.php +++ b/module/mark/model.php @@ -42,7 +42,8 @@ class markModel extends model foreach($objects as $object) { - $objectMarks = array_filter($marks, function($mark) use($object) { + $objectMarks = array_filter($marks, function($mark) use($object) + { return $mark->objectID == $object->id && $mark->version == $object->version; }); From cf25c23a770e26e3d522b09dd4327f1623c3d594 Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 17:07:39 +0800 Subject: [PATCH 242/300] * [task#132857] fix get pairs method. --- module/system/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/system/model.php b/module/system/model.php index f34d064005..3cbb1c8d06 100644 --- a/module/system/model.php +++ b/module/system/model.php @@ -43,7 +43,7 @@ class systemModel extends model { return $this->dao->select('id, name')->from(TABLE_SYSTEM) ->where('deleted')->eq('0') - ->beginIF($integrated)->andWhere('integrated')->eq($integrated)->fi() + ->beginIF($integrated !== '')->andWhere('integrated')->eq($integrated)->fi() ->fetchPairs('id', 'name'); } From 3dd510175424488f21a8411d97e7728cf4d689fd Mon Sep 17 00:00:00 2001 From: liyang Date: Wed, 20 Nov 2024 17:07:59 +0800 Subject: [PATCH 243/300] * [task#132857] fix get pairs method. --- module/release/ui/browse.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/release/ui/browse.html.php b/module/release/ui/browse.html.php index 677acb8983..40c4806dc1 100644 --- a/module/release/ui/browse.html.php +++ b/module/release/ui/browse.html.php @@ -24,7 +24,7 @@ featureBar $canCreateRelease = hasPriv('release', 'create') && common::canModify('product', $product); $canManageSystem = hasPriv('system', 'browse') && common::canModify('product', $product); if($canCreateRelease) $createItem = array('icon' => 'plus', 'class' => 'primary', 'text' => $lang->release->create, 'url' => $this->createLink('release', 'create', "productID={$product->id}&branch={$branch}")); -if($canManageSystem) $manageSystemItem = array('icon' => 'plus', 'class' => 'primary', 'text' => $lang->release->manageSystem, 'url' => $this->createLink('system', 'browse', "productID={$product->id}&branch={$branch}"), 'data-app' => 'product'); +if($canManageSystem) $manageSystemItem = array('class' => 'ghost', 'text' => $lang->release->manageSystem, 'url' => $this->createLink('system', 'browse', "productID={$product->id}&branch={$branch}"), 'data-app' => 'product'); toolbar ( !empty($manageSystemItem) ? item(set($manageSystemItem)) : null, From afb6c836438b3fde5a5bc369809fbe0e4938e5d5 Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Wed, 20 Nov 2024 17:15:25 +0800 Subject: [PATCH 244/300] * [Ticket#1594,done,1h,0h] If the parent task has no associated requirements, then display the sub-tasks. --- module/product/control.php | 9 +++++++++ module/product/js/track.ui.js | 2 +- module/product/ui/track.html.php | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/module/product/control.php b/module/product/control.php index 5fc357c5fb..89f4cc9f0a 100755 --- a/module/product/control.php +++ b/module/product/control.php @@ -843,6 +843,14 @@ class product extends control $tracks['cols'] = $cols; } + $tasks = array(); + foreach($tracks['items'] as $lane) + { + $taskIdList = array_column($lane['task'], 'id'); + $taskList = array_combine($taskIdList, $lane['task']); + $tasks += $taskList; + } + /* Build search form. */ $this->productZen->buildSearchFormForTrack($productID, $branch, $projectID, $browseType, $param, $storyType); @@ -862,6 +870,7 @@ class product extends control $this->view->storyTypeList = $storyTypeList; $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted'); $this->view->projectProducts = $this->product->getProductPairsByProject($projectID); + $this->view->tasks = $tasks; $this->display(); } diff --git a/module/product/js/track.ui.js b/module/product/js/track.ui.js index 12712924ad..da0809ba2b 100644 --- a/module/product/js/track.ui.js +++ b/module/product/js/track.ui.js @@ -103,7 +103,7 @@ window.itemRender = function(info) const col = info.col; if(col == 'task') { - if(info.item.parent > '0') info.item.className.push('hidden childTask parent-' + info.item.parent); + if(info.item.parent > '0' && tasks[info.item.parent] !== undefined) info.item.className.push('hidden childTask parent-' + info.item.parent); if(info.item.parent == '-1') info.item.className.push('parentTask'); } diff --git a/module/product/ui/track.html.php b/module/product/ui/track.html.php index f88bf15021..7203b671cd 100644 --- a/module/product/ui/track.html.php +++ b/module/product/ui/track.html.php @@ -118,6 +118,7 @@ jsVar('orderByTitle', $orderByTitle); jsVar('storyType', $storyType); jsVar('users', $users); jsVar('privs', $privs); +jsVar('tasks', $tasks); empty($tracks) ? div(setClass('dtable-empty-tip bg-white shadow'), span(setClass('text-gray'), $lang->noData)) : div ( From f84e1170a087fa4b47cefff593dccd19b2d3199a Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 20 Nov 2024 17:11:48 +0800 Subject: [PATCH 245/300] * [refac] rename parameter $primaryKey to $keyField. --- lib/base/mao/mao.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 83abdd083d..a3c34d9645 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -692,17 +692,17 @@ class baseMao * Append cache fields to data. * * @param array $data - * @param string $primaryKey + * @param string $keyField * @access public * @return void */ - public function into(array $data, $primaryKey) + public function into(array $data, $keyField) { if(empty($data)) return; /* Get data keys as conditions. */ $keyList = []; - foreach($data as $index => $row) $keyList[$index] = $row->$primaryKey; + foreach($data as $index => $row) $keyList[$index] = $row->$keyField; $cacheResult = $this->cache->fetchAll($this->table, array_unique($keyList)); From 413e30932e69d5f8cfc468208a863d553745b892 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 20 Nov 2024 17:15:19 +0800 Subject: [PATCH 246/300] * [refac] return if the parameter $keyField is empty. --- lib/base/mao/mao.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index a3c34d9645..fea2a5f7d0 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -698,7 +698,7 @@ class baseMao */ public function into(array $data, $keyField) { - if(empty($data)) return; + if(empty($data) || empty($keyField)) return $data; /* Get data keys as conditions. */ $keyList = []; From 2785d6da8e80479537af62db739c13aab189b496 Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 20 Nov 2024 17:16:16 +0800 Subject: [PATCH 247/300] * [refac] if cache is disabled fetch records from database. --- lib/base/mao/mao.class.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index fea2a5f7d0..0150f5e44c 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -704,7 +704,19 @@ class baseMao $keyList = []; foreach($data as $index => $row) $keyList[$index] = $row->$keyField; - $cacheResult = $this->cache->fetchAll($this->table, array_unique($keyList)); + if(empty($this->cache)) + { + $fields = []; + foreach($this->fields as $field => $alias) $fields[] = $field == $alias ? $field : "$field AS $alias"; + $fields = implode(',', $fields); + + $primaryKey = $this->config->cache->raw[$this->table]; + $cacheResult = $this->dao->select($fields)->from($this->table)->where($primaryKey)->in(array_unique($keyList))->fetchAll($primaryKey); + } + else + { + $cacheResult = $this->cache->fetchAll($this->table, array_unique($keyList)); + } foreach($data as $index => $row) { From da9d075fbc372ddc27ce189eb8095f30f6dafa1e Mon Sep 17 00:00:00 2001 From: liugang Date: Wed, 20 Nov 2024 17:22:17 +0800 Subject: [PATCH 248/300] + [refac] add comment. --- lib/base/mao/mao.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/base/mao/mao.class.php b/lib/base/mao/mao.class.php index 0150f5e44c..cb081c9880 100644 --- a/lib/base/mao/mao.class.php +++ b/lib/base/mao/mao.class.php @@ -706,6 +706,7 @@ class baseMao if(empty($this->cache)) { + /* 如果缓存关闭,从数据库中获取。If the cache is off, get from the database. */ $fields = []; foreach($this->fields as $field => $alias) $fields[] = $field == $alias ? $field : "$field AS $alias"; $fields = implode(',', $fields); From ad91b36492d0abbb2932f176bc452bdf4afc02be Mon Sep 17 00:00:00 2001 From: wangyuting Date: Thu, 21 Nov 2024 09:03:27 +0800 Subject: [PATCH 249/300] * [feddback#7711,done,1h,0h] Add check flow conditions. --- module/story/tao.php | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/module/story/tao.php b/module/story/tao.php index 3c3bcf0cd7..e3c4b96756 100644 --- a/module/story/tao.php +++ b/module/story/tao.php @@ -1900,6 +1900,33 @@ class storyTao extends storyModel return $story; } + /** + * 检查操作按钮的触发条件。 + * Check action conditions. + * + * @param string $method + * @param object $story + * @access protected + * @return bool + */ + protected function checkConditions(string $method, object $story) + { + static $flowActions = []; + if(empty($flowActions)) $flowActions = $this->loadModel('workflowaction')->getList($story->type); + $this->loadModel('flow'); + + $isClickable = true; + foreach($flowActions as $flowAction) + { + if($flowAction->action == $method && $flowAction->extensionType != 'none' && $flowAction->status == 'enable' && !empty($flowAction->conditions)) + { + $isClickable = $this->flow->checkConditions($flowAction->conditions, $story); + } + } + + return $isClickable; + } + /** * 构建需求列表中的操作按钮。 * Build action buttons on the browse page. @@ -1962,7 +1989,7 @@ class storyTao extends storyModel /* Change button. */ $canChange = common::hasPriv($story->type, 'change') && $this->isClickable($story, 'change'); $title = $canChange ? $lang->story->change : $this->lang->story->changeTip; - if(common::hasPriv($story->type, 'change')) $actions[] = array('name' => 'change', 'url' => $canChange ? $changeLink : null, 'hint' => $title, 'disabled' => !$canChange, 'class' => 'story-change-btn'); + if(common::hasPriv($story->type, 'change') && $this->checkConditions('change', $story)) $actions[] = array('name' => 'change', 'url' => $canChange ? $changeLink : null, 'hint' => $title, 'disabled' => !$canChange, 'class' => 'story-change-btn'); /* Submitreview, review, recall buttons. */ if(strpos('draft,changing', $story->status) !== false) From 2a28f18053b59f3acd6e6799a6fb0c65f6911a33 Mon Sep 17 00:00:00 2001 From: zhouxin Date: Wed, 20 Nov 2024 15:55:08 +0800 Subject: [PATCH 250/300] * [task#133010,doing,0.5h] add label of new version. --- module/pivot/lang/de.php | 3 +++ module/pivot/lang/en.php | 3 +++ module/pivot/lang/fr.php | 3 +++ module/pivot/lang/zh-cn.php | 3 +++ module/pivot/ui/show.html.php | 18 +++++++++++++++--- 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/module/pivot/lang/de.php b/module/pivot/lang/de.php index 1224b80162..f9a79ab539 100644 --- a/module/pivot/lang/de.php +++ b/module/pivot/lang/de.php @@ -491,3 +491,6 @@ $lang->pivot->drill->productName = "Product Name"; $lang->pivot->drill->activatedBug = "Activated Bug Count"; $lang->pivot->drill->auto = "Auto"; $lang->pivot->drill->designChangedTip = 'Setting columns field change, please check'; + +$lang->pivot->tipNewVersion = 'Has New Version!'; +$lang->pivot->checkNewVersion = 'Click to check'; diff --git a/module/pivot/lang/en.php b/module/pivot/lang/en.php index 3debc727f3..8c1c976cab 100644 --- a/module/pivot/lang/en.php +++ b/module/pivot/lang/en.php @@ -491,3 +491,6 @@ $lang->pivot->drill->productName = "Product Name"; $lang->pivot->drill->activatedBug = "Activated Bug Count"; $lang->pivot->drill->auto = "Auto"; $lang->pivot->drill->designChangedTip = 'Setting columns field change, please check'; + +$lang->pivot->tipNewVersion = 'Has New Version!'; +$lang->pivot->checkNewVersion = 'Click to check'; diff --git a/module/pivot/lang/fr.php b/module/pivot/lang/fr.php index e4248a4dbf..6d35995d34 100644 --- a/module/pivot/lang/fr.php +++ b/module/pivot/lang/fr.php @@ -491,3 +491,6 @@ $lang->pivot->drill->productName = "Product Name"; $lang->pivot->drill->activatedBug = "Activated Bug Count"; $lang->pivot->drill->auto = "Auto"; $lang->pivot->drill->designChangedTip = 'Setting columns field change, please check'; + +$lang->pivot->tipNewVersion = 'Has New Version!'; +$lang->pivot->checkNewVersion = 'Click to check'; diff --git a/module/pivot/lang/zh-cn.php b/module/pivot/lang/zh-cn.php index 627f924793..3d9e740176 100644 --- a/module/pivot/lang/zh-cn.php +++ b/module/pivot/lang/zh-cn.php @@ -491,3 +491,6 @@ $lang->pivot->drill->productName = "产品名称"; $lang->pivot->drill->activatedBug = "激活的Bug数"; $lang->pivot->drill->auto = "自动"; $lang->pivot->drill->designChangedTip = '设计变更,请检查'; + +$lang->pivot->tipNewVersion = '有新版本更新'; +$lang->pivot->checkNewVersion = '点击查看'; diff --git a/module/pivot/ui/show.html.php b/module/pivot/ui/show.html.php index 61b389151e..b9dd261304 100644 --- a/module/pivot/ui/show.html.php +++ b/module/pivot/ui/show.html.php @@ -83,14 +83,26 @@ $generateData = function() use ($lang, $pivotName, $pivot, $data, $configs, $sho set::shadow(false), set::headingClass('h-12'), set::bodyClass('pt-0'), - $pivot->desc ? to::titleSuffix( - icon + to::titleSuffix + ( + $pivot->desc ? icon ( setClass('cursor-pointer'), setData(array('toggle' => 'tooltip', 'title' => $pivot->desc, 'placement' => 'right', 'className' => 'text-gray border border-light', 'type' => 'white')), 'help' + ) : null, + span + ( + set::style(array('font-weight' => 'normal')), + $lang->pivot->tipNewVersion . $lang->comma, + h::a + ( + $lang->pivot->checkNewVersion, + set('data-toggle', 'modal'), + set::url($this->createLink('pivot', 'versions', "pivotID={$pivot->id}")) + ) ) - ) : null, + ), toolbar ( item From 1d42c07c1e5475481638ee2b10a5b4e966cf2b5e Mon Sep 17 00:00:00 2001 From: daitingting Date: Wed, 20 Nov 2024 08:48:34 +0000 Subject: [PATCH 251/300] * [task#133009,doing,0.1h] Adjust create table sql. --- db/zentao.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zentao.sql b/db/zentao.sql index 1cbd64d773..9e67e20a2e 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -15382,7 +15382,7 @@ CREATE INDEX `dimension` ON `zt_pivot` (`dimension`); CREATE INDEX `group` ON `zt_pivot` (`group`); -- DROP TABLE IF EXISTS `zt_pivotspec`; -CREATE TABLE `zt_pivotspec` ( +CREATE TABLE IF NOT EXISTS `zt_pivotspec` ( `pivot` mediumint(8) NOT NULL, `version` varchar(10) NOT NULL, `driver` enum('mysql', 'duckdb') NOT NULL default 'mysql', From 5d60dbf2eab08bb86384f05ab2bc0cce09ca810f Mon Sep 17 00:00:00 2001 From: daitingting Date: Thu, 21 Nov 2024 02:05:47 +0000 Subject: [PATCH 252/300] * [task#133009,done,0.1h] Set default version is 0. --- db/update21.1.sql | 2 +- db/zentao.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/update21.1.sql b/db/update21.1.sql index d210a228bd..28b13ca9db 100644 --- a/db/update21.1.sql +++ b/db/update21.1.sql @@ -51,7 +51,7 @@ CREATE TABLE IF NOT EXISTS `zt_pivotspec` ( ) ENGINE = InnoDB DEFAULT CHARSET=utf8; CREATE UNIQUE INDEX `idx_pivot_version` ON `zt_pivotspec`(`pivot`, `version`); -ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL DEFAULT '1' AFTER `builtin`; +ALTER TABLE `zt_pivot` ADD `version` varchar(10) NOT NULL DEFAULT '0' AFTER `builtin`; ALTER TABLE `zt_pivot` CHANGE `mode` `mode` varchar(10) NOT NULL DEFAULT 'builder'; ALTER TABLE `zt_pivot` CHANGE `sql` `sql` text NULL; ALTER TABLE `zt_pivot` CHANGE `fields` `fields` text NULL; diff --git a/db/zentao.sql b/db/zentao.sql index 9e67e20a2e..7d132b2500 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -15370,7 +15370,7 @@ CREATE TABLE IF NOT EXISTS `zt_pivot` ( `step` tinyint(3) unsigned NOT NULL DEFAULT '0', `stage` enum('draft','published') NOT NULL DEFAULT 'draft', `builtin` enum('0', '1') NOT NULL DEFAULT '0', - `version` varchar(10) NOT NULL, + `version` varchar(10) NOT NULL DEFAULT '0', `createdBy` varchar(30) NOT NULL DEFAULT '', `createdDate` datetime NULL, `editedBy` varchar(30) NOT NULL DEFAULT '', From 918f9007a2640190fb4f15d547247a4a28f2fa2c Mon Sep 17 00:00:00 2001 From: songchenxuan Date: Thu, 21 Nov 2024 10:19:44 +0800 Subject: [PATCH 253/300] *[feedback#7726,3h,done]. --- module/api/js/common.ui.js | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/module/api/js/common.ui.js b/module/api/js/common.ui.js index d386907cc1..2f43491143 100644 --- a/module/api/js/common.ui.js +++ b/module/api/js/common.ui.js @@ -1,4 +1,4 @@ -$('.form-group').on('click', '.btn-add', function() +$(document).off('click', '.form-group .btn-add').on('click', '.form-group .btn-add', function() { let $newRow = $(this).closest('tr').clone(); @@ -26,7 +26,7 @@ $('.form-group').on('click', '.btn-add', function() } }); -$('.form-group').on('click', '.btn-split', function() +$(document).off('click', '.form-group .btn-split').on('click', '.form-group .btn-split', function() { let $newRow = $(this).closest('tr').clone(); $newRow.find('input').val(''); @@ -41,7 +41,7 @@ $('.form-group').on('click', '.btn-split', function() $(this).closest('tr').after($newRow); }); -$('.form-group').on('click', '.btn-delete', function() +$(document).off('click', '.form-group .btn-delete').on('click', '.form-group .btn-delete', function() { if($(this).closest('table').find('.input-row').length == 1) return false; @@ -59,20 +59,24 @@ $('.form-group').on('click', '.btn-delete', function() } }); -$('.params-group').on('keyup', 'input,textarea', function(){ +$(document).off('keyup', '.params-group input, .params-group textarea').on('keyup', '.params-group input, .params-group textarea', function() +{ generateParams($(this)); }) -$('.params-group').on('change', 'input[type=checkbox]', function(){ +$(document).off('change', '.params-group input[type=checkbox]').on('change', '.params-group input[type=checkbox]', function() +{ generateParams($(this)); }) -$('.params-group').on('change', 'select', function(){ +$(document).off('change', '.params-group select').on('change', '.params-group select', function() +{ generateParams($(this)); }) -/* 变更请求类型时,判断是否隐藏拆分按钮. */ -$('.form-group').on('change', '.objectType', function(){ +$(document).off('change', '.form-group .objectType').on('change', '.form-group .objectType', function() +{ + /* 变更请求类型时,判断是否隐藏拆分按钮. */ if($(this).val() != 'array' && $(this).val() != 'object') { $(this).closest('tr').find('.btn-split').addClass('hidden'); @@ -84,12 +88,21 @@ $('.form-group').on('change', '.objectType', function(){ }) /* 请求响应单独绑定事件. */ -$('#form-response').on('keyup', 'input,textarea', function(){generateResponse($(this))}); -$('#form-response').on('change', 'input[type=checkbox]', function(){generateResponse($(this))}); -$('#form-response').on('change', 'select', function(){generateResponse($(this))}); +$(document).off('keyup', '#form-response input, #form-response textarea').on('keyup', '#form-response input, #form-response textarea', function() +{ + generateResponse($(this)); +}); +$(document).off('change', '#form-response input[type=checkbox]').on('change', '#form-response input[type=checkbox]', function() +{ + generateResponse($(this)); +}); +$(document).off('change', '#form-response select').on('change', '#form-response select', function() +{ + generateResponse($(this)); +}); /* 更改请求体类型. */ -$('.params-group').on('change', 'input[type=radio]', function() +$(document).off('change', '.params-group input[type=radio]').on('change', '.params-group input[type=radio]', function() { const isStruct = $(this).closest('div.form-group').hasClass('struct'); if(!isStruct) From faccadd5f1da98969d4b0385d1be32cc7127d16b Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:29:58 +0800 Subject: [PATCH 254/300] * [task#132858] add edit app config. --- module/system/config/form.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/system/config/form.php b/module/system/config/form.php index 2ae2d9985c..15a5ef0ab9 100644 --- a/module/system/config/form.php +++ b/module/system/config/form.php @@ -9,3 +9,8 @@ $config->system->form->create['name'] = array('type' => 'string', 'requir $config->system->form->create['children'] = array('type' => 'array', 'required' => false, 'default' => array(), 'filter' => 'join'); $config->system->form->create['desc'] = array('type' => 'string', 'required' => false, 'default' => '', 'filter' => 'trim'); $config->system->form->create['createdDate'] = array('type' => 'datetime', 'required' => false, 'default' => helper::now()); + +$config->system->form->edit['name'] = array('type' => 'string', 'required' => true, 'filter' => 'trim'); +$config->system->form->edit['children'] = array('type' => 'array', 'required' => false, 'default' => array(), 'filter' => 'join'); +$config->system->form->edit['desc'] = array('type' => 'string', 'required' => false, 'default' => '', 'filter' => 'trim'); +$config->system->form->edit['editedDate'] = array('type' => 'datetime', 'required' => false, 'default' => helper::now()); From 45d3388edc15aa9196f78df73c7dad8ebe4552df Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:30:09 +0800 Subject: [PATCH 255/300] * [task#132858] add edit app config. --- module/system/config.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/module/system/config.php b/module/system/config.php index d6b592508c..6d8e9df604 100644 --- a/module/system/config.php +++ b/module/system/config.php @@ -5,6 +5,9 @@ $config->system = new stdclass(); $config->system->create = new stdclass(); $config->system->create->requiredFields = 'name'; +$config->system->edit = new stdclass(); +$config->system->edit->requiredFields = 'name'; + $config->system->groupPrivs = array(); $config->system->groupPrivs['dashboard'] = 'backup|index'; $config->system->groupPrivs['deletebackup'] = 'backup|delete'; From 4c6bb2f98b1e65e673f200a7c0d04c13279cfa6d Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:31:25 +0800 Subject: [PATCH 256/300] * [task#132858] add edit app control method. --- module/system/control.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/module/system/control.php b/module/system/control.php index 7320876721..44ea8aae88 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -565,7 +565,7 @@ class system extends control * @access public * @return void */ - public function create($productID) + public function create(int $productID) { if($_POST) { @@ -576,6 +576,7 @@ class system extends control ->setDefault('product', $productID) ->setDefault('status', 'active') ->setDefault('createdBy', $this->app->user->account) + ->setIF($integrated == '0', 'children', '') ->get(); $systemID = $this->system->create($formData); @@ -589,4 +590,21 @@ class system extends control $this->view->systemList = $this->system->getPairs('0'); $this->display(); } + + /** + * 编辑应用。 + * Edit application. + * + * @param int $id + * @access public + * @return void + */ + public function edit(int $id) + { + $system = $this->system->fetchByID($id); + $this->view->title = $this->lang->system->edit; + $this->view->system = $system; + $this->view->systemList = $this->system->getPairs('0'); + $this->display(); + } } From a6c0739ae962731e9524ef6135085f860a283fac Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:31:33 +0800 Subject: [PATCH 257/300] * [task#132858] add edit app control method. --- module/system/control.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/module/system/control.php b/module/system/control.php index 44ea8aae88..dc7e929e52 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -602,6 +602,24 @@ class system extends control public function edit(int $id) { $system = $this->system->fetchByID($id); + if($_POST) + { + $integrated = $system->integrated; + if($integrated) $this->config->system->edit->requiredFields .= ',children'; + + $formData = form::data($this->config->system->form->edit) + ->setDefault('editedBy', $this->app->user->account) + ->setDefault('integrated', $integrated) + ->setDefault('editedBy', $this->app->user->account) + ->get(); + + $this->system->update($id, $formData); + if(dao::isError()) return $this->sendError(dao::getError()); + + $this->loadModel('action')->create('system', $id, 'edited'); + $this->sendSuccess(array('load' => true)); + } + $this->view->title = $this->lang->system->edit; $this->view->system = $system; $this->view->systemList = $this->system->getPairs('0'); From 3043f6958d1a857f0430b26e6ca445b0bd211cfe Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:33:00 +0800 Subject: [PATCH 258/300] * [task#132858] add update method unit test. --- module/system/test/model/update.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 module/system/test/model/update.php diff --git a/module/system/test/model/update.php b/module/system/test/model/update.php new file mode 100755 index 0000000000..46ebbc9b95 --- /dev/null +++ b/module/system/test/model/update.php @@ -0,0 +1,20 @@ +#!/usr/bin/env php +gen(10); + +/** + +title=测试 systemModel::create(); +timeout=0 +cid=1 + +- 默认修改应用 @1 +- 修改重名提示第name条的0属性 @『应用名称』已经有『应用2』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。 +- 修改空提示第name条的0属性 @『应用名称』不能为空。 +- 修改非数字第product条的0属性 @『product』应当是数字。 +- 修改非法状态第status条的0属性 @『状态』不符合格式,应当为:『/active|inactive/』。 +*/ +global $tester; +$system = $tester->loadModel('system'); From a3c1ae3df82d69203e2cdc1613250deae009b1b4 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:33:19 +0800 Subject: [PATCH 259/300] * [task#132858] add update method unit test. --- module/system/test/model/update.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/module/system/test/model/update.php b/module/system/test/model/update.php index 46ebbc9b95..102f0af01d 100755 --- a/module/system/test/model/update.php +++ b/module/system/test/model/update.php @@ -18,3 +18,9 @@ cid=1 */ global $tester; $system = $tester->loadModel('system'); + +$default = new stdclass(); +$default->name = '应用11'; +$default->desc = '应用描述'; +$default->editedBy = 'admin'; +$default->editedDate = '2024-01-01 00:00:00'; From dce57a812b3763bbd422d0fa5857ffabf090d6cc Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:33:24 +0800 Subject: [PATCH 260/300] * [task#132858] add update method unit test. --- module/system/test/model/update.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/module/system/test/model/update.php b/module/system/test/model/update.php index 102f0af01d..78660f6dce 100755 --- a/module/system/test/model/update.php +++ b/module/system/test/model/update.php @@ -24,3 +24,19 @@ $default->name = '应用11'; $default->desc = '应用描述'; $default->editedBy = 'admin'; $default->editedDate = '2024-01-01 00:00:00'; + +r($system->update(1, $default)) && p() && e('1'); // 默认修改应用 +$default->name = '应用2'; +$system->update(1, $default); +r(dao::getError()) && p('name:0') && e('『应用名称』已经有『应用2』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。'); // 修改重名提示 +$default->name = ''; +$system->update(1, $default); +r(dao::getError()) && p('name:0') && e('『应用名称』不能为空。'); // 修改空提示 +$default->name = '应用11'; +$default->product = '字符串'; +$system->update(1, $default); +r(dao::getError()) && p('product:0') && e('『product』应当是数字。'); // 修改非数字 +$default->status = 1; +$default->product = 1; +$system->update(1, $default, false); +r(dao::getError()) && p('status:0') && e('『状态』不符合格式,应当为:『/active|inactive/』。'); // 修改非法状态 From d360f499b357dfb47e37cca0366462982b8c99ed Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:34:21 +0800 Subject: [PATCH 261/300] * [task#132858] update create unit test. --- module/system/test/model/create.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/module/system/test/model/create.php b/module/system/test/model/create.php index 99b7e3bae5..fcab3e8dfa 100755 --- a/module/system/test/model/create.php +++ b/module/system/test/model/create.php @@ -10,11 +10,11 @@ title=测试 systemModel::create(); timeout=0 cid=1 -- 查询默认排序应用 @1 -- 查询默认排序应用第name条的0属性 @『应用名称』已经有『应用10』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。 -- 执行第name条的0属性 @『应用名称』不能为空。 -- 执行第product条的0属性 @『product』应当是数字。 -- 执行第status条的0属性 @『状态』不符合格式,应当为:『/active|inactive/』。 +- 默认创建 @1 +- 重复创建第name条的0属性 @『应用名称』已经有『应用10』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。 +- 创建空名称失败第name条的0属性 @『应用名称』不能为空。 +- 创建非数字失败第product条的0属性 @『product』应当是数字。 +- 创建非法状态失败第status条的0属性 @『状态』不符合格式,应当为:『/active|inactive/』。 */ global $tester; $system = $tester->loadModel('system'); @@ -29,17 +29,17 @@ $default->desc = '应用描述'; $default->createdBy = 'admin'; $default->createdDate = '2024-01-01 00:00:00'; -r($system->create($default)) && p() && e('1'); // 查询默认排序应用 +r($system->create($default)) && p() && e('1'); // 默认创建 $system->create($default); -r(dao::getError()) && p('name:0') && e('『应用名称』已经有『应用10』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。'); // 查询默认排序应用 +r(dao::getError()) && p('name:0') && e('『应用名称』已经有『应用10』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。'); // 重复创建 $default->name = ''; $system->create($default); -r(dao::getError()) && p('name:0') && e('『应用名称』不能为空。'); +r(dao::getError()) && p('name:0') && e('『应用名称』不能为空。'); // 创建空名称失败 $default->name = '应用11'; $default->product = '字符串'; $system->create($default); -r(dao::getError()) && p('product:0') && e('『product』应当是数字。'); +r(dao::getError()) && p('product:0') && e('『product』应当是数字。'); // 创建非数字失败 $default->product = 1; $default->status = 'test'; $system->create($default); -r(dao::getError()) && p('status:0') && e('『状态』不符合格式,应当为:『/active|inactive/』。'); +r(dao::getError()) && p('status:0') && e('『状态』不符合格式,应当为:『/active|inactive/』。'); // 创建非法状态失败 From b6e0f4487648347b48ff72c945f0b7bfe45cd483 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:35:59 +0800 Subject: [PATCH 262/300] * [task#132858] add update method. --- module/system/model.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/system/model.php b/module/system/model.php index 3cbb1c8d06..74eeaff1e8 100644 --- a/module/system/model.php +++ b/module/system/model.php @@ -67,6 +67,26 @@ class systemModel extends model return $this->dao->lastInsertID(); } + /** + * 编辑应用。 + * Edit an app. + * + * @param int $id + * @param object $formData + * @access public + * @return bool + */ + public function update(int $id, object $formData, bool $checkRequired = true): bool + { + $this->dao->update(TABLE_SYSTEM)->data($formData) + ->check('name', 'unique', '`id` != ' . $id) + ->autoCheck() + ->beginIF($checkRequired)->batchCheck($this->config->system->edit->requiredFields, 'notempty')->fi() + ->where('id')->eq($id) + ->exec(); + return !dao::isError(); + } + /** * 获取自定义的域名设置。 * Get customized domain settings. From 44ec2c143303787be11acdc3e1d233c70174504f Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:37:00 +0800 Subject: [PATCH 263/300] * [task#132858] add edit page. --- module/system/ui/edit.html.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 module/system/ui/edit.html.php diff --git a/module/system/ui/edit.html.php b/module/system/ui/edit.html.php new file mode 100644 index 0000000000..bf3680b7d5 --- /dev/null +++ b/module/system/ui/edit.html.php @@ -0,0 +1,18 @@ + + * @package system + * @link https://www.zentao.net + */ +namespace zin; + +formPanel +( + set::id('systemEditForm'), + set::title($lang->system->edit), + set::submitBtnText($lang->save), +); From 4dcc045888bf643d79c5b66223041012c9ca4ce3 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:37:48 +0800 Subject: [PATCH 264/300] * [task#132858] add edit page. --- module/system/ui/edit.html.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/system/ui/edit.html.php b/module/system/ui/edit.html.php index bf3680b7d5..35ab49d265 100644 --- a/module/system/ui/edit.html.php +++ b/module/system/ui/edit.html.php @@ -15,4 +15,24 @@ formPanel set::id('systemEditForm'), set::title($lang->system->edit), set::submitBtnText($lang->save), + formGroup + ( + set::name('integrated'), + set::width('1/2'), + set::label($lang->system->integrated), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::control('radioListInline'), + set::value($system->integrated), + set::items($lang->system->integratedList), + set::disabled(true) + ), + formGroup + ( + set::name('name'), + set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::label($lang->system->name), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::required(true), + set::value($system->name) + ), ); From bdb1eacdf6be926085f29b6750577fcc714a67ba Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:38:12 +0800 Subject: [PATCH 265/300] * [task#132858] add edit page. --- module/system/ui/edit.html.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/module/system/ui/edit.html.php b/module/system/ui/edit.html.php index 35ab49d265..9df726c897 100644 --- a/module/system/ui/edit.html.php +++ b/module/system/ui/edit.html.php @@ -35,4 +35,18 @@ formPanel set::required(true), set::value($system->name) ), + formGroup + ( + setID('children'), + $system->integrated ? null : setClass('hidden'), + set::name('children'), + set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::required(true), + set::label($lang->system->children), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::control('picker'), + set::items($systemList), + set::multiple(true), + set::value($system->children) + ), ); From 1d595e7e0b781d7a5831551f0c8765dca2909c49 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:38:17 +0800 Subject: [PATCH 266/300] * [task#132858] add edit page. --- module/system/ui/edit.html.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/module/system/ui/edit.html.php b/module/system/ui/edit.html.php index 9df726c897..3872e44c28 100644 --- a/module/system/ui/edit.html.php +++ b/module/system/ui/edit.html.php @@ -49,4 +49,12 @@ formPanel set::multiple(true), set::value($system->children) ), + formGroup + ( + set::name('desc'), + set::label($lang->system->desc), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), + set::control(array('type' => 'textarea', 'rows' => '4')), + set::value($system->desc) + ) ); From 38cf85a692e8c1138893144c3f386e41d7af7c78 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 10:48:08 +0800 Subject: [PATCH 267/300] * [task#132858] adjust create and edit page. --- module/system/ui/create.html.php | 10 ++++------ module/system/ui/edit.html.php | 7 ++++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/module/system/ui/create.html.php b/module/system/ui/create.html.php index 93f1d1a8ed..3c3a8407e8 100644 --- a/module/system/ui/create.html.php +++ b/module/system/ui/create.html.php @@ -10,17 +10,18 @@ declare(strict_types=1); */ namespace zin; +$width = common::checkNotCN() ? '3/4' : '1/2'; formPanel ( set::id('systemCreateForm'), set::title($lang->system->create), set::submitBtnText($lang->save), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), formGroup ( set::name('integrated'), set::width('1/2'), set::label($lang->system->integrated), - set::labelWidth(common::checkNotCN() ? '160px' : '100px'), set::control('radioListInline'), set::value(0), set::items($lang->system->integratedList), @@ -29,9 +30,8 @@ formPanel formGroup ( set::name('name'), - set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::width($width), set::label($lang->system->name), - set::labelWidth(common::checkNotCN() ? '160px' : '100px'), set::required(true) ), formGroup @@ -39,10 +39,9 @@ formPanel setID('children'), setClass('hidden'), set::name('children'), - set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::width($width), set::required(true), set::label($lang->system->children), - set::labelWidth(common::checkNotCN() ? '160px' : '100px'), set::control('picker'), set::items($systemList), set::multiple(true) @@ -51,7 +50,6 @@ formPanel ( set::name('desc'), set::label($lang->system->desc), - set::labelWidth(common::checkNotCN() ? '160px' : '100px'), set::control(array('type' => 'textarea', 'rows' => '4')) ) ); diff --git a/module/system/ui/edit.html.php b/module/system/ui/edit.html.php index 3872e44c28..0ace9dcd0e 100644 --- a/module/system/ui/edit.html.php +++ b/module/system/ui/edit.html.php @@ -10,17 +10,18 @@ declare(strict_types=1); */ namespace zin; +$width = common::checkNotCN() ? '3/4' : '1/2'; formPanel ( set::id('systemEditForm'), set::title($lang->system->edit), set::submitBtnText($lang->save), + set::labelWidth(common::checkNotCN() ? '160px' : '100px'), formGroup ( set::name('integrated'), set::width('1/2'), set::label($lang->system->integrated), - set::labelWidth(common::checkNotCN() ? '160px' : '100px'), set::control('radioListInline'), set::value($system->integrated), set::items($lang->system->integratedList), @@ -29,7 +30,7 @@ formPanel formGroup ( set::name('name'), - set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::width($width), set::label($lang->system->name), set::labelWidth(common::checkNotCN() ? '160px' : '100px'), set::required(true), @@ -40,7 +41,7 @@ formPanel setID('children'), $system->integrated ? null : setClass('hidden'), set::name('children'), - set::width(common::checkNotCN() ? '3/4' : '1/2'), + set::width($width), set::required(true), set::label($lang->system->children), set::labelWidth(common::checkNotCN() ? '160px' : '100px'), From 814fdb533ecfc8b09456068a2b0a41f63c56d7b2 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 11:10:58 +0800 Subject: [PATCH 268/300] * [task#132856] add isClickable method. --- module/system/model.php | 16 ++++++++++++++++ module/system/test/model/update.php | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/module/system/model.php b/module/system/model.php index 74eeaff1e8..2270186625 100644 --- a/module/system/model.php +++ b/module/system/model.php @@ -395,4 +395,20 @@ class systemModel extends model return false; } + + /** + * 检查按钮是否可用。 + * Check if the button is clickable. + * + * @param object $system + * @param string $action + * @access public + * @return bool + */ + public function isClickable(object $system, string $action): bool + { + if($action == 'active') return $system->status == 'inactive'; + if($action == 'inactive') return $system->status == 'active'; + return true; + } } diff --git a/module/system/test/model/update.php b/module/system/test/model/update.php index 78660f6dce..bcf2e76bf4 100755 --- a/module/system/test/model/update.php +++ b/module/system/test/model/update.php @@ -6,7 +6,7 @@ zendata('system')->gen(10); /** -title=测试 systemModel::create(); +title=测试 systemModel::update(); timeout=0 cid=1 From bac37218526aa1698c56f230353c852c819e5958 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 11:11:35 +0800 Subject: [PATCH 269/300] * [task#132856] add isClickable method unit test. --- module/system/test/model/isclickable.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 module/system/test/model/isclickable.php diff --git a/module/system/test/model/isclickable.php b/module/system/test/model/isclickable.php new file mode 100644 index 0000000000..992c1362da --- /dev/null +++ b/module/system/test/model/isclickable.php @@ -0,0 +1,19 @@ +#!/usr/bin/env php + Date: Thu, 21 Nov 2024 11:11:43 +0800 Subject: [PATCH 270/300] * [task#132856] add isClickable method unit test. --- module/system/test/model/isclickable.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/module/system/test/model/isclickable.php b/module/system/test/model/isclickable.php index 992c1362da..7810f3567e 100644 --- a/module/system/test/model/isclickable.php +++ b/module/system/test/model/isclickable.php @@ -17,3 +17,22 @@ cid=1 - 编辑按钮正常展示 @1 - 删除按钮正常展示 @1 */ + +global $tester; +$system = $tester->loadModel('system'); + +$default = new stdclass(); +$default->status = 'active'; + +$action = array('active', 'inactive', 'edit', 'delete'); + +r($system->isClickable($default, $action[0])) && p() && e('0'); // 上架状态,不显示上键按钮 +r($system->isClickable($default, $action[1])) && p() && e('1'); // 上架状态,显示下键按钮 +r($system->isClickable($default, $action[2])) && p() && e('1'); // 编辑按钮正常展示 +r($system->isClickable($default, $action[3])) && p() && e('1'); // 删除按钮正常展示 + +$default->status = 'inactive'; +r($system->isClickable($default, $action[0])) && p() && e('1'); // 下架状态,显示上键按钮 +r($system->isClickable($default, $action[1])) && p() && e('0'); // 下架状态,不显示下键按钮 +r($system->isClickable($default, $action[2])) && p() && e('1'); // 编辑按钮正常展示 +r($system->isClickable($default, $action[3])) && p() && e('1'); // 删除按钮正常展示 From acba4f586c5efc1c39748c9bacdf3c139ef0eb53 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 08:54:51 +0800 Subject: [PATCH 271/300] * [task#133020,doing,0.2h,4.8h] Add product data. --- module/execution/test/ui/view.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 module/execution/test/ui/view.php diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php new file mode 100644 index 0000000000..3e5d1fa020 --- /dev/null +++ b/module/execution/test/ui/view.php @@ -0,0 +1,17 @@ +#!/usr/bin/env php +id->range('1-100'); +$product->name->range('产品1'); +$product->type->range('normal'); +$product->gen(1); From ecb6ff11e3dc4b0fdc7c8554ae51cdc95b390a1d Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:14:14 +0800 Subject: [PATCH 272/300] * [task#133020,doing,0.2h,4.8h] Add project data. --- module/execution/test/ui/view.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index 3e5d1fa020..b1fc89b508 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -15,3 +15,21 @@ $product->id->range('1-100'); $product->name->range('产品1'); $product->type->range('normal'); $product->gen(1); + +$project = zenData('project'); +$project->id->range('1-100'); +$project->project->range('0, 0, 1'); +$project->model->range('[], scrum, []'); +$project->type->range('program, project, sprint'); +$project->auth->range('[], extend, []'); +$project->storyType->range('[], story, []'); +$project->parent->range('0, 1, 2'); +$project->path->range('`,1,`, `,1,2,`, `,1,2,3`'); +$project->grade->range('1, 2, 1'); +$project->name->range('项目集, 项目, 执行'); +$project->begin->range('2024-11-01')->type('timestamp')->format('YY/MM/DD'); +$project->end->range('2024-11-31')->type('timestamp')->format('YY/MM/DD'); +$project->openedBy->range('user1'); +$project->acl->range('open'); +$project->status->range('doing'); +$project->gen(3); From c7ae57c2b0e903a30d34dcfe3735310d57a64595 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:15:57 +0800 Subject: [PATCH 273/300] * [task#133020,doing,0.2h,4.6h] Add projectproduct data. --- module/execution/test/ui/view.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index b1fc89b508..51f7504c9b 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -33,3 +33,8 @@ $project->openedBy->range('user1'); $project->acl->range('open'); $project->status->range('doing'); $project->gen(3); + +$projectproduct = zenData('projectproduct'); +$projectproduct->project->range('2, 3'); +$projectproduct->product->range('1'); +$projectproduct->gen(2); From a0765ffc8c0908b76473fd984b7c9218f2eef51f Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:23:57 +0800 Subject: [PATCH 274/300] * [task#133020,doing,0.2h,4.5h] Add story data. --- module/execution/test/ui/view.php | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index 51f7504c9b..d5ee3c6722 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -12,9 +12,9 @@ include '../lib/view.ui.class.php'; $product = zenData('product'); $product->id->range('1-100'); -$product->name->range('产品1'); +$product->name->range('产品1,产品2'); $product->type->range('normal'); -$product->gen(1); +$product->gen(2); $project = zenData('project'); $project->id->range('1-100'); @@ -35,6 +35,24 @@ $project->status->range('doing'); $project->gen(3); $projectproduct = zenData('projectproduct'); -$projectproduct->project->range('2, 3'); -$projectproduct->product->range('1'); -$projectproduct->gen(2); +$projectproduct->project->range('2{2}, 3'); +$projectproduct->product->range('1, 2, 1'); +$projectproduct->gen(3); + +$story->id->range('1-100'); +$story->parent->range('0'); +$story->isParent->range('0'); +$story->root->range('1-100'); +$story->path->range('`,1,`, `,2,`, `,3,`'); +$story->grade->range('1'); +$story->product->range('1'); +$story->module->range('0'); +$story->plan->range('0'); +$story->title->range('1-100'); +$story->type->range('story'); +$story->estimate->range('0'); +$story->status->range('active'); +$story->stage->range('projected'); +$story->assignedTo->range('[]'); +$story->version->range('1'); +$story->gen(3); From 17aabb28163b51147867b7e8eaeb79eddddacc97 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:27:48 +0800 Subject: [PATCH 275/300] * [task#133020,doing,0.1h,4.5h] Add projectStory data. --- module/execution/test/ui/view.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index d5ee3c6722..d8f1d7141c 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -56,3 +56,17 @@ $story->stage->range('projected'); $story->assignedTo->range('[]'); $story->version->range('1'); $story->gen(3); + +$storySpec = zenData('storyspec'); +$storySpec->story->range('1-15'); +$storySpec->version->range('1'); +$storySpec->title->range('1-15'); +$storySpec->gen(3); + +$projectStory = zenData('projectstory'); +$projectStory->project->range('2{3}, 3{2}'); +$projectStory->product->range('1'); +$projectStory->branch->range('0'); +$projectStory->story->range('1-3, 1, 2'); +$projectStory->version->range('1'); +$projectStory->gen(5); From 55733feea6f43f4149975c6d72745cf0f6b90bc6 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:30:30 +0800 Subject: [PATCH 276/300] * [task#133020,doing,0.1h,4.5h] Add team and user data. --- module/execution/test/ui/view.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index d8f1d7141c..39fa4532db 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -39,6 +39,21 @@ $projectproduct->project->range('2{2}, 3'); $projectproduct->product->range('1, 2, 1'); $projectproduct->gen(3); +$user = zenData('user'); +$user->id->range('1-100'); +$user->dept->range('0'); +$user->account->range('admin, user1, user2'); +$user->realname->range('admin, USER1, USER2'); +$user->password->range($config->uitest->defaultPassword)->format('md5'); +$user->gen(3); + +$team = zenData('team'); +$team->id->range('1-100'); +$team->root->range('2{3}, 3{2}'); +$team->type->range('project{3}, execution{2}'); +$team->account->range('admin, user1, user2, admin, user1'); +$team->gen(5); + $story->id->range('1-100'); $story->parent->range('0'); $story->isParent->range('0'); From 7c70a52ece4660a330b07d13cf8f43c8b5a4fdcc Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:36:58 +0800 Subject: [PATCH 277/300] * [task#133020,doing,0.2h,4.5h] Add task data. --- module/execution/test/ui/view.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index 39fa4532db..89f723e9f5 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -85,3 +85,21 @@ $projectStory->branch->range('0'); $projectStory->story->range('1-3, 1, 2'); $projectStory->version->range('1'); $projectStory->gen(5); + +$task = zenData('task'); +$task->id->range('1-100'); +$task->project->range('2'); +$task->execution->range('3'); +$task->story->range('0'); +$task->storyVersion->range('1'); +$task->name->range('1-100'); +$task->type->range('devel'); +$task->deadline->range(' (-5D)-(-4D):1D, []{11}')->type('timestamp')->format('YY/MM/DD'); +$task->status->range('wait{3}, doing{3}, done{2}, cancel, closed{3}'); +$task->openedBy->range('admin{6}, user1{6}'); +$task->assignedTo->range('[]{3}, user1{4}, admin{2}, closed{3}'); +$task->finishedBy->range('[]{6}, admin, user1, [], admin, user1{2}'); +$task->canceledBy->range('[]{8}, admin, []{3}'); +$task->closedBy->range('[]{9}, admin{2}, user1'); +$task->deleted->range('0{11}, 1'); +$task->gen(12); From 7cf00dee706fe5fafa79dc892ae04b838ff74948 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:37:36 +0800 Subject: [PATCH 278/300] * [task#133020,doing,0.1h,4.4h] Add taskSpec data. --- module/execution/test/ui/view.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index 89f723e9f5..33ac2dec58 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -103,3 +103,9 @@ $task->canceledBy->range('[]{8}, admin, []{3}'); $task->closedBy->range('[]{9}, admin{2}, user1'); $task->deleted->range('0{11}, 1'); $task->gen(12); + +$taskSpec = zenData('taskspec'); +$taskSpec->task->range('1-100'); +$taskSpec->version->range('0'); +$taskSpec->name->range('1-100'); +$taskSpec->gen(12); From 7154ee61eec5c1bc1ec909983fe17b0001ea05f6 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:45:47 +0800 Subject: [PATCH 279/300] * [task#133020,doing,0.1h,4.4h] Add bug data. --- module/execution/test/ui/view.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index 33ac2dec58..a4861e00e7 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -109,3 +109,13 @@ $taskSpec->task->range('1-100'); $taskSpec->version->range('0'); $taskSpec->name->range('1-100'); $taskSpec->gen(12); + +$bug = zenData('bug'); +$bug->id->range('1-100'); +$bug->project->range('2{12}, 0{100}'); +$bug->product->range('1{8},2{100}'); +$bug->execution->range('2{5}, 0{100}'); +$bug->title->range('1-100'); +$bug->status->range('active{5}, resolved{3}, closed{100}'); +$bug->assignedTo->range('[]'); +$bug->gen(12); From fefac69ada92871a26bda2505d4d2cfcde0b31d3 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 09:55:48 +0800 Subject: [PATCH 280/300] * [task#133020,doing,0.1h,4.4h] Add doclib data. --- module/execution/test/ui/view.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index a4861e00e7..e3f0ae695e 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -119,3 +119,11 @@ $bug->title->range('1-100'); $bug->status->range('active{5}, resolved{3}, closed{100}'); $bug->assignedTo->range('[]'); $bug->gen(12); + +$doclib = zenData('doclib'); +$doclib->id->range('1-100'); +$doclib->type('project, execution{2}'); +$doclib->project->range('2'); +$doclib->execution->range('0, 3'); +$doclib->name->range('项目库, 执行库1, 执行库2'); +$doclib->gen(3); From 8b4331b7dc2e23094fca3dbd6005846da6f61323 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 10:22:02 +0800 Subject: [PATCH 281/300] * [task#133020,doing,0.1h,4.3h] Add class. --- module/execution/test/lib/view.ui.class.php | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 module/execution/test/lib/view.ui.class.php diff --git a/module/execution/test/lib/view.ui.class.php b/module/execution/test/lib/view.ui.class.php new file mode 100644 index 0000000000..864ac9121a --- /dev/null +++ b/module/execution/test/lib/view.ui.class.php @@ -0,0 +1,5 @@ + Date: Wed, 20 Nov 2024 10:51:20 +0800 Subject: [PATCH 282/300] * [task#133020,doing,0.2h,4.2h] Modify data. --- module/execution/test/ui/view.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index e3f0ae695e..bae0a62a20 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -18,7 +18,7 @@ $product->gen(2); $project = zenData('project'); $project->id->range('1-100'); -$project->project->range('0, 0, 1'); +$project->project->range('0, 0, 2'); $project->model->range('[], scrum, []'); $project->type->range('program, project, sprint'); $project->auth->range('[], extend, []'); @@ -54,6 +54,7 @@ $team->type->range('project{3}, execution{2}'); $team->account->range('admin, user1, user2, admin, user1'); $team->gen(5); +$story = zenData('story'); $story->id->range('1-100'); $story->parent->range('0'); $story->isParent->range('0'); @@ -114,16 +115,17 @@ $bug = zenData('bug'); $bug->id->range('1-100'); $bug->project->range('2{12}, 0{100}'); $bug->product->range('1{8},2{100}'); -$bug->execution->range('2{5}, 0{100}'); +$bug->execution->range('3{5}, 0{100}'); $bug->title->range('1-100'); -$bug->status->range('active{5}, resolved{3}, closed{100}'); +$bug->status->range('active{3}, resolved{3}, closed{100}'); $bug->assignedTo->range('[]'); $bug->gen(12); $doclib = zenData('doclib'); $doclib->id->range('1-100'); -$doclib->type('project, execution{2}'); +$doclib->type->range('project, execution{2}'); +$doclib->product->range('0'); $doclib->project->range('2'); -$doclib->execution->range('0, 3'); +$doclib->execution->range('0, 3, 3'); $doclib->name->range('项目库, 执行库1, 执行库2'); $doclib->gen(3); From 202114896fb51112cb27b70182149d97bd889f88 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 10:53:07 +0800 Subject: [PATCH 283/300] * [task#133020,doing,0.2h,4.2h] Modify data. --- module/execution/test/ui/view.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index bae0a62a20..c0736dc766 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -27,8 +27,6 @@ $project->parent->range('0, 1, 2'); $project->path->range('`,1,`, `,1,2,`, `,1,2,3`'); $project->grade->range('1, 2, 1'); $project->name->range('项目集, 项目, 执行'); -$project->begin->range('2024-11-01')->type('timestamp')->format('YY/MM/DD'); -$project->end->range('2024-11-31')->type('timestamp')->format('YY/MM/DD'); $project->openedBy->range('user1'); $project->acl->range('open'); $project->status->range('doing'); From 352aad4f1212e5a000faf59896853fd878d0076d Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 13:16:25 +0800 Subject: [PATCH 284/300] * [task#133020,doing,0.2h,4.1h] Add xpath. --- module/execution/test/ui/page/view.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/module/execution/test/ui/page/view.php b/module/execution/test/ui/page/view.php index 154cc45e0f..2a2ad96c62 100755 --- a/module/execution/test/ui/page/view.php +++ b/module/execution/test/ui/page/view.php @@ -17,6 +17,14 @@ class viewPage extends page 'linckedProducta' => "//*[@id='mainContent']/div[2]/div[1]/div/table[1]/tbody/tr[1]", 'linckedProductb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[1]/tbody/tr[2]", 'productsNev' => "//*[@data-id='products']", + 'moreProducts' => "//*[@id='mainContent']/div[2]/div[1]/div/table[1]/thead/tr/th[1]/div/a/span", + 'teamMembera' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[1]/span[1]", + 'teamMemberb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[2]/span[1]", + 'moreTeamMembers' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/thead/tr/th/div/a/span", + 'docliba' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[1]/a", + 'doclibb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[2]/a", + 'moreDoclibs' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/thead/tr/th/div/a/span", + 'createDoclib' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[3]/a/span" /* 编辑执行弹窗中元素 */ 'products' => "//*[@name='products[0]']", 'productsTip' => "//*[@id='products[0]Tip']", From 56ac1b004d387b734f2b0afe6a450291703202b8 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 13:50:58 +0800 Subject: [PATCH 285/300] * [task#133020,doing,0.5h,3.8h] Add function. --- module/execution/test/lib/view.ui.class.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/module/execution/test/lib/view.ui.class.php b/module/execution/test/lib/view.ui.class.php index 864ac9121a..b3544852f2 100644 --- a/module/execution/test/lib/view.ui.class.php +++ b/module/execution/test/lib/view.ui.class.php @@ -2,4 +2,22 @@ include dirname(__FILE__, 5) . '/test/lib/ui.php'; class viewTester extends tester { + /** + * 检查执行概况页面执行基础信息 + * Check the basic information of the execution. + * + * @access public + * @return object + */ + public function checkBasic() + { + $form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution'); + if($form->dom->executionName->getText() != '执行') return $this->failed('执行名称不正确'); + if($form->dom->programName->getText() != '项目集') return $this->failed('项目集名称不正确'); + if($form->dom->projectName->getText() != '项目') return $this->failed('项目名称不正确'); + if($form->dom->storyNum->getText() != '2') return $this->failed('需求数不正确'); + if($form->dom->taskNum->getText() != '11') return $this->failed('任务数不正确'); + if($form->dom->bugNum->getText() != '5') return $this->failed('Bug数不正确'); + return $this->success('执行基础信息正确'); + } } From 8628e178fed4c2e991da5286d13b326c184fd3ef Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 13:51:25 +0800 Subject: [PATCH 286/300] * [task#133020,doing,0.1h,3.8h] Add xpath. --- module/execution/test/ui/page/view.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/execution/test/ui/page/view.php b/module/execution/test/ui/page/view.php index 2a2ad96c62..446037cab5 100755 --- a/module/execution/test/ui/page/view.php +++ b/module/execution/test/ui/page/view.php @@ -7,8 +7,13 @@ class viewPage extends page $xpath = array( /* 执行概况页元素 */ 'executionName' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[1]/div", + 'programName' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[2]/div[1]/div/a", + 'projectName' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[2]/div[2]/a", 'status' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[1]/span[2]", 'acl' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[1]/span[3]", + 'storyNum' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/div", + 'taskNum' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[2]/div", + 'bugNum' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[3]/div", 'plannedBegin' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[1]/span[2]", 'plannedEnd' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[2]/span[2]", 'realBeganView' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[3]/span[2]", From 9c428c359a654c768888c36bff1f5a530276e329 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 13:53:10 +0800 Subject: [PATCH 287/300] * [task#133020,doing,0.1h,3.8h] Modify data. --- module/execution/test/ui/view.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index c0736dc766..0ac01e405e 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -12,7 +12,7 @@ include '../lib/view.ui.class.php'; $product = zenData('product'); $product->id->range('1-100'); -$product->name->range('产品1,产品2'); +$product->name->range('产品1, 产品2'); $product->type->range('normal'); $product->gen(2); @@ -112,7 +112,7 @@ $taskSpec->gen(12); $bug = zenData('bug'); $bug->id->range('1-100'); $bug->project->range('2{12}, 0{100}'); -$bug->product->range('1{8},2{100}'); +$bug->product->range('1{8}, 2{100}'); $bug->execution->range('3{5}, 0{100}'); $bug->title->range('1-100'); $bug->status->range('active{3}, resolved{3}, closed{100}'); From 76cc57947d240b0b9b98634a82f3d0308971550f Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 13:56:47 +0800 Subject: [PATCH 288/300] * [task#133020,doing,0.1h,3.8h] Modify xpath. --- module/execution/test/ui/page/view.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/execution/test/ui/page/view.php b/module/execution/test/ui/page/view.php index 446037cab5..81d61e1804 100755 --- a/module/execution/test/ui/page/view.php +++ b/module/execution/test/ui/page/view.php @@ -29,7 +29,7 @@ class viewPage extends page 'docliba' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[1]/a", 'doclibb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[2]/a", 'moreDoclibs' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/thead/tr/th/div/a/span", - 'createDoclib' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[3]/a/span" + 'createDoclib' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[3]/a/span", /* 编辑执行弹窗中元素 */ 'products' => "//*[@name='products[0]']", 'productsTip' => "//*[@id='products[0]Tip']", From b580aaf02e4d542cc315841d8277f718f96f25b8 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 14:28:39 +0800 Subject: [PATCH 289/300] * [task#133020,doing,0.5h,3.3h] Add function to check product. --- module/execution/test/lib/view.ui.class.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/module/execution/test/lib/view.ui.class.php b/module/execution/test/lib/view.ui.class.php index b3544852f2..6198d56fa1 100644 --- a/module/execution/test/lib/view.ui.class.php +++ b/module/execution/test/lib/view.ui.class.php @@ -20,4 +20,23 @@ class viewTester extends tester if($form->dom->bugNum->getText() != '5') return $this->failed('Bug数不正确'); return $this->success('执行基础信息正确'); } + + /** + * 检查执行概况页面产品信息 + * Check the product information of the execution. + * + * @access public + * @return object + */ + public function checkProduct() + { + $form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution'); + if($form->dom->linckedProducta->getText() != '产品1') return $this->failed('关联产品不正确'); + $form->dom->moreProducts->click(); + $form->wait(1); + $url = $this->response(); + if($url['module'] != 'execution') return $this->failed('页面跳转后module不正确'); + if($url['method'] != 'manageproducts') return $this->failed('页面跳转后method不正确'); + return $this->success('产品信息正确'); + } } From 07747b288cd37b279f24dc84824553d9efb14998 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 14:43:48 +0800 Subject: [PATCH 290/300] * [task#133020,doing,0.5h,3.0h] Add function to check team. --- module/execution/test/lib/view.ui.class.php | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/module/execution/test/lib/view.ui.class.php b/module/execution/test/lib/view.ui.class.php index 6198d56fa1..73e1168164 100644 --- a/module/execution/test/lib/view.ui.class.php +++ b/module/execution/test/lib/view.ui.class.php @@ -39,4 +39,26 @@ class viewTester extends tester if($url['method'] != 'manageproducts') return $this->failed('页面跳转后method不正确'); return $this->success('产品信息正确'); } + + /** + * 检查执行概况页面团队成员信息 + * Check the team members of the execution. + * + * @access public + * @return object + */ + public function checkMember() + { + $form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution'); + if($form->dom->teamMembera->getText() != 'admin') return $this->failed('团队成员不正确'); + if($form->dom->teamMemberb->getText() != 'USER1') return $this->failed('团队成员不正确'); + $form->dom->moreTeamMembers->click(); + $form->wait(1); + $url = $this->response(); + if($url['module'] != 'execution') return $this->failed('点击更多按钮,页面跳转后module不正确'); + if($url['method'] != 'team') return $this->failed('点击更多按钮,页面跳转后method不正确'); + $form->dom->btn($this->lang->overview)->click(); + $form->dom->manageMembers->click(); + return $this->success('团队成员信息正确'); + } } From c41485660df08f5c0ad0d3ca059b7b692c7a885f Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 14:49:26 +0800 Subject: [PATCH 291/300] * [task#133020,doing,0.1h,3.0h] Modify Xpath. --- module/execution/test/ui/page/view.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/execution/test/ui/page/view.php b/module/execution/test/ui/page/view.php index 81d61e1804..8155c134c2 100755 --- a/module/execution/test/ui/page/view.php +++ b/module/execution/test/ui/page/view.php @@ -26,6 +26,7 @@ class viewPage extends page 'teamMembera' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[1]/span[1]", 'teamMemberb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[2]/span[1]", 'moreTeamMembers' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/thead/tr/th/div/a/span", + 'manageMembers' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/a", 'docliba' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[1]/a", 'doclibb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[2]/a", 'moreDoclibs' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/thead/tr/th/div/a/span", From c78532234a6cfa09760701c5e4ee6cdccad7ef6d Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 15:30:12 +0800 Subject: [PATCH 292/300] * [task#133020,doing,1h,3.0h] Add function. --- module/execution/test/lib/view.ui.class.php | 70 ++++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/module/execution/test/lib/view.ui.class.php b/module/execution/test/lib/view.ui.class.php index 73e1168164..2789e03a55 100644 --- a/module/execution/test/lib/view.ui.class.php +++ b/module/execution/test/lib/view.ui.class.php @@ -3,62 +3,98 @@ include dirname(__FILE__, 5) . '/test/lib/ui.php'; class viewTester extends tester { /** - * 检查执行概况页面执行基础信息 + * 检查执行概况页面执行基础信息。 * Check the basic information of the execution. * + * @paran array $basic * @access public * @return object */ - public function checkBasic() + public function checkBasic($basic) { $form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution'); - if($form->dom->executionName->getText() != '执行') return $this->failed('执行名称不正确'); - if($form->dom->programName->getText() != '项目集') return $this->failed('项目集名称不正确'); - if($form->dom->projectName->getText() != '项目') return $this->failed('项目名称不正确'); - if($form->dom->storyNum->getText() != '2') return $this->failed('需求数不正确'); - if($form->dom->taskNum->getText() != '11') return $this->failed('任务数不正确'); - if($form->dom->bugNum->getText() != '5') return $this->failed('Bug数不正确'); + if($form->dom->executionName->getText() != $basic['executionName']) return $this->failed('执行名称不正确'); + if($form->dom->programName->getText() != $basic['programName']) return $this->failed('项目集名称不正确'); + if($form->dom->projectName->getText() != $basic['projectName']) return $this->failed('项目名称不正确'); + if($form->dom->storyNum->getText() != $basic['storyNum']) return $this->failed('需求数不正确'); + if($form->dom->taskNum->getText() != $basic['taskNum']) return $this->failed('任务数不正确'); + if($form->dom->bugNum->getText() != $basic['bugNum']) return $this->failed('Bug数不正确'); return $this->success('执行基础信息正确'); } /** - * 检查执行概况页面产品信息 + * 检查执行概况页面产品信息。 * Check the product information of the execution. * + * @param string $product * @access public * @return object */ - public function checkProduct() + public function checkProduct($product) { $form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution'); - if($form->dom->linckedProducta->getText() != '产品1') return $this->failed('关联产品不正确'); + if($form->dom->linckedProducta->getText() != $product) return $this->failed('关联产品不正确'); $form->dom->moreProducts->click(); $form->wait(1); - $url = $this->response(); + $url = $this->response(); if($url['module'] != 'execution') return $this->failed('页面跳转后module不正确'); if($url['method'] != 'manageproducts') return $this->failed('页面跳转后method不正确'); return $this->success('产品信息正确'); } /** - * 检查执行概况页面团队成员信息 + * 检查执行概况页面团队成员信息。 * Check the team members of the execution. * + * @param string $membera + * @param string $memberb * @access public * @return object */ - public function checkMember() + public function checkMember($membera, $memberb) { $form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution'); - if($form->dom->teamMembera->getText() != 'admin') return $this->failed('团队成员不正确'); - if($form->dom->teamMemberb->getText() != 'USER1') return $this->failed('团队成员不正确'); + if($form->dom->teamMembera->getText() != $membera) return $this->failed('团队成员不正确'); + if($form->dom->teamMemberb->getText() != $memberb) return $this->failed('团队成员不正确'); $form->dom->moreTeamMembers->click(); $form->wait(1); - $url = $this->response(); + $url = $this->response(); if($url['module'] != 'execution') return $this->failed('点击更多按钮,页面跳转后module不正确'); if($url['method'] != 'team') return $this->failed('点击更多按钮,页面跳转后method不正确'); $form->dom->btn($this->lang->overview)->click(); + $form->wait(1); $form->dom->manageMembers->click(); + $form->wait(1); + $url = $this->response(); + if($url['module'] != 'execution') return $this->failed('点击管理按钮,页面跳转后module不正确'); + if($url['method'] != 'manageMembers') return $this->failed('点击管理按钮,页面跳转后method不正确'); return $this->success('团队成员信息正确'); } + + /** + * 检查执行概况页面文档库信息。 + * Check the doclib information of the execution. + * + * @param string $doclaba + * @param string $doclibb + * @access public + * @return object + */ + public function checkDoclib($doclaba, $doclibb) + { + $form = $this->initForm('execution', 'view', array('execution' => '3'), 'appIframe-execution'); + if($form->dom->docliba->getText() != $doclaba) return $this->failed('文档库名称不正确'); + if($form->dom->doclibb->getText() != $doclibb) return $this->failed('文档库名称不正确'); + $form->dom->moreDoclibs->click(); + $form->wait(1); + $url = $this->response(); + if($url['module'] != 'execution') return $this->failed('点击更多按钮,页面跳转后module不正确'); + if($url['method'] != 'doc') return $this->failed('点击更多按钮,页面跳转后method不正确'); + $form->dom->btn($this->lang->settings)->click(); + $form->wait(1); + $form->dom->createDoclib->click(); + $form->wait(1); + if(!is_object($form->dom->doclibModal)) return $this->failed('创建文档库模态框不存在'); + return $this->success('文档库信息正确'); + } } From c45891947af9d6525f51c655b059ea21c65658a6 Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 15:31:21 +0800 Subject: [PATCH 293/300] * [task#133020,doing,0.1h,2.0h] Modify xpath. --- module/execution/test/ui/page/view.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/execution/test/ui/page/view.php b/module/execution/test/ui/page/view.php index 8155c134c2..bb2600493e 100755 --- a/module/execution/test/ui/page/view.php +++ b/module/execution/test/ui/page/view.php @@ -31,6 +31,7 @@ class viewPage extends page 'doclibb' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[2]/a", 'moreDoclibs' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/thead/tr/th/div/a/span", 'createDoclib' => "//*[@id='mainContent']/div[2]/div[1]/div/table[5]/tbody/tr/td/div/div[3]/a/span", + 'doclibModal' => "//*[@class='modal-dialog']", /* 编辑执行弹窗中元素 */ 'products' => "//*[@name='products[0]']", 'productsTip' => "//*[@id='products[0]Tip']", From 6209ffa599df5b556ff5bbeee3570d08d91aee5f Mon Sep 17 00:00:00 2001 From: yulujie Date: Wed, 20 Nov 2024 15:31:48 +0800 Subject: [PATCH 294/300] * [task#133020,done,1h] Add cases. --- module/execution/test/ui/view.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/module/execution/test/ui/view.php b/module/execution/test/ui/view.php index 0ac01e405e..b25c843111 100644 --- a/module/execution/test/ui/view.php +++ b/module/execution/test/ui/view.php @@ -127,3 +127,21 @@ $doclib->project->range('2'); $doclib->execution->range('0, 3, 3'); $doclib->name->range('项目库, 执行库1, 执行库2'); $doclib->gen(3); + +$tester = new viewTester(); +$tester->login(); + +$basic = array( + 'executionName' => '执行', + 'programName' => '项目集', + 'projectName' => '项目', + 'storyNum' => '2', + 'taskNum' => '11', + 'bugNum' => '5', +); + +r($tester->checkBasic($basic)) && p('status,message') && e('SUCCESS,执行基础信息正确'); +r($tester->checkProduct('产品1')) && p('status,message') && e('SUCCESS,产品信息正确'); +r($tester->checkMember('admin', 'USER1')) && p('status,message') && e('SUCCESS,团队成员信息正确'); +r($tester->checkDoclib('执行库1', '执行库2')) && p('status,message') && e('SUCCESS,文档库信息正确'); +$tester->closeBrowser(); From 054384fe6990ce18f14e28cc62e059cd7a07ff5e Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 11:24:58 +0800 Subject: [PATCH 295/300] * [task#132867] add active control method. --- module/system/control.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/module/system/control.php b/module/system/control.php index dc7e929e52..3bbbfece49 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -625,4 +625,24 @@ class system extends control $this->view->systemList = $this->system->getPairs('0'); $this->display(); } + + /** + * 上架应用。 + * Active application. + * + * @param int $id + * @access public + * @return void + */ + public function active(int $id) + { + $system = new stdclass(); + $system->status = 'active'; + + $this->system->update($id, $system, false); + if(dao::isError()) return $this->sendError(dao::getError()); + + $this->loadModel('action')->create('system', $id, 'active'); + $this->sendSuccess(array('load' => true)); + } } From dfa3bffd39ccdd3755e4e4ec4615850619cd29ff Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 11:25:25 +0800 Subject: [PATCH 296/300] * [task#132866] add inactive control method. --- module/system/control.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/module/system/control.php b/module/system/control.php index 3bbbfece49..96454e8a8e 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -645,4 +645,16 @@ class system extends control $this->loadModel('action')->create('system', $id, 'active'); $this->sendSuccess(array('load' => true)); } + + /** + * 下架应用。 + * Inactive application. + * + * @param int $id + * @access public + * @return void + */ + public function inactive(int $id) + { + } } From 2a49ae2b14f456bc64a57faeeb8dbdd44f7deb46 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 11:25:31 +0800 Subject: [PATCH 297/300] * [task#132866] add inactive control method. --- module/system/control.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/module/system/control.php b/module/system/control.php index 96454e8a8e..7591617b56 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -656,5 +656,13 @@ class system extends control */ public function inactive(int $id) { + $system = new stdclass(); + $system->status = 'inactive'; + + $this->system->update($id, $system, false); + if(dao::isError()) return $this->sendError(dao::getError()); + + $this->loadModel('action')->create('system', $id, 'inactive'); + $this->sendSuccess(array('load' => true)); } } From efd63b2d69eb7ccc95e30d248cb719375edcb3f9 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 13:15:48 +0800 Subject: [PATCH 298/300] * [task#133161] add delete method. --- module/system/control.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/module/system/control.php b/module/system/control.php index 7591617b56..4bbc0dce4d 100644 --- a/module/system/control.php +++ b/module/system/control.php @@ -665,4 +665,20 @@ class system extends control $this->loadModel('action')->create('system', $id, 'inactive'); $this->sendSuccess(array('load' => true)); } + + /** + * 删除应用。 + * Delete application. + * + * @param int $id + * @access public + * @return void + */ + public function delete(int $id) + { + $this->system->delete(TABLE_SYSTEM, $id); + + if(dao::isError()) return $this->sendError(dao::getError()); + $this->sendSuccess(array('load' => true)); + } } From f78f1228deb7cdf50b51bd5880349afa2c92af96 Mon Sep 17 00:00:00 2001 From: liyang Date: Thu, 21 Nov 2024 14:06:22 +0800 Subject: [PATCH 299/300] * [task#133161] add privs group. --- module/group/lang/de.php | 2 +- module/group/lang/en.php | 1 + module/group/lang/fr.php | 1 + module/group/lang/resource.php | 6 ++++++ module/group/lang/zh-cn.php | 1 + module/group/packagemanager.php | 11 +++++++++++ 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/module/group/lang/de.php b/module/group/lang/de.php index eb58846536..17891a8ed7 100644 --- a/module/group/lang/de.php +++ b/module/group/lang/de.php @@ -598,6 +598,6 @@ $lang->group->package->deleteJob = 'Delete PipeLine'; $lang->group->package->browseApplication = 'Application List'; $lang->group->package->mangeApplication = 'Manage Application'; $lang->group->package->trainPracticeLib = 'Practice Library'; -$lang->group->package->other = 'Other'; +$lang->group->package->application = 'Manage Application'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/lang/en.php b/module/group/lang/en.php index e55e96983b..3115b43465 100644 --- a/module/group/lang/en.php +++ b/module/group/lang/en.php @@ -598,5 +598,6 @@ $lang->group->package->deleteJob = 'Delete PipeLine'; $lang->group->package->browseApplication = 'Application List'; $lang->group->package->mangeApplication = 'Manage Application'; $lang->group->package->trainPracticeLib = 'Practice Library'; +$lang->group->package->application = 'Manage Application'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/lang/fr.php b/module/group/lang/fr.php index 4628b085fc..14ff7ef567 100644 --- a/module/group/lang/fr.php +++ b/module/group/lang/fr.php @@ -598,5 +598,6 @@ $lang->group->package->deleteJob = 'Delete PipeLine'; $lang->group->package->browseApplication = 'Application List'; $lang->group->package->mangeApplication = 'Manage Application'; $lang->group->package->trainPracticeLib = 'Practice Library'; +$lang->group->package->application = 'Manage Application'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index f8010155ec..0397aeeba7 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -1495,6 +1495,12 @@ $lang->resource->system->dashboard = 'dashboard'; $lang->resource->system->dblist = 'dbList'; $lang->resource->system->configdomain = 'configDomain'; $lang->resource->system->ossview = 'ossView'; +$lang->resource->system->browse = 'browse'; +$lang->resource->system->create = 'create'; +$lang->resource->system->edit = 'edit'; +$lang->resource->system->delete = 'delete'; +$lang->resource->system->active = 'active'; +$lang->resource->system->inactive = 'inactive'; $lang->resource->ops = new stdclass(); $lang->resource->ops->provider = 'provider'; diff --git a/module/group/lang/zh-cn.php b/module/group/lang/zh-cn.php index e8abf51548..390b516590 100644 --- a/module/group/lang/zh-cn.php +++ b/module/group/lang/zh-cn.php @@ -598,5 +598,6 @@ $lang->group->package->deleteJob = '删除流水线'; $lang->group->package->browseApplication = '浏览应用'; $lang->group->package->mangeApplication = '管理应用'; $lang->group->package->trainPracticeLib = '实践库'; +$lang->group->package->application = '管理应用'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/packagemanager.php b/module/group/packagemanager.php index 7120b5a8ad..6349f47206 100644 --- a/module/group/packagemanager.php +++ b/module/group/packagemanager.php @@ -941,6 +941,17 @@ $config->group->package->releaseNotify->subset = 'release'; $config->group->package->releaseNotify->privs = array(); $config->group->package->releaseNotify->privs['release-notify'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 70, 'depend' => array('release-view'), 'recommend' => array('release-create', 'release-edit', 'release-linkBug', 'release-linkStory', 'release-unlinkBug', 'release-unlinkStory')); +$config->group->package->application = new stdclass(); +$config->group->package->application->order = 30; +$config->group->package->application->subset = 'release'; +$config->group->package->application->privs = array(); +$config->group->package->application->privs['system-browse'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('release-browse'), 'recommend' => array('system-create', 'system-edit', 'system-active', 'system-inactive', 'system-delete')); +$config->group->package->application->privs['system-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-edit', 'system-active', 'system-inactive', 'system-delete')); +$config->group->package->application->privs['system-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-active', 'system-inactive', 'system-delete')); +$config->group->package->application->privs['system-active'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-edit', 'system-inactive', 'system-delete')); +$config->group->package->application->privs['system-inactive'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-edit', 'system-active', 'system-delete')); +$config->group->package->application->privs['system-delete'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('system-browse'), 'recommend' => array('system-create', 'system-edit', 'system-active', 'system-inactive')); + $config->group->package->projectPlan = new stdclass(); $config->group->package->projectPlan->order = 5; $config->group->package->projectPlan->subset = 'projectplan'; From 398b04527f0c9d4c3f218c22a6e4c842ab72c4f3 Mon Sep 17 00:00:00 2001 From: daitingting Date: Thu, 21 Nov 2024 05:51:55 +0000 Subject: [PATCH 300/300] * [task#133009,doing,1h] Get version data. --- module/pivot/tao.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/module/pivot/tao.php b/module/pivot/tao.php index eda310c83d..6671366256 100644 --- a/module/pivot/tao.php +++ b/module/pivot/tao.php @@ -12,10 +12,14 @@ class pivotTao extends pivotModel */ protected function fetchPivot(int $id): object|bool { - return $this->dao->select('*')->from(TABLE_PIVOT) - ->where('id')->eq($id) - ->andWhere('deleted')->eq('0') - ->fetch(); + $pivot = $this->dao->select('*')->from(TABLE_PIVOT)->where('id')->eq($id)->andWhere('deleted')->eq('0')->fetch(); + if(!$pivot) return false; + + $specData = $this->dao->select('*')->from(TABLE_PIVOTSPEC)->where('pivot')->eq($id)->andWhere('version')->eq($pivot->version)->fetch(); + if(!$specData) return $pivot; + + foreach($specData as $specKey => $specValue) $pivot->$specKey = $specValue; + return $pivot; } /** * 获取产品列表。