VM暫存
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

439 行
14KB

  1. <?php
  2. /*
  3. -----
  4. file: station_services.php 場站應用系統服務程式
  5. workerman執行:
  6. /usr/bin/php /home/bigbang/apps/coworker/station_services.php start -d
  7. workerman停止
  8. /usr/bin/php /home/bigbang/apps/coworker/station_services.php stop
  9. -----
  10. */
  11. require_once '/home/bigbang/libs/Workerman/Autoloader.php';
  12. use Workerman\Worker;
  13. Worker::$logFile = '/dev/null'; // 不記錄log file
  14. //Worker::$pidFile = '/home/bigbang/libs/Workerman/' .basename(__FILE__).'.pid';
  15. // 場站共用設定檔
  16. require_once '/home/bigbang/apps/coworker/station.config.php';
  17. define('APP_NAME', 'stations'); // application name
  18. // 序號檔路徑
  19. define('SEQNO_PATH', '/home/data/seqno/');
  20. define('WORKERMAN_DEBUG', 1);
  21. if (WORKERMAN_DEBUG)
  22. {
  23. ini_set('display_errors', '1');
  24. error_reporting(E_ALL);
  25. set_error_handler('error_handler', E_ALL);
  26. }
  27. // 每月最後一日
  28. $last_date_month = array
  29. (
  30. 1 => 31,
  31. 2 => 28,
  32. 3 => 31,
  33. 4 => 30,
  34. 5 => 31,
  35. 6 => 30,
  36. 7 => 31,
  37. 8 => 31,
  38. 9 => 30,
  39. 10 => 31,
  40. 11 => 30,
  41. 12 => 31
  42. );
  43. $xvars = array(); // 共用變數
  44. // 共用記憶體
  45. $mem = new Memcache;
  46. if (! $mem->pconnect(MEMCACHE_HOST, MEMCACHE_PORT))
  47. {
  48. echo 'Could not connect memcache';
  49. Worker::stopAll();
  50. }
  51. // 連接總管理處資料庫
  52. $pdo = new PDO($dbs['dsn'], $dbs['user_name'], $dbs['password']);
  53. // 讀取info場站共用資訊
  54. $sql_info = 'select station_no, station_name, station_full_name, company_no, station_ip, hq_url, origin_url, period_name, park_time,member_attr_list, period_list from info where seqno = 1';
  55. $info_arr = $pdo->query($sql_info)->fetch(PDO::FETCH_ASSOC);
  56. // global variable
  57. $pdo = null;
  58. $query_syncs = null;
  59. $query_sync2hq_ok = null;
  60. // 未同步者批次同步至總管理處
  61. $sql_syncs = 'select st_sync_no, station_no, act, hq_tname, st_tname, st_seqno, sync_data from syncs where synced = 0 and st_sync_no = ?';
  62. // $query_syncs = $pdo->prepare($sql_syncs);
  63. // 同步成功
  64. $sql_sync2hq_ok = 'update syncs set synced = 1, hq_sync_no = ? where st_sync_no = ?';
  65. // $query_sync2hq_ok = $pdo->prepare($sql_sync2hq_ok);
  66. // 預設curl參數
  67. $curl_ch = curl_init();
  68. $curl_options = array
  69. (
  70. CURLOPT_URL => $info_arr['hq_url'],
  71. CURLOPT_HEADER => 0,
  72. CURLOPT_RETURNTRANSFER => 1, // 返回值不顯示, 只做變數用
  73. CURLOPT_POST => 1,
  74. CURLOPT_CONNECTTIMEOUT => 5,
  75. CURLOPT_TIMEOUT => 5
  76. );
  77. init_station_xvars();
  78. // 建立一個Worker監聽60133埠,不使用任何應用層協定
  79. $worker = new Worker("http://0.0.0.0:60133");
  80. // 啟動N個進程對外提供服務
  81. $worker->count = 4;
  82. // ----- program start -----
  83. // 當用戶端發來數據(主程式)
  84. $worker->onMessage = function($connection, $msg_in)
  85. {
  86. global $dbs, $pdo, $sql_syncs, $sql_sync2hq_ok, $query_syncs, $query_sync2hq_ok;
  87. // 無連線或status != Localhost via UNIX socket, 重連線之
  88. if (empty($pdo) || preg_match('/socket$/', $pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS)) != 1 )
  89. {
  90. $pdo = new PDO($dbs['dsn'], $dbs['user_name'], $dbs['password'], array(PDO::ATTR_PERSISTENT => true));
  91. $query_syncs = $pdo->prepare($sql_syncs);
  92. $query_sync2hq_ok = $pdo->prepare($sql_sync2hq_ok);
  93. trigger_error('new pdo');
  94. }
  95. if (!empty($_REQUEST['cmd']))
  96. {
  97. trigger_error('worker.onMessage: ' . print_r($_REQUEST, true));
  98. $funcs = @$_REQUEST['cmd'];
  99. $funcs($connection);
  100. }
  101. };
  102. // 執行worker
  103. Worker::runAll();
  104. // ----- end of program -----
  105. // 同步化, 場站 -> 總管理處 , 參數:
  106. // http://hq_ip:60133/?cmd=st_syncs&station_no=12110&act=U&tname=members&key=member_no&kval=12110101&jdata=json_str&ck=str32
  107. function st_syncs($connection)
  108. {
  109. global $pdo_hq, $query_hq_sync;
  110. // 暫不驗證
  111. $syncs_sql = parms2sql($_REQUEST);
  112. // exec:傳回筆數, query:傳回資料
  113. $rows_affected = $pdo_hq->exec($syncs_sql);
  114. // 如果新增資料, 回傳insert ID
  115. if ($_REQUEST['act'] == 'A') $rows_affected = $pdo_hq->lastInsertId();
  116. @$connection->send($rows_affected); // 回應當前序號或筆數
  117. $confirms = $rows_affected > 0 ? 1 : 0;
  118. $query_hq_sync->execute(array($_REQUEST['station_no'], $_REQUEST['act'], $_REQUEST['tname'], $_REQUEST['data'], $confirms));
  119. }
  120. // 讀取序號
  121. // http://localhost:60133/?cmd=seqno&seqname=members&init_no=1025
  122. function seqno($connection)
  123. {
  124. $seqno_fname = SEQNO_PATH . "{$_REQUEST['seqname']}.txt";
  125. $fp = fopen($seqno_fname, 'r+');
  126. // lock, 讀入序號, 加1寫回, close
  127. if ($fp)
  128. {
  129. flock($fp, LOCK_EX);
  130. $seqno = fread($fp, 80);
  131. $next_no = $seqno + 1;
  132. rewind($fp);
  133. fwrite($fp, $next_no);
  134. flock($fp, LOCK_UN);
  135. fclose($fp);
  136. }
  137. else // 如無此序號檔, 新建之, 並傳回初始值
  138. {
  139. $seqno = empty($_REQUEST['init_no']) ? 1 : $_REQUEST['init_no'];
  140. $next_no = $seqno + 1;
  141. file_put_contents($seqno_fname, $next_no, FILE_APPEND);
  142. }
  143. @$connection->send($seqno); // 回應當前序號
  144. }
  145. // 檢查是否有連線 ?
  146. function check_connect($connection)
  147. {
  148. $connected = @fsockopen($_REQUEST['ip'], $_REQUEST['port'], $errno, $errstr, $_REQUEST['timeout']);
  149. if ($connected)
  150. {
  151. fclose($connected);
  152. $is_connected = 1; //action when connected
  153. }
  154. else
  155. {
  156. $is_connected = 0; //action in connection failure
  157. }
  158. return $is_connected;
  159. }
  160. // 取得下一租期期最後一日
  161. function last_date_next($connection)
  162. {
  163. @cross_header();
  164. @$connection->send(last_date_next_period($_REQUEST['last_date_curr'], $_REQUEST['fee_period'])); // 參數:本期截止日,繳期
  165. }
  166. // 取得下一租期期最後一日, 參數: 本期截止日, 繳期
  167. function last_date_next_period($last_date_curr, $fee_period)
  168. {
  169. global $last_date_month;
  170. $arr = explode('-', $last_date_curr);
  171. $yy = (int) $arr[0]; // 取年份
  172. $mm = (int) $arr[1]; // 取月份
  173. $mm += $fee_period;
  174. if ($mm > 12) // 超過12月, 年度+1, 折算明年度月份
  175. {
  176. ++$yy;
  177. $mm -= 12;
  178. }
  179. $dd = $mm == 2 && ($yy % 4) == 0 ? 29 : $last_date_month[$mm];
  180. if ($mm < 10) $mm = "0{$mm}"; // 個位數前面補0
  181. return "{$yy}-{$mm}-{$dd}";
  182. }
  183. // web跨網域header設定
  184. function cross_header()
  185. {
  186. global $info_arr;
  187. \Workerman\Protocols\Http::header('Access-Control-Allow-Origin: ' . "{$info_arr['origin_url']}");
  188. \Workerman\Protocols\Http::header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  189. \Workerman\Protocols\Http::header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, Accept');
  190. }
  191. // 將參數轉成sql命令字串
  192. function parms2sql($parms)
  193. {
  194. $act = $parms['act'];
  195. switch($act)
  196. {
  197. case 'U': // 更新場站資料庫 ????
  198. $sql = "update {$parms['tname']} set {$parms['data']} where {$parms['key']} = '{$parms['kval']}';";
  199. break;
  200. case 'A': // 新增資料
  201. $fields_str = implode(',', array_keys($data));
  202. $values_str = "'".implode("',", array_values($data))."'";
  203. $sql = "insert into {$parms['tname']} ({$fields_str}) values({$values_str});";
  204. break;
  205. case 'D': // 刪除資料
  206. $sql = "delete {$parms['tname']} where {$parms['key']} = '{$parms['kval']}';";
  207. break;
  208. }
  209. }
  210. // 場站初始化參數
  211. function init_station_xvars()
  212. {
  213. global $mem, $info_arr, $xvars;
  214. $data = array
  215. (
  216. 'cmd' => 'init_station',
  217. 'station_no' => $info_arr['station_no'],
  218. 'park_time' => $info_arr['park_time']
  219. );
  220. $jdata = worker_tx($data);
  221. if(empty($jdata))
  222. {
  223. trigger_error('中控已斷線, 建立預設值..'.print_r($jdata, true));
  224. // 中控已斷線, 建立預設值
  225. $xvars = array();
  226. $xvars['info']['period_name'] = array(1 => "月繳", 2 => "雙月繳", 3 => "季繳", 6 => "雙季繳", 12 => "四季繳");
  227. $xvars['info']['member_attr'] = array(1 => "一般", 2 => "里民", 3 => "身障", 4 => "員工", 5 => "里長", 250 => "VIP");
  228. $xvars['pt'] =
  229. array(
  230. 'RE' => array(
  231. 'seqno' => 1000,
  232. 'timex' => array(
  233. '0' => array('type' => 1, 'w_start' => 0, 'w_end' => 6, 'time_start' => '00:00:00', 'time_end' => '23:59:59')
  234. ),
  235. 'remarks' => '全天全時段'
  236. ),
  237. 'NF' => array(
  238. 'seqno' => 1010,
  239. 'timex' => array(
  240. '0' => array('type' => 1, 'w_start' => 1, 'w_end' => 5, 'time_start' => '18:00:00', 'time_end' => '23:59:59'),
  241. '1' => array('type' => 1, 'w_start' => 1, 'w_end' => 5, 'time_start' => '00:00:00', 'time_end' => '07:59:59')
  242. ),
  243. 'remarks' => '週一至週五: 00:00:00 - 07:59:59 <br/>週一至週五: 18:00:00 - 23:59:59'
  244. ),
  245. 'WK66' => array(
  246. 'seqno' => 1026,
  247. 'timex' => array(
  248. '0' => array('type' => 1, 'w_start' => 1, 'w_end' => 5, 'time_start' => '06:00:00', 'time_end' => '17:59:59')
  249. ),
  250. 'remarks' => '週一至週五: 06:00:00 - 17:59:59'
  251. ),
  252. 'WK' => array(
  253. 'seqno' => 1020,
  254. 'timex' => array(
  255. '0' => array('type' => 1, 'w_start' => 1, 'w_end' => 5, 'time_start' => '07:30:00', 'time_end' => '18:29:59')
  256. ),
  257. 'remarks' => '週一至週五: 07:30:00 - 18:29:59'
  258. ),
  259. 'HO' => array(
  260. 'seqno' => 1030,
  261. 'timex' => array(
  262. '0' => array('type' => 1, 'w_start' => 6, 'w_end' => 6, 'time_start' => '00:00:00', 'time_end' => '23:59:59'),
  263. '1' => array('type' => 1, 'w_start' => 0, 'w_end' => 0, 'time_start' => '00:00:00', 'time_end' => '23:59:59')
  264. ),
  265. 'remarks' => '週六日全時段'
  266. )
  267. );
  268. }
  269. else
  270. {
  271. $xvars = json_decode($jdata, true);
  272. // 篩選繳期名稱
  273. $arr = array();
  274. $arr_list = explode(',', $info_arr['period_list']);
  275. foreach($arr_list as $idx) $arr[$idx] = $xvars['info']['period_name'][$idx];
  276. $xvars['info']['period_name'] = $arr;
  277. // 篩選會員身份
  278. $arr = array();
  279. $arr_list = explode(',', $info_arr['member_attr_list']);
  280. foreach($arr_list as $idx) $arr[$idx] = $xvars['info']['member_attr'][$idx];
  281. $xvars['info']['member_attr'] = $arr;
  282. }
  283. $mem->set('st_info', $info_arr);
  284. $mem->set('info', $xvars['info']);
  285. $mem->set('pt', $xvars['pt']);
  286. trigger_error('st_info: '.print_r($info_arr, true));
  287. trigger_error('xvars: '.print_r($xvars, true));
  288. }
  289. // 顯示場站初始化參數
  290. function show_all_vars($connection)
  291. {
  292. global $info_arr, $xvars;
  293. $connection->send(json_encode($xvars, JSON_UNESCAPED_UNICODE));
  294. }
  295. // 場站初始化參數
  296. function get_var($connection)
  297. {
  298. global $xvars;
  299. $str = gettype($xvars[$_REQUEST['var']]) == 'array' ? json_encode($xvars[$_REQUEST['var']], JSON_UNESCAPED_UNICODE) : $xvars[$_REQUEST['var']];
  300. $connection->send($str);
  301. }
  302. // 顯示場站初始化參數
  303. function set_var($connection)
  304. {
  305. global $xvars;
  306. $xvars[$_REQUEST['var']] = $_REQUEST['val'];
  307. $connection->send('1');
  308. }
  309. // 批次同步資料表至總管理處
  310. function sync_batch($connection)
  311. {
  312. global $query_syncs, $query_sync2hq_ok;
  313. //trigger_error(__FUNCTION__ . '..start..');
  314. $connection->close(); // 先斷線再繼續處理
  315. foreach(explode(',', $_REQUEST['sync_seqnos']) as $st_sync_no)
  316. {
  317. if(empty($st_sync_no)) continue;
  318. $query_syncs->execute(array($st_sync_no));
  319. $rows_syncs = $query_syncs->fetch(PDO::FETCH_ASSOC);
  320. $tx = $rows_syncs;
  321. if(empty($tx)) continue;
  322. $tx['cmd'] = 'sync_st2hq'; // 場站同步至總管理處
  323. $hq_sync_no = worker_tx($tx);
  324. //trigger_error(__FUNCTION__ . '| 1. st_sync_no: ' . $st_sync_no . ', 2. rows_syncs: ' . $rows_syncs . ', 3. hq_sync_no: ' . $hq_sync_no);
  325. // 同步成功, 記錄之 ( note: 總管理處的 hq_sync 記錄完成就視為完成,各場站 sync.synced = 1 )
  326. if ($hq_sync_no > 0)
  327. {
  328. $query_sync2hq_ok->execute(array($hq_sync_no, $st_sync_no));
  329. }
  330. else
  331. {
  332. trigger_error('sync err:'.json_encode($tx, JSON_UNESCAPED_UNICODE)); // TODO: sync 失敗的處理方式
  333. }
  334. }
  335. //trigger_error(__FUNCTION__ . '..end..');
  336. }
  337. // 月租金額計算
  338. function calculate_rents_amt($connection)
  339. {
  340. $_REQUEST['rents_amt1'] = 100;
  341. $_REQUEST['rents_amt2'] = 200;
  342. cross_header();
  343. $connection->send(json_encode($_REQUEST, JSON_UNESCAPED_UNICODE));
  344. }
  345. // worker connect至總管理處
  346. function worker_tx($data)
  347. {
  348. global $curl_ch, $curl_options;
  349. $curl_options[CURLOPT_POSTFIELDS] = $data;
  350. trigger_error('curl:'. print_r($curl_options, true));
  351. curl_setopt_array($curl_ch, $curl_options);
  352. return(curl_exec($curl_ch));
  353. }
  354. // 發生錯誤時集中在此處理
  355. function error_handler($errno, $errstr, $errfile, $errline, $errcontext)
  356. {
  357. $str = date('H:i:s')."|{$errstr}|{$errfile}|{$errline}|{$errno}\n";
  358. error_log($str, 3, LOG_PATH.APP_NAME . '.' . date('Ymd').'.log.txt'); // 3代表參考後面的檔名
  359. }