Browse Source

导入签约清单,置换导入控件,完善测试代码

MaiXinRong 6 years ago
parent
commit
01639609f1

+ 27 - 8
app/controller/deal_bills_controller.js

@@ -11,7 +11,13 @@
 const fs = require('fs');
 const path = require('path');
 const excel = require('node-xlsx');
+const xlsx = require('js-xlsx');
 const sendToWormhole = require('stream-wormhole');
+const loadExcelType = {
+    display: 1,
+    actual: 2,
+};
+const loadType = loadExcelType.display;
 
 module.exports = app => {
     class DealBillsController extends app.BaseController {
@@ -52,14 +58,27 @@ module.exports = app => {
                 const fileName = this.app.baseDir + '/app/public/deal_bills/uploads/' + 'deal_bills' + create_time + fileInfo.ext;
                 // 保存文件
                 await ctx.helper.saveStreamFile(stream, fileName);
-                // 读取文件
-                const sheet = excel.parse(fileName);
-                if (!sheet || sheet.length === 0 || sheet[0] === undefined || sheet[0].data === undefined) {
-                    throw 'excel没有对应数据';
-                }
-                const result = await ctx.service.dealBills.importData(sheet[0], ctx.tender.id);
-                if (!result) {
-                    throw '导入数据失败';
+                if (loadType === loadExcelType.display) {
+                    const wb = xlsx.readFile(fileName);
+                    const name = wb.SheetNames[0];
+                    const sheetData = {
+                        rows: xlsx.utils.sheet_to_json(wb.Sheets[name], {header: 1}),
+                        merge: wb.Sheets[name]["!merges"],
+                    };
+                    const result = await ctx.service.dealBills.importDataJsXlsx(sheetData.rows, ctx.tender.id);
+                    if (!result) {
+                        throw '导入数据失败';
+                    }
+                } else {
+                    // 读取文件
+                    const sheet = excel.parse(fileName);
+                    if (!sheet || sheet.length === 0 || sheet[0] === undefined || sheet[0].data === undefined) {
+                        throw 'excel没有对应数据';
+                    }
+                    const result = await ctx.service.dealBills.importData(sheet[0], ctx.tender.id);
+                    if (!result) {
+                        throw '导入数据失败';
+                    }
                 }
 
                 const dealBills = await this.ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });

+ 72 - 1
app/service/deal_bills.js

@@ -21,7 +21,7 @@ module.exports = app => {
         }
 
         /**
-         * 导入Excel数据
+         * 导入Excel数据(node-xlsx)
          *
          * @param {Array} sheet - Excel文件中的全部工作表
          * @param {Number} tenderId - 所属标段Id
@@ -90,6 +90,77 @@ module.exports = app => {
             }
             return result;
         }
+
+        /**
+         * 导入Excel数据(js-xlsx)
+         *
+         * @param {Array} sheet - Excel文件中的全部工作表
+         * @param {Number} tenderId - 所属标段Id
+         * @returns {Promise<boolean>}
+         */
+        async importDataJsXlsx(sheet, tenderId) {
+            let result = false;
+            const transaction = await this.db.beginTransaction();
+            try {
+                const bills = [];
+                let iCode = -1, iName = -1, iUnit = -1, iUp = -1, iQty = -1, iTp = -1, bCheckCol = false;
+                for (let iRow = 0; iRow < sheet.rows.length; iRow++) {
+                    const row = sheet.rows[iRow];
+                    if (!bCheckCol) {
+                        for (let iCol = 0; iCol < row.length; iCol++) {
+                            const value = row[iCol];
+                            if (typeof value !== "string") { continue }
+                            if (value === '子目号' || value === '清单编号') iCode = iCol;
+                            if (value.indexOf('名称') >= 0) iName = iCol;
+                            if (value.indexOf('单位') >= 0) iUnit = iCol;
+                            if (value.indexOf('单价') >= 0) iUp = iCol;
+                            if (value.indexOf('数量') >= 0) iQty = iCol;
+                            if (value.indexOf('金额') >= 0 || value.indexOf('合价') >= 0) iTp = iCol;
+                        }
+                        bCheckCol = (iCode >= 0 && iName >= 0 && iUnit >= 0 && iUp >= 0 && iQty >= 0);
+                        if (!bCheckCol) {
+                            iCode = -1;
+                            iName = -1;
+                            iUnit = -1;
+                            iUp = -1;
+                            iQty = -1;
+                        }
+                    } else if (this.ctx.helper.validBillsCode(row[iCode])) {
+                        const data = {
+                            deal_id: bills.length + 1,
+                            tender_id: tenderId,
+                            code: row[iCode],
+                            name: row[iName],
+                            unit: row[iUnit],
+                            unit_price: this._.toNumber(row[iUp]),
+                            quantity: this._.toNumber(row[iQty]),
+                        };
+                        if (this._.isNaN(data.unit_price) || this._.isNaN(data.quantity)) {
+                            throw '导入的Excel的数据类型有误,请检查第' + (iRow + 1) + '行';
+                        }
+                        bills.push(data);
+                    }
+                }
+                if (!bCheckCol) {
+                    throw '导入的Excel表头定义有误,请下载示例检查';
+                }
+                if (bills.length > 0) {
+                    await transaction.delete(this.tableName, {tender_id: tenderId});
+                    const billsResult = await transaction.insert(this.tableName, bills);
+                    if (billsResult.affectedRows !== bills.length) {
+                        throw '导入签约清单数据出错';
+                    }
+                } else {
+                    throw 'Excel文件中无签约清单数据';
+                }
+                await transaction.commit();
+                result = true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            return result;
+        }
     }
 
     return DealBills;

+ 119 - 5
test/app/service/deal_bills.test.js

@@ -10,19 +10,133 @@
 
 const { app, assert } = require('egg-mock/bootstrap');
 const excel = require('node-xlsx');
+const xlsx = require('js-xlsx');
+const addData = {
+    name: 'test_deal_bills',
+    category: null,
+};
+const mockData = {};
+const _ = require('lodash');
 
 describe('test/app/service/deal_bills.test.js', () => {
-    it('test import Excel data', function* () {
+    // 准备测试数据,新增测试标段
+    before(function* () {
         const ctx = app.mockContext();
+        // 模拟登录session
+        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;
+        // 移除旧测试数据
+        const testTender = yield ctx.service.tender.getDataByCondition({
+            name: addData.name,
+            project_id: ctx.session.sessionProject.id,
+        });
+        if (testTender) {
+            const result = yield ctx.service.tender.deleteTenderNoBackup(testTender.id);
+            assert(result);
+        }
+        // 新增测试用标段
+        const result = yield ctx.service.tender.add(addData);
+        assert(result);
+        const tender = yield ctx.service.tender.getDataByCondition({
+            name: addData.name,
+            project_id: ctx.session.sessionProject.id,
+        });
+        mockData.tender = {id: tender.id, data: tender};
+        ctx.tender = mockData.tender;
+    });
+    // 导入 Excel数据(数据源:node-xlsx导入)
+    it('test import Excel data by node-xlsx', function* () {
+        const ctx = app.mockContext(mockData);
+        const file = app.baseDir  + '/test/app/test_file/deal-upload-test.xls';
+        const sheets = excel.parse(file);
+
+        const result = yield ctx.service.dealBills.importData(sheets[0], ctx.tender.id);
+        assert(result);
+
+        const bills = yield ctx.service.dealBills.getAllDataByCondition({where: {tender_id: ctx.tender.id}});
+        assert(bills.length === 1);
+        assert(bills[0].code === '101-1');
+        assert(bills[0].tender_id === ctx.tender.id);
+    });
+    // 导入 Excel数据(数据源:js-xlsx导入)
+    it('test import Excel data by js-xlsx', function* () {
+        const ctx = app.mockContext(mockData);
         const file = app.baseDir  + '/test/app/test_file/deal-upload-test.xls';
-        const sheets = excel.parse(file), testTenderId = 3;
 
-        const result = yield ctx.service.dealBills.importData(sheets[0], testTenderId);
+        const wb = xlsx.readFile(file);
+        const name = wb.SheetNames[0];
+        const sheetData = {
+            rows: xlsx.utils.sheet_to_json(wb.Sheets[name], {header: 1}),
+            merge: wb.Sheets[name]["!merges"],
+        };
+        const result = yield ctx.service.dealBills.importDataJsXlsx(sheetData, ctx.tender.id);
         assert(result);
 
-        const bills = yield ctx.service.dealBills.getAllDataByCondition({where: {tender_id: testTenderId}});
+        const bills = yield ctx.service.dealBills.getAllDataByCondition({where: {tender_id: ctx.tender.id}});
         assert(bills.length === 1);
         assert(bills[0].code === '101-1');
-        assert(bills[0].tender_id === testTenderId);
+        assert(bills[0].tender_id === ctx.tender.id);
+    });
+    // 导入 数据类型有误的excel(数据源:js-xlsx导入)
+    it('test import Excel data with Type-Error by js-xlsx', function* () {
+        const ctx = app.mockContext(mockData);
+        const file = app.baseDir  + '/test/app/test_file/deal-bills-typeError.xlsx';
+
+        const wb = xlsx.readFile(file);
+        const name = wb.SheetNames[0];
+        const sheetData = {
+            rows: xlsx.utils.sheet_to_json(wb.Sheets[name], {header: 1}),
+            merge: wb.Sheets[name]["!merges"],
+        };
+        try {
+            const result = yield ctx.service.dealBills.importDataJsXlsx(sheetData, ctx.tender.id);
+            assert(false);
+        } catch (err) {
+            assert(err);
+            assert(err.indexOf('数据类型有误') > 0);
+        }
+    });
+    // 导入 表头定义有误的excel(数据源:js-xlsx导入)
+    it('test import Excel data with Col-Define-Error by js-xlsx', function* () {
+        const ctx = app.mockContext(mockData);
+        const file = app.baseDir  + '/test/app/test_file/deal-bills-colDefError.xlsx';
+
+        const wb = xlsx.readFile(file);
+        const name = wb.SheetNames[0];
+        const sheetData = {
+            rows: xlsx.utils.sheet_to_json(wb.Sheets[name], {header: 1}),
+            merge: wb.Sheets[name]["!merges"],
+        };
+        try {
+            const result = yield ctx.service.dealBills.importDataJsXlsx(sheetData, ctx.tender.id);
+            assert(false);
+        } catch (err) {
+            assert(err);
+            assert(err.indexOf('表头定义有误') > 0);
+        }
+    });
+    // 导入 表头定义有误的excel(数据源:js-xlsx导入)
+    it('test import Excel data with Col-Define-Error by js-xlsx', function* () {
+        const ctx = app.mockContext(mockData);
+        const file = app.baseDir  + '/test/app/test_file/deal-bills-filterInvalidRows.xlsx';
+
+        const wb = xlsx.readFile(file);
+        const name = wb.SheetNames[0];
+        const sheetData = {
+            rows: xlsx.utils.sheet_to_json(wb.Sheets[name], {header: 1}),
+            merge: wb.Sheets[name]["!merges"],
+        };
+        const result = yield ctx.service.dealBills.importDataJsXlsx(sheetData, ctx.tender.id);
+        assert(result);
+
+        const bills = yield ctx.service.dealBills.getAllDataByCondition({where: {tender_id: ctx.tender.id}});
+        assert(bills.length === 98);
     });
 });

BIN
test/app/test_file/deal-bills-colDefError.xlsx


BIN
test/app/test_file/deal-bills-filterInvalidRows.xlsx


BIN
test/app/test_file/deal-bills-typeError.xlsx