瀏覽代碼

报表,数据分析方法类,添加单元测试

MaiXinRong 5 年之前
父節點
當前提交
54e52925d6

+ 22 - 15
app/extend/helper.js

@@ -391,30 +391,37 @@ module.exports = {
             return -1;
         }
 
-        const path1 = str1.split(symbol);
-        const path2 = str2.split(symbol);
-        const reg = /^[0-9]*$/;
-        for (let i = 0, iLen = Math.min(path1.length, path2.length); i < iLen; i++) {
-            if (reg.test(path1[i]) && reg.test(path2[i])) {
-                const num1 = parseInt(path1[i]);
-                const num2 = parseInt(path2[i]);
-                if (num1 !== num2)  {
-                    return num1 - num2;
+        function compareSubCode(code1, code2) {
+            if (numReg.test(code1)) {
+                if (numReg.test(code2)) {
+                    return parseInt(code1) - parseInt(code2);
+                } else {
+                    return -1
                 }
-            } else if (path1[i] < path2[i]) {
-                return -1;
-            } else if (path1[i] > path2[i]) {
-                return 1;
+            } else {
+                if (numReg.test(code2)) {
+                    return 1;
+                } else {
+                    return code1 === code2 ? 0 : (code1 < code2 ? -1 : 1); //code1.localeCompare(code2);
+                }
+            }
+        }
+        const numReg = /^[0-9]+$/;
+        const aCodes = str1.split(symbol), bCodes = str2.split(symbol);
+        for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
+            const iCompare = compareSubCode(aCodes[i], bCodes[i]);
+            if (iCompare !== 0) {
+                return iCompare;
             }
         }
-        return path1.length - path2.length;
+        return aCodes.length - bCodes.length;
     },
 
     getChapterCode(code, symbol = '-') {
         if (!code || code === '') return '';
         const codePath = code.split(symbol);
         const reg = /^[0-9]*$/;
-        if (reg.text(codePath[0])) {
+        if (reg.test(codePath[0])) {
             const num = parseInt(codePath[0]);
             return this.mul(this.div(num, 100, 0), 100);
         } else {

+ 6 - 7
app/lib/rpt_data_analysis.js

@@ -18,11 +18,11 @@ const changeSort = {
      * @param fieldsKey - 计算字段
      */
     fun: function (ctx, data, fieldsKey) {
-        if (!data.change || data.change_audit_list) return;
+        if (!data.change || !data.change_audit_list) return;
         // 变更令排序
         data.change.sort(function (a, b) {
             return a.code.localeCompare(b.code);
-        };
+        });
         data.change_audit_list.sort(function (a, b) {
             const aCIndex = data.change.findIndex(function (c) {
                 return c.cid === a.cid;
@@ -32,8 +32,8 @@ const changeSort = {
             });
             return aCIndex === bCIndex
                 ? ctx.helper.compareCode(a.code, b.code)
-                : aCIndex - bCindex;
-        })
+                : aCIndex - bCIndex;
+        });
     },
 };
 const gatherGcl = {
@@ -76,7 +76,7 @@ const gatherGcl = {
                     gcl = {
                         b_code: b.b_code, name: b.name, unit: b.unit,
                         unit_price: b.unit_price,
-                        gc_bgl_code: [],
+                        qc_bgl_code: [],
                     };
                     gclBills.push(gcl);
                 }
@@ -119,12 +119,11 @@ const sortGcl = {
         '  1. 默认"未计入清单章节项"排列在最后\n' +
         '  2. 如须"未计入清单章节项"排在100章之后,请在清单编号字段后,依次勾选"章节编号(chapter)", "名称(name)"\n',
     fun: function (ctx, data, fieldsKey) {
-        if (fieldsKey.length !== 1 || fieldsKey.length !== 3) return;
+        if (fieldsKey.length !== 1 && fieldsKey.length !== 3) return;
         const code = fieldsKey[0].field;
         const chapter = fieldsKey.length > 1 ? fieldsKey[1].field : '';
         const name = fieldsKey.length > 2 ? fieldsKey[2].field : '';
 
-
         const sortData = data[fieldsKey[0].table];
         if (!sortData) return;
 

+ 112 - 0
app/service/report.js

@@ -0,0 +1,112 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+    class Report extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局context
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+        }
+
+        async getReportData(params, filters, memFieldKeys) {
+            const service = this.ctx.service;
+            const rst = {};
+            const runnableRst = [];
+            const runnableKey = []; // 这个配合runnableRst用,未来考虑并行查询优化
+            for (const filter of filters) {
+                if (runnableKey.indexOf(filter) < 0) {
+                    switch (filter) {
+                        case 'project' :
+                            runnableRst.push(service.project.getProjectById(params.project_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'tender_info' :
+                            runnableRst.push(service.tenderInfo.getTenderInfo(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'ledger' :
+                            runnableRst.push(service.ledger.getData(params.tender_id, 0));
+                            runnableKey.push(filter);
+                            break;
+                        case 'stage_bills':
+                            runnableRst.push(service.stageBills.getLastestStageData(params.tender_id, params.stage_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'stage_bills_final':
+                            await checkStg(this.ctx, params);
+                            runnableRst.push(service.stageBillsFinal.getFinalDataEx(params.tender_id, params.stage_order));
+                            runnableKey.push(filter);
+                            break;
+                        case 'stage':
+                            runnableRst.push(service.stage.getStageById(params.stage_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'stage_pay':
+                            await checkStg(this.ctx, params);
+                            runnableRst.push(service.stagePay.getAuditorStageData(params.stage_id, params.stage_times, params.stage_order));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_stage_im_zl':
+                            runnableRst.push(service.reportMemory.getStageImZlData(params.tender_id, params.stage_id, memFieldKeys[filter]));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_month_progress':
+                            runnableRst.push(service.reportMemory.getMonthProgress(params.tender_id, memFieldKeys[filter]));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_stage_bills':
+                            runnableRst.push(service.reportMemory.getStageBillsData(params.tender_id, params.stage_id, memFieldKeys[filter]));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_stage_pos':
+                            runnableRst.push(service.reportMemory.getStagePosData(params.tender_id, params.stage_id, memFieldKeys[filter]));
+                            runnableKey.push(filter);
+                            break;
+                        case 'change':
+                            runnableRst.push(service.change.getListByStatus(params.tender_id, 3)); // 获取所有审核通过的变更主信息
+                            runnableKey.push(filter);
+                            break;
+                        case 'change_audit_list':
+                            runnableRst.push(service.changeAuditList.getChangeAuditBills(params.tender_id)); // 获取所有审核通过的变更清单
+                            runnableKey.push(filter);
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+            const queryRst = await Promise.all(runnableRst);
+            for (let idx = 0; idx < runnableKey.length; idx++) {
+                rst[runnableKey[idx]] = queryRst[idx];
+            }
+            for (const filter of filters) {
+                switch (filter) {
+                    case 'mem_stage_im_tz':
+                        rst[filter] = await service.reportMemory.getStageImTzData(params.tender_id, params.stage_id, memFieldKeys[filter]);
+                        break;
+                    case 'mem_stage_im_tz_bills':
+                        rst[filter] = await service.reportMemory.getStageImTzBillsData(params.tender_id, params.stage_id, memFieldKeys[filter]);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            return rst;
+        }
+    }
+
+    return Report;
+};

+ 3 - 96
test/app/controller/report_controller.test.js

@@ -12,99 +12,6 @@ const { app, assert } = require('egg-mock/bootstrap');
 const mockData = {};
 const path = require('path');
 
-async function getReportData(ctx, params, filters, memFieldKeys) {
-    const rst = {};
-    const runnableRst = [];
-    const runnableKey = []; // 这个配合runnableRst用,未来考虑并行查询优化
-    // console.log('params');
-    // console.log(params);
-    // console.log('memFieldKeys');
-    // console.log(memFieldKeys);
-    for (const filter of filters) {
-        if (runnableKey.indexOf(filter) < 0) {
-            switch (filter) {
-                case 'project' :
-                    runnableRst.push(ctx.service.project.getProjectById(params.project_id));
-                    runnableKey.push('project');
-                    break;
-                case 'tender_info' :
-                    runnableRst.push(ctx.service.tenderInfo.getTenderInfo(params.tender_id));
-                    runnableKey.push('tender_info');
-                    break;
-                case 'ledger' :
-                    runnableRst.push(ctx.service.ledger.getData(params.tender_id, 0));
-                    runnableKey.push('ledger');
-                    break;
-                case 'stage_bills':
-                    runnableRst.push(ctx.service.stageBills.getLastestStageData(params.tender_id, params.stage_id));
-                    runnableKey.push('stage_bills');
-                    break;
-                case 'stage_bills_final':
-                    await checkStg(ctx, params);
-                    runnableRst.push(ctx.service.stageBillsFinal.getFinalDataEx(params.tender_id, params.stage_order));
-                    runnableKey.push('stage_bills_final');
-                    break;
-                case 'stage':
-                    runnableRst.push(ctx.service.stage.getStageById(params.stage_id));
-                    runnableKey.push('stage');
-                    break;
-                case 'stage_pay':
-                    await checkStg(ctx, params);
-                    runnableRst.push(ctx.service.stagePay.getAuditorStageData(params.stage_id, params.stage_times, params.stage_order));
-                    runnableKey.push('stage_pay');
-                    break;
-                case 'mem_stage_im_zl':
-                    // memFieldKeys[filter]
-                    runnableRst.push(ctx.service.reportMemory.getStageImZlData(params.tender_id, params.stage_id));
-                    runnableKey.push('mem_stage_im_zl');
-                    break;
-                case 'mem_month_progress':
-                    runnableRst.push(ctx.service.reportMemory.getMonthProgress(params.tender_id));
-                    runnableKey.push('mem_month_progress');
-                    break;
-                case 'mem_stage_bills':
-                    runnableRst.push(ctx.service.reportMemory.getStageBillsData(params.tender_id, params.stage_id));
-                    runnableKey.push('mem_stage_pos');
-                    break;
-                case 'mem_stage_pos':
-                    runnableRst.push(ctx.service.reportMemory.getStagePosData(params.tender_id, params.stage_id));
-                    runnableKey.push('mem_stage_pos');
-                    break;
-                case 'change':
-                    runnableRst.push(ctx.service.change.getListByStatus(params.tender_id, 3)); // 获取所有审核通过的变更主信息
-                    runnableKey.push('change');
-                    break;
-                case 'change_audit_list':
-                    runnableRst.push(ctx.service.changeAuditList.getChangeAuditBills(params.tender_id)); // 获取所有审核通过的变更清单
-                    runnableKey.push('change_audit_list');
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-    const queryRst = await Promise.all(runnableRst);
-    for (let idx = 0; idx < runnableKey.length; idx++) {
-        rst[runnableKey[idx]] = queryRst[idx];
-        if (['change', 'change_audit_list'].indexOf(runnableKey[idx]) >= 0) {
-            await ctx.helper.saveBufferFile(JSON.stringify(queryRst[idx],"","\t"), ctx.app.baseDir + '/' + runnableKey[idx] +'.json');
-        }
-    }
-    for (const filter of filters) {
-        switch (filter) {
-            case 'mem_stage_im_tz':
-                rst[filter] = await ctx.service.reportMemory.getStageImTzData(params.tender_id, params.stage_id);
-                break;
-            case 'mem_stage_im_tz_bills':
-                rst[filter] = await ctx.service.reportMemory.getStageImTzBillsData(params.tender_id, params.stage_id);
-                break;
-            default:
-                break;
-        }
-    }
-    return rst;
-}
-
 describe('test/app/service/report_memory.test.js', () => {
     // 准备测试数据
     before(function* () {
@@ -132,11 +39,11 @@ describe('test/app/service/report_memory.test.js', () => {
         // test12 - 第6期
         const stage = yield ctx.service.stage.getDataByCondition({tid: 12, order: 6});
         const params = {
-            tid: stage.tid,
-            sid: stage.sid,
+            tender_id: stage.tid,
+            stage_id: stage.id,
         };
         const filters = ['change', 'change_audit_list'];
-        const result = yield getReportData(ctx, params, filters);
+        const result = yield ctx.service.report.getReportData(ctx, params, filters);
         const savePath = path.join(ctx.app.baseDir, 'report_temp');
         yield ctx.helper.recursiveMkdirSync(savePath);
         for (const table in result) {

+ 113 - 0
test/app/lib/rpt_data_analysis.test.js

@@ -0,0 +1,113 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const { app, assert } = require('egg-mock/bootstrap');
+const mockData = {};
+const path = require('path');
+
+const reportDataAnalysis = require('../../../app/lib/rpt_data_analysis');
+
+describe('test/app/service/report_memory.test.js', () => {
+    // 准备测试数据
+    before(function* () {
+        const ctx = app.mockContext();
+        // 模拟登录session
+        // const postData = {
+        //     account: 'fuqingqing',
+        //     project: 'P0505',
+        //     project_password: '123456',
+        // };
+        const postData = {
+            account: '734406061@qq.com',
+            project: 'T201711273363',
+            project_password: 'mai654321',
+        };
+        ctx.session = {};
+        const loginResult = yield ctx.service.projectAccount.accountLogin(postData, 2);
+        assert(loginResult);
+        mockData.session = ctx.session;
+    });
+    // 数据
+    it('test changeSort', function* () {
+        const ctx = app.mockContext(mockData);
+
+        // test12 - 第6期
+        const stage = yield ctx.service.stage.getDataByCondition({tid: 12, order: 6});
+        const params = {
+            tender_id: stage.tid,
+            stage_id: stage.id,
+        };
+        const filters = ['change', 'change_audit_list'];
+        const data = yield ctx.service.report.getReportData(params, filters);
+        reportDataAnalysis.changeSort.fun(ctx, data);
+        assert(data.change[0].code === 'test7-BG-001');
+        assert(data.change[2].code === 'test7-BG-003');
+        const changeCid = ctx.helper._.map(data.change, 'cid');
+        const changeBillsCid = ctx.helper._.uniq(ctx.helper._.map(data.change_audit_list, 'cid'));
+        assert(changeCid.length === changeBillsCid.length);
+        changeCid.forEach(function (a, i) {
+            assert(a === changeBillsCid[i]);
+        });
+    });
+    it('test gatherGcl && sortGcl', function* () {
+        const ctx = app.mockContext(mockData);
+
+        // test12 - 第6期
+        const stage = yield ctx.service.stage.getDataByCondition({tid: 12, order: 6});
+        const params = {
+            tender_id: stage.tid,
+            stage_id: stage.id,
+        };
+        const filters = ['mem_stage_bills'];
+        const data = yield ctx.service.report.getReportData(params, filters, {
+            mem_stage_bills: [
+                'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
+                'code', 'b_code', 'name', 'unit', 'unit_price',
+                'deal_qty', 'deal_tp',
+                'sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price',
+                'dgn_qty1', 'dgn_qty2',
+                'drawing_code', 'memo', 'node_type', 'is_tp',
+                'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp', 'postil',
+                'pre_contract_qty', 'pre_contract_tp', 'pre_qc_qty', 'pre_qc_tp', 'pre_gather_qty', 'pre_gather_tp',
+                'end_contract_qty', 'end_contract_tp', 'end_qc_qty', 'end_qc_tp', 'end_gather_qty', 'end_gather_tp',
+                'final_tp', 'final_ratio',
+                'qc_bgl_code',
+                'chapter',
+            ]
+        });
+        assert(data.mem_stage_bills.length === 216);
+        reportDataAnalysis.gatherGcl.fun(ctx, data, [
+            {field: 'b_code', table: 'mem_stage_bills'},
+            {field: 'name', table: 'mem_stage_bills'},
+            {field: 'unit', table: 'mem_stage_bills'},
+            {field: 'unit_price', table: 'mem_stage_bills'},
+            {field: 'is_leaf', table: 'mem_stage_bills'},
+        ]);
+        assert(data.mem_stage_bills.length === 43);
+        reportDataAnalysis.sortGcl.fun(ctx, data, [
+            {field: 'b_code', table: 'mem_stage_bills'},
+        ]);
+        const codeIndex = ctx.helper._.map(data.mem_stage_bills, 'b_code');
+        const codeIndex100 = ['103-1', '103-2', '103-3-a', '103-3-b', '103-4', '104-1'];
+        const codeIndex200 = [
+            '203-1-a', '203-1-b', '203-1-d', '204-1-b', '204-1-g-4', '204-1-j', '205-1-o-1', '206-2',
+            '207-1-b', '207-2-a', '207-3-a', '207-4-a', '208-1-a', '208-3-a', '208-3-c', '208-4-b-1', '209-1-a',
+        ];
+        const codeIndex400 = [
+            '403-1-a', '403-1-b', '403-2-a', '403-2-b', '403-3-a', '403-3-b', '403-4-a', '403-4-b',
+            '405-1-b-5', '405-1-b-6', '410-1-b', '410-1-c', '410-2-b', '410-2-c', '410-6-c',
+            '411-5', '411-7-a', '411-8-a', '413-1-a', '417-5-a'
+        ];
+        let codeResult = codeIndex100.concat(codeIndex200, codeIndex400);
+        codeIndex.forEach(function (a, i) {
+            assert(a === codeResult[i]);
+        });
+    });
+});