FeatureService.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. <?php
  2. namespace App\Services;
  3. use App\Feature;
  4. Class FeatureService
  5. {
  6. public function getMapArray()
  7. {
  8. $features = Feature::query()->get();
  9. $map = [];
  10. foreach ($features as $feature){
  11. $map[$feature->id] = ["type"=>$feature->type,"logic"=>$feature->logic,"describe"=>$feature->describe];
  12. }
  13. return $map;
  14. }
  15. /**
  16. * 将特征翻译为数组显示
  17. * $str : "1&2|(3&4)"
  18. * array:[
  19. * "strategyGroupStartSign" => false, //是否为策略组起点,这将在解析时解析为 (
  20. "calculation" => "", //运算规则,目前仅有 &,| 翻译后填入
  21. "type"=>"", //特征类型
  22. "id"=>"", //特征ID
  23. "logic"=>"", //特征逻辑
  24. "describe"=>"", //特征信息
  25. "strategyGroupEndSign" => false, //是否为策略组终点,这将在解析时解析为 )
  26. * ]
  27. *
  28. * @param string $str
  29. * @return array
  30. */
  31. public function translationFeature($str)
  32. {
  33. if (!$str)return null;
  34. $result = [];
  35. preg_match_all('/\d+|[\&\|\(\)]/',$str,$result); //初次匹配以数字&|()为分隔符生成数组
  36. $sign = 0; //为第二次切割做起点标记
  37. $model = [];//第二次切割数组
  38. $ids = [];//记录出现的特征ID,统一查询
  39. foreach ($result[0] as $index => &$item){
  40. if (is_numeric($item)){
  41. $model[] = array_slice($result[0],$sign,($index-$sign)+1);
  42. $sign = $index+1;
  43. $ids[] = $item;
  44. continue;
  45. }
  46. if ($index == count($result[0])-1 && $item == ')'){
  47. $model[] = [")"];
  48. }
  49. }//以数字为标准切割策略组
  50. $features = Feature::query()->find($ids);//查询出现的特征
  51. $featureMap = [];
  52. foreach ($features as $index => $feature){
  53. $featureMap[$feature->id] = $index;
  54. }//为查询的特征重组为key-val形式数组留做引用
  55. foreach ($model as $index => &$m){
  56. $arr = [
  57. "strategyGroupStartSign" => false,//是否为策略组起点,这将在解析时解析为 (
  58. "calculation" => "",//运算规则,目前仅有 &,| 翻译后填入
  59. "type"=>"", //特征类型
  60. "id"=>"", //特征ID
  61. "logic"=>"", //特征逻辑
  62. "describe"=>"", //特征信息
  63. "strategyGroupEndSign" => false,//是否为策略组终点,这将在解析时解析为 )
  64. ];//最终对象组模型,策略组将几组特征组合引用
  65. foreach ($m as $string){
  66. if (is_numeric($string)){//填入特征信息
  67. if (isset($featureMap[$string])){
  68. $arr["type"] = $features[$featureMap[$string]]->type;
  69. $arr["id"] = $features[$featureMap[$string]]->id;
  70. $arr["logic"] = $features[$featureMap[$string]]->logic;
  71. $arr["describe"] = $features[$featureMap[$string]]->describe;
  72. }
  73. continue;
  74. }
  75. switch ($string){//特殊字符的翻译
  76. case "(":
  77. $arr["strategyGroupStartSign"] = true;
  78. break;
  79. case ")":
  80. $model[$index-1]["strategyGroupEndSign"] = true;
  81. break;
  82. case "&":
  83. $arr["calculation"] = "并且";
  84. break;
  85. case "|":
  86. $arr["calculation"] = "或";
  87. break;
  88. }
  89. }
  90. if (!$arr["id"]){
  91. unset($model[$index]);
  92. continue;
  93. }
  94. $m = $arr;//变更当前指针为翻译结果
  95. }
  96. return $model;
  97. }
  98. /**
  99. * 与translationFeature相反,将一组与translationFeature参数解析为简述
  100. * $features:[
  101. * "strategyGroupStartSign" => false, //是否为策略组起点,这将在解析时解析为 (
  102. "calculation" => "", //运算规则,目前仅有 &,| 翻译后填入
  103. "type"=>"", //特征类型
  104. "id"=>"", //特征ID
  105. "logic"=>"", //特征逻辑
  106. "describe"=>"", //特征信息
  107. "strategyGroupEndSign" => false, //是否为策略组终点,这将在解析时解析为 )
  108. * ]
  109. * array:[
  110. * "feature" //简述结果
  111. * "map" //简述中出现的Map特征对象重组为key-val数组
  112. * ]
  113. * @param array $features
  114. * @return array
  115. */
  116. public function analysisFeature($features)
  117. {
  118. $str = "";
  119. $map = [];
  120. foreach ($features as &$feature){
  121. if (!$feature["type"] || !$feature["logic"] || !$feature["describe"])continue;
  122. $f = Feature::query()->firstOrCreate([
  123. "type"=>$feature["type"], //特征类型
  124. "logic"=>$feature["logic"], //特征逻辑
  125. "describe"=>$feature["describe"], //特征信息
  126. ]);
  127. $feature["id"] = $f->id;
  128. $map[$feature["id"]] = $f;
  129. if ($feature["calculation"] == '并且')$str .= '&';
  130. if ($feature["calculation"] == '或') $str .= '|';
  131. if ($feature["strategyGroupStartSign"])$str .= "(";
  132. $str .= $feature["id"];
  133. if ($feature["strategyGroupEndSign"])$str .= ")";
  134. }
  135. return ['feature'=>$str,"map"=>$map];
  136. }
  137. /**
  138. * 格式化简述特征为可阅读显示
  139. * $features : 特征对象集 重组为key-val数组数据,key为ID
  140. * $value : 简述特征 例: "1&2|(3&4)"
  141. * $columnMapping : 特征type属性字段映射 例:["商品名称"=>"commodity_name"],指定该参数会使特征格式化为SQL段
  142. * string : 例:"商品名称 包含 衣服 并且(订单类型 等于 创建订单 或者 承运商 不包含 顺丰)"
  143. *
  144. * @param array $features
  145. * @param string $value
  146. * @param array $columnMapping
  147. * @return string
  148. */
  149. public function formatFeature(array $features, $value, $columnMapping = null)
  150. {
  151. if (!$features)return $value;
  152. preg_match_all('/\d+|[\&\|\(\)]/',$value,$result);
  153. foreach ($result[0] as &$str){
  154. if (is_numeric($str) && isset($features[$str])){
  155. $column = $features[$str]["type"];
  156. $logic = $features[$str]["logic"];
  157. $describe = $features[$str]["describe"];
  158. if ($columnMapping){
  159. $column = $columnMapping[$column] ?? $column;
  160. switch ($logic){
  161. case "包含":
  162. $logic = " like ";
  163. $describe = "%".$describe."%";
  164. break;
  165. case "不包含":
  166. $logic = " not like ";
  167. $describe = "%".$describe."%";
  168. break;
  169. case "等于":
  170. $logic = " = ";
  171. break;
  172. }
  173. $str = $column.$logic."'".$describe."'";
  174. }else $str = $features[$str]["type"].' '.$features[$str]["logic"].' '.$features[$str]["describe"];
  175. }
  176. if ($str == "&"){
  177. if ($columnMapping) $str = " and ";
  178. else $str = " 并且 ";
  179. }
  180. if ($str == "|"){
  181. if ($columnMapping) $str = " or ";
  182. else $str = " 或 ";
  183. }
  184. }
  185. return implode("",$result[0]);
  186. }
  187. /**
  188. * 匹配特征
  189. * $vale : 特征简述 例: "1&2|(3|4)"
  190. * $columnMapping : 列映射 例:["商品名称"=>"commodity_name"]
  191. * $matchObject : 被匹配对象,必须存在列映射所指定字段 例:["commodity_name"=>"衣服"]
  192. * bool true匹配成功 false匹配失败
  193. *
  194. * @param string $value
  195. * @param array $columnMapping
  196. * @param array $matchObject
  197. * @return bool
  198. */
  199. public function matchFeature($value, $columnMapping, $matchObject) :bool
  200. {
  201. preg_match_all('/\d+|[\&\|\(\)]/',$value,$result);
  202. if (implode("",$result[0]) != $value)return false;
  203. preg_match_all('/\d+/',$value,$ids);
  204. if ($ids[0])$fs = Feature::query()->whereIn("id",$ids[0])->get();
  205. else return false;
  206. $features = [];
  207. foreach ($fs as $f){
  208. $features[$f->id] = $f;
  209. }
  210. foreach ($result[0] as &$str) {
  211. if (is_numeric($str) && isset($features[$str])) {
  212. $column = $features[$str]["type"];
  213. $logic = $features[$str]["logic"];
  214. $describe = $features[$str]["describe"];
  215. $value = isset($columnMapping[$column]) ? $matchObject[$columnMapping[$column]] : '';
  216. switch ($logic) {
  217. case "包含":
  218. $str = mb_strpos($value,$describe) === false ? 'false' : 'true';
  219. break;
  220. case "不包含":
  221. $str = mb_strpos($value,$describe) === false ? 'true' : 'false';
  222. break;
  223. case "等于":
  224. $str = $value == $describe ? 'true' : 'false';
  225. break;
  226. }
  227. continue;
  228. }
  229. if ($str == "&") {
  230. $str = '&&';
  231. continue;
  232. }
  233. if ($str == "|") {
  234. $str = '||';
  235. }
  236. }
  237. $is = implode("",$result[0]);
  238. return eval("return $is;");
  239. }
  240. }