Pārlūkot izejas kodu

清单指引类别库算法更新

lishihao 3 gadi atpakaļ
vecāks
revīzija
68dd751c6f

+ 1 - 1
modules/all_models/bill_class.js

@@ -10,7 +10,7 @@ const billClass = new Schema({
     itemCharacter: String,
     class: { type: Number, index: true }, // 分类
     classCode: { type: String, index: true }, // 类别别名
-    requiredRationIDs: { type: [Number], default: [] }, // 必套定额ID
+    requiredRationIDs: { type:[[Number]], default: [[]] }, // 必套定额ID
     optionalRationIDs: { type: [Number], default: [] }, // 选套定额ID
     errorRationIDs: { type: [Number], default: [] }, // 错套定额ID
 }, {versionKey: false});

+ 160 - 86
modules/std_billsGuidance_lib/facade/facades.js

@@ -484,28 +484,12 @@ function isOptionNode(node) {
 }
 
     // 判断蓝色子项和孙子项是否打勾了必填
-    function hasRequireData(node, hasRequire = false) {
-        if (node.children
-            && node.children.length
-            && node.children[0].children
-            && node.children[0].children.length
-            && isProcessNode(node.children[0].children[0])
-        ) {
-            node.children.forEach(subNode => {
-                if (subNode.children && subNode.children.length) {
-                    subNode.children.forEach(subSubNode => {
-                        if (subSubNode.data.required) {
-                            hasRequire = true;
-                        }
-                    })
-                } else {
-                    subNode.children.forEach(subSubNode => {
-                        hasRequire = hasRequireData(subSubNode.children);
-                    })
-                }
-            })
-        }
-        return hasRequire;
+    function hasRequireData(node) {
+        const requiredList = node.getPosterity().filter(subNode =>
+            subNode.data.required === true
+        );
+        if (requiredList.length) { return true }
+        return false;
     }
 
     // 这里判断有无无定额的情况,有的就返回true,没有就返回false
@@ -518,8 +502,17 @@ function isOptionNode(node) {
                 }
             })
         }
+        //这里是兼容第一层直接是定额的白色节点
+        if (node.children && node.children.length) {
+            node.children.forEach(subNode => {
+                if (subNode.data.rationID) {
+                    isRequired = true;
+                }
+            })
+        }
         return isRequired;
     }
