370 lines
10 KiB
JavaScript
370 lines
10 KiB
JavaScript
const util = require('util');
|
||
const utils = require('./utils');
|
||
const db = require("./db");
|
||
const dbhelper = require("./dbhelper");
|
||
const log = require("./log");
|
||
const dblog = require("./dblog");
|
||
const bc = require('./blockchain');
|
||
const metamgr = require('./metamgr');
|
||
const C = require('./C');
|
||
|
||
/*
|
||
1、数据库被认为是可靠如果数据库操作失败则应该挂起
|
||
2、区块链查询操作没啥成本可以多次调用,写入操作需谨慎!!!
|
||
*/
|
||
|
||
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() {
|
||
if (!this.isPaid()) {
|
||
//同步到链上
|
||
await this.sync();
|
||
//等待确认
|
||
await this.confirm();
|
||
}
|
||
//创建nft
|
||
await this.createNft();
|
||
}
|
||
|
||
async sync() {
|
||
if (!this.isSynced()) {
|
||
//查询
|
||
const found = await this.bcSearch();
|
||
if (!found) {
|
||
//上链
|
||
const ok = await this.bcSync();
|
||
if (!ok) {
|
||
utils.emitEvent(C.REMOVE_PENDING_ORDER_EVENT, this.getOrderId());
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
async bcSearch() {
|
||
const logClass = 'bcSearch';
|
||
let events = [];
|
||
if (this.getSyncBlockNumber() <= 0) {
|
||
return false;
|
||
}
|
||
while (true) {
|
||
try {
|
||
const fromBlock = Math.max(this.getSyncBlockNumber(), this.getBlockNumber());
|
||
events = await bc.mallInstance.getPastEvents(
|
||
'BEBoxPaid',
|
||
{
|
||
fromBlock: fromBlock,
|
||
toBlock: fromBlock + 5000 - 1,
|
||
filter: {
|
||
boxId: this.getOrderId()
|
||
}
|
||
});
|
||
break;
|
||
} catch (err) {
|
||
log.warning(util.format('%s getPastEvents orderDb:%s err:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.getOrderDb()),
|
||
err
|
||
));
|
||
}
|
||
await utils.sleep(1000 * 3);
|
||
}
|
||
if (events.length > 0) {
|
||
const blockNumber = events[0]['blockNumber'];
|
||
if (!blockNumber) {
|
||
await this.doHangUp(logClass, 'blocNumber is empty');
|
||
}
|
||
await this.updateDbMustBeSuccess(
|
||
logClass,
|
||
[
|
||
['state', C.ORDER_STATE_PAID],
|
||
['bc_synced', 1],
|
||
['bc_result', 1],
|
||
['bc_block_number', blockNumber],
|
||
]);
|
||
this.orderDb['bc_synced'] = 1;
|
||
this.orderDb['bc_sync_count'] += 1;
|
||
this.orderDb['bc_sync_number'] = blockNumber;
|
||
}
|
||
return events.length > 0;
|
||
}
|
||
|
||
async bcSync() {
|
||
const logClass = 'bcSync';
|
||
await bc.mustBeActive();
|
||
if (this.getSyncBlockNumber() <= 0) {
|
||
await this.updateDbMustBeSuccess(
|
||
logClass,
|
||
[
|
||
['bc_sync_block_number', bc.getCurrBlockNumber()],
|
||
]
|
||
);
|
||
this.orderDb['bc_sync_block_number'] = this.getSyncBlockNumber();
|
||
}
|
||
const nowTime = utils.getUtcTime();
|
||
const doSync = async () => {
|
||
let result = null;
|
||
try {
|
||
dblog.addLog(
|
||
'order.sync',
|
||
'prepare',
|
||
this.getOrderId(),
|
||
utils.jsonEncode(this.getOrderDb()));
|
||
result = await bc.mallInstance.methods.payForBoxWithSignature(
|
||
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']).send({gas: 1000000});
|
||
dblog.addLog(
|
||
'order.sync',
|
||
'success',
|
||
this.getOrderId(),
|
||
utils.jsonEncode(this.getOrderDb()),
|
||
utils.jsonEncode(result));
|
||
} catch (err) {
|
||
dblog.addLog(
|
||
'order.sync',
|
||
'failed',
|
||
this.getOrderId(),
|
||
utils.jsonEncode(this.getOrderDb()),
|
||
err);
|
||
await this.updateDbMustBeSuccess(
|
||
logClass,
|
||
[
|
||
['suspend', 1],
|
||
['suspend_reason', '' . err],
|
||
]
|
||
);
|
||
await this.doHangUp(logClass, err);
|
||
}
|
||
return result;
|
||
};
|
||
try {
|
||
const result = await doSync();
|
||
const blockNumber = result['blockNumber'];
|
||
await this.updateDbMustBeSuccess(
|
||
logClass,
|
||
[
|
||
['state', C.ORDER_STATE_PAID],
|
||
['bc_synced', 1],
|
||
['bc_sync_count', this.orderDb['bc_sync_count'] + 1],
|
||
['bc_sync_time', nowTime],
|
||
['bc_result', 1],
|
||
['bc_block_number', blockNumber],
|
||
['bc_fail_reason', ''],
|
||
]
|
||
);
|
||
this.orderDb['state'] = C.ORDER_STATE_PAID;
|
||
this.orderDb['bc_synced'] = 1;
|
||
this.orderDb['bc_sync_count'] += 1;
|
||
this.orderDb['bc_sync_time'] = nowTime;
|
||
this.orderDb['bc_result'] = 1;
|
||
this.orderDb['bc_sync_number'] = blockNumber;
|
||
return true;
|
||
} catch (e) {
|
||
log.warning(util.format('%s end orderDb:%s err:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.orderDb),
|
||
e
|
||
)
|
||
);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
async confirm() {
|
||
const logClass = 'confirm';
|
||
|
||
while (true) {
|
||
if (bc.isComfirmed(this.getBlockNumber())) {
|
||
break;
|
||
}
|
||
await utils.sleep(1000 * 3);
|
||
}
|
||
}
|
||
|
||
async createNft() {
|
||
const logClass = 'createNft';
|
||
log.info(util.format('%s begin orderDb:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.getOrderDb()),
|
||
)
|
||
);
|
||
await this.bcMintHero();
|
||
await this.updateDbMustBeSuccess(logClass,
|
||
[
|
||
['done', 1]
|
||
]);
|
||
log.info(util.format('%s end orderDb:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.getOrderDb()),
|
||
)
|
||
);
|
||
utils.emitEvent(C.REMOVE_PENDING_ORDER_EVENT, this.getOrderId());
|
||
}
|
||
|
||
async bcMintHero() {
|
||
const logClass = 'bcMintHero';
|
||
const buyerAddress = this.getBuyerAddress();
|
||
const tokenId = this.getTokenId();
|
||
const nowTime = utils.getUtcTime();
|
||
if (this.isMinted()) {
|
||
const result = await bc.factoryInstance.methods.mintHeroTo(
|
||
buyerAddress,
|
||
tokenId).send({ gas: 1000000 });
|
||
await this.updateDbMustBeSuccess(
|
||
logClass,
|
||
[
|
||
['bc_minted', 1],
|
||
['bc_mint_time', nowTime],
|
||
]
|
||
);
|
||
this.orderDb['bc_minted'] = 1;
|
||
this.orderDb['bc_mint_time'] = nowTime;
|
||
}
|
||
{
|
||
const fieldList = [
|
||
['token_id', tokenId],
|
||
['item_id', this.orderDb['item_id']],
|
||
['owner_id', ''],
|
||
['owner_address', this.orderDb['buyer_address']],
|
||
['owner_name', ''],
|
||
['createtime', nowTime],
|
||
['modifytime', nowTime],
|
||
];
|
||
const err = await dbhelper.insert(
|
||
't_nft',
|
||
fieldList);
|
||
if (err) {
|
||
log.error(util.format('%s insert nft table orderDb:%s fieldList:%s err:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.orderDb),
|
||
utils.jsonEncode(fieldList),
|
||
err
|
||
)
|
||
);
|
||
} else {
|
||
log.info(util.format('%s insert nft table orderDb:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.orderDb),
|
||
utils.jsonEncode(fieldList)
|
||
)
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
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.doHangUp(logClass + ' updateDbMustBeSuccess ', err);
|
||
} else {
|
||
log.info(util.format('%s updateDbMustBeSuccess orderDb:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.orderDb),
|
||
utils.jsonEncode(fieldList)
|
||
)
|
||
);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
async doHangUp(logClass, reason) {
|
||
//挂起等待人工处理
|
||
try {
|
||
log.warning(util.format('%s doHangUp orderDb:%s reason:%s',
|
||
logClass,
|
||
utils.jsonEncode(this.orderDb),
|
||
reason
|
||
)
|
||
);
|
||
} catch (err) {
|
||
log.error(util.format('%s doHangUp 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'];
|
||
}
|
||
|
||
getBlockNumber() {
|
||
return this.orderDb['bc_block_number'];
|
||
}
|
||
|
||
isPaid() {
|
||
return this.orderDb['state'] == C.ORDER_STATE_PAID;
|
||
}
|
||
|
||
isSynced() {
|
||
return this.orderDb['bc_synced'] != 0;
|
||
}
|
||
|
||
isMinted() {
|
||
return this.orderDb['bc_minted'] != 0;
|
||
}
|
||
|
||
getSyncBlockNumber() {
|
||
return this.orderDb['bc_sync_block_number'];
|
||
}
|
||
|
||
isFail() {
|
||
return this.isSynced() && this.orderDb['bc_result'] != C.BC_RESULT_OK;
|
||
}
|
||
|
||
isNetError(err) {
|
||
const errStr = '' + err;
|
||
return errStr && errStr.indexOf('connection not open on send') >= 0;
|
||
}
|
||
|
||
};
|
||
|
||
exports.BoxOrder = BoxOrder;
|