info_price_facade.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /**
  2. * Created by zhang on 2020/7/20
  3. */
  4. module.exports={
  5. getOptions,
  6. getDataByCondition,
  7. getClassByAreaID,
  8. mutiApplyInfoPrice
  9. };
  10. const mongoose = require('mongoose');
  11. let infoLibModel = mongoose.model("std_price_info_lib");
  12. let infoClassModel = mongoose.model("std_price_info_class");
  13. let infoItemsModel = mongoose.model("std_price_info_items");
  14. let infoAreasModel = mongoose.model("std_price_info_areas");
  15. let unitPriceModel = mongoose.model("unit_price");
  16. let _ = require("lodash");
  17. let gljUtil = require('../../../public/web/gljUtil');
  18. const scMathUtil = require('../../../public/scMathUtil').getUtil();
  19. let projectfacade = require('./project_facade');
  20. // 载入模块
  21. var Segment = require('segment');
  22. // 创建实例
  23. var segment = new Segment();
  24. // 使用默认的识别模块及字典,载入字典文件需要1秒,仅初始化时执行一次即可
  25. segment.useDefault();
  26. let nodejieba = require('../../dict/jieba');
  27. let nameWeightMap ={
  28. // '普通':-99
  29. }
  30. async function getOptions(data,compilation){//data 是预留对象,暂时不用
  31. let compilationID = compilation._id;
  32. let areaMap={};
  33. let periodMap={};
  34. let areas =await infoAreasModel.find({"compilationID":compilationID}).lean();
  35. let libList = await infoLibModel.find({"compilationID":compilationID}).lean();
  36. for(let l of libList){
  37. // for(let area of l.areas){
  38. // if(!areaMap[area]) areas.push(area);
  39. // }
  40. //2020-05
  41. let periodArray = l.period.split("-");
  42. periodMap[periodArray[0]]?periodMap[periodArray[0]].push(periodArray[1]):periodMap[periodArray[0]]=[periodArray[1]]
  43. }
  44. for(let key in periodMap){
  45. periodMap[key] = _.sortBy(periodMap[key]);
  46. }
  47. return {areas:areas,periodMap:periodMap}
  48. }
  49. async function getClassByAreaID(data,compilation){
  50. //要先知道根据期数和编办查找库ID
  51. let newList = [];
  52. let lib = await infoLibModel.findOne({compilationID:compilation._id,period:data.period})
  53. if(lib){
  54. let tList =await getClassList(data.areaID,lib.ID);
  55. newList.push(...tList);
  56. if(data.commonInfoPriceID){
  57. cList = await getClassList(data.commonInfoPriceID,lib.ID);
  58. newList.push(...cList);
  59. }
  60. }
  61. async function getClassList(areaID,libID){
  62. let temList = [];
  63. let infoClass = await infoClassModel.find({areaID:areaID,libID:libID}).lean();
  64. let parentMap=_.groupBy(infoClass, 'ParentID');
  65. for(let key in parentMap){
  66. parentMap[key] = projectfacade.sortChildren(parentMap[key]);
  67. }
  68. if(parentMap && parentMap['-1']){
  69. getChildern(parentMap['-1'],temList,parentMap)
  70. }
  71. return temList;
  72. }
  73. function getChildern(children,list,pm){
  74. for(let c of children){
  75. list.push(c);
  76. if(pm[c.ID]){
  77. getChildern(pm[c.ID],list,pm)
  78. }
  79. }
  80. }
  81. return newList;
  82. }
  83. async function getDataByCondition(data,compilation){
  84. let result = {};
  85. data.condition["compilationID"] = compilation._id;
  86. //特殊处理重庆的,地区选择非“通用”时,搜索范围应是当前选择的地区,加上“通用”中的信息价。
  87. if (data.condition.commonInfoPriceID) {
  88. let idArray = [data.condition.areaID,data.condition.commonInfoPriceID];
  89. data.condition.areaID = {$in: idArray}
  90. delete data.condition.commonInfoPriceID;
  91. }
  92. //根据地区+期数+材料编号的前4位与信息价材料的分类编号匹配,如果有数据,则显示数据出来。
  93. //先按编号匹配
  94. if (data.code) {
  95. result = await getDataByCode(data.code, data);
  96. if (result.totalSize > 0) return result;
  97. //如果分类编码有结果,但是确没有匹配的条数,说明关键字没匹配上,直接返回没有信息价
  98. if(result.allItems > 0 && result.totalSize === 0){
  99. return result;
  100. }
  101. }
  102. //编号匹配不上的情况:
  103. //有关键字的情况
  104. if (data.keyWord) {
  105. return await getDataByKeyWord(data.keyWord,data);
  106. }
  107. //查询所有的情况
  108. if(data.lastID){ //有最后一行说明是查询下一页
  109. data.condition["_id"] = {$gt:mongoose.Types.ObjectId(data.lastID)};
  110. }else{
  111. result.totalSize = await infoItemsModel.find(data.condition).count();
  112. }
  113. result.items = await infoItemsModel.find(data.condition).lean().sort({"_id":1}).limit(50);
  114. return result;
  115. }
  116. async function getDataByCode(code, data) {
  117. let condition = { ...data.condition };
  118. condition.code = code;
  119. /*
  120. 2021-03-31 先按编号去取,所有匹配结果再过滤,不能再分类了
  121. let totalSize = await infoItemsModel.find(condition).count();
  122. if (data.lastID) { //有最后一行说明是查询下一页
  123. condition["_id"] = {$gt:mongoose.Types.ObjectId(data.lastID)};
  124. }
  125. let items = [];
  126. if (totalSize > 0) {
  127. items = await infoItemsModel.find(condition).lean().sort({"_id":1}).limit(50);
  128. } */
  129. //新需求 ---------
  130. let allItems = await infoItemsModel.find(condition).lean().sort({"_id":1});
  131. let items = [];
  132. if(data.keyWord && allItems.length > 1){
  133. for(let item of allItems){
  134. if(item.name.indexOf(data.keyWord) != -1) items.push(item) //有完全匹配的,就不用编码下的返回所有数据了
  135. }
  136. }
  137. //没有完全匹配的,分词匹配关键字
  138. if (items.length === 0 && data.keyWord){
  139. let nameArray = nodejieba.cut(data.keyWord);
  140. nameArray = cusSegment(nameArray,data.keyWord);
  141. let result = getMatchPrice(allItems,nameArray);
  142. items = result.items;
  143. }
  144. let totalSize = items.length;
  145. //新需求结束 ---------
  146. return {totalSize,items,allItems}
  147. }
  148. async function getDataByKeyWord(keyword, data) {
  149. //先按全字匹配
  150. let fullResult =await getDataByFullKeyWord(keyword, data);
  151. if(fullResult.totalSize > 0) return fullResult;
  152. //如果没有才按模糊匹配
  153. return await getDataByFuzzyMatch(keyword, data);
  154. }
  155. //按全关键字匹配
  156. async function getDataByFullKeyWord(keyword, data){
  157. let items = [];
  158. data.condition.name = new RegExp(keyword);
  159. items = await infoItemsModel.find(data.condition).lean().sort({"_id":1});
  160. delete data.condition.name;
  161. return{totalSize:items.length,items}
  162. }
  163. function handelThreeWord(word){
  164. let arr = [];
  165. getArr(word[0]+word[1],arr);
  166. getArr(word[1]+word[2],arr);
  167. if(arr.length > 0) return arr;
  168. return[word];
  169. function getArr(tem,list){
  170. let nameArray = segment.doSegment(tem, {
  171. simple: true, //不返回词性
  172. stripPunctuation: true //去除标点符号
  173. });
  174. //说明是一个词
  175. if(nameArray.length == 1) list.push(tem)
  176. }
  177. }
  178. function getShortNameArray(nameArray){
  179. let newArray = [];
  180. for(let n of nameArray){
  181. if(n.length >= 5){
  182. newArray.push(...nodejieba.cutSmall(n,3))
  183. }else{
  184. newArray.push(n);
  185. }
  186. }
  187. return newArray;
  188. }
  189. function getMatchPrice(allInfoPrice,nameArray,needHandleLongWord = true){
  190. let items = [];
  191. let maxNum = 0;//最大匹配数
  192. let matchMap = {};//匹配储存
  193. let handleLongWord = false;
  194. if(needHandleLongWord){
  195. for(let na of nameArray){
  196. if(na.length >= 5) handleLongWord = true;
  197. }
  198. }
  199. for (let info of allInfoPrice) {
  200. //specs
  201. let mstring = info.name + info.specs;
  202. mstring = mstring.replace(/混凝土/g, "砼");
  203. info.mstring = mstring;
  204. let matchCount = 0;
  205. for (let na of nameArray) {
  206. if (mstring.indexOf(na) != -1) {
  207. matchCount++;
  208. if(needHandleLongWord && na.length >= 5) handleLongWord = false//有5个字的,并且匹配上了,这里就为false不用再处理一次了
  209. }
  210. }
  211. if (matchCount > 0) {
  212. matchMap[matchCount] ? matchMap[matchCount].push(info) : matchMap[matchCount] = [info];
  213. if (matchCount > maxNum) maxNum = matchCount;
  214. }
  215. }
  216. if (maxNum > 0) items = matchMap[maxNum];
  217. totalSize = items.length
  218. return {totalSize,items,handleLongWord};
  219. }
  220. //自定义特殊处理
  221. function cusSegment(nameArray,keyword){
  222. let temArr = [];
  223. for (let a of nameArray) {
  224. //混凝土 和 砼 统一认成砼
  225. if (a == "混凝土") a = '砼';
  226. if(a == '砼'||a == '砖' ){
  227. temArr.push(a);
  228. }else if(a.length > 1){
  229. //如果是三个字的词,优先识别成两个字
  230. if(a.length == 3){
  231. let sArr = handelThreeWord(a);
  232. temArr.push(...sArr);
  233. }else{
  234. temArr.push(a);
  235. }
  236. }
  237. }
  238. if (keyword.length == 1 && temArr.length == 0) temArr.push(keyword);
  239. return temArr;
  240. }
  241. //模糊匹配
  242. async function getDataByFuzzyMatch(keyword, data){
  243. let nameArray = [];
  244. if (keyword.length < 3) {
  245. nameArray.push(keyword)
  246. } else {
  247. nameArray = nodejieba.cut(keyword)
  248. }
  249. //自定义处理
  250. nameArray = cusSegment(nameArray,keyword);
  251. console.log(nameArray);
  252. let allInfoPrice = await infoItemsModel.find(data.condition).lean().sort({"_id":1});
  253. let {totalSize,items,handleLongWord} = getMatchPrice(allInfoPrice,nameArray)
  254. if(handleLongWord === true){
  255. nameArray = getShortNameArray(nameArray);
  256. console.log(`二次匹配:[${nameArray}]`);
  257. let newResult = getMatchPrice(allInfoPrice,nameArray,false)
  258. totalSize = newResult.totalSize;
  259. items = newResult.items;
  260. }
  261. //关键词按权重排序,为了给结果排序
  262. nameArray = _.sortBy(nameArray,(name)=>{
  263. if(nameWeightMap[name]) return nameWeightMap[name]*-1;//sortBy是升序排序,我们要的是权重越小排到越后即倒序 所以这里乘以-1
  264. return 1
  265. })
  266. //按匹配位置排序 如[ '橡胶', '胶圈', '给水' ] 先显示橡胶
  267. items = _.sortBy(items,(item)=>{
  268. let ms = item.mstring;
  269. for(let i = 0;i < nameArray.length;i ++){
  270. if (ms.indexOf(nameArray[i]) != -1) return i;
  271. }
  272. })
  273. return {totalSize,items}
  274. }
  275. async function mutiApplyInfoPrice(data,compilation){
  276. data.condition["compilationID"] = compilation._id;
  277. let infoPrices = await infoItemsModel.find(data.condition).lean();
  278. let tasks = [];
  279. let projectGLJMap = {};
  280. for(let info of infoPrices){
  281. let index = gljUtil.getIndex(info,["name","specs","unit"]);
  282. if(data.pgljMap[index]){
  283. for(let obj of data.pgljMap[index]){
  284. let infoPrice = gljUtil.getInfoMarketPrice(info,data.taxType);
  285. infoPrice = scMathUtil.roundToString(infoPrice,data.decimal);
  286. const marketPrice = gljUtil.calcMarketPriceByInfoPrice(infoPrice,obj.purchaseFeeRate,data.decimal,scMathUtil);
  287. let doc = {'market_price':marketPrice,'priceFrom':data.priceFrom,infoPrice};
  288. let task = {
  289. updateOne:{
  290. filter:{'id':obj.unitPriceID},
  291. update:doc
  292. }
  293. };
  294. tasks.push(task);
  295. projectGLJMap[obj.pgljID] = {index:obj.fullIndex,doc:doc};
  296. }
  297. }
  298. }
  299. if(tasks.length > 0) await unitPriceModel.bulkWrite(tasks);
  300. return projectGLJMap;
  301. }