Account.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <?php
  2. /**
  3. * 用户基础信息
  4. * @author benzhan
  5. */
  6. class Account extends Model {
  7. protected $tableName = 'account';
  8. protected $dbKey = 'dw_eos';
  9. const PRE_KEY_TOKEN = 'globals:token:';
  10. const PRE_KEY_RANDOM = 'globals:random_1:';
  11. const EOS_REWARD_RATE = 0.002;
  12. const REWARD_GT = 50000;
  13. public static $vip_return_map = [
  14. 1 => 0.0001,
  15. 2 => 0.0002,
  16. 3 => 0.0003,
  17. 4 => 0.0004,
  18. 5 => 0.0005,
  19. 6 => 0.0007,
  20. 7 => 0.0009,
  21. 8 => 0.0011,
  22. 9 => 0.0013,
  23. 10 => 0.0015,
  24. ];
  25. public static $vip_need_map = [
  26. 1 => 1000,
  27. 2 => 5000,
  28. 3 => 10000,
  29. 4 => 50000,
  30. 5 => 100000,
  31. 6 => 500000,
  32. 7 => 1000000,
  33. 8 => 5000000,
  34. 9 => 10000000,
  35. 10 => 50000000,
  36. ];
  37. public static $vip_airdrop_map = [
  38. 1 => 66,
  39. 2 => 266,
  40. 3 => 566,
  41. 4 => 2666,
  42. 5 => 5666,
  43. 6 => 26666,
  44. 7 => 56666,
  45. 8 => 266666,
  46. 9 => 566666,
  47. 10 => 2666666,
  48. ];
  49. public function getBalance($player, $field = 'balance') {
  50. $where = compact('player');
  51. $_field = $field;
  52. return (int) $this->objTable->getOne($where, compact('_field'));
  53. }
  54. /**
  55. * 修改用户余额
  56. * @param $player
  57. * @param $amount
  58. * @param string $type 类型:pledge:抵押, win:获得奖励, return:归还,deposit:充值, withdraw:提现
  59. * @param $memo
  60. * @param $others { transaction_state, transaction_id, block_num, wait_amount}
  61. * @return array
  62. * @throws Exception
  63. */
  64. public function changeBalance($player, $amount, $type, $memo, $others = null) {
  65. $objBalaceLog = new TableHelper('balance_log', 'dw_eos');
  66. $objLock = new Lock('changeBalance', 30);
  67. $objLock->lockWait($player);
  68. $this->objTable->autoCommit(false);
  69. try {
  70. // 检查修改后,余额是否小于0
  71. $account = $this->objTable->getRow(['player' => $player]);
  72. $account_balance = intval($account['balance']) + $amount;
  73. $beforeBalance = (int)$account['balance'];
  74. if ($account_balance < 0) {
  75. // 需要告警
  76. $backtrace = debug_backtrace();
  77. $msg = "changeBalance, player:{$player}, memo:{$memo}, account_balance < 0, backtrace:" . json_encode($backtrace);
  78. alermErrorMsg($msg);
  79. $objLock->unlock($player);
  80. throw new Exception('system error: account_balance < 0', CODE_UNKNOW_ERROT);
  81. }
  82. // 检查交易id是否存在
  83. $transaction_id = $others['transaction_id'];
  84. if ($transaction_id) {
  85. $count = $objBalaceLog->getCount(['transaction_id' => $transaction_id]);
  86. if ($count) {
  87. // 需要告警
  88. $backtrace = debug_backtrace();
  89. $msg = "changeBalance, player:{$player}, memo:{$memo}, transaction_id has exist, backtrace:" . json_encode($backtrace);
  90. alermErrorMsg($msg);
  91. $objLock->unlock($player);
  92. throw new Exception('system error, transaction_id has exist.!', CODE_NORMAL_ERROR);
  93. }
  94. }
  95. $newData = [
  96. 'balance' => $account['balance'],
  97. ];
  98. // 待上链的余额
  99. $wait_amount = arrayPop($others, 'wait_amount');
  100. if ($wait_amount) {
  101. $newData['wait_amount'] = $account['wait_amount'] + $wait_amount;
  102. }
  103. // 处理抵押
  104. if ($others['roomAmount']) {
  105. $roomAmount = $others['roomAmount'];
  106. $newData['game_bet'] = $account['game_bet'] - $roomAmount;
  107. if ($newData['game_bet'] < 0) {
  108. // 需要告警
  109. $msg = "changeBalance, player:{$player}, memo:{$memo}, game_bet < 0;";
  110. alermErrorMsg($msg);
  111. $objLock->unlock($player);
  112. throw new Exception('system error: game_bet < 0', CODE_UNKNOW_ERROT);
  113. }
  114. // 赢钱的抵押一起归还,输钱的只加奖金
  115. if ($others['act'] != 'lose') {
  116. $newData['balance'] += $roomAmount;
  117. // 插入归还记录
  118. $balance_log = [
  119. 'appid' => 'box',
  120. 'player' => $player,
  121. 'change_amount' => $roomAmount,
  122. 'type' => 'return',
  123. 'before_amount' => $beforeBalance,
  124. 'after_amount' => $newData['balance'],
  125. 'eos_balance' => $account['eos_balance'],
  126. 'memo' => $memo,
  127. 'create_time' => date('Y-m-d H:i:s'),
  128. ];
  129. $beforeBalance += $roomAmount;
  130. if (!$objBalaceLog->addObject($balance_log)) {
  131. $objLock->unlock($player);
  132. throw new Exception('system error: add balance log, try again!', CODE_NORMAL_ERROR);
  133. }
  134. }
  135. unset($others['roomAmount'], $others['act']);
  136. }
  137. // 本次操作金额
  138. $newData['balance'] += $amount;
  139. if ($account) {
  140. // 不能修改update_time,update_time只能用于同步
  141. $ret = $this->objTable->updateObject($newData, ['player' => $player, 'balance' => $account['balance']]);
  142. } else {
  143. $newData['player'] = $player;
  144. $newData['create_time'] = date('Y-m-d H:i:s');
  145. $newData['update_time'] = date('Y-m-d H:i:s');
  146. $ret = $this->objTable->addObject($newData);
  147. }
  148. if (!$ret) {
  149. // 需要告警
  150. $backtrace = debug_backtrace();
  151. $msg = "changeBalance, player:{$player}, memo:{$memo}, balance change fail, backtrace:" . json_encode($backtrace);
  152. alermErrorMsg($msg);
  153. $objLock->unlock($player);
  154. throw new Exception('system error: change account balance, try again!', CODE_UNKNOW_ERROT);
  155. }
  156. // 防止关闭游戏重复插入记录
  157. if (0 != $amount) {
  158. //插入balance_log
  159. $balance_log = [
  160. 'appid' => 'box',
  161. 'player' => $player,
  162. 'change_amount' => $amount,
  163. 'type' => $type,
  164. 'before_amount' => $beforeBalance,
  165. 'after_amount' => $newData['balance'],
  166. 'eos_balance' => $account['eos_balance'],
  167. 'memo' => $memo,
  168. 'create_time' => date('Y-m-d H:i:s'),
  169. ];
  170. if ($others) {
  171. $balance_log += $others;
  172. }
  173. if (!$objBalaceLog->addObject($balance_log)) {
  174. $objLock->unlock($player);
  175. throw new Exception('system error: add balance log, try again!', CODE_NORMAL_ERROR);
  176. }
  177. }
  178. $this->objTable->commit();
  179. $result = true;
  180. $msg = 'success';
  181. } catch (Exception $e) {
  182. $this->objTable->rollback();
  183. // 发出告警
  184. alermErrorMsg($e->getMessage());
  185. $result = false;
  186. $msg = $e->getMessage();
  187. }
  188. $objLock->unlock($player);
  189. return compact('result', 'msg');
  190. }
  191. /**
  192. * 增加下注额
  193. * @param $player
  194. * @param $bet_amount
  195. */
  196. public function addBetAmount($player, $bet_amount) {
  197. if ($bet_amount >= 0) {
  198. // 增加下注总额度
  199. $account = $this->objTable->getRow(['player' => $player]);
  200. $vip_level = $this->updateBetAmount($player, $account['total_bet_amount'] + $bet_amount);
  201. // 增加vip返现
  202. $map = self::$vip_return_map;
  203. if ($vip_level && $map[$vip_level]) {
  204. $return_amount = intval($bet_amount * $map[$vip_level]);
  205. if ($return_amount > 0) {
  206. $bet_eos = sprintf("%.4f EOS", round($bet_amount / 10000, 4));
  207. $wait_amount = $return_amount;
  208. $memo = "vip{$vip_level} return for {$bet_eos}";
  209. $this->changeBalance($player, $return_amount, 'vip_return', $memo, compact('wait_amount'));
  210. }
  211. }
  212. }
  213. }
  214. /**
  215. * 更新下注额
  216. * @param $player
  217. * @param $total_bet_amount
  218. */
  219. public function updateBetAmount($player, $total_bet_amount) {
  220. $vip_level = self::getVipLevel($total_bet_amount);
  221. $newData = compact('total_bet_amount', 'vip_level');
  222. $this->objTable->updateObject($newData, compact('player'));
  223. if ($vip_level > 0) {
  224. // 尝试添加vip空投
  225. $this->addVipAirdrop($player, $vip_level);
  226. }
  227. return $vip_level;
  228. }
  229. public function addVipAirdrop($player, $vip_level) {
  230. $map = self::$vip_airdrop_map;
  231. // 如果没有空投则空投
  232. $objVipAirdropLog = new TableHelper('vip_airdrop_log', 'dw_eos');
  233. $data = compact('player', 'vip_level');
  234. $data['gt_amount'] = $map[$vip_level] * 10000;
  235. $data['create_time'] = date('Y-m-d H:i:s');
  236. $data['update_time'] = $data['create_time'];
  237. $objVipAirdropLog->addObjectsIfNoExist([$data]);
  238. }
  239. /**
  240. * 根据下注额获取vip等级
  241. * @param $bet_amount
  242. *
  243. * @return int
  244. */
  245. public static function getVipLevel($bet_amount) {
  246. $bet_amount = intval($bet_amount / 10000);
  247. for ($i = 10; $i > 0; $i--) {
  248. if ($bet_amount >= self::$vip_need_map[$i]) {
  249. return $i;
  250. }
  251. }
  252. return 0;
  253. }
  254. public static function getTokenKey($token) {
  255. return self::PRE_KEY_TOKEN . $token;
  256. }
  257. /**
  258. * 检查token是否存在
  259. * @param $player
  260. *
  261. * @return bool
  262. */
  263. public static function checkToken() {
  264. $objRedis = dwRedis::init('dw_dice');
  265. if ($_REQUEST['token'] && $_REQUEST['account']) {
  266. $token = $_REQUEST['token'];
  267. $account = $_REQUEST['account'];
  268. } else {
  269. $token = $_COOKIE['token'];
  270. $account = $_COOKIE['account'];
  271. }
  272. if ($token) {
  273. $tokenKey = self::getTokenKey($token);
  274. $value = $objRedis->get($tokenKey);
  275. if ($value) {
  276. if ($value == $account) {
  277. return true;
  278. }
  279. } else {
  280. self::clearCookie();
  281. }
  282. }
  283. return false;
  284. }
  285. /**
  286. * 生成账号
  287. */
  288. public static function genToken($account) {
  289. $token = uuid16();
  290. $tokenKey = self::getTokenKey($token);
  291. $objRedis = dwRedis::init();
  292. // $objRedis->set($tokenKey, $account);
  293. $objRedis->setex($tokenKey, 86400, $account);
  294. return $token;
  295. }
  296. /**
  297. * 设置用户的Cookie
  298. * @param $account
  299. *
  300. * @return array
  301. */
  302. public static function setCookie($account) {
  303. $token = Account::genToken($account);
  304. $expire = time() + 86400 * 7;
  305. $path = '/';
  306. $domain = '';
  307. if (ENV != ENV_DEV) {
  308. $domain = 'eosget.io';
  309. }
  310. // 生成token
  311. setcookie('token', $token, $expire, $path, $domain);
  312. setcookie('account', $account, $expire, $path, $domain);
  313. return compact('account', 'token');
  314. }
  315. public static function clearCookie() {
  316. $expire = time() - 86400;
  317. $path = '/';
  318. // 删除正式环境的cookie
  319. setcookie('token', null, $expire, $path, 'eosget.io');
  320. setcookie('account', null, $expire, $path, 'eosget.io');
  321. if (ENV == ENV_DEV) {
  322. // 删除cookie
  323. setcookie('token', null, $expire, $path);
  324. setcookie('account', null, $expire, $path);
  325. }
  326. }
  327. public static function getRandomKey($account) {
  328. return self::PRE_KEY_RANDOM . $account;
  329. }
  330. /**
  331. * 获取随机数
  332. * @param $player
  333. * @return string
  334. */
  335. public static function getRandom($account) {
  336. $objRedis = dwRedis::init();
  337. $randomKey = self::getRandomKey($account);
  338. $random = $objRedis->get($randomKey);
  339. if (!$random) {
  340. $random = uuid16();
  341. }
  342. $objRedis->setex($randomKey, 900, $random);
  343. return $random;
  344. }
  345. public static function verifyMsg($pubkey, $account, $data, $sign) {
  346. $json = Eos::getAccount($account);
  347. $user = json_decode($json, true);
  348. // $user = EosRpcApi::getAccount($account);
  349. $active = $user['permissions'][0]['required_auth']['keys'][0]['key'];
  350. $owner = $user['permissions'][1]['required_auth']['keys'][0]['key'];
  351. if ($pubkey != $active && $pubkey != $owner) {
  352. return false;
  353. }
  354. $cmd = "node {$GLOBALS['verifyPath']} '$data' '$pubkey' '$sign'";
  355. // 返回true表示验证通过
  356. $ret = Eos::execCmd($cmd);
  357. return trim($ret) == 'true';
  358. }
  359. /**
  360. * 增加推荐佣金
  361. * @param $bet_player
  362. * @param $referee_player
  363. */
  364. public static function addRefereeReward($bet_player, $referee_player, $bet_data) {
  365. $objLock = new Lock('addRefereeReward');
  366. $objLock->lock($bet_player);
  367. //判断是否为第一次奖励
  368. $objRefereeReward = new TableHelper('referee_reward_log', 'dw_eos');
  369. $row = $objRefereeReward->getRow(['player' => $bet_player, 'referee_player' => $referee_player]);
  370. //奖励
  371. $objAccountReward = new TableHelper('account_reward_info', 'dw_eos');
  372. $objRefereeReward->autoCommit(false);
  373. try {
  374. $log_data = [
  375. 'appid' => 'k3',
  376. 'player' => $bet_player,
  377. 'referee_player' => $referee_player,
  378. 'reward_eos' => floor(self::EOS_REWARD_RATE * $bet_data['bet_amount']),
  379. 'commission_rate' => self::EOS_REWARD_RATE,
  380. 'create_time' => date('Y-m-d H:i:s'),
  381. ];
  382. $log_data += $bet_data;
  383. if (!$row) {
  384. //增加初次gt奖励
  385. $log_data['reward_gt'] = self::REWARD_GT;
  386. }
  387. if (!$objRefereeReward->addObject($log_data)) {
  388. throw new Exception('system error: add balance log, try again!', CODE_NORMAL_ERROR);
  389. }
  390. //增加奖励总数
  391. $user = $objAccountReward->getRow(['player' => $referee_player]);
  392. $user_data = [];
  393. if ($user) {
  394. $user_data['eos_balance'] = $user['eos_balance'] + $log_data['reward_eos'];
  395. if ($log_data['reward_gt']) {
  396. $user_data['gt'] = intval($user['gt']) + intval($log_data['reward_gt']);
  397. }
  398. $ret = $objAccountReward->updateObject($user_data, ['player' => $referee_player]);
  399. } else {
  400. $user_data['eos_balance'] = $log_data['reward_eos'];
  401. if ($log_data['reward_gt']) {
  402. $user_data['gt'] = $log_data['reward_gt'];
  403. }
  404. $user_data['player'] = $referee_player;
  405. $ret = $objAccountReward->addObject($user_data);
  406. }
  407. if (!$ret) {
  408. throw new Exception('system error: add balance log, try again!', CODE_NORMAL_ERROR);
  409. }
  410. $objRefereeReward->commit();
  411. } catch (Exception $e) {
  412. $objRefereeReward->rollback();
  413. $objLock->unlock($bet_player);
  414. alermErrorMsg($e->getMessage());
  415. return [false, $e->getMessage()];
  416. }
  417. $objLock->unlock($bet_player);
  418. return [true, ''];
  419. }
  420. /**
  421. * 绑定推荐账户
  422. * @param $bet_player
  423. * @param $referee_player
  424. * @param $objReferee
  425. * @param $objAccount
  426. * @return array
  427. */
  428. public static function bindReferee($bet_player, $referee_player, $objReferee = null, $objAccount = null) {
  429. if ($bet_player == $referee_player) {
  430. return [false, '推荐人不能为自己'];
  431. }
  432. //用户只能有一个推荐人
  433. !$objReferee && $objReferee = new TableHelper('referee_relation', 'dw_eos');
  434. $row = $objReferee->getRow(['player' => $bet_player]);
  435. if (!$row) {
  436. !$objAccount && $objAccount = new Account();
  437. $account = $objAccount->objTable->getRow(['player' => $bet_player], ['_field' => 'total_bet_amount']);
  438. if ($account['total_bet_amount'] > 0) {
  439. return [false, '只有用户第一次下注才能绑定邀请推广'];
  440. } else {
  441. // 可以发奖励
  442. $objReferee->addObject(['player' => $bet_player, 'referee_player' => $referee_player, 'create_time' => date('Y-m-d H:i:s')]);
  443. return [true, ''];
  444. }
  445. } else {
  446. if ($row['referee_player'] != $referee_player) {
  447. //一个用户只能有一个上线
  448. return [false, '一个用户只能有一个邀请人,此用户的邀请人为:'. $row['referee_player']];
  449. } else {
  450. return [true, ''];
  451. }
  452. }
  453. }
  454. /**
  455. * 玩家vip等级
  456. * @author solu
  457. * @param $players
  458. * @return array
  459. */
  460. public function getVipLevels($players) {
  461. if (!$players) {
  462. return [];
  463. }
  464. $items = $this->objTable->getAll(['player' => $players], ['_field' => 'player, vip_level']);
  465. return arrayFormatKey($items, 'player', 'vip_level');
  466. }
  467. }