diff --git a/doc/Avatar.py b/doc/Avatar.py new file mode 100644 index 00000000..62ef3855 --- /dev/null +++ b/doc/Avatar.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +import _common + +class Avatar(object): + + def __init__(self): + self.apis = [ + { + 'name': 'avatarMetaList', + 'desc': 'item配置表中装饰列表', + 'group': 'Avatar', + 'url': 'webapp/index.php?c=Avatar&a=avatarMetaList', + 'params': [ + _common.ReqHead(), + ], + 'response': [ + _common.RspHead(), + ['!list', [], '装饰列表(参数见item.xlsx字段)'] + ] + },{ + 'name': 'avatarList', + 'desc': '获取装饰物品列表', + 'group': 'Avatar', + 'url': 'webapp/index.php?c=Avatar&a=avatarList', + 'params': [ + _common.ReqHead(), + ], + 'response': [ + _common.RspHead(), + ['!list', [_common.AvatarInfo()], '装饰列表'] + ] + },{ + 'name': 'equip', + 'desc': '穿戴装饰物品', + 'group': 'Avatar', + 'url': 'webapp/index.php?c=Avatar&a=equip', + 'params': [ + _common.ReqHead(), + ['hero_uniid', '', '英雄唯一id'], + ['avatar_uniid', '', '装饰唯一id'], + ], + 'response': [ + _common.RspHead(), + ['property_chg', _common.PropertyChg(), '属性变更'], + ] + },{ + 'name': 'buyAvatar', + 'desc': '购买装饰物品', + 'group': 'Avatar', + 'url': 'webapp/index.php?c=Avatar&a=buyAvatar', + 'params': [ + _common.ReqHead(), + ['item_id', '', '装饰物品itemId'], + ], + 'response': [ + _common.RspHead(), + ] + },{ + 'name': 'remove', + 'desc': '卸下装饰物品', + 'group': 'Avatar', + 'url': 'webapp/index.php?c=Avatar&a=remove', + 'params': [ + _common.ReqHead(), + ['avatar_uniid', '', '装饰唯一id'], + ], + 'response': [ + _common.RspHead(), + ['property_chg', _common.PropertyChg(), '属性变更'], + ] + },{ + 'name': 'clearAvatar', + 'desc': '全部卸下', + 'group': 'Avatar', + 'url': 'webapp/index.php?c=Avatar&a=clearAvatar', + 'params': [ + _common.ReqHead(), + ['hero_uniid', '', '英雄唯一id'], + ], + 'response': [ + _common.RspHead(), + ['property_chg', _common.PropertyChg(), '属性变更'], + ] + }, + ] + + diff --git a/doc/_common.py b/doc/_common.py index 32a27f28..66cbe777 100644 --- a/doc/_common.py +++ b/doc/_common.py @@ -272,6 +272,7 @@ class Hero(object): ['advanced_count', 0, '进阶次数'], ['offer_reward_state', 0, '是否悬赏中'], ['tags', '', '1:Gen状态'], + ['!avatarInfo', [AvatarInfo()], '装饰信息'], ] @@ -1480,6 +1481,19 @@ class StakingPreview(object): ['interest', 0, '利息'], ] +class AvatarInfo(object): + + def __init__(self): + self.fields = [ + ['avatar_uniid', '', '装饰唯一id'], + ['account_id', '', 'account_id'], + ['token_id', '', 'token_id'], + ['item_id', 0, 'item_id'], + ['item_type', 0, '1:翅膀'], + ['status', 0, '0:为穿戴 1:已穿戴'], + ['hero_uniid', 0, '穿戴的英雄唯一id'], + ] + class BattleData(object): def __init__(self): diff --git a/sql/gamedb.sql b/sql/gamedb.sql index a8fa2add..59d04cdc 100644 --- a/sql/gamedb.sql +++ b/sql/gamedb.sql @@ -1671,3 +1671,26 @@ CREATE TABLE `t_mail` ( KEY `account_id_mailid` (`account_id`, `mailid`) ) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; /*!40101 SET character_set_client = @saved_cs_client */; + + +-- +-- Table structure for table `t_avatar` +-- + +DROP TABLE IF EXISTS `t_avatar`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `t_avatar` ( + `idx` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id', + `account_id` varchar(60) NOT NULL DEFAULT '' COMMENT '账号id(channel + "_" + gameid + "_" + openid)', + `token_id` varchar(60) COMMENT 'token_id', + `item_id` int(11) NOT NULL COMMENT 'item_id', + `item_type` int(11) NOT NULL COMMENT 'item类型', + `status` int(11) NOT NULL DEFAULT '0' COMMENT '装备状态', + `hero_idx` bigint DEFAULT NULL COMMENT '英雄外键id', + `createtime` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `modifytime` int(11) NOT NULL DEFAULT '0' COMMENT '修改时间', + PRIMARY KEY (`idx`), + UNIQUE KEY `hero_idx_type` (`hero_idx`, `item_type`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; +/*!40101 SET character_set_client = @saved_cs_client */; \ No newline at end of file diff --git a/sql/gamedb2006_migrate_230920_01.sql b/sql/gamedb2006_migrate_230920_01.sql new file mode 100644 index 00000000..bbd3f238 --- /dev/null +++ b/sql/gamedb2006_migrate_230920_01.sql @@ -0,0 +1,19 @@ +begin; + +CREATE TABLE `t_avatar` ( + `idx` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id', + `account_id` varchar(60) NOT NULL DEFAULT '' COMMENT '账号id(channel + "_" + gameid + "_" + openid)', + `token_id` varchar(60) COMMENT 'token_id', + `item_id` int(11) NOT NULL COMMENT 'item_id', + `item_type` int(11) NOT NULL COMMENT 'item类型', + `status` int(11) NOT NULL DEFAULT '0' COMMENT '装备状态', + `hero_idx` bigint DEFAULT NULL COMMENT '英雄外键id', + `createtime` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `modifytime` int(11) NOT NULL DEFAULT '0' COMMENT '修改时间', + PRIMARY KEY (`idx`), + UNIQUE KEY `hero_idx_type` (`hero_idx`, `item_type`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + +insert into version (version) values(2023092001); + +commit; diff --git a/sql/migrate/.gitkeep b/sql/migrate/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/webapp/controller/AvatarController.class.php b/webapp/controller/AvatarController.class.php new file mode 100644 index 00000000..232c8de6 --- /dev/null +++ b/webapp/controller/AvatarController.class.php @@ -0,0 +1,163 @@ +_rspData(array( + 'list' => $metaList + )); + } + + public function avatarList(){ + $avatarList = array(); + Avatar::getAvatarList(function ($row) use (&$avatarList){ + array_push($avatarList,Avatar::toDto($row)); + }); + $this->_rspData(array( + 'list' => $avatarList + )); + } + + public function equip(){ + $heroUniid = trim(getReqVal('hero_uniid', 0)); + $avatarUniid = trim(getReqVal('avatar_uniid', 0)); + + $heroDb = Hero::find($heroUniid); + if (!$heroDb){ + $this->_rspErr(1, 'hero_uniid error'); + return; + } + $avatarDb = Avatar::find($avatarUniid); + if (!$avatarDb){ + $this->_rspErr(1, 'avatar_uniid error'); + return; + } + if ($heroDb['idx'] == $avatarDb['hero_idx']){ + $this->_rspErr(1, 'avatar_uniid error'); + return; + } + $randAttr = emptyReplace(json_decode($heroDb['rand_attr'], true), array()); + $itemMeta = \mt\Item::get($avatarDb['item_id']); + $quality = 1; + foreach ($randAttr as $value){ + $quality = max($quality,$value['quality']); + } + switch ($itemMeta['quality']){ + case 2 : { + if ($quality < 3){ + $this->_rspErr(1, 'Can not meet the wearing condition'); + return; + } + } + break; + case 3 :{ + if ($quality < 4){ + $this->_rspErr(1, 'Can not meet the wearing condition'); + return; + } + } + break; + case 4 :{ + if ($quality < 5){ + $this->_rspErr(1, 'Can not meet the wearing condition'); + return; + } + } + } + + Avatar::equipUpdate($avatarDb,$heroUniid); + $propertyChgService = new services\PropertyChgService(); + $propertyChgService->addHeroChg(); + $this->_rspData(array( + 'property_chg' => $propertyChgService->toDto(), + )); + } + + public function remove(){ + $avatarUniid = trim(getReqVal('avatar_uniid', 0)); + $avatarDb = Avatar::find($avatarUniid); + if (!$avatarDb){ + $this->_rspErr(1, 'avatar_uniid error'); + return; + } + Avatar::update($avatarUniid,array( + 'hero_idx' => null, + 'status' => 0, + 'modifytime' => myself()->_getNowTime(), + )); + $propertyChgService = new services\PropertyChgService(); + $propertyChgService->addHeroChg(); + $this->_rspData(array( + 'property_chg' => $propertyChgService->toDto(), + )); + } + + public function clearAvatar(){ + $heroUniid = trim(getReqVal('hero_uniid', 0)); + $heroDb = Hero::find($heroUniid); + if (!$heroDb){ + $this->_rspErr(1, 'hero_uniid error'); + return; + } + $avatarDbs = Avatar::getAvatarByHeroIdx($heroUniid); + if (!$avatarDbs){ + $this->_rspErr(1, 'Meaningless operation'); + return; + } + foreach ($avatarDbs as $avatarDb){ + Avatar::update($avatarDb['idx'],array( + 'hero_idx' => null, + 'status' => 0, + 'modifytime' => myself()->_getNowTime(), + )); + } + $propertyChgService = new services\PropertyChgService(); + $propertyChgService->addHeroChg(); + $this->_rspData(array( + 'property_chg' => $propertyChgService->toDto(), + )); + } + + + public function buyAvatar(){ + $itemId = trim(getReqVal('item_id', 0)); + $itemMeta = \mt\Item::get($itemId); + if (!$itemMeta || $itemMeta['type'] != \mt\Item::AVATAR_TYPE){ + $this->_rspErr(1, 'item id error'); + return; + } + //检验钻石是否足够 并消耗钻石 + $costItems = array( + array( + 'item_id' => V_ITEM_DIAMOND, + 'item_num' => $itemMeta['diamond'] + ), + ); + $lackItem = null; + if (!$this->_hasEnoughItems($costItems, $lackItem)) { + $this->_rspErr(3, $this->_getLackItemErrMsg($lackItem)); + return; + } + $this->_decItems($costItems); + { + //埋点 + $event = [ + 'name' => LogService::BUY_HERO_AVATAR, + 'val' => $itemMeta['diamond'] + ]; + LogService::consumeDiamond($event); + } + Avatar::addAvatar($itemMeta); + $this->_rspOk(); + } +} + diff --git a/webapp/controller/BaseAuthedController.class.php b/webapp/controller/BaseAuthedController.class.php index cdef5724..c84bea3a 100644 --- a/webapp/controller/BaseAuthedController.class.php +++ b/webapp/controller/BaseAuthedController.class.php @@ -15,6 +15,7 @@ require_once('models/UserSeasonRing.php'); require_once('models/Parachute.php'); require_once('models/Chip.php'); require_once('models/Pass.php'); +require_once('models/Avatar.php'); require_once('mt/Parameter.php'); require_once('mt/RankSeason.php'); require_once('mt/LevelUp.php'); @@ -36,6 +37,7 @@ use models\Parachute; use models\UserSeasonRing; use models\Chip; use models\Pass; +use models\Avatar; use services\LogService; class BaseAuthedController extends BaseController { @@ -555,6 +557,11 @@ class BaseAuthedController extends BaseController { $this->_openRandomBox($itemMeta,$awardService,$propertyService); } break; + case mt\Item::AVATAR_TYPE: + { + Avatar::addAvatar($itemMeta); + } + break; default: { $this->_addLog('additems', 'invalid_item', array( diff --git a/webapp/controller/BattleController.class.php b/webapp/controller/BattleController.class.php index 980d245b..30abcbf6 100644 --- a/webapp/controller/BattleController.class.php +++ b/webapp/controller/BattleController.class.php @@ -293,6 +293,7 @@ class BattleController extends BaseAuthedController { if ($heroDb) { $info['is_valid_battle'] = 1; $info['hero_dto'] = Hero::toDto($heroDb); + $info['hero_dto']['avatar_info'] = Hero::avatarInfo($heroDb); } else { $info['errcode'] = 51; $info['errmsg'] = 'paramater error'; diff --git a/webapp/controller/DailyRequestController.class.php b/webapp/controller/DailyRequestController.class.php index 44fdfb82..75509aa9 100644 --- a/webapp/controller/DailyRequestController.class.php +++ b/webapp/controller/DailyRequestController.class.php @@ -70,7 +70,10 @@ class DailyRequestController extends BaseAuthedController { } error_log("DailyRequestController : Mission Star Season ".$season." Settlement Success !" . " Request Time : ". date('Y-M-D h:i:s',time())); RealtimeData::setMissionSeason($currMissionSeasonMeta['id']); + $this->_rspData(array('message'=>'星星之路结算成功')); + return; } + $this->_rspOk(); } /** @@ -90,7 +93,10 @@ class DailyRequestController extends BaseAuthedController { ); error_log("DailyRequestController : Battle Pass Season ".$season." Settlement Success !" . " Request Time : ". date('Y-M-D h:i:s',time())); RealtimeData::setPassSeason($currSeasonMeta['id']); + $this->_rspData(array('message'=>'通行证结算成功')); + return; } + $this->_rspOk(); } /** @@ -155,6 +161,8 @@ class DailyRequestController extends BaseAuthedController { } error_log("DailyRequestController : Battle Ranking Season ".$season." Settlement Success !" . " Request Time : ". date('Y-M-D h:i:s',time())); RealtimeData::setRankSeason($lastSeasonMeta['id']); + $this->_rspData(array('message'=>'排位结算成功')); + return; } $this->_rspOk(); } @@ -176,6 +184,8 @@ class DailyRequestController extends BaseAuthedController { error_log("computingPowerRewards : ACCOUNT_ID_{$row['account_id']} CEC Reward record completion"); } } + $this->_rspData(array('message'=>'算力结算成功')); + return; } $this->_rspOk(); } diff --git a/webapp/controller/HeroController.class.php b/webapp/controller/HeroController.class.php index 57cc72d6..18cdc58e 100644 --- a/webapp/controller/HeroController.class.php +++ b/webapp/controller/HeroController.class.php @@ -88,6 +88,7 @@ class HeroController extends BaseAuthedController { $resetLv_state = $this->_getDailyV(TN_DAILY_RESET_HERO_LEVEL_STATE, $unique_id); $hero = Hero::toDto($heroDb); + $hero['avatarInfo'] = Hero::avatarInfo($heroDb); $hero['resetLv_state'] = $resetLv_state; $this->_rspData(array( 'data' => $hero diff --git a/webapp/models/Avatar.php b/webapp/models/Avatar.php new file mode 100644 index 00000000..54b75917 --- /dev/null +++ b/webapp/models/Avatar.php @@ -0,0 +1,177 @@ +_getAccountId(), myself()->_getAddress(), $avatarUniId); + } + + private static function internalFind($accountId, $address, $avatarUniId) + { + $row = SqlHelper::ormSelectOne( + myself()->_getMysql($accountId), + 't_avatar', + array( + 'idx' => $avatarUniId, + ) + ); + if ($row) { + $row['avatar_uniid'] = $row['idx']; + if ($row['account_id'] != $accountId) { + $openId = $address; + if (!NftService::isAvatarOwner($openId, $row['token_id'])) { + $row = null; + } + } + } + return $row; + } + + public static function getAvatarByHeroIdx($heroUniid){ + $rows = SqlHelper::ormSelect( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'hero_idx' => $heroUniid, + ) + ); + $avatarList = array(); + if (count($rows) > 0){ + foreach ($rows as $row){ + $row['avatar_uniid'] = $row['idx']; + if ($row['account_id'] != myself()->_getAccountId()) { + $openId = myself()->_getAddress(); + if (NftService::isAvatarOwner($openId, $row['token_id'])) { + array_push($avatarList,$row); + } + }else{ + array_push($avatarList,$row); + } + } + } + return $avatarList; + } + + public static function getOneByType($heroUniid,$itemType){ + $row = SqlHelper::ormSelectOne( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'hero_idx' => $heroUniid, + 'item_type' => $itemType, + ) + ); + if ($row) { + $row['avatar_uniid'] = $row['idx']; + if ($row['account_id'] != myself()->_getAccountId()) { + $openId = myself()->_getAddress(); + if (!NftService::isAvatarOwner($openId, $row['token_id'])) { + $row = null; + } + } + } + return $row; + } + + + public static function getAvatarList($cb){ + SqlHelper::ormSelect( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'account_id' => myself()->_getAccountId() + ), + function ($row) use($cb) { + $cb($row); + } + ); + foreach (NftService::getAvatar(myself()->_getAddress()) as $nftDb) { + if (! $nftDb['deleted']){ + $row = SqlHelper::ormSelectOne( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'token_id' => $nftDb['token_id'], + ) + ); + $cb($row); + } + } + } + + public static function toDto($row){ + $dto = array( + 'idx' => $row['idx'], + 'avatar_uniid' => $row['idx'], + 'token_id' => $row['token_id'], + 'hero_uniid' => $row['hero_idx'], + 'item_id' => $row['item_id'], + 'item_type' => $row['item_type'], + 'status' => $row['status'], + ); + return $dto; + } + + public static function addAvatar($avatarMeta){ + SqlHelper::insert( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'account_id' => myself()->_getAccountId(), + 'item_id' => $avatarMeta['id'], + 'item_type' => $avatarMeta['sub_type'], + 'createtime' => myself()->_getNowTime(), + 'modifytime' => myself()->_getNowTime(), + ) + ); + } + + public static function equipUpdate($avatarDb,$heroUniid){ + $row = self::getOneByType($heroUniid,$avatarDb['item_type']); + if ($row){ + SqlHelper::update( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'idx' => $row['idx'], + ), + array( + 'status' => 0, + 'hero_idx' => null, + 'modifytime' => myself()->_getNowTime(), + ) + ); + } + SqlHelper::update( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'idx' => $avatarDb['idx'], + ), + array( + 'status' => 1, + 'hero_idx' => $heroUniid, + 'modifytime' => myself()->_getNowTime(), + ) + ); + } + + public static function update($avatarUniid,$feildKv){ + SqlHelper::update( + myself()->_getSelfMysql(), + 't_avatar', + array( + 'idx' => $avatarUniid, + ), + $feildKv + ); + } + +} \ No newline at end of file diff --git a/webapp/models/Hero.php b/webapp/models/Hero.php index d873ca0f..c14bcbc3 100644 --- a/webapp/models/Hero.php +++ b/webapp/models/Hero.php @@ -11,6 +11,7 @@ require_once('mt/Item.php'); require_once('models/HeroSkin.php'); require_once('models/Chip.php'); require_once('models/User.php'); +require_once('models/Avatar.php'); require_once('models/ChipPlugin.php'); require_once('services/NftService.php'); require_once('services/FormulaService.php'); @@ -313,6 +314,17 @@ class Hero extends BaseModel { return $dto; } + public static function avatarInfo($row){ + $avatarDbs = Avatar::getAvatarByHeroIdx($row['idx']); + $avatarInfos = array(); + if ($avatarDbs){ + foreach ($avatarDbs as $avatarDb){ + array_push($avatarInfos,Avatar::toDto($avatarDb)); + } + } + return $avatarInfos; + } + public static function addFreeHero($heroMeta) { return self::internalAddHero( diff --git a/webapp/models/Nft.php b/webapp/models/Nft.php index afd46c67..f9aae1a5 100644 --- a/webapp/models/Nft.php +++ b/webapp/models/Nft.php @@ -26,6 +26,7 @@ class Nft extends BaseModel const GENESIS_TYPE = 7; //创世徽章 const PLANET_TYPE = 8; //星球 const RING_TYPE = 19; //戒指 + const AVATAR_TYPE = 30; //装饰 const GENESIS_TAG = 1; diff --git a/webapp/mt/Item.php b/webapp/mt/Item.php index 9b2e3ce0..9151fade 100644 --- a/webapp/mt/Item.php +++ b/webapp/mt/Item.php @@ -104,6 +104,7 @@ class Item { const CHEST_BOX_TYPE = 23; const PLANET_TYPE = 28; + const AVATAR_TYPE = 29; const FUNC_RENAME_CARD_SUBTYPE = 1; const FUNC_GUILD_CARD_SUBTYPE = 3; diff --git a/webapp/services/LogService.php b/webapp/services/LogService.php index 75d4fdcb..867358a0 100644 --- a/webapp/services/LogService.php +++ b/webapp/services/LogService.php @@ -17,6 +17,7 @@ class LogService extends BaseService const CHIP_LEVEL_UP = "chip_level_Up"; //芯片升级 const BUY_BATTLE_PASS = "buy_battle_pass"; //通行证购买 const BUY_PASS_EXP = "buy_pass_exp"; //购买通行证经验 + const BUY_HERO_AVATAR = "buy_hero_avatar"; //购买英雄装饰 const SHOP_BUY_ITEM = "shop_buy_item_normal"; //商城购买物品 const SHOP_BUY_ITEM_DAILY = "shop_buy_item_daily"; //商城每日精选购买物品 diff --git a/webapp/services/NftService.php b/webapp/services/NftService.php index e13aec34..457d9d3c 100644 --- a/webapp/services/NftService.php +++ b/webapp/services/NftService.php @@ -21,6 +21,7 @@ class NftService extends BaseService { 'equip' => Nft::EQUIP_TYPE, 'chip' => Nft::CHIP_TYPE, 'ring' => Nft::RING_TYPE, + 'avatar' => Nft::AVATAR_TYPE, ); public static function getChipBlance($account, $tokenId) @@ -28,6 +29,11 @@ class NftService extends BaseService { return Nft::getChipBlance($account, $tokenId); } + public static function isAvatarOwner($openId, $tokenId) + { + return self::internalIsOwner($openId, 'avatar', $tokenId); + } + public static function isHeroOwner($openId, $tokenId) { return self::internalIsOwner($openId, 'hero', $tokenId); @@ -62,6 +68,10 @@ class NftService extends BaseService { { return self::internalGetList($openId, 'ring'); } + public static function getAvatar($openId) + { + return self::internalGetList($openId, 'avatar'); + } private static function internalGetList($openId, $name) {