aozhiwei 7ea7e6154b 1
2022-02-09 17:16:46 +08:00

370 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;