1
0

2 Коммиты 83ffd4d88c ... 158dabe8dd

Автор SHA1 Сообщение Дата
  MaiXinRong 158dabe8dd 1. ai识别填写前,先对清单类型进行判断,仅对已知类型提供 1 неделя назад
  MaiXinRong e92498ea54 ai配置写入config 1 неделя назад

+ 43 - 8
app/controller/ledger_controller.js

@@ -1192,7 +1192,7 @@ module.exports = app => {
 
             const validColSet = data.template.col_set.filter(x => { return !x.expr; });
             const AIInspect = require('../lib/ai_inspect');
-            const aiInspect = new AIInspect({ apiKey: 'app-BIOhBbFlNSakoRGKU91e2ksJ', baseUrl: 'http://39.108.81.93:8080/v1' });
+            const aiInspect = new AIInspect({ apiKey: ctx.difyInfo.apiKey.image2PosCalcDetail, baseUrl: ctx.difyInfo.baseUrl });
             const transResult = await aiInspect.posCalcDetailRead(validColSet, data.billsName, data.keyword, data.files);
             const rows = transResult.res.split('\n').filter(x => { return !!x; });
             rows.shift();
@@ -1211,13 +1211,10 @@ module.exports = app => {
             const responseData = await ctx.service.posCalcDetail.updateDatas({ add: pcdData });
             ctx.body = { err: 0, msg: '', data: responseData };
         }
-        async _aiCompliePosAndDetail(ctx, data) {
-            const bills = await this.ctx.service.ledger.getDataById(data.bills);
-            if (!bills) throw '选择的清单不存在,请刷新页面后再试';
-
+        async _aiComplieSteelPos(ctx, bills, data) {
             const validColSet = data.template.col_set.filter(x => { return !x.expr; });
             const AIInspect = require('../lib/ai_inspect');
-            const aiInspect = new AIInspect({ apiKey: 'app-FtAs299O9xFzTOVB4TlaZ73O', baseUrl: 'http://39.108.81.93:8080/v1' });
+            const aiInspect = new AIInspect({ apiKey: ctx.difyInfo.apiKey.image2Pos, baseUrl: ctx.difyInfo.baseUrl });
             const transResult = await aiInspect.posCalcDetailRead(validColSet, data.billsName, data.keyword, data.files);
             const parts = transResult.res.split('\n==++==\n').filter(x => { return !!x; });
             const pos = [];
@@ -1230,7 +1227,8 @@ module.exports = app => {
                     const pinfo = JSON.parse(pjson);
                     newPos.name = pinfo.pname;
                 }
-                const detail = p.substr(nameStr.length, p.length - nameStr.length);
+                const detailStr = p.substr(nameStr.length, p.length - nameStr.length);
+                const [detail, ancGcl] = detailStr.split(`\n==+ancGcl+==\n`);
                 const rows = detail.split('\n').filter(x => { return !!x; });
                 rows.shift();
                 newPos.calcDetail = rows.map(x => {
@@ -1245,11 +1243,47 @@ module.exports = app => {
                     }
                     return nd;
                 });
+                const agRows = ancGcl.split('\n').filter(x => { return !!x; });
+                agRows.shift();
+                newPos.ancGcl = agRows.map(x => {
+                    const rd = x.split('|');
+                    return { pid: pos.id, lid: pos.lid, name: rd[0] || '', unit: rd[1] || 0, quantity:  ctx.helper._.toNumber(rd[2]) || 0 };
+                });
                 pos.push(newPos);
             }
             const responseData = await ctx.service.pos.savePosData({ updateType: 'add-with-detail', updateData: { lid: bills.id, pos} }, ctx.tender.id);
             ctx.body = { err: 0, msg: '', data: responseData };
         }
+        async _aiComplieConcretePos(ctx, bills, data) {
+            const validColSet = data.template.col_set.filter(x => { return !x.expr; });
+            const AIInspect = require('../lib/ai_inspect');
+            const aiInspect = new AIInspect({ apiKey: ctx.difyInfo.apiKey.image2PosQty, baseUrl: ctx.difyInfo.baseUrl });
+            const transResult = await aiInspect.posCalcDetailRead(validColSet, data.billsName, data.keyword, data.files);
+            const parts = transResult.res.split('\n==++==\n').filter(x => { return !!x; });
+            const pos = [];
+            for (const p of parts) {
+                const newPos = { lid: bills.id };
+                const pInfo = JSON.parse(p);
+                newPos.name = pInfo.name;
+                newPos.sgfh_qty = ctx.helper._.toNumber(pInfo.qty) || 0;
+            }
+            const responseData = await ctx.service.pos.savePosData({ updateType: 'add-with-detail', updateData: { lid: bills.id, pos} }, ctx.tender.id);
+            ctx.body = { err: 0, msg: '', data: responseData };
+        }
+        async _aiCompliePosAndDetail(ctx, data) {
+            const bills = await this.ctx.service.ledger.getDataById(data.bills);
+            if (!bills) throw '选择的清单不存在,请刷新页面后再试';
+
+            switch(data.billsType) {
+                case '钢筋':
+                case '钢绞线':
+                    return this._aiComplieSteelPos(ctx, bills, data);
+                case '混凝土':
+                    return this._aiComplieConcretePos(ctx, bills, data);
+                default: throw `暂不支持清单类型为${data.billsType}的清单,进行ai识别填写`
+
+            }
+        }
         async aiComplie(ctx) {
             let stream;
             try {
@@ -1265,6 +1299,7 @@ module.exports = app => {
                 const rela_template = parts.field.rela_template;
                 const template = await ctx.service.calcTmpl.getTemplate(rela_template);
                 const keyword = parts.field.rela_keyword;
+                const billsType = parts.field.rela_billsType;
 
                 const uploadfiles = [];
                 while (stream !== undefined) {
@@ -1299,7 +1334,7 @@ module.exports = app => {
                         await this._aiCompliePosCalcDetail(ctx, { billsName, template, pos: rela_id, files: result, keyword });
                         break;
                     case 'bills':
-                        await this._aiCompliePosAndDetail(ctx, { billsName, template, bills: rela_id, files: result, keyword });
+                        await this._aiCompliePosAndDetail(ctx, { billsName, template, bills: rela_id, files: result, keyword, billsType });
                         break;
                     default:
                         throw '暂不提供该ai识别内容';

+ 15 - 2
app/controller/tender_controller.js

@@ -1820,7 +1820,7 @@ module.exports = app => {
             }
         }
 
-        async aiTrans(ctx) {
+        async aiImage2Excel(ctx) {
             let stream;
             try {
                 const parts = ctx.multipart({ autoFields: true });
@@ -1861,7 +1861,7 @@ module.exports = app => {
 
                 const result = await ctx.service.tempFile.addFiles(ctx.tender.id, uploadfiles, user);
                 const AIInspect = require('../lib/ai_inspect');
-                const aiInspect = new AIInspect({ apiKey: 'app-UGCJ6nlhZIcldyArOUyNwM21', baseUrl: 'http://39.108.81.93:8080/v1' });
+                const aiInspect = new AIInspect({ apiKey: ctx.difyInfo.apiKey.image2Excel, baseUrl: ctx.difyInfo.baseUrl });
                 const transResult = await aiInspect.imageTransExcel(result);
                 ctx.body = { err: 0, msg: '', data: transResult.res };
             } catch (err) {
@@ -1871,6 +1871,19 @@ module.exports = app => {
                 ctx.ajaxErrorBody(err, '图片转换失败');
             }
         }
+        async aiBillsIdentify (ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (data.name === undefined || data.unit === undefined) throw '参数错误';
+                const AIInspect = require('../lib/ai_inspect');
+                const aiInspect = new AIInspect({ apiKey: ctx.difyInfo.apiKey.billsIdentify, baseUrl: ctx.difyInfo.baseUrl });
+                const result = await aiInspect.billsIdentify(data.name, data.unit);
+                ctx.body = { err: 0, msg: '', data: result.res };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '识别清单类型失败');
+            }
+        }
     }
 
     return TenderController;

+ 4 - 0
app/extend/context.js

@@ -90,6 +90,10 @@ module.exports = {
         return this.app.config.stashOssPath;
     },
 
+    get difyInfo() {
+        return this.app.config.difyInfo;
+    },
+
     print(str) {
         this.getLogger('out').info(str);
     },

+ 9 - 0
app/lib/ai_inspect.js

@@ -99,6 +99,15 @@ class AiInspect {
         }
         return await this.workflows(body);
     }
+
+    async billsIdentify(name, unit, userId = `user_123`) {
+        const body = {
+            inputs: { billsName: name || '' + ( unit ? `(${unit})` : '') },
+            user: userId,
+            response_mode: 'blocking',
+        };
+        return await this.workflows(body);
+    }
 }
 
 module.exports = AiInspect;

+ 9 - 2
app/public/js/ledger.js

@@ -6488,8 +6488,15 @@ $(document).ready(function() {
         }
     });
     $('.ai-btn').click(function() {
-        if (aiTrans.rela_type() === 'pos') aiTrans.transDetail(posCalcDetail.afterPostData);
-        if (aiTrans.rela_type() === 'bills') aiTrans.transDetail(posOperationObj.afterPostData);
+        aiTrans.billsIdentify(function(result) {
+            if (result === '无') {
+                toastr.warning('该清单类型暂不支持ai识别');
+                return;
+            }
+
+            if (aiTrans.rela_type() === 'pos') aiTrans.transDetail(posCalcDetail.afterPostData);
+            if (aiTrans.rela_type() === 'bills') aiTrans.transDetail(posOperationObj.afterPostData);
+        });
     });
 });
 // 生成当前节点列表

