UserDigital.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <?php
  2. namespace common\models\user;
  3. use addons\Devote\common\models\DevoteFrozenLog;
  4. use addons\Devote\common\models\DevoteWallet;
  5. use common\enums\AddonsEnum;
  6. use common\enums\StatusEnum;
  7. use common\helpers\LockHelper;
  8. use common\models\common\CapitalLog;
  9. use common\models\common\MallSetting;
  10. use common\models\mall\MallAddons;
  11. use Yii;
  12. use Exception;
  13. /**
  14. * This is the model class for table "{{%user_digital}}".
  15. *
  16. * @property int $id
  17. * @property int $mall_id 商城id
  18. * @property int $user_id 会员id
  19. * @property float $digital 可用数量
  20. * @property float $frozen_digital 冻结数量
  21. * @property int|null $status 状态[-1:删除;0:禁用;1启用]
  22. * @property int $created_at
  23. * @property int $updated_at
  24. */
  25. class UserDigital extends \common\models\base\BaseActiveRecord
  26. {
  27. /**
  28. * {@inheritdoc}
  29. */
  30. public static function tableName()
  31. {
  32. return '{{%user_digital}}';
  33. }
  34. /**
  35. * {@inheritdoc}
  36. */
  37. public function rules()
  38. {
  39. return [
  40. [['mall_id', 'user_id', 'status', 'created_at', 'updated_at'], 'integer'],
  41. [['digital', 'frozen_digital'], 'number'],
  42. ];
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function attributeLabels()
  48. {
  49. return [
  50. 'id' => 'ID',
  51. 'mall_id' => 'Mall ID',
  52. 'user_id' => 'User ID',
  53. 'digital' => 'Digital',
  54. 'frozen_digital' => 'Frozen Digital',
  55. 'status' => 'Status',
  56. 'created_at' => 'Created At',
  57. 'updated_at' => 'Updated At',
  58. ];
  59. }
  60. private static function getKey($user_id)
  61. {
  62. return 'lock_user_digital_' . $user_id;
  63. }
  64. /**
  65. * 保存数据
  66. * @param array $params
  67. * @return bool|UserExtra
  68. */
  69. public static function setData(array $params)
  70. {
  71. try {
  72. if (!empty($params['user_id'])) {
  73. $model = self::findOne(['user_id' => $params['user_id']]);
  74. if (empty($model)) {
  75. $model = new self();
  76. $model->loadDefaultValues();
  77. }
  78. if (!$model->load($params, '') || !$model->save()) throw new Exception($model->getErrorMessage());
  79. return $model;
  80. }
  81. } catch (Exception $e) {
  82. self::$static_error = $e->getMessage();
  83. return false;
  84. }
  85. }
  86. /**
  87. * 操作用户账户
  88. * @param $mall_id
  89. * @param $user_id
  90. * @param $change_num
  91. * @param bool $is_frozen
  92. * @param array $params
  93. * @return bool
  94. */
  95. public static function handleUserDigital($mall_id, $user_id, $change_num, $is_frozen = false, $params = [])
  96. {
  97. if (bccomp($change_num, 0, 10) == 0) {
  98. self::$static_error = '操作金额为0';
  99. return false;
  100. }
  101. //获取设置是否启用
  102. $is_enable = MallSetting::conf($mall_id,'digital.is_enable');
  103. if(!$is_enable) {
  104. self::$static_error = '数字币未启用';
  105. return false;
  106. }
  107. $origin_change_num = $change_num;
  108. $lock_key = self::getKey($user_id);
  109. $identification = uniqid();
  110. if (LockHelper::lock($lock_key, $identification, 100, 100)) {
  111. $t = \Yii::$app->db->beginTransaction();
  112. try {
  113. $edit_field = $is_frozen ? 'frozen_digital' : 'digital';
  114. $user_wallet_model = self::findOne(['mall_id' => $mall_id, 'user_id' => $user_id, 'status' => StatusEnum::ENABLED]);
  115. if (empty($user_wallet_model)) {
  116. $user_wallet_model = self::setData([
  117. 'user_id' => $user_id,
  118. 'mall_id' => $mall_id,
  119. 'status' => StatusEnum::ENABLED
  120. ]);
  121. if (!$user_wallet_model) throw new Exception(self::getStaticError());
  122. }
  123. //扣除对应的贡献值
  124. $open_deduct_redpack = MallSetting::conf($mall_id,'digital.deduct_redpack');
  125. $is_installed_devote = MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_DEVOTE);
  126. $from_type = $params['from_type'] ?? '';
  127. $is_addons = MallAddons::isInstall($mall_id, $from_type);
  128. $force_deduct_redpack = $params['force_deduct_redpack'] ?? false;
  129. if(($open_deduct_redpack || $force_deduct_redpack) && $is_installed_devote && $change_num > 0 && $is_addons){
  130. $deduct_redpack_ratio = MallSetting::conf($mall_id,'digital.deduct_redpack_ratio');
  131. $deduct_num = bcmul($deduct_redpack_ratio,$change_num,2);
  132. //发放数字币 需要扣除贡献值
  133. $params['addons_name'] = $from_type;
  134. $real_deduct_num = DevoteFrozenLog::deduct_frozen($mall_id,$user_id,$deduct_num,$params);
  135. if(!$real_deduct_num) throw new Exception('扣除贡献值失败');
  136. if($real_deduct_num != $deduct_num){
  137. //实际该发放的数字币
  138. $change_num = bcdiv($real_deduct_num,$deduct_redpack_ratio,2);
  139. }
  140. $desc = $params['desc'] ?? '';
  141. if($origin_change_num != $change_num && $desc){
  142. //用户(103939),获得订单(9224)分销商奖励,奖励类型:间推奖励,金额:279.80
  143. $params['desc'] = str_replace(':'.$origin_change_num,':'.$change_num,$desc);
  144. }
  145. }else{
  146. $is_installed_devote = empty($is_installed_devote) ? 'false' : 'true';
  147. $is_addons = empty($is_addons) ? 'false' : 'true';
  148. Yii::error(__CLASS__ . ' handleUserDigital notice : ' . $open_deduct_redpack.$is_installed_devote.$is_addons);
  149. }
  150. $current_num = $user_wallet_model->$edit_field;
  151. $after_num = bcadd($current_num, $change_num, 2);
  152. if (bccomp($after_num, 0, 10) < 0) {
  153. $msg = $is_frozen ? '冻结数字币' : '数字币';
  154. throw new Exception('用户' . $msg . '不足');
  155. }
  156. $user_wallet_model->$edit_field = $after_num;
  157. if (!$user_wallet_model->save()) throw new Exception($user_wallet_model->getErrorMessage());
  158. $log = [
  159. 'mall_id' => $mall_id,
  160. 'user_id' => $user_id,
  161. 'change_type' => $change_num > 0 ? CapitalLog::CHANGE_TYPE_ADD : CapitalLog::CHANGE_TYPE_SUB,
  162. 'change_num' => abs($change_num),
  163. 'current_num' => $current_num,
  164. 'desc' => $params['desc'] ?? '',
  165. 'source_table' => $params['source_table'] ?? '',
  166. 'source_table_id' => $params['source_table_id'] ?? 0,
  167. 'from_type' => $params['from_type'] ?? '',
  168. 'capital_type' => $params['capital_type'] ?? '',
  169. 'order_no' => $params['order_no'] ?? '',
  170. 'contributor_name' => $params['contributor_name'] ?? '',
  171. 'contributor_money' => $params['contributor_money'] ?? 0,
  172. 'contributor_id' => $params['contributor_id'] ?? 0,
  173. 'send_status' => $is_frozen ? DigitalLog::SEND_STATUS_FROZEN : DigitalLog::SEND_STATUS_NORMAL
  174. ];
  175. if (DigitalLog::setData($log) === false) throw new Exception(DigitalLog::getStaticError());
  176. $t->commit();
  177. LockHelper::uLock($lock_key, $identification);
  178. return abs($change_num);
  179. } catch (Exception $e) {
  180. Yii::error(__CLASS__ . ' handleUserDigital exception : ' . $e->getMessage() . ',user_id:' . $user_id);
  181. self::$static_error = $e->getMessage();
  182. $t->rollBack();
  183. LockHelper::uLock($lock_key, $identification);
  184. return false;
  185. }
  186. } else {
  187. //抢不到锁
  188. Yii::error(__CLASS__ . ' handleUserDigital error : 抢不到锁,user_id:' . $user_id);
  189. self::$static_error = '操作太频繁';
  190. return false;
  191. }
  192. }
  193. /**
  194. * 操作冻结数字币
  195. * @param $log_id
  196. * @param bool $is_send true-释放,false-取消
  197. * @return
  198. */
  199. public static function unfrozen($log_id, $is_send = true)
  200. {
  201. $t = \Yii::$app->db->beginTransaction();
  202. try {
  203. $log = DigitalLog::findOne(['id' => $log_id, 'status' => StatusEnum::ENABLED,'send_status' =>DigitalLog::SEND_STATUS_FROZEN ]);
  204. if (empty($log)) {
  205. throw new Exception('操作记录不存在');
  206. }
  207. $lock_key = self::getKey($log->user_id);
  208. $identification = uniqid();
  209. if (LockHelper::lock($lock_key, $identification, 100, 100)) {
  210. $user_wallet_model = self::findOne(['mall_id' => $log->mall_id, 'user_id' => $log->user_id, 'status' => StatusEnum::ENABLED]);
  211. $current_num = $user_wallet_model->frozen_digital;
  212. $after_num = bcsub($current_num, $log->change_num, 2);
  213. if (bccomp($after_num, 0, 10) < 0) {
  214. throw new Exception('用户冻结数字币不足');
  215. }
  216. $user_wallet_model->frozen_digital = $after_num;
  217. if ($is_send) {
  218. $log->send_status = DigitalLog::SEND_STATUS_NORMAL;
  219. $user_wallet_model->digital = bcadd($user_wallet_model->digital,$log->change_num,2);
  220. }else{
  221. $log->send_status = DigitalLog::SEND_STATUS_INVALID;
  222. }
  223. if (!$user_wallet_model->save()) throw new Exception($user_wallet_model->getErrorMessage());
  224. if (!$log->save()) throw new Exception($log->getErrorMessage());
  225. }
  226. $t->commit();
  227. LockHelper::uLock($lock_key, $identification);
  228. } catch (Exception $e) {
  229. $t->rollBack();
  230. Yii::error(__CLASS__ . ' unfrozen exception : ' . $e->getMessage());
  231. self::$static_error = $e->getMessage();
  232. return false;
  233. }
  234. }
  235. }