first();
$oSiteMap = ModelSiteMap::where("code", $sSiteCode)->first();
$sSiteGroupCode = $oSiteMap->group_code;
$sSiteName = $oSiteMap->name;
// $sArticleConfig = ModelArticleConfig::where("article_group_code", $oArticleSchema->group_code)->where("site_group_code", $sSiteGroupCode)->first()?->config;
// $this->aArticleConfig = json_decode($sArticleConfig, true);
$oInstance = new self();
$oInstance->sArticleCode = $sArticleCode;
$oInstance->sSiteCode = $sSiteCode;
$oInstance->sSiteGroupCode = $oSiteMap->group_code;
$oInstance->sSiteName = $oSiteMap->name;
$oInstance->sSiteUrl = $oSiteMap->url;
// $oInstance->sArticleConfig = $sArticleConfig;
$oInstance->oArticle = $oArticleSchema;
return $oInstance;
}
public function build()
{
$sMethod = "build_article__".$this->sSiteGroupCode;
if (method_exists($this, $sMethod)) {
$oArticleFlow = $this->$sMethod();
} else {
return Flow::start("build")->fail("没有".$sMethod."的对应处理");
}
return $oArticleFlow;
}
private function build_article__resource()
{
$oFlow = Flow::start("build_article__resource");
$aSchema = json_decode($this->oArticle->schema, true) ?? [];
$aSchemaData = $aSchema["data"] ?? [];
$sSchemaType = $aSchema["type"] ?? '';
$aDataSend = []; // 它是各站各异的,不保存
$aSchemaPro = []; // 他是上者的通用版本,保存一份
$iRowId = 0;
foreach ($aSchemaData as $aSchemaDataRow) {
$iRowId++;
$aSchemaProRow = [];
$aSchemaProRow["type"] = $aSchemaDataRow["type"] ?? '';
$aSchemaProRow["data"] = []; // 默认空,等于判断之外的给空
if ($sSchemaType == "cache_tg" || $sSchemaType == "cache_tg_new") {
$sImgHandle = ModelOption::where("k", "cache_tg_new::image_handle")->first()?->v;
if ($this->oArticle->group_code == "tg_xiuren_a" || $sImgHandle == "xiuren_a") {
if ($aSchemaProRow["type"] == "photo") {
// $sRowUuid = $sUuid = Str::uuid()->toString();
$iPhotoFullSizeKey = $aSchemaDataRow["photo_fullSizeKey"];
$aPhotoFullSize = $aSchemaDataRow["photo"][$iPhotoFullSizeKey];
// --- 裁剪参数配置区(指挥部) ---
$iWatermarkTop = 50; // 顶部裁掉的水印高度
$iWatermarkBottom = 38; // 底部裁掉的水印高度 (合计88)
$iThumbClipHeight = 500; // 裁剪取样的原始高度
$iTargetHeight = 400; // 缩略图最终输出高度
// 各部位起始偏移(基于裁完水印后的底部向上计算)
$iOffsetLeg = 0; // 腿:贴底
$iOffsetBody = 500; // 身:从底往上500位置开始
$iOffsetHead = 600; // 头:从底往上600位置开始
// --- 1. 初始化阵地 ---
$oImageManager = new ImageManager(new Driver());
$oImgMain = $oImageManager->read($aPhotoFullSize["file_path"]);
$sExtension = pathinfo($aPhotoFullSize["file_path"], PATHINFO_EXTENSION);
$sUploadPath = 'upload/';
$sUploadDir = config('system.dir_base') . "public/" . $sUploadPath;
$aSavedPath = []; // 弹药库:存放所有生成的路径
// --- 2. 预处理:裁掉水印 ---
$iMainHeight = $oImgMain->height() - ($iWatermarkTop + $iWatermarkBottom);
// 保险:防止原图高度不足以支撑裁切
$iMainHeight = max(1, $iMainHeight);
$oImgMain->crop($oImgMain->width(), $iMainHeight, 0, $iWatermarkTop);
// 保存全尺寸去水印图
$sTmp = $this->oArticle->code . '-'.$iRowId.'-full.' . $sExtension;
$sFullSavePath = $sUploadDir . $sTmp;
$sFullSavePathUrl = $sUploadPath . $sTmp;
$oImgMain->save($sFullSavePath);
// chmod($sFullSavePath, 0777);
$aSavedPath['full'] = $sFullSavePathUrl;
// --- 3. 局部裁剪与归档:[后缀 => 距离底部的偏移] ---
$aClips = [
'thumb_a' => $iOffsetHead,
'thumb_b' => $iOffsetBody,
'thumb_c' => $iOffsetLeg,
];
// 遍历截取图片
foreach ($aClips as $sSuffix => $iBottomOffset) {
// 必须克隆,保护主阵地不被连环裁剪搞残
$oTmp = clone $oImgMain;
$iCurrentH = $oTmp->height();
// 计算 Y 轴起点:总高 - 底部偏移 - 准备截取的高度
$iY = max(0, $iCurrentH - $iBottomOffset - $iThumbClipHeight);
// 计算实际可截取高度:防止上方空间不足
$iH = ($iCurrentH > $iBottomOffset) ? min($iThumbClipHeight, $iCurrentH - $iBottomOffset) : 1;
// 拼装存储路径
$sTmp = $this->oArticle->code.'-'.$iRowId.'-'.$sSuffix.'.'.$sExtension;
$sCurrentPath = $sUploadDir . $sTmp;
$sCurrentPathUrl = $sUploadPath . $sTmp;
// 执行:裁切 -> 等比例缩放 -> 保存
$oTmp->crop($oTmp->width(), $iH, 0, $iY)
->scale(height: $iTargetHeight)
->save($sCurrentPath);
// chmod($sCurrentPath, 0777);
// 路径入库
$aSavedPath[$sSuffix] = $sCurrentPathUrl;
unset($oTmp); // 及时释放资源,老兵的职业操守
}
$aSchemaProRow["data"] = $aSavedPath;
$aSchemaPro[] = $aSchemaProRow; // 关键
$aDataSend[] = $aSchemaProRow; // 公约
} // is photo
} else { // 通用, 只有full
if ($aSchemaProRow["type"] == "photo") {
$iPhotoFullSizeKey = $aSchemaDataRow["photo_fullSizeKey"];
$aPhotoFullSize = $aSchemaDataRow["photo"][$iPhotoFullSizeKey];
$oImageManager = new ImageManager(new Driver());
$oImgMain = $oImageManager->read($aPhotoFullSize["file_path"]);
$sExtension = pathinfo($aPhotoFullSize["file_path"], PATHINFO_EXTENSION);
$sUploadPath = 'upload/';
$sUploadDir = config('system.dir_base') . "public/" . $sUploadPath;
$aSavedPath = []; // 弹药库:存放所有生成的路径
$sTmp = $this->oArticle->code . '-'.$iRowId.'-full.' . $sExtension;
$sFullSavePath = $sUploadDir . $sTmp;
$sFullSavePathUrl = $sUploadPath . $sTmp;
$oImgMain->save($sFullSavePath);
$aSavedPath['full'] = $sFullSavePathUrl;
$aSchemaProRow["data"] = $aSavedPath;
$aSchemaPro[] = $aSchemaProRow; // 关键
$aDataSend[] = $aSchemaProRow; // 公约
}
} // xiuren end
} // cache_tg end
} // schema foreach end
$this->oArticle->schema_pro = $aSchemaPro;
$this->oArticle->save();
return $oFlow->setDataA($aDataSend)->done();
}
private function build_article__jccp_a()
{
$oFlow = Flow::start("build_article__cp_a");
$sArticleCode = $this->sArticleCode;
$sSiteGroupCode = $this->sSiteGroupCode;
$sSiteCode = $this->sSiteCode;
$sSiteName = $this->sSiteName;
$sSiteUrl = $this->sSiteUrl;
$aSchema = json_decode($this->oArticle->schema, true);
$isTest = $this->isTest;
$aArticle["data"] = [];
foreach ($aSchema as $k => $aArticleSchemaRow) {
$sContent = $aArticleSchemaRow["content"]["base"] ?? "⚠️ 没有content?";
if ($aArticleSchemaRow['type'] == 'str') {
if ($aArticleSchemaRow['name'] == "schema_name") {
$sSchemaName = $aArticleSchemaRow["content"]["base"] ?? "⚠️ 提取name失败?";
$sSchemaName = trim($sSchemaName);
$aArticle["name"] = $sSchemaName;
$aArticle["title"] = $this->nameToTitleCS($sSchemaName);
}
$sContent = str_replace("{{sAuthorName}}", $sSiteName, $sContent);
$sContent = str_replace("{{sIconText}}", "###
", $sContent);
$aArticleSchemaRow["content"]["base"] = $sContent;
$aArticle["data"][] = $aArticleSchemaRow;
} else if ($aArticleSchemaRow['type'] == 'img') {
$sContent = "![".$aArticleSchemaRow["content"]["base"]."](".$aArticleSchemaRow["content"]["img"]["base"].")";
$aArticleSchemaRow["content"]["base"] = $sContent;
$aArticle["data"][] = $aArticleSchemaRow;
} else {
$aArticle["data"][] = $aArticleSchemaRow;
}
}
////// step 2 排序
//// 找排序option,没有就走默认
$sOrderKey = "article::schema::order::{$sSiteCode}::name";
$sOrderName = ModelOption::_hit($sOrderKey) ?? '';
$aOrderName = explode(',', $sOrderName);
if (!$sOrderName) {
TeleSlave::notify()->send("未设置option:".$sOrderKey."使用默认。");
$sDefaultOrderKey = "article::schema::order::default::type";
$sDefaultOrderType = ModelOption::_hit($sDefaultOrderKey) ?? '';
$aDefaultOrderType = explode(',', $sDefaultOrderType);
if (!$sDefaultOrderType) {
TeleSlave::fail()->send("未设置option:".$sOrderKey."使用默认。");
return $oFlow->fail("未设置option的default排序");
}
}
if ($sOrderName) {
$aArticle["dataOrder"] = $this->articleSort($aArticle["data"], "name", $aOrderName);
} else {
$aArticle["dataOrder"] = $this->articleSort($aArticle["data"], "type", $aDefaultOrderType);
}
////// step 3 文章化
$aArticleOrder = $aArticle["dataOrder"];
$sEnter = "
";
$sArticle = '';
$sArticle .= $sEnter;
$j = false;
foreach ($aArticleOrder as $aArticleOrderRow) {
$sContent = $aArticleOrderRow["content"]["base"] ?? "⚠️ content里的base没有?";
$sContent = trim($sContent);
$sContent = str_replace("
", $sEnter, $sContent);
$sTmpName = $aArticleOrderRow["name"] ?? '';
if ($sTmpName == "tixingnin") {
$sArticle .= "::: tip {$sSiteName}提醒您,";
$sArticle .= $sContent;
$sArticle .= $sEnter;
$sArticle .= ":::";
$sArticle .= $sEnter;
} else if ($sTmpName == "jichangzijie") {
// $sArticle .= "::: note 机场自介";
// $sArticle .= $sContent;
// $sArticle .= $sEnter;
// $sArticle .= ":::";
// $sArticle .= $sEnter;
$sArticle .= $sEnter;
$sArticle .= $sEnter;
$sArticle .= "## 机场自介";
$sArticle .= $sEnter;
$sArticle .= $sEnter;
$sArticle .= $sContent;
} else if ($sTmpName == "lianjiedaohang") {
$sContent = nl2br($sContent);
$br = "
";
$sContent = preg_replace('/套餐.*'.$br.'/', '', $sContent);
$sContent = preg_replace('/论坛.*\.html/', '', $sContent);
// $sArticle .= "::: info 链接导航";
// $sArticle .= $this->clearBr($sContent);
// $sArticle .= $sEnter;
// $sArticle .= ":::";
// $sArticle .= $sEnter;
$sArticle .= $sEnter;
$sArticle .= $sEnter;
$sArticle .= "## 链接导航";
$sArticle .= $sEnter;
$sArticle .= $sEnter;
$sArticle .= $this->clearBr($sContent);
} else if ($sTmpName == "zhuguandianping") {
$sArticle .= "::: important {$sSiteName}点评";
$sArticle .= $sContent;
$sArticle .= $sEnter;
$sArticle .= ":::";
$sArticle .= $sEnter;
} else if ($sTmpName == "zhuguanpingfen") {
$sContent = preg_replace('/(以上评分仅供参考)/', '', $sContent);
$sContent = preg_replace('/(? $aArticle["title"] ?? "⚠️ 没有titile?",
"content" => $sArticle,
];
return $oFlow->setDataA($aResultData)->done();
}
private function clearBr($sStr)
{
$sEnter = "
";
return str_replace("
", $sEnter, $sStr);
}
private function articleSort(array $data, string $field, array $priorityOrder): array
{
// 保留原始键名
uasort($data, function ($a, $b) use ($field, $priorityOrder) {
$aValue = $a[$field] ?? null;
$bValue = $b[$field] ?? null;
$aIndex = array_search($aValue, $priorityOrder);
$bIndex = array_search($bValue, $priorityOrder);
// 如果找不到,排在最后
$aIndex = $aIndex === false ? PHP_INT_MAX : $aIndex;
$bIndex = $bIndex === false ? PHP_INT_MAX : $bIndex;
return $aIndex <=> $bIndex;
});
return $data;
}
private function nameToTitleCS($sName)
{
$sTitle = trim($sName);
$iCesuCount = ModelArticleSchema::where("name", $sName)->count();
if ($iCesuCount > 1) {
$sWrap = ModelOption::_hit("ffq_tg_a-title_wrap");
if (!$sWrap) {
return "未设置dog_option::ffq_tg-title_wrap";
}
$sTitle = str_replace('{{name}}', $sName, $sWrap);
$sTitle = str_replace('{{cesu_count}}', (string) $iCesuCount, $sTitle);
} else {
$sWrap = ModelOption::_hit("ffq_tg_a-title_wrap-first");
if (!$sWrap) {
return "未设置dog_option::ffq_tg-title_wrap-first";
}
$sTitle = str_replace('{{name}}', $sName, $sWrap);
}
return $sTitle;
}
}