+
     // 获取选套定额
     function getOptionalData(node, list = []) {
         node.children.forEach(element => {
@@ -572,11 +565,97 @@ function isOptionNode(node) {
         })
         return { requireRation, optionalRation };
     }
-// 从指引节点,获取分类特征、必套定额数据
-function getItemClassData(nodes, prefix) {
-    let nodeRequireRation = [];
-        let optionalRationList = [];
+
+    //获取定额数据
+    // requireRationData必套定额对象
+    // optionalRationData 选逃定额对象
+    // classGroups classCode文字和id键值对
+    // classCodeList 各个classCode的pID和ID的关系
+    // prefixID为上一个必填项的选项ID
+    function getItemData(nodes, requireRationData={}, optionalRationData={}, classGroups={},  prefixID = '') {
         const processNodes = nodes.filter(node => isProcessNode(node));
+        // const classGroups = []; // 同层必填选项的数组(二维数组)
+        processNodes.forEach(processNode => {
+            // 蓝色节点,必填
+            if (processNode.data.required) {
+                // 白色节点
+                const optionNodes = processNode.children.filter(node => isOptionNode(node));
+                optionNodes.forEach(optionNode => {
+                    if (!requireRationData[optionNode.data.ID]) requireRationData[optionNode.data.ID] = [];
+                    if (!optionalRationData[optionNode.data.ID]) optionalRationData[optionNode.data.ID] = [];
+                    //白色节点下没有蓝色节点,就是到底了
+                    if (!optionNode.children.some(subOptionNode => isProcessNode(subOptionNode))) {
+                        // 是否必套定额
+                        if (isRequireData(optionNode)) {
+                            optionNode.children.forEach(subOptionNode => {
+                                requireRationData[optionNode.data.ID].push([subOptionNode.data.rationID])
+                            })
+                        } else {
+                            optionalRationData[optionNode.data.ID] = getOptionalData(optionNode);
+                        }
+                        const kV = {};
+                        kV[optionNode.data.ID] = optionNode.data.name;
+                        Object.assign(classGroups, kV);
+                    } else {
+                        const kV = {};
+                        kV[optionNode.data.ID] = optionNode.data.name;
+                        Object.assign(classGroups, kV);
+                        // 后代项是否有必填
+                        if (hasRequireData(optionNode)) {
+                            //后代项有必填
+                            getItemData(optionNode.children, requireRationData, optionalRationData, classGroups, optionNode.data.ID)
+                            
+                        } else {
+                            //后代项无必填
+                            optionNode.children.forEach(subOptionNode => {
+                                // 是否必套定额
+                                if (isRequireData(optionNode)) {
+                                    if (!requireRationData[subOptionNode.parent.data.ID]) requireRationData[subOptionNode.parent.data.ID] = [];
+                                    requireRationData[subOptionNode.parent.data.ID].push(getOptionalData(subOptionNode));
+                                } else {
+                                    if (!optionalRationData[subOptionNode.parent.data.ID]) optionalRationData[subOptionNode.data.ID] = [];
+                                    optionalRationData[subOptionNode.parent.data.ID].push(...getOptionalData(subOptionNode));
+                                }
+                            })
+                        }
+                    }
+                })
+            } else {
+                // 蓝色节点,非必填
+                if (hasRequireData(processNode)) {
+                    // debugger
+                     //后代项有必填
+                   if(isProcessNode(processNode)){
+                        //蓝色
+                        processNode.children.forEach((subProcessNode)=>{
+                            subProcessNode.children.forEach((sSubProcessNode)=>{
+                                getItemData([sSubProcessNode], requireRationData, optionalRationData, classGroups,prefixID)
+                            })
+                        })
+                   }
+                } else {
+                    let key = processNode.data.ID;
+                    if (prefixID) key = prefixID;
+                    // 是否必套定额
+                    if (isRequireData(processNode)) {
+                        if (!requireRationData[key]) requireRationData[processNode.data.ID] = [];
+                        requireRationData[key].push(getOptionalData(processNode));
+                    } else {
+                        if (!optionalRationData[key]) optionalRationData[processNode.data.ID] = [];
+                        optionalRationData[key].push(...getOptionalData(processNode));
+                    }
+                }
+            }
+           
+        })
+        return { requireRationData, optionalRationData, classGroups}
+    }
+
+
+// 从指引节点,获取分类特征、必套定额数据
+function getItemClassData(nodes, prefix,
+    prefixID) {
+    const processNodes = nodes.filter(node => isProcessNode(node));
         const classGroups = []; // 同层必填选项的数组(二维数组)
         processNodes.forEach(processNode => {
             const classItems = [];
@@ -589,78 +668,41 @@ function getItemClassData(nodes, prefix) {
                     || !optionNode.children.some(node => isProcessNode(node)))) {
 
                     // 必套定额
-                    const requiredRationIDs = optionNode.children && optionNode.children.length ?
-                        optionNode.children.filter(node => !!node.data.rationID).map(node => node.data.rationID) : [];
-                    classItems.push({ name: optionNode.data.name, requiredRationIDs: [requiredRationIDs], optionalRationIDs: [] });
+                    classItems.push({ name: optionNode.data.name, ID: optionNode.data.ID });
 
                 } else {
-                   
-                    if (optionNode.parent&& optionNode.parent.data.required) {
-                        //这里就开始遍历必填项下面的节点
-                        const { requireRation, optionalRation } = getSubData([optionNode]);
-                        nodeRequireRation = requireRation;
-                        optionalRationList = optionalRation;
-                    }else {
-                        // 非必填的蓝色节点
-                        // 子项是否有勾中必填
-                       if( hasRequireData(optionNode.parent)){
-                           // 获取必套的逻辑
-                           const { requireRation, optionalRation }= getSubData([optionNode.parent]);
-                           nodeRequireRation = requireRation;
-                           optionalRationList = optionalRation;
-                       }else{
-                        // 子项没有勾中必填
-                        // 判断当前蓝色节点下有没有无定额的情况,存在无定额的就下面全是选套,无无定额的情况就下面全是必套
-                        if (isRequireData(optionNode.parent)) {
-                            optionNode.children.forEach((subNode) => {
-                                if (subNode.children && subNode.children[0] && subNode.children[0].data.rationID) {
-                                    nodeRation.push(subNode.children[0].data.rationID);
-                                } else {
-                                    getSubData(subNode.children);
-                                }
-                            })
-                        } else {
-                            
-                            // 这里是选套的逻辑
-                            if(optionNode.children&&optionNode.children.length){
-                                optionalRationList.push(...getOptionalData(optionNode.parent));
-                            }
-                            
-                        }
-                       }
-                    }
-
                     // classItems.push(...getItemCharacterData(optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : ''));
-                    const childrenClassItem = getItemCharacterData(optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : '');
+                    const childrenClassItem = getItemClassData(
+                        optionNode.children, 
+                        optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : '' ,
+                        optionNode.parent && optionNode.parent.data.required ? optionNode.data.ID : ''
+                    );
+
 
                     //如果返回的子项为空,但是父项又勾选了必填,则要把本身存入数组
                     if (optionNode.parent
                         && optionNode.parent.data.required
                         && childrenClassItem.length === 0
                     ) {
-                        classItems.push({ name: optionNode.data.name, requiredRationIDs: nodeRequireRation, optionalRationIDs: optionalRationList });
+                        classItems.push({ name: optionNode.data.name,ID: optionNode.data.ID });
                     } else {
-                        if (childrenClassItem.length > 0) {
-                           
-                            childrenClassItem.map(item => {
-                                item.requiredRationIDs =nodeRequireRation;
-                                item.optionalRationIDs = optionalRationList;
-                            })
-                        }
+                       
                         classItems.push(...childrenClassItem);
                     }
                 }
             });
             if (classItems.length) {
                 classGroups.push(classItems);
-
             }
         });