+ 10 - 4
app/public/js/shares/ai_trans.js

@@ -71,6 +71,7 @@ const AITrans = function() {
         }
         formData.append('rela_bills_name', transResultObj.rela_bills.name);
         formData.append('rela_keyword', $('#ai-trans-keyword').val());
+        formData.append('rela_billsType', transResultObj.billsType);
         for (const file of files) {
             if (file === undefined) {
                 toastr.error('未选择图片!');
@@ -95,17 +96,22 @@ const AITrans = function() {
         });
     });
     const transDetail = function(callback) {
-        if (!transResultObj.rela_id) toastr.warning('请先选择清单、计量单元、附属工程量后再使用');
-        // todo 判断条件,调整
-        if (!transResultObj.rela_bills || transResultObj.rela_bills.name.indexOf('钢筋') === 0) toastr.warning('当前清单暂不支持');
         transResultObj.callback = callback;
         $('#ai-trans').modal('show');
     };
+    const billsIdentify = function (callback) {
+        if (!transResultObj.rela_id) toastr.warning('请先选择清单、计量单元、附属工程量后再使用');
+        if (!transResultObj.rela_bills) toastr.warning('未选择清单,或者选择的计量单元、附属工程量找不到所属清单');
+        postData('ai/billsIdentify', { name: transResultObj.rela_bills.name, unit: transResultObj.rela_bills.unit }, function (data) {
+            transResultObj.billsType = data;
+            callback(data);
+        });
+    };
     const setRelaInfo = function(rela_type, rela_node, rela_bills) {
         transResultObj.rela_type = rela_type;
         transResultObj.rela_node = rela_node;
         transResultObj.rela_id = rela_node ? rela_node.id : undefined;
         transResultObj.rela_bills = rela_bills;
     };
