DigitalController.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <?php
  2. namespace api\modules\v1\controllers\user;
  3. use api\controllers\OnAuthController;
  4. use common\enums\StatusEnum;
  5. use common\enums\UserWalletEnum;
  6. use common\helpers\ApiCode;
  7. use common\helpers\LockHelper;
  8. use common\models\common\GlobalVarMap;
  9. use common\models\common\MallSetting;
  10. use common\models\user\DigitalLog;
  11. use common\models\user\User;
  12. use common\models\user\UserDigital;
  13. use services\common\UserWalletService;
  14. use Yii;
  15. use yii\db\Exception;
  16. class DigitalController extends OnAuthController
  17. {
  18. public $modelClass = '';
  19. public function beforeAction($action)
  20. {
  21. parent::beforeAction($action);
  22. $setting = MallSetting::conf($this->mall_id, 'digital');
  23. if (!$setting || !$setting['is_enable']) return $this->error('系统未开启');
  24. return $action;
  25. }
  26. /**
  27. *
  28. * 不用进行登录验证的方法
  29. *
  30. * 例如: ['index', 'update', 'create', 'view', 'delete']
  31. * 默认全部需要验证
  32. *
  33. * @var array
  34. */
  35. protected $authOptional = [];
  36. /**
  37. * 获取用户基本信息
  38. * @return array|mixed
  39. */
  40. public function actionUserInfo()
  41. {
  42. try {
  43. $setting = MallSetting::conf($this->mall_id, 'digital');
  44. $user_digital = UserDigital::findOne(['mall_id' => $this->mall_id, 'user_id' => $this->user_id, 'status' => StatusEnum::ENABLED]);
  45. $return['digital'] = $user_digital->digital ?? 0;
  46. //获取设置
  47. $return['enable_transfer'] = $setting['is_transfer'] ?? 0;
  48. $return['enable_to_balance'] = $setting['enable_to_balance'] ?? 0;
  49. $return['enable_to_score'] = $setting['enable_to_score'] ?? 0;
  50. $return['name'] = $setting['name'] ?? '数字币';
  51. $return['forwarding_charge'] = $setting['forwarding_charge'] ?? 0; //转送手续费
  52. $return['forwarding_charge'] = $return['forwarding_charge'] . '%';
  53. $return['to_balance_ratio'] = $setting['to_balance_ratio'] ?? 0; //数字币转余额比例
  54. $return['to_score_ratio'] = $setting['to_score_ratio'] ?? 0; //数字币转余额比例
  55. return $this->success(ApiCode::CODE_SUCCESS_TITLE, $return);
  56. } catch (\Exception $e) {
  57. return $this->error($e->getMessage());
  58. }
  59. }
  60. /**
  61. * 数字币列表
  62. */
  63. public function actionUserList()
  64. {
  65. try {
  66. $params = Yii::$app->request->get();
  67. $res = Yii::$app->services->validate->validate($params, [
  68. [['status'], 'safe'],
  69. ]);
  70. if ($res === false) return $this->error(Yii::$app->services->validate->getErrorMessage());
  71. $status = $params['status'] == 1 ? DigitalLog::SEND_STATUS_NORMAL : DigitalLog::SEND_STATUS_FROZEN;
  72. $where[] = ['in', 'status', [StatusEnum::DISABLED, StatusEnum::ENABLED]];
  73. $where[] = ['mall_id' => $this->mall_id];
  74. $where[] = ['user_id' => $this->user_id];
  75. $where[] = ['send_status' => $status];
  76. $params['select'] = 'id,change_type,change_num,desc,from_type,capital_type,created_at,order_no';
  77. $params['order'] = 'id desc';
  78. $params['where'] = $where;
  79. $page_data = DigitalLog::listPage($params, false);
  80. $commission_map = GlobalVarMap::getEnumMapByType('FromTypeMap');
  81. $capital_type_map = GlobalVarMap::getEnumMapByType('CapitalTypeMap');
  82. foreach ($page_data['list'] as $key => $val) {
  83. $val['source_text'] = ($commission_map[$val['from_type']] ?? '') . ($capital_type_map[$val['capital_type']] ? '-' . $capital_type_map[$val['capital_type']] : '');
  84. $val['created_at'] = date('Y-m-d H:i:s', $val['created_at']);
  85. $page_data['list'][$key] = $val;
  86. }
  87. return $this->success(ApiCode::CODE_SUCCESS_TITLE, $page_data);
  88. } catch (\Exception $e) {
  89. return $this->error($e->getMessage());
  90. }
  91. }
  92. /**
  93. * 转送
  94. */
  95. public function actionTransfer()
  96. {
  97. if (Yii::$app->request->isPost) {
  98. $lock_key = 'digital_transfer_' . $this->user_id;
  99. $identification = uniqid();
  100. $t = \Yii::$app->db->beginTransaction();
  101. if (!LockHelper::lock($lock_key, $identification, 30, 5)) return $this->error('操作太频繁');
  102. try {
  103. $params = Yii::$app->request->post();
  104. $res = Yii::$app->services->validate->validate($params, [
  105. [['num', 'user_id'], 'required'],
  106. [['num', 'user_id'], 'integer'],
  107. [['num'], 'compare', 'compareValue' => 0, 'operator' => '>']
  108. ]);
  109. if ($res === false) throw new \Exception(Yii::$app->services->validate->getErrorMessage());
  110. $setting = MallSetting::conf($this->mall_id, 'digital');
  111. if (!$setting['is_transfer']) throw new \Exception('系统未开启转送功能');
  112. if ($params['user_id'] == $this->user_id) throw new Exception('赠送的用户不能是本人哦');
  113. $user_digital = UserDigital::findOne(['mall_id' => $this->mall_id, 'user_id' => $this->user_id, 'status' => StatusEnum::ENABLED]);
  114. if (!$user_digital || $user_digital->digital < $params['num']) throw new \Exception('超出可转数量');
  115. $forwarding_charge_radio = $setting['forwarding_charge'] ?? 0;
  116. $forwarding_charge = bcmul($params['num'], $forwarding_charge_radio / 100, 2);
  117. $need_deduct_digital = bcadd($params['num'], $forwarding_charge, 2);
  118. if ($user_digital->digital < $need_deduct_digital) throw new \Exception('超出可转数量');
  119. // 检查用户是否存在
  120. $receive_user = User::getUser([['id' => $params['user_id'], 'mall_id' => $this->mall_id]], 'id');
  121. if (empty($receive_user)) throw new Exception('赠送的用户不存在');
  122. //开启事务
  123. $setting = MallSetting::conf($this->mall_id, 'digital');
  124. $digital_name = $setting['name'] ?? '数字币';
  125. $send_data = [
  126. 'desc' => '用户id: ' . $user_digital['user_id'] . '向用户id: ' . $params['user_id'] . '转送' . $params['num'] . $digital_name,
  127. 'source_table' => User::tableName(),
  128. 'source_table_id' => $params['user_id'],
  129. 'from_type' => UserWalletEnum::TRANSFER,
  130. 'capital_type' => UserWalletEnum::TRANSFER_GIVE,
  131. 'contributor_id' => $user_digital['user_id'],
  132. 'contributor_money' => $forwarding_charge, //手续费
  133. 'contributor_name' => '转送'
  134. ];
  135. $res = UserDigital::handleUserDigital($this->mall_id, $user_digital['user_id'], $need_deduct_digital * -1, false, $send_data);
  136. if ($res === false) throw new \Exception(UserDigital::getStaticError());
  137. $receive_data = [
  138. 'desc' => '用户id: ' . $user_digital['user_id'] . '向用户id: ' . $params['user_id'] . '转送' . $params['num'] . $digital_name,
  139. 'source_table' => User::tableName(),
  140. 'source_table_id' => $user_digital['user_id'],
  141. 'from_type' => UserWalletEnum::TRANSFER,
  142. 'capital_type' => UserWalletEnum::TRANSFER_GIVE,
  143. 'contributor_id' => $user_digital['user_id'],
  144. 'contributor_name' => '转送'
  145. ];
  146. $res = UserDigital::handleUserDigital($this->mall_id, $params['user_id'], $params['num'], false, $receive_data);
  147. if ($res === false) throw new \Exception(UserDigital::getStaticError());
  148. $t->commit();
  149. LockHelper::uLock($lock_key, $identification);
  150. return $this->success('转送成功');
  151. } catch (\Exception $e) {
  152. $t->rollBack();
  153. LockHelper::uLock($lock_key, $identification);
  154. return $this->error($e->getMessage());
  155. }
  156. }
  157. }
  158. public function actionPreviewConvert()
  159. {
  160. try {
  161. if (!Yii::$app->request->isGet) throw new \Exception('this request method is not supported');
  162. $params = Yii::$app->request->get();
  163. $res = Yii::$app->services->validate->validate($params, [
  164. [['from', 'to'], 'required'],
  165. [['from', 'to'], 'string'],
  166. ]);
  167. if ($res === false) throw new \Exception(Yii::$app->services->validate->getErrorMessage());
  168. $setting = MallSetting::conf($this->mall_id, 'digital');
  169. $is_open_switch = 0;
  170. $from_text = $setting['name'];
  171. $to_text = '';
  172. $from_ratio = 1;
  173. $to_ratio = 0;
  174. if ($params['from'] == 'digital' && $params['to'] == 'balance') {
  175. $is_open_switch = $setting['enable_to_balance'] ?? 0;
  176. $to_text = Yii::$app->services->setting->mall->get($this->mall_id, 'balance.balance_name') ?? '余额';
  177. $to_ratio = $setting['to_balance_ratio'] ?? 0;
  178. } elseif ($params['from'] == 'digital' && $params['to'] == 'score') {
  179. $is_open_switch = $setting['enable_to_score'] ?? 0;
  180. $to_text = \Yii::$app->services->setting->mall->get($this->mall_id, 'score.score_name') ?? '积分';
  181. $to_ratio = $setting['to_score_ratio'] ?? 0;
  182. }
  183. if (empty($is_open_switch)) throw new \Exception('system is not allowed to this action');
  184. if (empty($to_ratio)) throw new \Exception('system is not corrected to set setting');
  185. $user_digital = UserDigital::findOne(['mall_id' => $this->mall_id, 'user_id' => $this->user_id, 'status' => StatusEnum::ENABLED]);
  186. $currency = $user_digital->digital ?? 0;
  187. return $this->success('数据请求成功', compact('currency', 'from_text', 'to_text', 'from_ratio', 'to_ratio'));
  188. } catch (\Exception $e) {
  189. return $this->error($e->getMessage());
  190. }
  191. }
  192. public function actionConvert()
  193. {
  194. if (Yii::$app->request->isPost) {
  195. $lock_key = 'digital_convert_' . $this->user_id;
  196. $identification = uniqid();
  197. $t = \Yii::$app->db->beginTransaction();
  198. if (!LockHelper::lock($lock_key, $identification, 30, 5)) return $this->error('操作太频繁');
  199. try {
  200. $params = Yii::$app->request->post();
  201. $res = Yii::$app->services->validate->validate($params, [
  202. [['num', 'from', 'to'], 'required'],
  203. [['num',], 'integer'],
  204. [['from', 'to'], 'string'],
  205. [['num'], 'compare', 'compareValue' => 0, 'operator' => '>']
  206. ]);
  207. if ($res === false) throw new \Exception(Yii::$app->services->validate->getErrorMessage());
  208. $setting = MallSetting::conf($this->mall_id, 'digital');
  209. $is_open_switch = 0;
  210. $from_text = $setting['name'] ?? '数字币';
  211. $to_text = '';
  212. $from_ratio = 1;
  213. $to_ratio = 0;
  214. if ($params['from'] == 'digital' && $params['to'] == 'balance') {
  215. $is_open_switch = $setting['enable_to_balance'] ?? 0;
  216. $to_text = Yii::$app->services->setting->mall->get($this->mall_id, 'balance.balance_name') ?? '余额';
  217. $to_ratio = $setting['to_balance_ratio'] ?? 0;
  218. } elseif ($params['from'] == 'digital' && $params['to'] == 'score') {
  219. $is_open_switch = $setting['enable_to_score'] ?? 0;
  220. $to_text = \Yii::$app->services->setting->mall->get($this->mall_id, 'score.score_name') ?? '积分';
  221. $to_ratio = $setting['to_score_ratio'] ?? 0;
  222. }
  223. if (empty($is_open_switch)) throw new \Exception('system is not allowed to this action');
  224. if (empty($to_ratio)) throw new \Exception('system is not corrected to set setting');
  225. $user_digital = UserDigital::findOne(['mall_id' => $this->mall_id, 'user_id' => $this->user_id, 'status' => StatusEnum::ENABLED]);
  226. if (!$user_digital || $user_digital->digital < $params['num']) throw new \Exception("当前{$from_text}不足");
  227. $get_num = bcdiv(bcmul($to_ratio, $params['num'], 2), $from_ratio, 2);
  228. $desc = $params['num'] . $from_text . '转为' . $get_num . $to_text;
  229. $send_data = [
  230. 'desc' => $desc,
  231. 'source_table' => UserDigital::tableName(),
  232. 'source_table_id' => $user_digital['id'],
  233. 'from_type' => UserWalletEnum::TRANSFER,
  234. 'capital_type' => UserWalletEnum::TRANSFER_GIVE
  235. ];
  236. $res = UserDigital::handleUserDigital($this->mall_id, $user_digital['user_id'], $params['num'] * -1, false, $send_data);
  237. if ($res === false) throw new \Exception(UserDigital::getStaticError());
  238. $handle_wallet = [
  239. 'mall_id' => $this->mall_id,
  240. 'user_id' => $this->user_id,
  241. 'is_frozen' => false,
  242. 'wallet_type' => $params['to'] == 'balance' ? UserWalletService::WALLET_TYPE_BALANCE : UserWalletService::WALLET_TYPE_SCORE,
  243. 'money' => $get_num,
  244. 'desc' => $desc,
  245. 'source_table' => UserDigital::tableName(),
  246. 'source_table_id' => $user_digital['id'],
  247. 'from_type' => UserWalletEnum::TRANSFER,
  248. 'capital_type' => UserWalletEnum::TRANSFER_GIVE
  249. ];
  250. $res = UserWalletService::handle(0, $handle_wallet);
  251. if (!$res) {
  252. $error_msg = UserWalletService::$static_error ?? $to_text . '转换失败';
  253. throw new \Exception($error_msg);
  254. }
  255. $t->commit();
  256. LockHelper::uLock($lock_key, $identification);
  257. return $this->success('转换成功');
  258. } catch (\Exception $e) {
  259. $t->rollBack();
  260. LockHelper::uLock($lock_key, $identification);
  261. return $this->error($e->getMessage());
  262. }
  263. }
  264. }
  265. }