diff --git a/doc/InGameMall.py b/doc/InGameMall.py new file mode 100644 index 00000000..4d4fe7d4 --- /dev/null +++ b/doc/InGameMall.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +import _common + +class InGameMall(object): + + def __init__(self): + self.apis = [ + { + 'name': 'productList', + 'desc': '商品列表', + 'group': 'InGameMall', + 'url': 'webapp/index.php?c=InGameMall&a=productList', + 'params': [ + _common.ReqHead(), + ['page', 0, '第几页数据'], + ['seller', '', '查询指定账号'], + ['order_method', 0, '排序方式 0:默认排序(当前指向1) 1:上架时间 2:价格'], + ['order_asc', 0, '排序方向, 0:从小到大 1:从大到小'], + ['price_filter', '', '价格过滤(用|分割)'], + ], + 'response': [ + _common.RspHead(), + ['pagination', _common.Pagination(), '分页信息'], + ['!rows', [_common.InGameMallGoods()], '商品列表'] + ] + }, + { + 'name': 'sell', + 'desc': '出售', + 'group': 'InGameMall', + 'url': 'webapp/index.php?c=InGameMall&a=sell', + 'params': [ + _common.ReqHead(), + ['item_id', '', '道具id'], + ['amount', '', '出售数量'], + ['price', '', '出售价格'], + ], + 'response': [ + _common.RspHead() + ] + }, + { + 'name': 'buy', + 'desc': '购买', + 'group': 'InGameMall', + 'url': 'webapp/index.php?c=InGameMall&a=buy', + 'params': [ + _common.ReqHead(), + ['item_id', '', '道具id'], + ['amount', '', '出售数量'], + ['price', '', '出售价格'], + ], + 'response': [ + _common.RspHead() + ] + }, + { + 'name': 'cancel', + 'desc': '下架物品', + 'group': 'InGameMall', + 'url': 'webapp/index.php?c=InGameMall&a=cancel', + 'params': [ + _common.ReqHead(), + ['order_id', '', '订单id'], + ], + 'response': [ + _common.RspHead() + ] + }, + { + 'name': 'modifyPrice', + 'desc': '修改价格', + 'group': 'InGameMall', + 'url': 'webapp/index.php?c=InGameMall&a=modifyPrice', + 'params':[ + _common.ReqHead(), + ['order_id', '', '订单id'], + ['price', '', '价格'], + ], + 'response': [ + _common.RspHead() + ] + }, + ] diff --git a/doc/_common.py b/doc/_common.py index 736a11a1..23ee61f3 100644 --- a/doc/_common.py +++ b/doc/_common.py @@ -1356,6 +1356,22 @@ class MallGoods(object): ['modify_countdown', 0, '可修改价格倒计时:-1不可修改价格'], ] +class InGameMallGoods(object): + + def __init__(self): + self.fields = [ + ['seller', '', '出售方账号'], + ['seller_address', '', '出售方钱包地址'], + ['item_id', '', '商品道具id'], + ['item_num', '', '道具数量'], + ['currency', '', "选用币种 目前只支持CEG USDC USDT"], + ['price', '', '出售价格'], + ['selltime', 0, '上架时间'], + ['updatetime', 0, '修改时间(更新价格等)'], + ['cancel_countdown', 0, '可下架时间倒计时:-1不可下架'], + ['modify_countdown', 0, '可修改价格倒计时:-1不可修改价格'], + ] + class ComputingPowerCurr(object): def __init__(self): diff --git a/sql/gamedb.sql b/sql/gamedb.sql index e665c546..0fbc6bdf 100644 --- a/sql/gamedb.sql +++ b/sql/gamedb.sql @@ -1419,6 +1419,38 @@ CREATE TABLE `t_mall` ( UNIQUE KEY `goods_uuid` (`goods_uuid`) ) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; /*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `t_ingame_mall` +-- + +DROP TABLE IF EXISTS `t_ingame_mall`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `t_ingame_mall` ( + `idx` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id', + `order_id` varchar(255) NOT NULL DEFAULT '' COMMENT '订单id', + `seller` varchar(60) NOT NULL DEFAULT '' COMMENT 'seller', + `seller_address` varchar(60) NOT NULL DEFAULT '' COMMENT 'seller_address', + `item_id` int(11) NOT NULL DEFAULT '0' COMMENT '物品id', + `item_num` bigint NOT NULL DEFAULT '0' COMMENT '物品数量', + `price` bigint NOT NULL DEFAULT '0' COMMENT '价格', + `status` int(11) NOT NULL DEFAULT '0' COMMENT 'status', + `last_buy_time` int(11) NOT NULL DEFAULT '0' COMMENT '最后一次购买时间', + `last_buyer` varchar(60) NOT NULL DEFAULT '' COMMENT '最后一次购买者', + `last_modify_price_time` int(11) NOT NULL DEFAULT '0' COMMENT '最后一次修改价格事件', + `buyer` varchar(60) NOT NULL DEFAULT '' COMMENT '购买成功者', + `buy_ok_time` int(11) NOT NULL DEFAULT '0' COMMENT '购买成功时间', + `createtime` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `modifytime` int(11) NOT NULL DEFAULT '0' COMMENT '修改时间', + PRIMARY KEY (`idx`), + KEY `seller` (`seller`), + KEY `seller_address` (`seller_address`), + KEY `price` (`price`), + UNIQUE KEY `order_id` (`order_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `t_market` @@ -1769,4 +1801,4 @@ CREATE TABLE `t_global_data` ( `modifytime` int(11) NOT NULL DEFAULT '0' COMMENT '修改时间', PRIMARY KEY (`idx`), UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; \ No newline at end of file +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; diff --git a/webapp/controller/InGameMallController.class.php b/webapp/controller/InGameMallController.class.php new file mode 100644 index 00000000..dfadf82f --- /dev/null +++ b/webapp/controller/InGameMallController.class.php @@ -0,0 +1,265 @@ + array(), + 'rows' => array() + ); + SqlHelper::rawQueryPage( + myself()->_getMySql(''), + 'SELECT * FROM t_mall WHERE status=:status', + array( + ':status' => Mall::PENDING_STATE + ), + array( + 'page' => $page, + 'perPage' => 8, + 'filter' => array( + 'data' => $queryData, + 'fields' => array( + array( + 'name' => 'seller', + 'field_name' => 'seller_address', + 'cond' => '=', + 'ignore_empty' => true, + ), + array( + 'name' => 'price_filter', + 'field_name' => '', + 'cond' => 'custom', + 'ignore_empty' => true, + 'custom_func' => function () use ($queryData) { + $priceFilters = $queryData['price_filter']; + $arrPriceFilter = explode('|', $priceFilters); + $priceLow = $arrPriceFilter[0]; + $priceHigh = $arrPriceFilter[1]; + return " AND (length(price) >= length('${priceLow}') AND length(price) <= length('${priceHigh}')) " + . " AND (price >= '${priceLow}' AND price <= '${priceHigh}') "; + } + ), + ) + ), + 'orderBy' => $orderBy, + 'handle' => function ($row) use(&$out) { + array_push($out['rows'], Mall::toDto($row)); + } + ), + $out['pagination'] + ); + myself()->_rspData($out); + } + + public function sell() + { + $address = myself()->_getAddress(); + if (!$address) { + $this->_rspErr(1, 'address not found'); + return; + } + $itemId = getReqVal('item_id', ''); + $amount = intval(getReqVal('amount', ''), 10); + $currency = getReqVal('currency', ''); + $priceBn = phpcommon\bnInit(getReqVal('price', '')); + if ($itemId != V_ITEM_GOLD) { + $this->_rspErr(1, 'only support gold'); + return; + } + if ($amount <= 0) { + $this->_rspErr(1, 'amount must > 0'); + return; + } + if (!$this->checkPrice($priceBn)) { + return; + } + if (!in_array( + $currency, + array( + BlockChainService::CURRENCY_CEG, + BlockChainService::CURRENCY_USDC, + BlockChainService::CURRENCY_USDT, + ) + )) { + $this->_rspErr(1, 'paramater error currency'); + return; + } + $costItems = array( + array( + 'item_id' => $itemId, + 'item_num' => $amount + ) + ); + $lackItem = null; + if (!$this->_hasEnoughItems($costItems, $lackItem)) { + $this->_rspErr(2, $this->_getLackItemErrMsg($lackItem)); + return; + } + $this->_decItems($costItems); + if ($itemId == V_ITEM_GOLD) { + //埋点 + $event = [ + 'name' => LogService::MARKET_SELL_GOLD, + 'val' => $amount + ]; + LogService::consumeGold($event); + } + $orderId = OrderId::gen(); + Mall::Add( + $orderId, + $orderId, + $itemId, + $amount, + $currency, + phpcommon\bnToStr($priceBn) + ); + myself()->_rspOk(); + } + + public function cancel() + { + $goodsUuid = getReqVal('goods_uuid', ''); + $goodsDb = Mall::findByGoodsUuid($goodsUuid); + if (!$goodsDb) { + myself()->_rspErr(1, 'goods not found'); + return; + } + $goodsDto = Mall::toDto($goodsDb); + if ($goodsDto['cancel_countdown'] != 0) { + myself()->_rspErr(1, 'cant cancel'); + return; + } + $awardService = new AwardService(); + $propertyChgService = new PropertyChgService(); + switch ($goodsDb['item_id']) { + case V_ITEM_GOLD: + { + $items = array( + array( + 'item_id' => $goodsDb['item_id'], + 'item_num' => $goodsDb['item_num'], + ) + ); + Mall::cancel($goodsDto['goods_uuid']); + myself()->_addItems($items, $awardService, $propertyChgService); + { + //埋点 + $event = [ + 'name' => LogService::MARKET_CANCEL_SELL_GOLD, + 'val' => $goods['amount'] + ]; + LogService::productGold($event); + } + } + break; + default: + { + myself()->_rspErr(1, 'cant cancel'); + return; + } + break; + } + myself()->_rspData(array( + 'award' => $awardService->toDto(), + 'property_chg' => $propertyChgService->toDto(), + )); + } + + public function modifyPrice() + { + $goodsUuid = getReqVal('goods_uuid', ''); + $priceBn = phpcommon\bnInit(getReqVal('price', '')); + if (!$this->checkPrice($priceBn)) { + return; + } + $goodsDb = Mall::findByGoodsUuid($goodsUuid); + if (!$goodsDb) { + myself()->_rspErr(1, 'goods not found'); + return; + } + $goodsDto = Mall::toDto($goodsDb); + if ($goodsDto['modify_countdown'] != 0) { + myself()->_rspErr(1, 'cant modify price'); + return; + } + Mall::modifyPrice($goodsDto['goods_uuid'], phpcommon\bnToStr($priceBn)); + myself()->_rspOk(); + } + + private function checkPrice($priceBn) + { + if ($priceBn === false) { + myself()->_rspErr(1, 'price format error1'); + return false; + } + if (phpcommon\bnCmp($this->priceLowBn, $priceBn) > 0) { + myself()->_rspErr(1, 'price format error2'); + return false; + } + if (phpcommon\bnCmp($this->priceHighBn, $priceBn) < 0) { + myself()->_rspErr(1, 'price format error3'); + return false; + } + return true; + } + +}