get(); $map = []; foreach ($features as $feature){ $map[$feature->id] = ["type"=>$feature->type,"logic"=>$feature->logic,"describe"=>$feature->describe]; } return $map; } /** * 将特征翻译为数组显示 * $str : "1&2|(3&4)" * array:[ * "strategyGroupStartSign" => false, //是否为策略组起点,这将在解析时解析为 ( "calculation" => "", //运算规则,目前仅有 &,| 翻译后填入 "type"=>"", //特征类型 "id"=>"", //特征ID "logic"=>"", //特征逻辑 "describe"=>"", //特征信息 "strategyGroupEndSign" => false, //是否为策略组终点,这将在解析时解析为 ) * ] * * @param string $str * @return array */ public function translationFeature($str) { if (!$str)return null; $result = []; preg_match_all('/\d+|[\&\|\(\)]/',$str,$result); //初次匹配以数字&|()为分隔符生成数组 $sign = 0; //为第二次切割做起点标记 $model = [];//第二次切割数组 $ids = [];//记录出现的特征ID,统一查询 foreach ($result[0] as $index => &$item){ if (is_numeric($item)){ $model[] = array_slice($result[0],$sign,($index-$sign)+1); $sign = $index+1; $ids[] = $item; continue; } if ($index == count($result[0])-1 && $item == ')'){ $model[] = [")"]; } }//以数字为标准切割策略组 $features = Feature::query()->find($ids);//查询出现的特征 $featureMap = []; foreach ($features as $index => $feature){ $featureMap[$feature->id] = $index; }//为查询的特征重组为key-val形式数组留做引用 foreach ($model as $index => &$m){ $arr = [ "strategyGroupStartSign" => false,//是否为策略组起点,这将在解析时解析为 ( "calculation" => "",//运算规则,目前仅有 &,| 翻译后填入 "type"=>"", //特征类型 "id"=>"", //特征ID "logic"=>"", //特征逻辑 "describe"=>"", //特征信息 "strategyGroupEndSign" => false,//是否为策略组终点,这将在解析时解析为 ) ];//最终对象组模型,策略组将几组特征组合引用 foreach ($m as $string){ if (is_numeric($string)){//填入特征信息 if (isset($featureMap[$string])){ $arr["type"] = Feature::type[$features[$featureMap[$string]]->type]; $arr["id"] = $features[$featureMap[$string]]->id; $arr["logic"] = $features[$featureMap[$string]]->logic; $arr["describe"] = $features[$featureMap[$string]]->describe; } continue; } switch ($string){//特殊字符的翻译 case "(": $arr["strategyGroupStartSign"] = true; break; case ")": $model[$index-1]["strategyGroupEndSign"] = true; break; case "&": $arr["calculation"] = "并且"; break; case "|": $arr["calculation"] = "或"; break; } } if (!$arr["id"]){ unset($model[$index]); continue; } $m = $arr;//变更当前指针为翻译结果 } return $model; } /** * 与translationFeature相反,将一组与translationFeature参数解析为简述 * $features:[ * "strategyGroupStartSign" => false, //是否为策略组起点,这将在解析时解析为 ( "calculation" => "", //运算规则,目前仅有 &,| 翻译后填入 "type"=>"", //特征类型 "id"=>"", //特征ID "logic"=>"", //特征逻辑 "describe"=>"", //特征信息 "strategyGroupEndSign" => false, //是否为策略组终点,这将在解析时解析为 ) * ] * array:[ * "feature" //简述结果 * "map" //简述中出现的Map特征对象重组为key-val数组 * ] * @param array $features * @return array */ public function analysisFeature($features) { $str = ""; $map = []; foreach ($features as &$feature){ if (!$feature["type"] || !$feature["logic"] || !$feature["describe"])continue; $typeMap = array_flip(Feature::type); $f = Feature::query()->firstOrCreate([ "type"=>$typeMap[$feature["type"]], //特征类型 "logic"=>$feature["logic"], //特征逻辑 "describe"=>$feature["describe"], //特征信息 ]); $feature["id"] = $f->id; $map[$feature["id"]] = $f; if ($feature["calculation"] == '并且')$str .= '&'; if ($feature["calculation"] == '或') $str .= '|'; if ($feature["strategyGroupStartSign"])$str .= "("; $str .= $feature["id"]; if ($feature["strategyGroupEndSign"])$str .= ")"; } return ['feature'=>$str,"map"=>$map]; } /** * 格式化简述特征为可阅读显示 * $features : 特征对象集 重组为key-val数组数据,key为ID * $value : 简述特征 例: "1&2|(3&4)" * $columnMapping : 特征type属性字段映射 例:["商品名称"=>"commodity_name"],指定该参数会使特征格式化为SQL段 * string : 例:"商品名称 包含 衣服 并且(订单类型 等于 创建订单 或者 承运商 不包含 顺丰)" * * @param array $features * @param string $value * @param array $columnMapping * @return string */ public function formatFeature(array $features, $value, $columnMapping = null) { if (!$features)return $value; preg_match_all('/\d+|[\&\|\(\)]/',$value,$result); foreach ($result[0] as &$str){ if (is_numeric($str) && isset($features[$str])){ $column = Feature::type[$features[$str]["type"]]; $logic = $features[$str]["logic"]; $describe = $features[$str]["describe"]; if ($columnMapping){ $column = $columnMapping[$column] ?? $column; switch ($logic){ case "包含": $logic = " like "; $describe = "%".$describe."%"; break; case "不包含": $logic = " not like "; $describe = "%".$describe."%"; break; case "等于": $logic = " = "; break; } $str = $column.$logic."'".$describe."'"; }else $str = $column.' '.$logic.' '.$describe; } if ($str == "&"){ if ($columnMapping) $str = " and "; else $str = " 并且 "; } if ($str == "|"){ if ($columnMapping) $str = " or "; else $str = " 或 "; } } return implode("",$result[0]); } /** * 匹配特征 * $vale : 特征简述 例: "1&2|(3|4)" * $columnMapping : 列映射 例:["商品名称"=>"commodity_name"] * $matchObject : 被匹配对象,必须存在列映射所指定字段 例:["commodity_name"=>"衣服"] * $isMultiMatching 是否开启多重匹配 开启将匹配对象视为二维数组深层寻找商品(入库需要) * bool true匹配成功 false匹配失败 * * @param string $value * @param array $columnMapping * @param array $matchObject * @param bool $isMultiMatching * @return bool */ public function matchFeature($value, $columnMapping, $matchObject, $isMultiMatching = false) :bool { if (!$value)return true; preg_match_all('/\d+|[\&\|\(\)]/',$value,$result); if (implode("",$result[0]) != $value)return false; $features = app(CacheService::class)->getOrExecute($value,function ()use($value){ preg_match_all('/\d+/',$value,$ids); if ($ids[0])$fs = Feature::query()->whereIn("id",$ids[0])->get(); else return false; $features = []; foreach ($fs as $f){ $features[$f->id] = $f; } return $features; }); foreach ($result[0] as &$str) { if (is_numeric($str) && isset($features[$str])) { $column = Feature::type[$features[$str]["type"]]; $logic = $features[$str]["logic"]; $describe = $features[$str]["describe"]; if ($column == '商品名称' && $isMultiMatching){ $packageColumn = $columnMapping["packages"] ?? "packages"; $packages = $matchObject[$packageColumn] ?? []; $str = $this->multiMatching($packages,$logic,$describe,$columnMapping[$column] ?? ''); continue; } $value = isset($columnMapping[$column]) ? ($matchObject[$columnMapping[$column]] ?? '') : ''; switch ($logic) { case "包含": $str = mb_strpos($value,$describe) === false ? 'false' : 'true'; break; case "不包含": $str = mb_strpos($value,$describe) === false ? 'true' : 'false'; break; case "等于": $str = $value == $describe ? 'true' : 'false'; break; default: $str = 'false'; } continue; } if ($str == "&") { $str = '&&'; continue; } if ($str == "|") { $str = '||'; } } $is = implode("",$result[0]); return eval("return $is;"); } /** * 多重子项匹配 * * @param array $packages * @param string $logic * @param string $describe * @param string $column * @return string //"true" || "false" */ private function multiMatching($packages, $logic, $describe, $column):string { if(!$column)return 'false'; foreach ($packages as $package){ $value = $package[$column] ?? ''; switch ($logic) { case "包含": if (mb_strpos($value,$describe)!==false)return 'true'; break; case "不包含": if (mb_strpos($value,$describe) === false)return 'true'; break; case "等于": if ($value == $describe)return 'true'; break; } } return "false"; } }