+
+
         // 拼接上一文本
         if (classGroups[0] && classGroups[0].length) {
             // classGroups[0] = classGroups[0].map(name => prefix ? `${prefix}@${name}` : name);
             classGroups[0] = classGroups[0].map(item => {
                 item.name = prefix ? `${prefix}@${item.name}` : item.name
+                item.ID = prefixID ? `${prefixID}@${item.ID}` : item.ID
                 return item;
             });
         }
@@ -673,11 +715,8 @@ function getItemClassData(nodes, prefix) {
                 for (let j = 0; j < nextClassItems.length; j++) {
                     // 拼接文本
                     const mergedName = `${prevClassItems[i].name}@${nextClassItems[j].name}`;
-                    // 拼接必套定额
-                    const mergedRationIDs = [...prevClassItems[i].requiredRationIDs, ...nextClassItems[j].requiredRationIDs];
-                    const mergedOptionalRationIDs = [...prevClassItems[i].optionalRationIDs, ...nextClassItems[j].optionalRationIDs]
-
-                    mergedClassItems.push({ name: mergedName, requiredRationIDs: mergedRationIDs, optionalRationIDs: mergedOptionalRationIDs });
+                    const mergedID = `${prevClassItems[i].ID}@${nextClassItems[j].ID}`;
+                    mergedClassItems.push({ name: mergedName,ID:mergedID});
                 }
             }
             classGroups.splice(0, 2, mergedClassItems);
@@ -716,16 +755,49 @@ function getOptionalRationIDs(itemClassData, allRationIDs) {
 }
 
 // 获取错套定额:清单下所有定额,除了分类对应的必套、选套定额
