const util = require('util'); const utils = require('./utils'); const db = require("./db"); const dbhelper = require("./dbhelper"); const bchelper = require("./bchelper"); const log = require("./log"); const dblog = require("./dblog"); const bc = require('./blockchain'); const metamgr = require('./metamgr'); const C = require('./C'); /* 1、数据库被认为是可靠如果数据库操作失败则应该挂起 2、区块链查询操作没啥成本可以多次调用,写入操作需谨慎!!! 重试的情况: 1、调用合约后当前块的高度>合约高度 + 6并且getPastEvent里查询不到该事件 2、网络通(目前未处理) 3、 Returned error: replacement transaction underpriced */ class BoxOrder { constructor(orderDb, isNewOrder) { this.isNewOrder = isNewOrder; this.orderDb = orderDb; } init () { if (this.isNewOrder) { setTimeout(this.start.bind(this), utils.randRange(100, 1000)); } else { setTimeout(this.start.bind(this), utils.randRange(1000, 3000)); } } async start() { //支付 await this.payment(); //创建nft await this.mint(); } async payment() { const logClass = 'payment'; if (!this.isPaid()) { const eventFilter = { boxId: this.getOrderId() }; const onSearchSuccess = async (event) => { const blockNumber = event['blockNumber']; await this.updateDbMustBeSuccess( logClass, [ ['bc_paid', 1], ['bc_pay_result', 1], ['bc_pay_success_block_number', blockNumber], ]); this.orderDb['bc_paid'] = 1; this.orderDb['bc_pay_result'] = 1; this.orderDb['bc_pay_success_block_number'] = blockNumber; }; const searchGetBlockNumber = () => { return this.orderDb['bc_pay_prepare_block_number']; }; const methodArgs = [ this.orderDb['order_id'], this.orderDb['type'], this.orderDb['buyer_address'], this.orderDb['price'], this.orderDb['payment_token_address'], this.orderDb['nonce'], this.orderDb['signature'] ]; const syncResetState = async () { await this.updateDbMustBeSuccess( logClass, [ ['bc_paid', 0], ['bc_pay_result', 0], ['bc_block_number', 0], ['bc_sync_block_number', 0], ]); this.orderDb['bc_paid'] = 0; this.orderDb['bc_pay_result'] = 0; this.orderDb['bc_block_number'] = 0; this.orderDb['bc_sync_block_number'] = 0; }; const syncGetBlockNumber = () => { return this.orderDb['bc_block_number']; }; const syncGetSyncCount = () => { return this.orderDb['bc_pay_count']; }; const onSyncPrepare = async () => { }; const onSyncSuccess = async () => { }; const onSyncFailed = async () => { }; const exec = new bchelper.ContractExecutor(); exec.instanceName = 'mallInstance'; exec.doSuspend = this.doSuspend.bind(this); exec.getLogKey = this.getOrderId.bind(this); exec.getLogData = this.getOrderDb.bind(this); exec.searchLogClass = 'order.search'; exec.eventName = 'BEBoxPaid'; exec.eventFilter = eventFilter; exec.onSearchSuccess = onSearchSuccess; exec.searchGetBlockNumber = searchGetBlockNumber; exec.syncLogClass = 'order.sync'; exec.methodName = 'payForBoxWithSignature'; exec.methodArgs = methodArgs; exec.syncDbLogClass = 'order.sync'; exec.syncResetState = syncResetState; exec.syncGetBlockNumber = syncGetBlockNumber; exec.syncGetSyncCount = syncGetSyncCount; exec.onSyncPrepare = onSyncPrepare; exec.onSyncSuccess = onSyncSuccess; exec.onSyncFailed = onSyncFailed; const ok = await exec.execute(); if (!ok) { await this.doSuspend(logClass, 'order not found'); } } } async mint() { const logClass = 'mint'; if (!this.isMinted()) { const exec = new bchelper.ContractExecutor(); exec.instanceName = 'factoryInstance'; exec.doSuspend = this.doSuspend.bind(this); exec.getLogKey = this.getOrderId.bind(this); exec.getLogData = this.getOrderDb.bind(this); exec.searchLogClass = 'mint.search'; exec.eventName = 'TokenMinted'; exec.eventFilter = { tokenId: this.getTokenId() }; exec.onSearchSuccess = async (event) => { const blockNumber = events['blockNumber']; const nowTime = utils.getUtcTime(); await this.updateDbMustBeSuccess( logClass, [ ['bc_minted', 1], ['bc_mint_time', nowTime], ['bc_mint_block_number', blockNumber], ]); this.orderDb['bc_minted'] = 1; this.orderDb['bc_mint_time'] = nowTime; this.orderDb['bc_mint_block_number'] = blockNumber; }; exec.searchGetBlockNumber = () => { return this.orderDb['bc_sync_block_number']; }; exec.syncLogClass = 'mint.sync'; exec.methodName = 'mintHeroTo'; exec.methodArgs = [ this.getBuyerAddress(), this.getTokenId() ]; exec.syncDbLogClass = 'mint.sync'; exec.syncResetState = async () => { await this.updateDbMustBeSuccess( logClass, [ ['bc_paid', 0], ['bc_pay_result', 0], ['bc_block_number', 0], ['bc_sync_block_number', 0], ]); this.orderDb['bc_paid'] = 0; this.orderDb['bc_pay_result'] = 0; this.orderDb['bc_block_number'] = 0; this.orderDb['bc_sync_block_number'] = 0; }; exec.syncGetBlockNumber = () => { return this.orderDb['bc_block_number']; }; exec.syncGetSyncCount = () => { return this.orderDb['bc_pay_count']; }; exec.onSyncPrepare = async () => { }; exec.onSyncSuccess = async () => { }; exec.onSyncFailed = async () => { }; const ok = await exec.execute(); if (!ok) { await this.doSuspend(logClass, 'order not found'); } } await this.dbMintHero(); await this.updateDbMustBeSuccess(logClass, [ ['done', 1] ]); utils.emitEvent(C.REMOVE_PENDING_ORDER_EVENT, this.getOrderId()); } async updateDbMustBeSuccess(logClass, fieldList) { const err = await dbhelper.update( 't_box_order', [ ['order_id', this.getOrderId()] ], fieldList); if (err) { log.error(util.format('%s updateDbMustBeSuccess orderDb:%s fieldList:%s err:%s', logClass, utils.jsonEncode(this.orderDb), utils.jsonEncode(fieldList), err ) ); await this.doSuspend(logClass + ' updateDbMustBeSuccess ', err); } else { log.info(util.format('%s updateDbMustBeSuccess orderDb:%s', logClass, utils.jsonEncode(this.orderDb), utils.jsonEncode(fieldList) ) ); } return err; } async doSuspend(logClass, reason) { //挂起等待人工处理 try { log.warning(util.format('%s doSuspend orderDb:%s reason:%s', logClass, utils.jsonEncode(this.orderDb), reason ) ); } catch (err) { log.error(util.format('%s doSuspend orderDb:%s err:%s', logClass, utils.jsonEncode(this.orderDb), err ) ); } while (true) { await utils.sleep(1000 * 3600 * 24); } } getOrderDb() { return this.orderDb; } getOrderId() { return this.orderDb['order_id']; } getBuyerAddress() { return this.orderDb['buyer_address']; } getTokenId() { return this.orderDb['token_id']; } isPaid() { return this.orderDb['bc_paid'] != 0; } isMinted() { return this.orderDb['bc_minted'] != 0; } isNetError(err) { const errStr = '' + err; return errStr && errStr.indexOf('connection not open on send') >= 0; } }; exports.BoxOrder = BoxOrder;