model.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <?php
  2. /**
  3. * The model file of holiday module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  8. * @package holiday
  9. * @version $Id
  10. * @link https://www.zentao.net
  11. */
  12. class holidayModel extends model
  13. {
  14. /**
  15. * 通过 ID 获取节假日。
  16. * Get holiday by id.
  17. *
  18. * @param int $id
  19. * @access public
  20. * @return object|bool
  21. */
  22. public function getById($id)
  23. {
  24. return $this->dao->select('*')->from(TABLE_HOLIDAY)->where('id')->eq($id)->fetch();
  25. }
  26. /**
  27. * 获取节假日列表。
  28. * Get holiday list.
  29. *
  30. * @param string $year
  31. * @param string $type
  32. * @access public
  33. * @return array
  34. */
  35. public function getList($year = '', $type = 'all')
  36. {
  37. return $this->dao->select('*')->from(TABLE_HOLIDAY)
  38. ->where('1=1')
  39. ->beginIf(!empty($year))
  40. ->andWhere('year', true)->eq($year)
  41. ->orWhere("DATE_FORMAT(`begin`, 'YYYY')")->eq($year)
  42. ->orWhere("DATE_FORMAT(`end`, 'YYYY')")->eq($year)
  43. ->markright(1)
  44. ->fi()
  45. ->beginIf($type != 'all' && $type)->andWhere('type')->eq($type)->fi()
  46. ->fetchAll('id');
  47. }
  48. /**
  49. * 获取年份列表。
  50. * Get year pairs.
  51. *
  52. * @access public
  53. * @return array
  54. */
  55. public function getYearPairs()
  56. {
  57. return $this->dao->select('year,year')->from(TABLE_HOLIDAY)->groupBy('year')->orderBy('year_desc')->fetchPairs();
  58. }
  59. /**
  60. * 创建一个节假日。
  61. * Create a holiday.
  62. *
  63. * @param object $holiday
  64. * @access public
  65. * @return int|bool
  66. */
  67. public function create($holiday)
  68. {
  69. $this->dao->insert(TABLE_HOLIDAY)->data($holiday)
  70. ->autoCheck()
  71. ->batchCheck($this->config->holiday->require->create, 'notempty')
  72. ->check('end', 'ge', $holiday->begin)
  73. ->exec();
  74. $lastInsertID = $this->dao->lastInsertID();
  75. if(dao::isError()) return false;
  76. $beginDate = $holiday->begin;
  77. $endDate = $holiday->end;
  78. /* Update project. */
  79. $this->updateProgramPlanDuration($beginDate, $endDate);
  80. $this->updateProjectRealDuration($beginDate, $endDate);
  81. /* Update task. */
  82. $this->updateTaskPlanDuration($beginDate, $endDate);
  83. $this->updateTaskRealDuration($beginDate, $endDate);
  84. return $lastInsertID;
  85. }
  86. /**
  87. * 编辑一个节假日。
  88. * Edit holiday.
  89. *
  90. * @param object $holiday
  91. * @access public
  92. * @return bool
  93. */
  94. public function update($holiday)
  95. {
  96. $this->dao->update(TABLE_HOLIDAY)
  97. ->data($holiday)
  98. ->autoCheck()
  99. ->batchCheck($this->config->holiday->require->edit, 'notempty')
  100. ->check('end', 'ge', $holiday->begin)
  101. ->where('id')->eq($holiday->id)
  102. ->exec();
  103. if(dao::isError()) return false;
  104. $beginDate = $holiday->begin;
  105. $endDate = $holiday->end;
  106. /* Update project. */
  107. $this->updateProgramPlanDuration($beginDate, $endDate);
  108. $this->updateProjectRealDuration($beginDate, $endDate);
  109. /* Update task. */
  110. $this->updateTaskPlanDuration($beginDate, $endDate);
  111. $this->updateTaskRealDuration($beginDate, $endDate);
  112. return !dao::isError();
  113. }
  114. /**
  115. * 通过开始和结束日期获取节假日。
  116. * Get holidays by begin and end.
  117. *
  118. * @param string $begin
  119. * @param string $end
  120. * @access public
  121. * @return array
  122. */
  123. public function getHolidays($begin, $end)
  124. {
  125. $records = $this->dao->select('*')->from(TABLE_HOLIDAY)
  126. ->where('type')->eq('holiday')
  127. ->andWhere('end')->ge($begin)
  128. ->andWhere('begin')->le($end)
  129. ->fetchAll('id');
  130. $naturalDays = $this->getDaysBetween($begin, $end);
  131. $holidays = array();
  132. foreach($records as $record)
  133. {
  134. $dates = $this->getDaysBetween($record->begin, $record->end);
  135. $holidays = array_merge($holidays, $dates);
  136. }
  137. return array_intersect($naturalDays, $holidays);
  138. }
  139. /**
  140. * 获取工作日。
  141. * Get working days.
  142. *
  143. * @param string $begin
  144. * @param string $end
  145. * @access public
  146. * @return array
  147. */
  148. public function getWorkingDays($begin = '', $end = '')
  149. {
  150. $records = $this->dao->select('*')->from(TABLE_HOLIDAY)
  151. ->where('type')->eq('working')
  152. ->andWhere('begin')->le($end)
  153. ->andWhere('end')->ge($begin)
  154. ->fetchAll('id');
  155. $workingDays = array();
  156. foreach($records as $record)
  157. {
  158. $dates = $this->getDaysBetween($record->begin, $record->end);
  159. $workingDays = array_merge($workingDays, $dates);
  160. }
  161. return $workingDays;
  162. }
  163. /**
  164. * 获取实际工作日。
  165. * Get actual working days.
  166. *
  167. * @param string $begin
  168. * @param string $end
  169. * @access public
  170. * @return array
  171. */
  172. public function getActualWorkingDays($begin, $end)
  173. {
  174. if(empty($begin) || empty($end) || $begin == '0000-00-00' || $end == '0000-00-00') return array();
  175. /* Get holidays, working days and weekend days .*/
  176. $holidays = $this->getHolidays($begin, $end);
  177. $workingDays = $this->getWorkingDays($begin, $end);
  178. $weekend = isset($this->config->execution->weekend) ? $this->config->execution->weekend : 2;
  179. /* When the start date and end date are the same. */
  180. $actualDays = array();
  181. if($begin == $end)
  182. {
  183. if(in_array($begin, $workingDays)) return array($begin);
  184. if(in_array($begin, $holidays)) return array();
  185. $w = date('w', strtotime($begin));
  186. if($w == 0 || ($weekend == 2 && $w == 6)) return array();
  187. return array($begin);
  188. }
  189. /* Process actual working days. */
  190. for($i = 0, $currentDay = $begin; $currentDay < $end; $i ++)
  191. {
  192. $currentDay = date('Y-m-d', strtotime("{$begin} + {$i} days"));
  193. $w = date('w', strtotime($currentDay));
  194. if(in_array($currentDay, $workingDays))
  195. {
  196. $actualDays[] = $currentDay;
  197. continue;
  198. }
  199. if(in_array($currentDay, $holidays)) continue;
  200. if($w == 0 || ($weekend == 2 && $w == 6)) continue;
  201. $actualDays[] = $currentDay;
  202. }
  203. return $actualDays;
  204. }
  205. /**
  206. * 获取开始和结束日期间的日期。
  207. * Get the dates between the begin and end.
  208. *
  209. * @param string $begin
  210. * @param string $end
  211. * @access public
  212. * @return array
  213. */
  214. public function getDaysBetween($begin, $end)
  215. {
  216. $beginTime = strtotime($begin);
  217. $endTime = strtotime($end);
  218. $days = ($endTime - $beginTime) / 86400;
  219. if(!$beginTime) return array();
  220. $dateList = array();
  221. for($i = 0; $i <= $days; $i ++) $dateList[] = date('Y-m-d', strtotime("+{$i} days", $beginTime));
  222. return $dateList;
  223. }
  224. /**
  225. * 判断一天是否是节假日。
  226. * Judge if is holiday.
  227. *
  228. * @param string $date
  229. * @access public
  230. * @return bool
  231. */
  232. public function isHoliday($date)
  233. {
  234. $record = $this->dao->select('*')->from(TABLE_HOLIDAY)
  235. ->where('type')->eq('holiday')
  236. ->andWhere('begin')->le($date)
  237. ->andWhere('end')->ge($date)
  238. ->fetch();
  239. return !empty($record);
  240. }
  241. /**
  242. * 判断一天是否是工作日。
  243. * Judge if is working days.
  244. *
  245. * @param string $date
  246. * @access public
  247. * @return bool
  248. */
  249. public function isWorkingDay($date)
  250. {
  251. $record = $this->dao->select('*')->from(TABLE_HOLIDAY)
  252. ->where('type')->eq('working')
  253. ->andWhere('begin')->le($date)
  254. ->andWhere('end')->ge($date)
  255. ->fetch();
  256. return !empty($record);
  257. }
  258. /**
  259. * 更新项目的工期。
  260. * Update project duration.
  261. *
  262. * @param string $beginDate
  263. * @param string $endDate
  264. * @access public
  265. * @return void
  266. */
  267. public function updateProgramPlanDuration($beginDate, $endDate)
  268. {
  269. $updateProjectList = $this->dao->select('id, begin, end')
  270. ->from(TABLE_PROJECT)
  271. ->where('status')->ne('done')
  272. ->andWhere('type')->ne('program')
  273. ->andWhere('end')->ne(LONG_TIME)
  274. ->andWhere('begin', true)->between($beginDate, $endDate)
  275. ->orWhere('end')->between($beginDate, $endDate)
  276. ->orWhere("(`begin` < '{$beginDate}' AND `end` > '{$endDate}')")
  277. ->markRight(1)
  278. ->fetchAll();
  279. foreach($updateProjectList as $project)
  280. {
  281. $realDuration = $this->getActualWorkingDays($project->begin, $project->end);
  282. $realDuration = count($realDuration);
  283. $this->dao->update(TABLE_PROJECT)->set('planDuration')->eq($realDuration)->where('id')->eq($project->id)->exec();
  284. }
  285. }
  286. /**
  287. * 测试更新项目实际工期。
  288. * Update project real duration.
  289. *
  290. * @param string $beginDate
  291. * @param string $endDate
  292. * @access public
  293. * @return void
  294. */
  295. public function updateProjectRealDuration($beginDate, $endDate)
  296. {
  297. $updateProjectList = $this->dao->select('id, realBegan, realEnd')
  298. ->from(TABLE_PROJECT)
  299. ->where('status')->ne('done')
  300. ->andWhere('type')->ne('program')
  301. ->andwhere('realBegan', true)->between($beginDate, $endDate)
  302. ->orWhere('realEnd')->between($beginDate, $endDate)
  303. ->orWhere("(`realBegan` < '{$beginDate}' AND `realEnd` > '{$endDate}')")
  304. ->markRight(1)
  305. ->fetchAll();
  306. foreach($updateProjectList as $project)
  307. {
  308. $realDuration = $this->getActualWorkingDays($project->realBegan, $project->realEnd);
  309. $realDuration = count($realDuration);
  310. $this->dao->update(TABLE_PROJECT)->set('realDuration')->eq($realDuration)->where('id')->eq($project->id)->exec();
  311. }
  312. }
  313. /**
  314. * 更新任务的计划工期。
  315. * Update task plan duration.
  316. *
  317. * @param string $beginDate
  318. * @param string $endDate
  319. * @access public
  320. * @return void
  321. */
  322. public function updateTaskPlanDuration($beginDate, $endDate)
  323. {
  324. $updateTaskList = $this->dao->select('id, estStarted, deadline')
  325. ->from(TABLE_TASK)
  326. ->where('estStarted')->between($beginDate, $endDate)
  327. ->orWhere('deadline')->between($beginDate, $endDate)
  328. ->orWhere("(`estStarted` < '{$beginDate}' AND `deadline` > '{$endDate}')")
  329. ->andWhere('status') ->ne('done')
  330. ->fetchAll();
  331. foreach($updateTaskList as $task)
  332. {
  333. $planduration = $this->getActualWorkingDays($task->estStarted, $task->deadline);
  334. $planduration = count($planduration);
  335. $this->dao->update(TABLE_TASK)->set('planduration')->eq($planduration)->where('id')->eq($task->id)->exec();
  336. }
  337. }
  338. /**
  339. * 更新任务的实际工期。
  340. * Update task real duration.
  341. *
  342. * @param string $beginDate
  343. * @param string $endDate
  344. * @access public
  345. * @return void
  346. */
  347. public function updateTaskRealDuration($beginDate, $endDate)
  348. {
  349. $updateTaskList = $this->dao->select('id, realStarted, finishedDate')
  350. ->from(TABLE_TASK)
  351. ->where('realStarted')->between($beginDate, $endDate)
  352. ->orWhere("date_format(`finishedDate`,'%Y-%m-%d')")->between($beginDate, $endDate)
  353. ->orWhere("(`realStarted` < '$beginDate' AND date_format(`finishedDate`,'%Y-%m-%d') > '$endDate')")
  354. ->andWhere('status')->ne('done')
  355. ->fetchAll();
  356. foreach($updateTaskList as $task)
  357. {
  358. $realDuration = $this->getActualWorkingDays($task->realStarted, date('Y-m-d', $task->finishedDate ? strtotime($task->finishedDate) : time()));
  359. $realDuration = count($realDuration);
  360. $this->dao->update(TABLE_TASK)->set('realDuration')->eq($realDuration)->where('id')->eq($task->id)->exec();
  361. }
  362. }
  363. /**
  364. * API: 获取节假日。
  365. * Get holidays by api.
  366. *
  367. * @param string $year
  368. * @access public
  369. * @return array
  370. */
  371. public function getHolidayByAPI($year = '')
  372. {
  373. /* Get holidays by file. */
  374. $yearFile = $this->app->wwwRoot . 'static/json/holiday/' . $year . '.json';
  375. $defaultFile = $this->app->wwwRoot . 'static/json/holiday/default.json';
  376. $data = file_exists($yearFile) ? file_get_contents($yearFile) : file_get_contents($defaultFile);
  377. $data = $data ? json_decode($data) : '';
  378. $days = isset($data->days) ? (array)$data->days : array();
  379. if(empty($days))
  380. {
  381. /* Get holidays by api. */
  382. if(empty($year)) $year = date('Y');
  383. $apiRoot = sprintf($this->config->holiday->apiRoot, $year);
  384. $data = json_decode(common::http($apiRoot));
  385. $days = isset($data->days) ? (array)$data->days : array();
  386. }
  387. /* Build holiday data. */
  388. $holidays = array();
  389. $privDay = 0;
  390. $prevDayOff = true;
  391. $daysIndex = count($days) - 1;
  392. foreach($days as $index => $day)
  393. {
  394. if($index < $daysIndex && (strtotime($day->date) - $privDay > 86400 || $day->isOffDay != $prevDayOff))
  395. {
  396. $holiday = new stdClass();
  397. $holiday->type = $day->isOffDay ? 'holiday' : 'working';
  398. $holiday->name = $day->name . zget($this->lang->holiday->typeList, $holiday->type);
  399. $holiday->begin = $day->date;
  400. $holiday->end = '';
  401. $holidays[] = $holiday;
  402. }
  403. if(isset($holiday) || $index == $daysIndex)
  404. {
  405. $holidayNum = count($holidays);
  406. if($holidayNum > 1)
  407. {
  408. if(isset($holiday))
  409. {
  410. $holidays[$holidayNum - 2]->end = date('Y-m-d', $privDay);
  411. }
  412. else
  413. {
  414. $holidays[$holidayNum - 1]->end = $day->date;
  415. }
  416. }
  417. if(isset($holiday)) unset($holiday);
  418. }
  419. $privDay = strtotime($day->date);
  420. $prevDayOff = $day->isOffDay;
  421. }
  422. return $holidays;
  423. }
  424. /**
  425. * 批量创建节假日。
  426. * Batch create holiday.
  427. *
  428. * @param array $holidays
  429. * @access public
  430. * @return int|bool
  431. */
  432. public function batchCreate($holidays)
  433. {
  434. foreach($holidays as $holiday)
  435. {
  436. $this->dao->insert(TABLE_HOLIDAY)->data($holiday)
  437. ->autoCheck()
  438. ->batchCheck($this->config->holiday->require->create, 'notempty')
  439. ->check('end', 'ge', $holiday->begin)
  440. ->exec();
  441. }
  442. if(dao::isError()) return false;
  443. $lastInsertID = $this->dao->lastInsertID();
  444. return $lastInsertID;
  445. }
  446. }