UserService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. namespace app\common\service;
  3. use app\common\model\UpgradeLogModel;
  4. use app\common\model\UserModel;
  5. use app\common\model\UserUnmoneyModel;
  6. use think\Exception;
  7. use think\facade\Db;
  8. use utils\RedisCache;
  9. /**
  10. * 用户服务 by wes
  11. * Class UserService
  12. * @package app\common\service
  13. */
  14. class UserService
  15. {
  16. protected static $instance = null;
  17. protected $model = null;
  18. public function __construct()
  19. {
  20. $this->model = new UserModel();
  21. }
  22. /**
  23. * 静态化入口
  24. * @return static|null
  25. */
  26. public static function make()
  27. {
  28. if(!self::$instance){
  29. self::$instance = new static();
  30. }
  31. return self::$instance;
  32. }
  33. /**
  34. * 用户缓存信息
  35. * @param int $uid
  36. * @return array|mixed
  37. */
  38. public function getCacheInfo($uid, $field='', $cache=false)
  39. {
  40. $cacheKey = "caches:users:info:u{$uid}".($field?'_'.md5($field):'');
  41. $data = RedisCache::get($cacheKey);
  42. if($data && $cache){
  43. return $data;
  44. }
  45. $where = ['id'=> $uid];
  46. $field = $field? $field : 'id,user_name,mobile,level,code,pid,money,score,profit_money,level_type,status,is_auth,is_reward,user_type,store_type,has_fd';
  47. $data = $this->model->where($where)->field($field)->findOrEmpty();
  48. $data = $data? $data->toArray():[];
  49. if($data && $cache){
  50. RedisCache::set($cacheKey, $data, rand(3,5));
  51. }
  52. return $data;
  53. }
  54. /**
  55. * 结算利润到余额
  56. * @param $uid
  57. * @throws Exception
  58. * @throws \think\db\exception\DataNotFoundException
  59. * @throws \think\db\exception\DbException
  60. * @throws \think\db\exception\ModelNotFoundException
  61. */
  62. public function switchProfitToMoney($uid)
  63. {
  64. $userInfo = UserModel::where('id', $uid)->field('id,mobile,money,profit_money,status')->find();
  65. $profitMoney = isset($userInfo['profit_money'])? intval($userInfo['profit_money']): 0;
  66. $userMoney = isset($userInfo['money'])? floatval($userInfo['money']): 0;
  67. $userStatus = isset($userInfo['status'])? $userInfo['status'] : 0;
  68. if(empty($userInfo) || $userStatus != 1){
  69. sr_throw('用户参数错误或已被冻结');
  70. }
  71. if ($profitMoney < 100){
  72. sr_throw('利润小于100,结算失败');
  73. }else{
  74. $profitMoney = $profitMoney - $profitMoney % 100;
  75. $updateAccount = UserModel::where('id', $uid)
  76. ->inc('money',$profitMoney)
  77. ->inc('total_profit_money', $profitMoney)
  78. ->dec('profit_money', $profitMoney)
  79. ->update();
  80. if(!$updateAccount){
  81. sr_throw('利润结算失败');
  82. }
  83. $data = [
  84. 'uid'=>$uid,
  85. 'type'=> 8,
  86. 'money'=> $profitMoney,
  87. 'status'=> 1,
  88. 'state'=> 2,
  89. 'before_money'=> $userMoney,
  90. 'after_money'=> floatval($userMoney+$profitMoney),
  91. 'remark'=> '利润结算到余额',
  92. 'create_time'=>date('Y-m-d H:i:s')
  93. ];
  94. if(!UserUnmoneyModel::insertGetId($data)){
  95. sr_throw('利润结算处理失败');
  96. }
  97. }
  98. }
  99. /**
  100. * 批量用户升级
  101. * @return array|string
  102. */
  103. public function updateLevel()
  104. {
  105. $cacheKey = "caches:users:updateLevel:".date('YmdHi');
  106. if(RedisCache::get($cacheKey.'_lock')){
  107. return '请不要频繁提交处理';
  108. }
  109. Db::startTrans();
  110. try {
  111. RedisCache::setnx($cacheKey . 'lock', date('Y-m-d H:i:s'), rand(5, 10));
  112. $list = $this->model->where('has_update_level', '>', 0)
  113. ->field('id,level,nickname,pid,path')
  114. ->select();
  115. $list = $list? $list->toArray() : [];
  116. if (empty($list)) {
  117. return '暂无用户需要升级';
  118. }
  119. // 等级配置
  120. $levelConfig = LevelSettingService::make()->getConfigData(0, 1);
  121. if (empty($levelConfig)) {
  122. return '请先配置等级升级参数';
  123. }
  124. // 处理升级
  125. $noCatchUids = []; // 未升级的用户
  126. $levelIds = []; // 升级了的用户
  127. foreach ($list as $item) {
  128. $uid = isset($item['id']) ? $item['id'] : 0;
  129. $path = isset($item['path']) ? $item['path'] : 0;
  130. $nickname = isset($item['nickname']) ? $item['nickname'] : '';
  131. $userLevel = isset($item['level']) ? intval($item['level']) : 0;
  132. // 判断团队人数获取可升等级
  133. $updateLevel = 0;
  134. $teamCount = $this->getTeamCount($uid, $path); // 团队人数
  135. foreach ($levelConfig as $val) {
  136. $needTeamCount = isset($val['team_num']) ? $val['team_num'] : 0;
  137. $level = isset($val['level']) ? $val['level'] : 0;
  138. // 如果团队人数满足当前等级要求(排除)
  139. if ($level > 0 && $needTeamCount && $teamCount >= $needTeamCount) {
  140. $updateLevel = $level;
  141. }
  142. }
  143. // 如果当前用户需要升级
  144. $ztCount = 0;
  145. if ($updateLevel > $userLevel) {
  146. // 所需直推等级
  147. $nextLevelData = isset($levelConfig[$updateLevel])?$levelConfig[$updateLevel] : [];
  148. $ztLevel = isset($nextLevelData['zt_level']) ? intval($nextLevelData['zt_level']) : 0;
  149. $ztCount = $this->getZtCount($uid, $ztLevel);
  150. // 可升级的等级所需直推人数
  151. $needZtCount = isset($nextLevelData['zt_num']) ? intval($nextLevelData['zt_num']) : 0;
  152. if ($needZtCount <= $ztCount) {
  153. // 当前用户升级,且去掉可升级状态
  154. $this->model->where('id', $uid)->update(['level' => $updateLevel, 'has_update_level' => 0, 'update_time' => date('Y-m-d H:i:s')]);
  155. UpgradeLogModel::insert(['uid' => $uid, 'nickname' => $nickname, 'original_level' => $userLevel, 'level' => $updateLevel, 'condition' => json_encode($nextLevelData), 'type' => 2, 'state' => 1]);
  156. $levelIds[] = $uid;
  157. }else{
  158. $noCatchUids[] = $uid;
  159. }
  160. }else{
  161. $noCatchUids[] = $uid;
  162. }
  163. // 用户升级后,处理上层用户升级(设置为可升级状态)
  164. if ($path && $updateLevel>$userLevel) {
  165. $this->model->whereIn('id', explode(',', $path))->update(['has_update_level' => 1, 'update_time' => date('Y-m-d H:i:s')]);
  166. }
  167. // 处理缓存
  168. RedisCache::set("caches:users:updateLevel:user_{$uid}:".date('YmdHi'), ['msg'=>'升级处理成功','data'=> $val,'team'=>$teamCount,'zt'=>$ztCount,'updateLevel'=> $updateLevel,'config'=>$levelConfig,'date'=>date('Y-m-d H:i:s')], 3600);
  169. }
  170. // 如果存在未升级用户清除升级状态
  171. if($noCatchUids){
  172. $this->model->whereIn('id', $noCatchUids)->update(['has_update_level' => 0, 'update_time' => date('Y-m-d H:i:s')]);
  173. }
  174. Db::commit();
  175. RedisCache::set($cacheKey.'_success', ['msg'=>'升级处理成功','total'=> count($list),'level'=>count($levelIds),'date'=>date('Y-m-d H:i:s')], 7200);
  176. return ['code'=>200, 'msg'=>"处理升级成功,累计处理用户".count($list)."个,升级用户".count($levelIds).'个'];
  177. } catch (\Exception $exception){
  178. Db::rollback();
  179. RedisCache::set($cacheKey.'_fail', ['msg'=> $exception->getMessage(),'trace'=> $exception->getTrace(),'date'=>date('Y-m-d H:i:s')], 7200);
  180. return ['code'=>500, 'msg'=>"处理升级失败:".$exception->getMessage()];
  181. }
  182. }
  183. /**
  184. * 用户升级
  185. * @param $uid
  186. * @param int $type
  187. * @return array
  188. * @throws \think\Exception
  189. * @throws \think\db\exception\DataNotFoundException
  190. * @throws \think\db\exception\DbException
  191. * @throws \think\db\exception\ModelNotFoundException
  192. */
  193. public function upgradeLevel($uid, $type=2)
  194. {
  195. $cacheKey = "caches:users:updateLevel:{$uid}_".date('YmdHi');
  196. if(RedisCache::get($cacheKey.'_lock')){
  197. sr_throw('暂不需要升级');
  198. }
  199. RedisCache::setnx($cacheKey . 'lock', date('Y-m-d H:i:s'), rand(5, 10));
  200. $info = $this->model->where('id', $uid)
  201. ->field('id,level,nickname,pid,path')
  202. ->findOrEmpty();
  203. $path = isset($info['path']) ? $info['path'] : 0;
  204. $nickname = isset($info['nickname']) ? $info['nickname'] : '';
  205. $userLevel = isset($info['level']) ? intval($info['level']) : 0;
  206. // 等级配置
  207. $levelConfig = LevelSettingService::make()->getConfigData(0, 1);
  208. if (empty($levelConfig)) {
  209. sr_throw('请先配置等级升级参数');
  210. }
  211. // 判断团队人数获取可升等级
  212. $updateLevel = 0;
  213. $teamCount = $this->getTeamCount($uid, $path); // 团队人数
  214. foreach ($levelConfig as $val) {
  215. $needTeamCount = isset($val['team_num']) ? $val['team_num'] : 0;
  216. $level = isset($val['level']) ? $val['level'] : 0;
  217. // 如果团队人数满足当前等级要求(排除)
  218. if ($level > 0 && $needTeamCount && $teamCount >= $needTeamCount) {
  219. $updateLevel = $level;
  220. }
  221. }
  222. // 如果当前用户需要升级
  223. $canUpgrade = false;
  224. if ($updateLevel > $userLevel) {
  225. // 所需直推等级
  226. $nextLevelData = isset($levelConfig[$updateLevel])?$levelConfig[$updateLevel] : [];
  227. $ztLevel = isset($nextLevelData['zt_level']) ? intval($nextLevelData['zt_level']) : 0;
  228. $ztCount = $this->getZtCount($uid, $ztLevel);
  229. // 可升级的等级所需直推人数
  230. $needZtCount = isset($nextLevelData['zt_num']) ? intval($nextLevelData['zt_num']) : 0;
  231. if ($needZtCount <= $ztCount) {
  232. $canUpgrade == true;
  233. }
  234. }
  235. if($type == 2){
  236. if($canUpgrade){
  237. return ['code'=>206, 'msg'=>'当前可以升级'];
  238. }else{
  239. throw new Exception('未达到升级条件', 205);
  240. }
  241. }
  242. if($type == 1 && $canUpgrade){
  243. // 当前用户升级,且去掉可升级状态
  244. $this->model->where('id', $uid)->update(['level' => $updateLevel, 'has_update_level' => 0, 'update_time' => date('Y-m-d H:i:s')]);
  245. UpgradeLogModel::insert(['uid' => $uid, 'nickname' => $nickname, 'original_level' => $userLevel, 'level' => $updateLevel, 'condition' => json_encode($nextLevelData), 'type' => 2, 'state' => 1]);
  246. // 用户升级后,处理上层用户升级(设置为可升级状态)
  247. if ($path) {
  248. $this->model->whereIn('id', explode(',', $path))->update(['has_update_level' => 1, 'update_time' => date('Y-m-d H:i:s')]);
  249. }
  250. return ['msg' => '升级成功,当前等级为:' . $nextLevelData['name'], 'data'=>['level_name' => $nextLevelData['name']]];
  251. }
  252. sr_throw('未达到升级条件');
  253. }
  254. /**
  255. * 统计有效直推人数
  256. * @param $uid
  257. * @param int $level 等级
  258. * @return array|int|mixed
  259. * @throws \think\db\exception\DbException
  260. */
  261. public function getZtCount($uid, $level = 0)
  262. {
  263. $cacheKey = "caches:users:counts:zt_num_{$uid}_{$level}";
  264. $data = RedisCache::get($cacheKey);
  265. if($data){
  266. return $data;
  267. }
  268. $where = ['pid'=> $uid,'has_fd'=>1];
  269. if($level>0){
  270. $where['level'] = $level;
  271. }
  272. $data = $this->model->where($where)->count('id');
  273. if($data){
  274. RedisCache::set($cacheKey, $data, rand(10,20));
  275. }
  276. return $data;
  277. }
  278. /**
  279. * 统计有效团队人数
  280. * @param $uid 用户ID
  281. * @param $path
  282. * @return array|int|mixed
  283. * @throws \think\db\exception\DbException
  284. */
  285. public function getTeamCount($uid, $path)
  286. {
  287. $cacheKey = "caches:users:counts:team_num_{$uid}";
  288. $data = RedisCache::get($cacheKey);
  289. if($data){
  290. return $data;
  291. }
  292. $data = $this->model->where('path','like',"%{$path}")
  293. ->where('has_fd',1)
  294. ->count('id');
  295. if($data){
  296. RedisCache::set($cacheKey, $data, rand(10,20));
  297. }
  298. return $data;
  299. }
  300. }