Orl 短網址,供三星、福斯使用

ProductManagementController.php 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <?php
  2. namespace App\Http\Controllers\Backend\EcManagement;
  3. use App\Http\Services\CheckParamService;
  4. use Illuminate\Http\Request;
  5. use App\Http\Services\Backend\EcManagement\ProductManagementService;
  6. use App\Http\Controllers\Controller;
  7. use App\Http\Services\ConstDef\GeneralConst;
  8. use Redis;
  9. use Aws\S3\S3Client;
  10. class ProductManagementController extends Controller
  11. {
  12. // 相關私有服務層調用器宣告
  13. private $productManagementSv;
  14. private $checkParamSv;
  15. private $s3Client;
  16. public function __construct()
  17. {
  18. // 建構服務層調用器
  19. $this->productManagementSv = new ProductManagementService();
  20. $this->checkParamSv = new CheckParamService();
  21. $this->s3Client = new S3Client([
  22. 'credentials' => [
  23. 'key' => env('AWS_S3_KEY'),
  24. 'secret' => env('AWS_S3_SECRET'),
  25. ],
  26. 'region' => env('AWS_S3_REGION'),
  27. 'version' => 'latest',
  28. ]);
  29. // 時區調整
  30. date_default_timezone_set("Asia/Taipei");
  31. }
  32. /**
  33. * 列表頁
  34. * 本端點非 API,直接渲染到 view
  35. * @return array: 返回渲染頁
  36. */
  37. public function index()
  38. {
  39. // 渲染
  40. return view('admin.EcManagement.ProductManagement', [
  41. 'can_step_0' => (Redis::get('TLW_0_MAN') || Redis::get('TLW_0_STOP')) ? false : true,
  42. 'can_step_2' => (Redis::get('TLW_2_CSV')) ? false : true,
  43. 'step_2_s3' => env("AWS_S3_BUCKET"),
  44. 'step_2_env' => env("APP_ENV"),
  45. 'brands' => $this->productManagementSv->getBrands(),
  46. 'batchs' => $this->productManagementSv->getBatchs(),
  47. 'mstatuss' => $this->productManagementSv->getMStatuss(),
  48. ]);
  49. }
  50. /**
  51. * 列表API
  52. * 接收搜尋參數與分頁等信息,返回JSON
  53. * @return string: 返回JSON
  54. */
  55. public function grid()
  56. {
  57. // 取得參數
  58. $param = $_GET;
  59. if ($param == null) exit();
  60. $draw = $param["draw"]; //客戶端傳來的查詢次數,無條件回傳用以核對
  61. $orderColumn = $param["order"][0]["column"] + 1; //前端從 0 開始送,但 mysql 從 1 開始算
  62. $orderDir = $param["order"][0]["dir"];
  63. $start = $param["start"]; // 頁碼
  64. $length = $param["length"]; // 一頁多大
  65. $searchValue = $param["search"]["value"];
  66. //客製化搜尋欄位
  67. $brand_no = $param["columns"][1]["search"]["value"];
  68. $batch_no = $param["columns"][2]["search"]["value"];
  69. $g_title = $param["columns"][3]["search"]["value"];
  70. $g_description = $param["columns"][4]["search"]["value"];
  71. $m_comment = $param["columns"][5]["search"]["value"];
  72. $m_status = $param["columns"][6]["search"]["value"];
  73. // 驗證
  74. if ($g_title != filter_var($g_title, FILTER_SANITIZE_SPECIAL_CHARS)) $g_title = "___CANNOT_FIND_STRING___";
  75. if (!$this->checkParamSv->LenMToN($g_title, 0, 50)) $g_title = "___CANNOT_FIND_STRING___";
  76. if ($g_description != filter_var($g_description, FILTER_SANITIZE_SPECIAL_CHARS)) $g_description = "___CANNOT_FIND_STRING___";
  77. if (!$this->checkParamSv->LenMToN($g_description, 0, 50)) $g_description = "___CANNOT_FIND_STRING___";
  78. if ($m_comment != filter_var($m_comment, FILTER_SANITIZE_SPECIAL_CHARS)) $m_comment = "___CANNOT_FIND_STRING___";
  79. if (!$this->checkParamSv->LenMToN($m_comment, 0, 50)) $m_comment = "___CANNOT_FIND_STRING___";
  80. //資料庫
  81. $recordsTotal = 0;
  82. $result = $this->productManagementSv->getProdocts(
  83. $recordsTotal,
  84. $orderColumn,
  85. $orderDir,
  86. $start,
  87. $length,
  88. $searchValue,
  89. $brand_no,
  90. $batch_no,
  91. $g_title,
  92. $g_description,
  93. $m_comment,
  94. $m_status
  95. );
  96. // 整理返回資料
  97. $data = array();
  98. for ($i = 0; $i < count($result); $i++) {
  99. $data[] = array(
  100. //一般資料
  101. $result[ $i ]["serno"],
  102. $result[ $i ]["g_id"],
  103. $result[ $i ]["g_title"],
  104. $result[ $i ]["g_description"],
  105. $result[ $i ]["g_image_link"],
  106. $result[ $i ]["g_price"],
  107. $result[ $i ]["g_sale_price"],
  108. $result[ $i ]["m_status"],
  109. $result[ $i ]["m_comment"],
  110. );
  111. }
  112. $json = array(
  113. "draw" => $draw,
  114. "recordsTotal" => $recordsTotal,
  115. "recordsFiltered" => $recordsTotal, //其實還是填入所有筆數,本次筆數可從陣列取得
  116. "data" => $data,
  117. );
  118. // 返回
  119. return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
  120. }
  121. public function manualXml()
  122. {
  123. Redis::set('TLW_0_MAN', 'true');
  124. return $this->manualXmlStatus();
  125. }
  126. public function manualXmlStatus()
  127. {
  128. $json = [
  129. 'can_step_0' => (Redis::get('TLW_0_MAN') || Redis::get('TLW_0_STOP')) ? false : true,
  130. ];
  131. return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
  132. }
  133. public function importCsv(Request $request)
  134. {
  135. // 上傳錯誤偵測(沒有包含檔案格式)
  136. switch ($_FILES['csv']['error']) {
  137. case UPLOAD_ERR_OK:
  138. break;
  139. case UPLOAD_ERR_NO_FILE:
  140. return json_decode(json_encode(['code' => 0, 'err' => '檔案大小錯誤'], JSON_NUMERIC_CHECK), true);
  141. case UPLOAD_ERR_INI_SIZE:
  142. case UPLOAD_ERR_FORM_SIZE:
  143. return json_decode(json_encode(['code' => 1, 'err' => '檔案太大'], JSON_NUMERIC_CHECK), true);
  144. default:
  145. return json_decode(json_encode(['code' => 1, 'err' => '系統錯誤'], JSON_NUMERIC_CHECK), true);
  146. }
  147. if ($_FILES['csv']['size'] > 100 * 1024 * 1024) {
  148. return json_decode(json_encode(['code' => 1, 'err' => '檔案最大100MB'], JSON_NUMERIC_CHECK), true);
  149. }
  150. // 路徑命名
  151. $path = '/tmp/tlwcsv/';
  152. if (!file_exists($path)) mkdir($path, 0777, true);
  153. chmod($path, 0777);
  154. $date = date('Y-m-d');
  155. if (!file_exists($path . 'tempImportCsv')) mkdir($path . 'tempImportCsv', 0777, true);
  156. chmod($path . 'tempImportCsv', 0777);
  157. if (!file_exists($path . 'tempImportCsv/' . $date)) mkdir($path . 'tempImportCsv/' . $date, 0777, true);
  158. chmod($path . 'tempImportCsv/' . $date, 0777);
  159. // 保存文件
  160. $time = time();
  161. move_uploaded_file($_FILES['csv']['tmp_name'], $path . 'tempImportCsv/' . $date . '/' . $time . '.csv');
  162. chmod($path . 'tempImportCsv/' . $date . '/' . $time . '.csv', 0777);
  163. // 異步處裡文件
  164. Redis::set('TLW_2_CSV', $path . 'tempImportCsv/' . $date . '/' . $time . '.csv' . '|' . $_POST['brand_no'] . '|' . $_POST['batch_no']);
  165. return json_decode(json_encode($_FILES, JSON_NUMERIC_CHECK), true);
  166. }
  167. public function importCsvStatus()
  168. {
  169. $json = [
  170. 'can_step_2' => (Redis::get('TLW_2_CSV') || Redis::get('TLW_2_MAN')) ? false : true,
  171. ];
  172. return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
  173. }
  174. public function importCsvHandle($info)
  175. {
  176. // 上鎖
  177. Redis::set('TLW_2_MAN', 'true');
  178. // 剖析資訊
  179. $info = explode('|', $info);
  180. $csv = $info[0];
  181. $brand_no = $info[1];
  182. $batch_no = $info[2];
  183. // 更新產品訊息
  184. $fileR = fopen($csv, "r");
  185. while (!feof($fileR)) {
  186. $line = fgets($fileR);
  187. $cell = str_getcsv($line, ',', '"');
  188. if ($cell[0] != 'Item ID') {
  189. $g_id = $cell[0];
  190. $m_comment = $cell[4] ?? '';
  191. $m_status = $cell[5] ?? '';
  192. switch ($m_status) {
  193. case GeneralConst::$mStatusMap[ GeneralConst::MSTATUS_WARNING ]['BNAME']:
  194. $detail = $this->productManagementSv->getProductDetail($brand_no, $batch_no, $g_id);
  195. $comment = (strpos($detail['m_comment'], $m_comment . ' / ') !== false) ? $detail['m_comment'] : $detail['m_comment'] . $m_comment . ' / ';
  196. if ($detail['m_status'] == GeneralConst::MSTATUS_DISAPPROVED) {
  197. // 原本是錯誤,但目前讀取到警告,就依然要保持錯誤狀態
  198. $this->productManagementSv->updateProduct($brand_no, $batch_no, $g_id, GeneralConst::MSTATUS_DISAPPROVED, $comment);
  199. } else {
  200. // 原本是正常或警告,目前讀取到警告,就要將狀態設為警告
  201. $this->productManagementSv->updateProduct($brand_no, $batch_no, $g_id, GeneralConst::MSTATUS_WARNING, $comment);
  202. }
  203. break;
  204. case GeneralConst::$mStatusMap[ GeneralConst::MSTATUS_DISAPPROVED ]['BNAME']:
  205. $detail = $this->productManagementSv->getProductDetail($brand_no, $batch_no, $g_id);
  206. $comment = (strpos($detail['m_comment'], $m_comment . ' / ') !== false) ? $detail['m_comment'] : $detail['m_comment'] . $m_comment . ' / ';
  207. // 原本不管是甚麼,目前讀取到錯誤,就要將狀態設為錯誤
  208. $this->productManagementSv->updateProduct($brand_no, $batch_no, $g_id, GeneralConst::MSTATUS_DISAPPROVED, $comment);
  209. break;
  210. }
  211. }
  212. }
  213. fclose($fileR);
  214. // 刪除檔案(權限問題無法刪除)
  215. unlink($csv);
  216. }
  217. public function manualEndpoint()
  218. {
  219. $param = $_GET;
  220. Redis::set('TLW_4_MAN', $param['brand_no'] . '|' . $param['batch_no']);
  221. return $this->manualEndpointStatus();
  222. }
  223. public function manualEndpointStatus()
  224. {
  225. $json = [
  226. 'can_step_4' => (Redis::get('TLW_4_MAN') || Redis::get('TLW_4_STOP')) ? false : true,
  227. ];
  228. return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
  229. }
  230. public function exportCsv(Request $request)
  231. {
  232. $title = [
  233. '系統號',
  234. '商品號',
  235. '品名',
  236. '描述',
  237. '網頁',
  238. '圖片路徑',
  239. '定價',
  240. '售價',
  241. '掃描結果',
  242. '掃描問題',
  243. ];
  244. $products = $this->productManagementSv->getExportProdocts($request);
  245. $this->productManagementSv->downloadExcel($title, $products, date("YmdHis") . '-REPORT-' . GeneralConst::$brandMap[ $request->brand_no ]['LB']);
  246. }
  247. // XML轉換腳本
  248. public function xml()
  249. {
  250. // 防重鎖
  251. if (!Redis::get('TLW_0_XML')) {
  252. Redis::set('TLW_0_XML', 'true');
  253. Redis::expire('TLW_0_XML', 1200); // 20 分鐘
  254. // 產生批次號
  255. $batch_no = date("Y-m-d H:i:s");
  256. $batch_no_digit = date("YmdHis");
  257. // 產生刪除批次號
  258. $del_batch_no = date('Y-m-d', strtotime(date("Y-m-d H:i:s") . "-14 days"));
  259. $del_batch_no_digit = date('Ymd', strtotime(date("Y-m-d") . "-14 days"));
  260. // 逐一品牌處理
  261. foreach (GeneralConst::$brandMap as $k => $v) {
  262. for ($i = 1; $i <= 14; $i++) {
  263. }
  264. // 載入品牌原始 XML
  265. $original = simplexml_load_file($v['URL']);
  266. // 開始寫入檔案
  267. $path = '/tmp/tlw/' . env('APP_ENV') . '/' . $v['LB'] . '/';
  268. if (!file_exists($path)) mkdir($path, 0777, true);
  269. $fileW = fopen($path . $batch_no_digit . '.xml', "w");
  270. fwrite($fileW, '<?xml version="1.0" encoding="UTF-8"?><rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"><channel><title>特力屋</title><link>' . $v['URL'] . '</link><description></description>' . "\n");
  271. // 原始商品逐筆處裡(保留符號已被CDATA處理)
  272. // 注意可能出現相同品牌之中有相同 g_id 的現象,但由於不影響資料流程所以不處理
  273. $data = [];
  274. foreach ($original->Product as $p) {
  275. // custom_label_1 為客戶系統自定義欄位,Y代表可讓GMC上架,N代表不可讓GM上架
  276. if ((string)$p->custom_label_1 == GeneralConst::PUBLISH_YES) {
  277. // XML
  278. fwrite($fileW, '<item>' . "\n");
  279. fwrite($fileW, '<g:id>' . (string)$p->SKU . '</g:id>' . "\n");
  280. fwrite($fileW, '<g:title><![CDATA[' . (string)$p->Name . ']]></g:title>' . "\n");
  281. fwrite($fileW, '<g:description><![CDATA[' . (string)$p->Description . ']]></g:description>' . "\n");
  282. fwrite($fileW, '<g:link><![CDATA[' . (string)$p->URL . ']]></g:link>' . "\n");
  283. fwrite($fileW, '<g:image_link><![CDATA[' . (string)$p->LargeImage . ']]></g:image_link>' . "\n");
  284. fwrite($fileW, '<g:condition>' . (string)$p->Condition . '</g:condition>' . "\n");
  285. fwrite($fileW, '<g:availability>' . (string)'in stock' . '</g:availability>' . "\n");
  286. fwrite($fileW, '<g:price>' . (string)$p->Price . ' TWD' . '</g:price>' . "\n");
  287. fwrite($fileW, '<g:sale_price>' . (string)$p->SalePrice . ' TWD' . '</g:sale_price>' . "\n");
  288. fwrite($fileW, '<g:brand><![CDATA[' . (string)'特力屋' . ']]></g:brand>' . "\n");
  289. fwrite($fileW, '<g:google_product_category>' . (string)'' . '</g:google_product_category>' . "\n");
  290. fwrite($fileW, '<g:product_type><![CDATA[' . (string)$p->Category . ']]></g:product_type>' . "\n");
  291. fwrite($fileW, '<g:custom_label_0><![CDATA[' . (string)$p->custom_label_0 . ']]></g:custom_label_0>' . "\n");
  292. fwrite($fileW, '</item>' . "\n");
  293. // 資料庫(保留符號已被框架處理)
  294. $data[] = [
  295. 'brand_no' => $k,
  296. 'batch_no' => $batch_no,
  297. 'g_id' => (string)$p->SKU,
  298. 'g_title' => (string)$p->Name,
  299. 'g_description' => (string)$p->Description,
  300. 'g_link' => (string)$p->URL,
  301. 'g_image_link' => (string)$p->LargeImage,
  302. 'g_price' => (string)$p->Price . ' TWD',
  303. 'g_sale_price' => (string)$p->SalePrice . ' TWD',
  304. 'g_product_type' => (string)$p->Category,
  305. 'g_custom_label_0' => (string)$p->custom_label_0,
  306. ];
  307. if (count($data) >= 100) {
  308. $this->productManagementSv->insertProduct($data);
  309. $data = [];
  310. }
  311. }
  312. }
  313. fwrite($fileW, '</channel><Updated>' . $batch_no . '</Updated></rss>' . "\n");
  314. $this->productManagementSv->insertProduct($data);
  315. // 結束寫入檔案
  316. fclose($fileW);
  317. // 檔案上傳至 S3
  318. $fileR = fopen($path . $batch_no_digit . '.xml', "r");
  319. $file_name = env('APP_ENV') . '/' . $v['LB'] . '/' . $batch_no_digit . '.xml';
  320. $this->s3Client->putObject([
  321. 'ACL' => 'public-read',
  322. 'Body' => $fileR,
  323. 'Bucket' => env('AWS_S3_BUCKET'),
  324. 'ContentType' => 'application/xml',
  325. 'Key' => $file_name,
  326. ]);
  327. fclose($fileR);
  328. // 刪除寫入的檔案
  329. unlink($path . $batch_no_digit . '.xml');
  330. // 刪除舊資料(XML)
  331. $objects = $this->s3Client->listObjects([
  332. 'Bucket' => env('AWS_S3_BUCKET'),
  333. 'Prefix' => env('APP_ENV') . '/' . $v['LB'] . '/' . $del_batch_no_digit,
  334. ]);
  335. foreach ($objects['Contents'] ?? [] as $object) {
  336. $tmpkey = $object['Key'] ?? null;
  337. if ($tmpkey) {
  338. $this->s3Client->deleteObjects([
  339. 'Bucket' => env('AWS_S3_BUCKET'),
  340. 'Delete' => ['Objects' => [['Key' => $tmpkey]]],
  341. ]);
  342. }
  343. }
  344. }
  345. // 刪除舊資料(SQL)
  346. $this->productManagementSv->deleteProductByBatchNo($del_batch_no);
  347. Redis::del('TLW_0_XML');
  348. }
  349. }
  350. // XML轉換腳本(特力屋新版)
  351. public function xmlG()
  352. {
  353. // 防重鎖
  354. if (!Redis::get('TLW_0_XML')) {
  355. Redis::set('TLW_0_XML', 'true');
  356. Redis::expire('TLW_0_XML', 1200); // 20 分鐘
  357. // 產生批次號
  358. $batch_no = date("Y-m-d H:i:s");
  359. $batch_no_digit = date("YmdHis");
  360. // 產生刪除批次號
  361. $del_batch_no = date('Y-m-d', strtotime(date("Y-m-d H:i:s") . "-14 days"));
  362. $del_batch_no_digit = date('Ymd', strtotime(date("Y-m-d") . "-14 days"));
  363. // 逐一品牌處理
  364. foreach (GeneralConst::$brandMap as $k => $v) {
  365. for ($i = 1; $i <= 14; $i++) {
  366. }
  367. // 載入品牌原始 XML
  368. $original = simplexml_load_file($v['URLG']); // [新舊版本差異]來源修改
  369. // 開始寫入檔案
  370. $path = '/tmp/tlw/' . env('APP_ENV') . '/' . $v['LB'] . '/';
  371. if (!file_exists($path)) mkdir($path, 0777, true);
  372. $fileW = fopen($path . $batch_no_digit . '.xml', "w");
  373. fwrite($fileW, '<?xml version="1.0" encoding="UTF-8"?><rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"><channel><title>特力屋</title><link>' . $v['URL'] . '</link><description></description>' . "\n");
  374. // 原始商品逐筆處裡(保留符號已被CDATA處理)
  375. // 注意可能出現相同品牌之中有相同 g_id 的現象,但由於不影響資料流程所以不處理
  376. $data = [];
  377. foreach ($original->channel->item as $p) { // [新舊版本差異]解析層級修改
  378. // custom_label_1 為客戶系統自定義欄位,Y代表可讓GMC上架,N代表不可讓GM上架
  379. if ((string)$p->custom_label_1 == GeneralConst::PUBLISH_YES) {
  380. // XML
  381. fwrite($fileW, '<item>' . "\n");
  382. fwrite($fileW, '<g:id>' . (string)$p->children('g', true)->id . '</g:id>' . "\n");
  383. fwrite($fileW, '<g:title><![CDATA[' . (string)$p->children('g', true)->title . ']]></g:title>' . "\n");
  384. fwrite($fileW, '<g:description><![CDATA[' . (string)$p->children('g', true)->description . ']]></g:description>' . "\n");
  385. fwrite($fileW, '<g:link><![CDATA[' . (string)$p->children('g', true)->link . ']]></g:link>' . "\n");
  386. fwrite($fileW, '<g:image_link><![CDATA[' . (string)$p->children('g', true)->image_link . ']]></g:image_link>' . "\n");
  387. fwrite($fileW, '<g:condition>' . (string)$p->children('g', true)->condition . '</g:condition>' . "\n");
  388. fwrite($fileW, '<g:availability>' . (string)$p->children('g', true)->availability . '</g:availability>' . "\n");
  389. fwrite($fileW, '<g:price>' . (string)$p->children('g', true)->price . '</g:price>' . "\n");
  390. fwrite($fileW, '<g:sale_price>' . (string)$p->children('g', true)->sale_price . '</g:sale_price>' . "\n");
  391. fwrite($fileW, '<g:brand><![CDATA[' . (string)$p->children('g', true)->brand . ']]></g:brand>' . "\n");
  392. fwrite($fileW, '<g:google_product_category>' . (string)'' . '</g:google_product_category>' . "\n");
  393. fwrite($fileW, '<g:product_type><![CDATA[' . (string)$p->children('g', true)->product_type . ']]></g:product_type>' . "\n");
  394. fwrite($fileW, '<g:custom_label_0><![CDATA[' . (string)$p->custom_label_0 . ']]></g:custom_label_0>' . "\n");
  395. fwrite($fileW, '</item>' . "\n");
  396. // 資料庫(保留符號已被框架處理)
  397. $data[] = [
  398. 'brand_no' => $k,
  399. 'batch_no' => $batch_no,
  400. 'g_id' => (string)$p->children('g', true)->id,
  401. 'g_title' => (string)$p->children('g', true)->title,
  402. 'g_description' => (string)$p->children('g', true)->description,
  403. 'g_link' => (string)$p->children('g', true)->link,
  404. 'g_image_link' => (string)$p->children('g', true)->image_link,
  405. 'g_price' => (string)$p->children('g', true)->price,
  406. 'g_sale_price' => (string)$p->children('g', true)->sale_price,
  407. 'g_product_type' => (string)$p->children('g', true)->product_type,
  408. 'g_custom_label_0' => (string)$p->custom_label_0,
  409. ];
  410. if (count($data) >= 100) {
  411. $this->productManagementSv->insertProduct($data);
  412. $data = [];
  413. }
  414. }
  415. }
  416. fwrite($fileW, '</channel><Updated>' . $batch_no . '</Updated></rss>' . "\n");
  417. $this->productManagementSv->insertProduct($data);
  418. // 結束寫入檔案
  419. fclose($fileW);
  420. // 檔案上傳至 S3
  421. $fileR = fopen($path . $batch_no_digit . '.xml', "r");
  422. $file_name = env('APP_ENV') . '/' . $v['LB'] . '/' . $batch_no_digit . '.xml';
  423. $this->s3Client->putObject([
  424. 'ACL' => 'public-read',
  425. 'Body' => $fileR,
  426. 'Bucket' => env('AWS_S3_BUCKET'),
  427. 'ContentType' => 'application/xml',
  428. 'Key' => $file_name,
  429. ]);
  430. fclose($fileR);
  431. // 刪除寫入的檔案
  432. unlink($path . $batch_no_digit . '.xml');
  433. // 刪除舊資料(XML)
  434. $objects = $this->s3Client->listObjects([
  435. 'Bucket' => env('AWS_S3_BUCKET'),
  436. 'Prefix' => env('APP_ENV') . '/' . $v['LB'] . '/' . $del_batch_no_digit,
  437. ]);
  438. foreach ($objects['Contents'] ?? [] as $object) {
  439. $tmpkey = $object['Key'] ?? null;
  440. if ($tmpkey) {
  441. $this->s3Client->deleteObjects([
  442. 'Bucket' => env('AWS_S3_BUCKET'),
  443. 'Delete' => ['Objects' => [['Key' => $tmpkey]]],
  444. ]);
  445. }
  446. }
  447. }
  448. // 刪除舊資料(SQL)
  449. $this->productManagementSv->deleteProductByBatchNo($del_batch_no);
  450. Redis::del('TLW_0_XML');
  451. }
  452. }
  453. // 更新動態饋給
  454. public function endpoint()
  455. {
  456. // 防重鎖
  457. if (!Redis::get('TLW_4_XML')) {
  458. Redis::set('TLW_4_XML', 'true');
  459. Redis::expire('TLW_4_XML', 1200); // 20 分鐘
  460. // 取出資料
  461. $info = explode('|', Redis::get('TLW_4_MAN'));
  462. $brand_no = $info[0];
  463. $batch_no = $info[1];
  464. $products = $this->productManagementSv->getEndpointProdocts($brand_no, $batch_no);
  465. // 拼裝XML
  466. // 開始寫入檔案
  467. $path = '/tmp/tlwendpoint/' . env('APP_ENV') . '/' . GeneralConst::$brandMap[ $brand_no ]['LB'] . '/';
  468. if (!file_exists($path)) mkdir($path, 0777, true);
  469. $fileW = fopen($path . 'FINAL.xml', "w");
  470. fwrite($fileW, '<?xml version="1.0" encoding="UTF-8"?><rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"><channel><title>特力屋</title><link>' . GeneralConst::$brandMap[ $brand_no ]['URL'] . '</link><description></description>' . "\n");
  471. foreach ($products as $p) {
  472. fwrite($fileW, '<item>' . "\n");
  473. fwrite($fileW, '<g:id>' . (string)$p['g_id'] . '</g:id>' . "\n");
  474. fwrite($fileW, '<g:title><![CDATA[' . (string)$p['g_title'] . ']]></g:title>' . "\n");
  475. fwrite($fileW, '<g:description><![CDATA[' . (string)$p['g_description'] . ']]></g:description>' . "\n");
  476. fwrite($fileW, '<g:link><![CDATA[' . (string)$p['g_link'] . ']]></g:link>' . "\n");
  477. fwrite($fileW, '<g:image_link><![CDATA[' . (string)$p['g_image_link'] . ']]></g:image_link>' . "\n");
  478. fwrite($fileW, '<g:condition>' . (string)$p['g_condition'] . '</g:condition>' . "\n");
  479. fwrite($fileW, '<g:availability>' . (string)$p['g_availability'] . '</g:availability>' . "\n");
  480. fwrite($fileW, '<g:price>' . (string)$p['g_price'] . '</g:price>' . "\n");
  481. fwrite($fileW, '<g:sale_price>' . (string)$p['g_sale_price'] . '</g:sale_price>' . "\n");
  482. fwrite($fileW, '<g:brand><![CDATA[' . (string)$p['g_brand'] . ']]></g:brand>' . "\n");
  483. fwrite($fileW, '<g:google_product_category>' . (string)$p['g_google_product_category'] . '</g:google_product_category>' . "\n");
  484. fwrite($fileW, '<g:product_type><![CDATA[' . (string)$p['g_product_type'] . ']]></g:product_type>' . "\n");
  485. fwrite($fileW, '<g:custom_label_0><![CDATA[' . (string)$p['g_custom_label_0'] . ']]></g:custom_label_0>' . "\n");
  486. fwrite($fileW, '</item>' . "\n");
  487. }
  488. fwrite($fileW, '</channel><Updated>' . $batch_no . '</Updated></rss>' . "\n");
  489. // 結束寫入檔案
  490. fclose($fileW);
  491. // 上傳至S3取代
  492. $fileR = fopen($path . 'FINAL.xml', "r");
  493. $file_name = env('APP_ENV') . '/' . GeneralConst::$brandMap[ $brand_no ]['LB'] . '/FINAL.xml';
  494. $this->s3Client->putObject([
  495. 'ACL' => 'public-read',
  496. 'Body' => $fileR,
  497. 'Bucket' => env('AWS_S3_BUCKET'),
  498. 'ContentType' => 'application/xml',
  499. 'Key' => $file_name,
  500. ]);
  501. fclose($fileR);
  502. // 刪除檔案
  503. unlink($path . 'FINAL.xml');
  504. Redis::del('TLW_4_XML');
  505. }
  506. }
  507. }