-function getErrorRationIDs(requiredRationIDs, optionalRationIDs, allRationIDs) {
+function getErrorRationIDs(requiredRationIDList, optionalRationIDs, allRationIDs) {
     const errorRationIDs = [];
     allRationIDs.forEach(rationID => {
-        if (!requiredRationIDs.includes(rationID) && !optionalRationIDs.includes(rationID)) {
-            errorRationIDs.push(rationID);
-        }
+        requiredRationIDList.forEach(requiredRationIDs=>{
+            if (!requiredRationIDs.includes(rationID) && !optionalRationIDs.includes(rationID)) {
+                errorRationIDs.push(rationID);
+            }
+        })
+        
     });
     return [...new Set(errorRationIDs)];
 }
 
+//把classcode和必套选套定额结合在一起
+function combineData(classCodeData,requireRationData,optionalRationData,classGroups){
+    return  classCodeData.map(classCodeData=>{
+        const errorRationIDs=[];
+        const optionalRationIDs=[];
+        const requiredRationIDs=[];
+        let name='';
+        const classCodeIDs=classCodeData.ID;
+        if(/@/.test(classCodeIDs)){
+            classCodeIDs.split('@').forEach((classCodeID)=>{
+                if(name){ 
+                    name=name+'@'+classGroups[classCodeID]
+                }else{
+                    name=classGroups[classCodeID]
+                };
+                if(requireRationData[classCodeID]&&requireRationData[classCodeID].length) requiredRationIDs.push(...requireRationData[classCodeID]);
+                if(optionalRationData[classCodeID]&&optionalRationData[classCodeID].length) optionalRationIDs.push(...optionalRationData[classCodeID]);
+            })
+            return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
+        }else{
+            name=classGroups[classCodeIDs];
+            if(requireRationData[classCodeIDs]&&requireRationData[classCodeIDs].length) requiredRationIDs.push(...requireRationData[classCodeIDs]);
+            if(optionalRationData[classCodeIDs]&&optionalRationData[classCodeIDs].length) optionalRationIDs.push(...optionalRationData[classCodeIDs]);
+            return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
+        }
+    })
+}
+
+
+
 // 生成清单分类
 async function generateClassData(libID) {
     const lib = await billsGuideLibModel.findOne({ ID: libID }).lean();
@@ -755,14 +827,16 @@ async function generateClassData(libID) {
         const guidanceTree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
         guidanceTree.loadDatas(guidanceItems);
         //console.log('getItemClassData start',billNode.data.name,billNode.data.code,billNode.data.ID);
-        const itemClassData = getItemClassData(guidanceTree.roots); // 必套定额在这个方法内就获取了,避免重复执行递归方法
+        const classData = getItemClassData(guidanceTree.roots); // 必套定额在这个方法内就获取了,避免重复执行递归方法
+        const {requireRationData,optionalRationData,classGroups} = getItemData(guidanceTree.roots);
+        const itemClassData= combineData(classData,requireRationData,optionalRationData,classGroups);
         const allRationIDs = guidanceTree.items.filter(node => !!node.data.rationID).map(node => node.data.rationID);
         // 选套定额ID
         // const optionalRationIDs = getOptionalRationIDs(itemClassData, allRationIDs);
         //if(itemClassData.length > 1000) console.log('getItemClassData end',billNode.data.name,billNode.data.code,billNode.data.ID,itemClassData.length)
         itemClassData.forEach(item => {
             // 错套定额
-            const errorRationIDs = getErrorRationIDs(item.requiredRationIDs, optionalRationIDs, allRationIDs);
+            const errorRationIDs = getErrorRationIDs(item.requiredRationIDs, item.optionalRationIDs, allRationIDs);
             if(billNode.data.ID == '1d32fa34-0a9b-11ea-a33d-5388f9783b09') console.log(item.name)
             billClassData.push({
                 itemCharacter: item.name,