productManagementSv = new ProductManagementService();
$this->checkParamSv = new CheckParamService();
$this->s3Client = new S3Client([
'credentials' => [
'key' => env('AWS_S3_KEY'),
'secret' => env('AWS_S3_SECRET'),
],
'region' => env('AWS_S3_REGION'),
'version' => 'latest',
]);
// 時區調整
date_default_timezone_set("Asia/Taipei");
}
/**
* 列表頁
* 本端點非 API,直接渲染到 view
* @return array: 返回渲染頁
*/
public function index()
{
// 渲染
return view('admin.EcManagement.ProductManagement', [
'can_step_0' => (Redis::get('TLW_0_MAN') || Redis::get('TLW_0_STOP')) ? false : true,
'can_step_2' => (Redis::get('TLW_2_CSV')) ? false : true,
'step_2_s3' => env("AWS_S3_BUCKET"),
'step_2_env' => env("APP_ENV"),
'brands' => $this->productManagementSv->getBrands(),
'batchs' => $this->productManagementSv->getBatchs(),
'mstatuss' => $this->productManagementSv->getMStatuss(),
]);
}
/**
* 列表API
* 接收搜尋參數與分頁等信息,返回JSON
* @return string: 返回JSON
*/
public function grid()
{
// 取得參數
$param = $_GET;
if ($param == null) exit();
$draw = $param["draw"]; //客戶端傳來的查詢次數,無條件回傳用以核對
$orderColumn = $param["order"][0]["column"] + 1; //前端從 0 開始送,但 mysql 從 1 開始算
$orderDir = $param["order"][0]["dir"];
$start = $param["start"]; // 頁碼
$length = $param["length"]; // 一頁多大
$searchValue = $param["search"]["value"];
//客製化搜尋欄位
$brand_no = $param["columns"][1]["search"]["value"];
$batch_no = $param["columns"][2]["search"]["value"];
$g_title = $param["columns"][3]["search"]["value"];
$g_description = $param["columns"][4]["search"]["value"];
$m_comment = $param["columns"][5]["search"]["value"];
$m_status = $param["columns"][6]["search"]["value"];
// 驗證
if ($g_title != filter_var($g_title, FILTER_SANITIZE_SPECIAL_CHARS)) $g_title = "___CANNOT_FIND_STRING___";
if (!$this->checkParamSv->LenMToN($g_title, 0, 50)) $g_title = "___CANNOT_FIND_STRING___";
if ($g_description != filter_var($g_description, FILTER_SANITIZE_SPECIAL_CHARS)) $g_description = "___CANNOT_FIND_STRING___";
if (!$this->checkParamSv->LenMToN($g_description, 0, 50)) $g_description = "___CANNOT_FIND_STRING___";
if ($m_comment != filter_var($m_comment, FILTER_SANITIZE_SPECIAL_CHARS)) $m_comment = "___CANNOT_FIND_STRING___";
if (!$this->checkParamSv->LenMToN($m_comment, 0, 50)) $m_comment = "___CANNOT_FIND_STRING___";
//資料庫
$recordsTotal = 0;
$result = $this->productManagementSv->getProdocts(
$recordsTotal,
$orderColumn,
$orderDir,
$start,
$length,
$searchValue,
$brand_no,
$batch_no,
$g_title,
$g_description,
$m_comment,
$m_status
);
// 整理返回資料
$data = array();
for ($i = 0; $i < count($result); $i++) {
$data[] = array(
//一般資料
$result[ $i ]["serno"],
$result[ $i ]["g_id"],
$result[ $i ]["g_title"],
$result[ $i ]["g_description"],
$result[ $i ]["g_image_link"],
$result[ $i ]["g_price"],
$result[ $i ]["g_sale_price"],
$result[ $i ]["m_status"],
$result[ $i ]["m_comment"],
);
}
$json = array(
"draw" => $draw,
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsTotal, //其實還是填入所有筆數,本次筆數可從陣列取得
"data" => $data,
);
// 返回
return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
}
public function manualXml()
{
Redis::set('TLW_0_MAN', 'true');
return $this->manualXmlStatus();
}
public function manualXmlStatus()
{
$json = [
'can_step_0' => (Redis::get('TLW_0_MAN') || Redis::get('TLW_0_STOP')) ? false : true,
];
return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
}
public function importCsv(Request $request)
{
// 上傳錯誤偵測(沒有包含檔案格式)
switch ($_FILES['csv']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
return json_decode(json_encode(['code' => 0, 'err' => '檔案大小錯誤'], JSON_NUMERIC_CHECK), true);
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
return json_decode(json_encode(['code' => 1, 'err' => '檔案太大'], JSON_NUMERIC_CHECK), true);
default:
return json_decode(json_encode(['code' => 1, 'err' => '系統錯誤'], JSON_NUMERIC_CHECK), true);
}
if ($_FILES['csv']['size'] > 100 * 1024 * 1024) {
return json_decode(json_encode(['code' => 1, 'err' => '檔案最大100MB'], JSON_NUMERIC_CHECK), true);
}
// 路徑命名
$path = '/tmp/tlwcsv/';
if (!file_exists($path)) mkdir($path, 0777, true);
chmod($path, 0777);
$date = date('Y-m-d');
if (!file_exists($path . 'tempImportCsv')) mkdir($path . 'tempImportCsv', 0777, true);
chmod($path . 'tempImportCsv', 0777);
if (!file_exists($path . 'tempImportCsv/' . $date)) mkdir($path . 'tempImportCsv/' . $date, 0777, true);
chmod($path . 'tempImportCsv/' . $date, 0777);
// 保存文件
$time = time();
move_uploaded_file($_FILES['csv']['tmp_name'], $path . 'tempImportCsv/' . $date . '/' . $time . '.csv');
chmod($path . 'tempImportCsv/' . $date . '/' . $time . '.csv', 0777);
// 異步處裡文件
Redis::set('TLW_2_CSV', $path . 'tempImportCsv/' . $date . '/' . $time . '.csv' . '|' . $_POST['brand_no'] . '|' . $_POST['batch_no']);
return json_decode(json_encode($_FILES, JSON_NUMERIC_CHECK), true);
}
public function importCsvStatus()
{
$json = [
'can_step_2' => (Redis::get('TLW_2_CSV') || Redis::get('TLW_2_MAN')) ? false : true,
];
return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
}
public function importCsvHandle($info)
{
// 上鎖
Redis::set('TLW_2_MAN', 'true');
// 剖析資訊
$info = explode('|', $info);
$csv = $info[0];
$brand_no = $info[1];
$batch_no = $info[2];
// 更新產品訊息
$fileR = fopen($csv, "r");
while (!feof($fileR)) {
$line = fgets($fileR);
$cell = str_getcsv($line, ',', '"');
if ($cell[0] != 'Item ID') {
$g_id = $cell[0];
$m_comment = $cell[4] ?? '';
$m_status = $cell[5] ?? '';
switch ($m_status) {
case GeneralConst::$mStatusMap[ GeneralConst::MSTATUS_WARNING ]['BNAME']:
$detail = $this->productManagementSv->getProductDetail($brand_no, $batch_no, $g_id);
$comment = (strpos($detail['m_comment'], $m_comment . ' / ') !== false) ? $detail['m_comment'] : $detail['m_comment'] . $m_comment . ' / ';
if ($detail['m_status'] == GeneralConst::MSTATUS_DISAPPROVED) {
// 原本是錯誤,但目前讀取到警告,就依然要保持錯誤狀態
$this->productManagementSv->updateProduct($brand_no, $batch_no, $g_id, GeneralConst::MSTATUS_DISAPPROVED, $comment);
} else {
// 原本是正常或警告,目前讀取到警告,就要將狀態設為警告
$this->productManagementSv->updateProduct($brand_no, $batch_no, $g_id, GeneralConst::MSTATUS_WARNING, $comment);
}
break;
case GeneralConst::$mStatusMap[ GeneralConst::MSTATUS_DISAPPROVED ]['BNAME']:
$detail = $this->productManagementSv->getProductDetail($brand_no, $batch_no, $g_id);
$comment = (strpos($detail['m_comment'], $m_comment . ' / ') !== false) ? $detail['m_comment'] : $detail['m_comment'] . $m_comment . ' / ';
// 原本不管是甚麼,目前讀取到錯誤,就要將狀態設為錯誤
$this->productManagementSv->updateProduct($brand_no, $batch_no, $g_id, GeneralConst::MSTATUS_DISAPPROVED, $comment);
break;
}
}
}
fclose($fileR);
// 刪除檔案(權限問題無法刪除)
unlink($csv);
}
public function manualEndpoint()
{
$param = $_GET;
Redis::set('TLW_4_MAN', $param['brand_no'] . '|' . $param['batch_no']);
return $this->manualEndpointStatus();
}
public function manualEndpointStatus()
{
$json = [
'can_step_4' => (Redis::get('TLW_4_MAN') || Redis::get('TLW_4_STOP')) ? false : true,
];
return json_decode(json_encode($json, JSON_NUMERIC_CHECK), true);
}
public function exportCsv(Request $request)
{
$title = [
'系統號',
'商品號',
'品名',
'描述',
'網頁',
'圖片路徑',
'定價',
'售價',
'掃描結果',
'掃描問題',
];
$products = $this->productManagementSv->getExportProdocts($request);
$this->productManagementSv->downloadExcel($title, $products, date("YmdHis") . '-REPORT-' . GeneralConst::$brandMap[ $request->brand_no ]['LB']);
}
// XML轉換腳本
public function xml()
{
// 防重鎖
if (!Redis::get('TLW_0_XML')) {
Redis::set('TLW_0_XML', 'true');
Redis::expire('TLW_0_XML', 1200); // 20 分鐘
// 產生批次號
$batch_no = date("Y-m-d H:i:s");
$batch_no_digit = date("YmdHis");
// 產生刪除批次號
$del_batch_no = date('Y-m-d', strtotime(date("Y-m-d H:i:s") . "-14 days"));
$del_batch_no_digit = date('Ymd', strtotime(date("Y-m-d") . "-14 days"));
// 逐一品牌處理
foreach (GeneralConst::$brandMap as $k => $v) {
for ($i = 1; $i <= 14; $i++) {
}
// 載入品牌原始 XML
$original = simplexml_load_file($v['URL']);
// 開始寫入檔案
$path = '/tmp/tlw/' . env('APP_ENV') . '/' . $v['LB'] . '/';
if (!file_exists($path)) mkdir($path, 0777, true);
$fileW = fopen($path . $batch_no_digit . '.xml', "w");
fwrite($fileW, '特力屋' . $v['URL'] . '' . "\n");
// 原始商品逐筆處裡(保留符號已被CDATA處理)
// 注意可能出現相同品牌之中有相同 g_id 的現象,但由於不影響資料流程所以不處理
$data = [];
foreach ($original->Product as $p) {
// custom_label_1 為客戶系統自定義欄位,Y代表可讓GMC上架,N代表不可讓GM上架
if ((string)$p->custom_label_1 == GeneralConst::PUBLISH_YES) {
// XML
fwrite($fileW, '- ' . "\n");
fwrite($fileW, '' . (string)$p->SKU . '' . "\n");
fwrite($fileW, 'Name . ']]>' . "\n");
fwrite($fileW, 'Description . ']]>' . "\n");
fwrite($fileW, 'URL . ']]>' . "\n");
fwrite($fileW, 'LargeImage . ']]>' . "\n");
fwrite($fileW, '' . (string)$p->Condition . '' . "\n");
fwrite($fileW, '' . (string)'in stock' . '' . "\n");
fwrite($fileW, '' . (string)$p->Price . ' TWD' . '' . "\n");
fwrite($fileW, '' . (string)$p->SalePrice . ' TWD' . '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '' . (string)'' . '' . "\n");
fwrite($fileW, 'Category . ']]>' . "\n");
fwrite($fileW, 'custom_label_0 . ']]>' . "\n");
fwrite($fileW, '
' . "\n");
// 資料庫(保留符號已被框架處理)
$data[] = [
'brand_no' => $k,
'batch_no' => $batch_no,
'g_id' => (string)$p->SKU,
'g_title' => (string)$p->Name,
'g_description' => (string)$p->Description,
'g_link' => (string)$p->URL,
'g_image_link' => (string)$p->LargeImage,
'g_price' => (string)$p->Price . ' TWD',
'g_sale_price' => (string)$p->SalePrice . ' TWD',
'g_product_type' => (string)$p->Category,
'g_custom_label_0' => (string)$p->custom_label_0,
];
if (count($data) >= 100) {
$this->productManagementSv->insertProduct($data);
$data = [];
}
}
}
fwrite($fileW, '' . $batch_no . '' . "\n");
$this->productManagementSv->insertProduct($data);
// 結束寫入檔案
fclose($fileW);
// 檔案上傳至 S3
$fileR = fopen($path . $batch_no_digit . '.xml', "r");
$file_name = env('APP_ENV') . '/' . $v['LB'] . '/' . $batch_no_digit . '.xml';
$this->s3Client->putObject([
'ACL' => 'public-read',
'Body' => $fileR,
'Bucket' => env('AWS_S3_BUCKET'),
'ContentType' => 'application/xml',
'Key' => $file_name,
]);
fclose($fileR);
// 刪除寫入的檔案
unlink($path . $batch_no_digit . '.xml');
// 刪除舊資料(XML)
$objects = $this->s3Client->listObjects([
'Bucket' => env('AWS_S3_BUCKET'),
'Prefix' => env('APP_ENV') . '/' . $v['LB'] . '/' . $del_batch_no_digit,
]);
foreach ($objects['Contents'] ?? [] as $object) {
$tmpkey = $object['Key'] ?? null;
if ($tmpkey) {
$this->s3Client->deleteObjects([
'Bucket' => env('AWS_S3_BUCKET'),
'Delete' => ['Objects' => [['Key' => $tmpkey]]],
]);
}
}
}
// 刪除舊資料(SQL)
$this->productManagementSv->deleteProductByBatchNo($del_batch_no);
Redis::del('TLW_0_XML');
}
}
// XML轉換腳本(特力屋新版)
public function xmlG()
{
// 防重鎖
if (!Redis::get('TLW_0_XML')) {
Redis::set('TLW_0_XML', 'true');
Redis::expire('TLW_0_XML', 1200); // 20 分鐘
// 產生批次號
$batch_no = date("Y-m-d H:i:s");
$batch_no_digit = date("YmdHis");
// 產生刪除批次號
$del_batch_no = date('Y-m-d', strtotime(date("Y-m-d H:i:s") . "-14 days"));
$del_batch_no_digit = date('Ymd', strtotime(date("Y-m-d") . "-14 days"));
// 逐一品牌處理
foreach (GeneralConst::$brandMap as $k => $v) {
for ($i = 1; $i <= 14; $i++) {
}
// 載入品牌原始 XML
$original = simplexml_load_file($v['URLG']); // [新舊版本差異]來源修改
// 開始寫入檔案
$path = '/tmp/tlw/' . env('APP_ENV') . '/' . $v['LB'] . '/';
if (!file_exists($path)) mkdir($path, 0777, true);
$fileW = fopen($path . $batch_no_digit . '.xml', "w");
fwrite($fileW, '特力屋' . $v['URL'] . '' . "\n");
// 原始商品逐筆處裡(保留符號已被CDATA處理)
// 注意可能出現相同品牌之中有相同 g_id 的現象,但由於不影響資料流程所以不處理
$data = [];
foreach ($original->channel->item as $p) { // [新舊版本差異]解析層級修改
// custom_label_1 為客戶系統自定義欄位,Y代表可讓GMC上架,N代表不可讓GM上架
if ((string)$p->custom_label_1 == GeneralConst::PUBLISH_YES) {
// XML
fwrite($fileW, '- ' . "\n");
fwrite($fileW, '' . (string)$p->children('g', true)->id . '' . "\n");
fwrite($fileW, 'children('g', true)->title . ']]>' . "\n");
fwrite($fileW, 'children('g', true)->description . ']]>' . "\n");
fwrite($fileW, 'children('g', true)->link . ']]>' . "\n");
fwrite($fileW, 'children('g', true)->image_link . ']]>' . "\n");
fwrite($fileW, '' . (string)$p->children('g', true)->condition . '' . "\n");
fwrite($fileW, '' . (string)$p->children('g', true)->availability . '' . "\n");
fwrite($fileW, '' . (string)$p->children('g', true)->price . '' . "\n");
fwrite($fileW, '' . (string)$p->children('g', true)->sale_price . '' . "\n");
fwrite($fileW, 'children('g', true)->brand . ']]>' . "\n");
fwrite($fileW, '' . (string)'' . '' . "\n");
fwrite($fileW, 'children('g', true)->product_type . ']]>' . "\n");
fwrite($fileW, 'custom_label_0 . ']]>' . "\n");
fwrite($fileW, '
' . "\n");
// 資料庫(保留符號已被框架處理)
$data[] = [
'brand_no' => $k,
'batch_no' => $batch_no,
'g_id' => (string)$p->children('g', true)->id,
'g_title' => (string)$p->children('g', true)->title,
'g_description' => (string)$p->children('g', true)->description,
'g_link' => (string)$p->children('g', true)->link,
'g_image_link' => (string)$p->children('g', true)->image_link,
'g_price' => (string)$p->children('g', true)->price,
'g_sale_price' => (string)$p->children('g', true)->sale_price,
'g_product_type' => (string)$p->children('g', true)->product_type,
'g_custom_label_0' => (string)$p->custom_label_0,
];
if (count($data) >= 100) {
$this->productManagementSv->insertProduct($data);
$data = [];
}
}
}
fwrite($fileW, '' . $batch_no . '' . "\n");
$this->productManagementSv->insertProduct($data);
// 結束寫入檔案
fclose($fileW);
// 檔案上傳至 S3
$fileR = fopen($path . $batch_no_digit . '.xml', "r");
$file_name = env('APP_ENV') . '/' . $v['LB'] . '/' . $batch_no_digit . '.xml';
$this->s3Client->putObject([
'ACL' => 'public-read',
'Body' => $fileR,
'Bucket' => env('AWS_S3_BUCKET'),
'ContentType' => 'application/xml',
'Key' => $file_name,
]);
fclose($fileR);
// 刪除寫入的檔案
unlink($path . $batch_no_digit . '.xml');
// 刪除舊資料(XML)
$objects = $this->s3Client->listObjects([
'Bucket' => env('AWS_S3_BUCKET'),
'Prefix' => env('APP_ENV') . '/' . $v['LB'] . '/' . $del_batch_no_digit,
]);
foreach ($objects['Contents'] ?? [] as $object) {
$tmpkey = $object['Key'] ?? null;
if ($tmpkey) {
$this->s3Client->deleteObjects([
'Bucket' => env('AWS_S3_BUCKET'),
'Delete' => ['Objects' => [['Key' => $tmpkey]]],
]);
}
}
}
// 刪除舊資料(SQL)
$this->productManagementSv->deleteProductByBatchNo($del_batch_no);
Redis::del('TLW_0_XML');
}
}
// 更新動態饋給
public function endpoint()
{
// 防重鎖
if (!Redis::get('TLW_4_XML')) {
Redis::set('TLW_4_XML', 'true');
Redis::expire('TLW_4_XML', 1200); // 20 分鐘
// 取出資料
$info = explode('|', Redis::get('TLW_4_MAN'));
$brand_no = $info[0];
$batch_no = $info[1];
$products = $this->productManagementSv->getEndpointProdocts($brand_no, $batch_no);
// 拼裝XML
// 開始寫入檔案
$path = '/tmp/tlwendpoint/' . env('APP_ENV') . '/' . GeneralConst::$brandMap[ $brand_no ]['LB'] . '/';
if (!file_exists($path)) mkdir($path, 0777, true);
$fileW = fopen($path . 'FINAL.xml', "w");
fwrite($fileW, '特力屋' . GeneralConst::$brandMap[ $brand_no ]['URL'] . '' . "\n");
foreach ($products as $p) {
fwrite($fileW, '- ' . "\n");
fwrite($fileW, '' . (string)$p['g_id'] . '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '' . (string)$p['g_condition'] . '' . "\n");
fwrite($fileW, '' . (string)$p['g_availability'] . '' . "\n");
fwrite($fileW, '' . (string)$p['g_price'] . '' . "\n");
fwrite($fileW, '' . (string)$p['g_sale_price'] . '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '' . (string)$p['g_google_product_category'] . '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '' . "\n");
fwrite($fileW, '
' . "\n");
}
fwrite($fileW, '' . $batch_no . '' . "\n");
// 結束寫入檔案
fclose($fileW);
// 上傳至S3取代
$fileR = fopen($path . 'FINAL.xml', "r");
$file_name = env('APP_ENV') . '/' . GeneralConst::$brandMap[ $brand_no ]['LB'] . '/FINAL.xml';
$this->s3Client->putObject([
'ACL' => 'public-read',
'Body' => $fileR,
'Bucket' => env('AWS_S3_BUCKET'),
'ContentType' => 'application/xml',
'Key' => $file_name,
]);
fclose($fileR);
// 刪除檔案
unlink($path . 'FINAL.xml');
Redis::del('TLW_4_XML');
}
}
}