aozhiwei 36f56c57f9 1
2022-02-16 16:30:52 +08:00

400 lines
11 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 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() {
if (!this.getMintMethod()) {
await this.doSuspend('start', 'mint method error');
return;
}
//支付
await this.payment();
//创建nft
await this.mint();
}
async payment() {
if (!this.isPaid()) {
const getPrepareBlockNumber = () => {
return this.orderDb['bc_pay_prepare_block_number'];
};
const setPrepareBlockNumber = async (logClass, blockNumber) => {
await this.updateDbMustBeSuccess(
logClass,
[
['bc_pay_prepare_block_number', blockNumber],
]
);
};
const getSuccessBlockNumber = () => {
return this.orderDb['bc_pay_success_block_number'];
};
const setSuccessBlockNumber = async (logClass, blockNumber) => {
await this.updateDbMustBeSuccess(
logClass,
[
['bc_pay_success_block_number', blockNumber],
]
);
};
const getSyncCount = () => {
return this.orderDb['bc_pay_count'];
};
const eventFilter = {
boxId: this.getOrderId()
};
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 onSyncSuccess = async (logClass, event) => {
const nowTime = utils.getUtcTime();
const blockNumber = event['blockNumber'];
await this.updateDbMustBeSuccess(
logClass,
[
['bc_pay_count', this.orderDb['bc_pay_count'] + 1],
['bc_pay_time', nowTime],
['bc_pay_success_block_number', blockNumber],
]
);
};
const onConfirmed = async (logClass) => {
await this.updateDbMustBeSuccess(
logClass,
[
['bc_paid', 1],
]
);
};
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.getPrepareBlockNumber = getPrepareBlockNumber;
exec.setPrepareBlockNumber = setPrepareBlockNumber;
exec.getSuccessBlockNumber = getSuccessBlockNumber;
exec.setSuccessBlockNumber = setSuccessBlockNumber;
exec.getSyncCount = getSyncCount;
exec.searchLogClass = 'order.search';
exec.eventName = 'BEBoxPaid';
exec.eventFilter = eventFilter;
exec.syncLogClass = 'order.sync';
exec.methodName = 'payForBoxWithSignature';
exec.methodArgs = methodArgs;
exec.syncDbLogClass = 'order.sync';
exec.onSyncSuccess = onSyncSuccess;
exec.onConfirmed = onConfirmed;
exec.init();
const ok = await exec.execute();
if (!ok) {
const logClass = 'payment';
await this.doSuspend(logClass, 'payment error');
}
}
}
async mint() {
if (!this.isMinted()) {
const getPrepareBlockNumber = () => {
return this.orderDb['bc_mint_prepare_block_number'];
};
const setPrepareBlockNumber = async (logClass, blockNumber) => {
await this.updateDbMustBeSuccess(
logClass,
[
['bc_mint_prepare_block_number', blockNumber],
]
);
};
const getSuccessBlockNumber = () => {
return this.orderDb['bc_mint_success_block_number'];
};
const setSuccessBlockNumber = async (logClass, blockNumber) => {
await this.updateDbMustBeSuccess(
logClass,
[
['bc_mint_success_block_number', blockNumber],
]
);
};
const getSyncCount = () => {
return this.orderDb['bc_mint_count'];
};
const eventFilter = {
tokenId: this.getTokenId()
};
const methodArgs = [
this.orderDb['buyer_address'],
this.getTokenId()
];
const onSyncSuccess = async (logClass, event) => {
const nowTime = utils.getUtcTime();
const blockNumber = event['blockNumber'];
await this.updateDbMustBeSuccess(
logClass,
[
['bc_mint_count', this.orderDb['bc_mint_count'] + 1],
['bc_mint_time', nowTime],
['bc_mint_success_block_number', blockNumber],
]
);
};
const onConfirmed = async (logClass) => {
await this.updateDbMustBeSuccess(
logClass,
[
['bc_minted', 1],
]
);
};
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.getPrepareBlockNumber = getPrepareBlockNumber;
exec.setPrepareBlockNumber = setPrepareBlockNumber;
exec.getSuccessBlockNumber = getSuccessBlockNumber;
exec.setSuccessBlockNumber = setSuccessBlockNumber;
exec.getSyncCount = getSyncCount;
exec.searchLogClass = 'mint.search';
exec.eventName = 'TokenMinted';
exec.eventFilter = eventFilter;
exec.syncLogClass = 'mint.sync';
exec.methodName = this.getMintMethod();
exec.methodArgs = methodArgs;
exec.syncDbLogClass = 'mint.sync';
exec.onSyncSuccess = onSyncSuccess;
exec.onConfirmed = onConfirmed;
exec.init();
const ok = await exec.execute();
if (!ok) {
const logClass = 'mint';
await this.doSuspend(logClass, 'mint error');
}
}
const logClass = 'mint';
await this.dbMint();
await this.updateDbMustBeSuccess(
logClass,
[
['done', 1]
]);
utils.emitEvent(C.REMOVE_PENDING_ORDER_EVENT, this.getOrderId());
}
async dbMint() {
const logClass = 'dbMint';
{
const {err, row} = dbhelper.ormSelectOne(
't_nft',
[
['token_id', this.getTokenId()]
]);
if (err) {
await this.doSuspend(logClass, err);
return;
}
if (row) {
if (row['owner_address'] != this.orderDb['buyer_address']) {
await this.doSuspend(logClass, 'owner_address error');
return;
}
return;
}
}
{
const nowTime = utils.getUtcTime();
const fieldList = [
['token_id', this.getTokenId()],
['item_id', this.orderDb['item_id']],
['game_id', this.orderDb['game_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
)
);
await this.doSuspend(logClass, 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.doSuspend(logClass + ' updateDbMustBeSuccess ', err);
} else {
fieldList.forEach((val) => {
const name = val[0];
const value = val[1];
this.orderDb[name] = value;
});
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'];
}
getTokenId() {
return this.orderDb['token_id'];
}
isPaid() {
return this.orderDb['bc_paid'] != 0;
}
isMinted() {
return this.orderDb['bc_minted'] != 0;
}
getMintMethod() {
switch (this.orderDb['token_type']) {
case C.TOKEN_TYPE_HERO:
{
return 'mintHeroTo';
}
break;
case C.TOKEN_TYPE_EQUIP:
{
return 'mintEquipTo';
}
break;
case C.TOKEN_TYPE_CHIP:
{
return 'mintChipTo';
}
break;
default:
{
return '';
break;
}
}
}
isNetError(err) {
const errStr = '' + err;
return errStr && errStr.indexOf('connection not open on send') >= 0;
}
};
exports.BoxOrder = BoxOrder;