-    return { show, setRelaInfo, transDetail, rela_type: function() {return transResultObj.rela_type} }
+    return { show, setRelaInfo, billsIdentify, transDetail, rela_type: function() {return transResultObj.rela_type} }
 };

+ 2 - 1
app/router.js

@@ -641,7 +641,8 @@ module.exports = app => {
     app.post('/tender/:id/saveRela', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.saveRelaData');
     app.get('/tender/:id/cert', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.certSet');
     app.post('/tender/:id/cert/save', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.saveCert');
-    app.post('/tender/:id/ai-trans', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.aiTrans');
+    app.post('/tender/:id/ai/image2excel', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.aiImage2Excel');
+    app.post('/tender/:id/ai/billsIdentify', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.aiBillsIdentify');
 
     app.get('/tender/:id/ctrl-price', sessionAuth, tenderCheck, subProjectCheck, 'ctrlPriceController.index');
     app.post('/tender/:id/ctrl-price/load', sessionAuth, tenderCheck, subProjectCheck, 'ctrlPriceController.load');

+ 13 - 1
app/service/pos.js

@@ -594,7 +594,7 @@ module.exports = app => {
                 ? { id: bills.id, sgfh_qty: bills.sgfh_qty, sjcl_qty: bills.sjcl_qty, qtcl_qty: bills.qtcl_qty, quantity: bills.quantity, ex_qty1: bills.ex_qty1 }
                 : { id: bills.id, sgfh_qty: 0, sjcl_qty: 0, qtcl_qty: 0, quantity: 0, ex_qty1: 0 };
 
-            const insertPos = [], insertDetail = [];
+            const insertPos = [], insertDetail = [], insertAncGcl = [];
             for (const [i, b] of data.pos.entries()) {
                 const nip = {};
                 insertPos.push(nip);
@@ -647,6 +647,16 @@ module.exports = app => {
                 }
                 this._calcExpr(nip, 'sgfh_qty', '', sumQty, precision);
 
+                for (const [iag, ag] of b.ancGcl.entries()) {
+                    const nig = {
+                        id: this.uuid.v4(), tid: this.ctx.tender.id,
+                        add_user_id: user_id, update_user_id: user_id,
+                        lid: nip.lid, pid: nip.id, g_order: iag + 1,
+                        is_aux: 0, name: ag.name || '', unit: ag.unit || '', quantity: ag.quantity || 0
+                    };
+                    insertAncGcl.push(nig);
+                }
+
                 nip.quantity = this.ctx.helper.add(nip.sgfh_qty, this.ctx.helper.add(nip.sjcl_qty, nip.qtcl_qty));
                 updateBills.sgfh_qty = this.ctx.helper.add(updateBills.sgfh_qty, nip.sgfh_qty);
             }
@@ -660,6 +670,7 @@ module.exports = app => {
             try {
                 await transaction.insert(this.tableName, insertPos);
                 if (insertDetail.length > 0) await transaction.insert(this.ctx.service.posCalcDetail.tableName, insertDetail);
+                if (insertAncGcl.length > 0) await transaction.insert(this.ctx.service.ancillaryGcl.tableName, insertAncGcl);
                 await transaction.update(this.ctx.service.ledger.tableName, updateBills);
                 await transaction.commit();
             } catch (err) {
@@ -670,6 +681,7 @@ module.exports = app => {
             updateBills.ledger_id = bills.ledger_id;
             result.ledger.update = [updateBills];
             result.posCalcDetail = { add: insertDetail };
+            result.ancGcl = { add: insertAncGcl };
             return result;
         }
 

+ 11 - 0
config/config.default.js

@@ -277,5 +277,16 @@ module.exports = appInfo => {
     };
 
     config.kkfileUrl = '/preview/onlinePreview?url=';
+
+    config.difyInfo = {
+        baseUrl: 'http://workflow.smartcost.com.cn/v1',
+        apiKey: {
+            image2Excel: 'app-UGCJ6nlhZIcldyArOUyNwM21',
+            image2PosCalcDetail: 'app-BIOhBbFlNSakoRGKU91e2ksJ',
+            image2AncGclDetail: 'app-BIOhBbFlNSakoRGKU91e2ksJ',
+            image2Pos: 'app-FtAs299O9xFzTOVB4TlaZ73O',
+            billsIdentify: 'app-ixkrpfrLOovuASn3DLJLT4VB',
+        },
+    };
     return config;
 };

+ 11 - 0
config/config.local.js

@@ -117,5 +117,16 @@ module.exports = appInfo => {
 
     config.url3f = 'http://127.0.0.1:7005/3f';
     config.kkfileUrl = 'http://jlqa.smartcost.com.cn:8012/onlinePreview?url=';
+
+    config.difyInfo = {
+        baseUrl: 'http://39.108.81.93:8080/v1',
+        apiKey: {
+            image2Excel: 'app-UGCJ6nlhZIcldyArOUyNwM21',
+            image2PosCalcDetail: 'app-BIOhBbFlNSakoRGKU91e2ksJ',
+            image2AncGclDetail: 'app-BIOhBbFlNSakoRGKU91e2ksJ',
+            image2Pos: 'app-FtAs299O9xFzTOVB4TlaZ73O',
+            billsIdentify: 'app-ixkrpfrLOovuASn3DLJLT4VB',
+        },
+    };
     return config;
 };

+ 11 - 0
config/config.qa.js

@@ -92,5 +92,16 @@ module.exports = appInfo => {
 
     config.url3f = 'http://jlqa.smartcost.com.cn:7005/3f';
     config.kkfileUrl = 'http://jlqa.smartcost.com.cn:8012/onlinePreview?url=';
+
+    config.difyInfo = {
+        baseUrl: 'http://39.108.81.93:8080/v1',
+        apiKey: {
+            image2Excel: 'app-UGCJ6nlhZIcldyArOUyNwM21',
+            image2PosCalcDetail: 'app-BIOhBbFlNSakoRGKU91e2ksJ',
+            image2AncGclDetail: 'app-BIOhBbFlNSakoRGKU91e2ksJ',
+            image2Pos: 'app-FtAs299O9xFzTOVB4TlaZ73O',
+            billsIdentify: 'app-ixkrpfrLOovuASn3DLJLT4VB',
+        },
+    };
     return config;
 };