AdminController.php 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Components\Helpers;
  4. use App\Components\IPIP;
  5. use App\Components\PushNotification;
  6. use App\Components\QQWry;
  7. use App\Models\Article;
  8. use App\Models\Config;
  9. use App\Models\Country;
  10. use App\Models\Invite;
  11. use App\Models\Label;
  12. use App\Models\Level;
  13. use App\Models\Node;
  14. use App\Models\NodeDailyDataFlow;
  15. use App\Models\NodeLabel;
  16. use App\Models\NodeOnlineUserIp;
  17. use App\Models\NotificationLog;
  18. use App\Models\Order;
  19. use App\Models\ReferralApply;
  20. use App\Models\ReferralLog;
  21. use App\Models\SsConfig;
  22. use App\Models\User;
  23. use App\Models\UserBanedLog;
  24. use App\Models\UserCreditLog;
  25. use App\Models\UserDataFlowLog;
  26. use App\Models\UserDataModifyLog;
  27. use App\Models\UserGroup;
  28. use App\Models\UserHourlyDataFlow;
  29. use App\Services\UserService;
  30. use Auth;
  31. use DB;
  32. use Exception;
  33. use Hash;
  34. use Illuminate\Http\JsonResponse;
  35. use Illuminate\Http\RedirectResponse;
  36. use Illuminate\Http\Request;
  37. use Log;
  38. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  39. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  40. use Redirect;
  41. use Response;
  42. use Session;
  43. use Str;
  44. use Validator;
  45. /**
  46. * 管理员控制器
  47. *
  48. * Class AdminController
  49. *
  50. * @package App\Http\Controllers
  51. */
  52. class AdminController extends Controller
  53. {
  54. public function index()
  55. {
  56. $past = strtotime("-" . sysConfig('expire_days') . " days");
  57. $view['expireDays'] = sysConfig('expire_days');
  58. $view['totalUserCount'] = User::count(); // 总用户数
  59. $view['enableUserCount'] = User::whereEnable(1)->count(
  60. ); // 有效用户数
  61. $view['activeUserCount'] = User::where('t', '>=', $past)->count(
  62. ); // 活跃用户数
  63. $view['unActiveUserCount'] = User::whereEnable(1)->whereBetween(
  64. 't',
  65. [
  66. 1,
  67. $past,
  68. ]
  69. )->count(); // 不活跃用户数
  70. $view['onlineUserCount'] = User::where(
  71. 't',
  72. '>=',
  73. strtotime("-10 minutes")
  74. )->count(); // 10分钟内在线用户数
  75. $view['expireWarningUserCount'] = User::whereBetween(
  76. 'expired_at',
  77. [date('Y-m-d'), strtotime("+" . sysConfig('expire_days') . " days")]
  78. )->count(); // 临近过期用户数
  79. $view['largeTrafficUserCount'] = User::whereRaw(
  80. '(u + d) >= 107374182400'
  81. )
  82. ->where('status', '<>', -1)
  83. ->count(); // 流量超过100G的用户
  84. $view['flowAbnormalUserCount'] = count(
  85. $this->trafficAbnormal()
  86. );// 1小时内流量异常用户
  87. $view['nodeCount'] = Node::count();
  88. $view['unnormalNodeCount'] = Node::whereStatus(0)->count();
  89. $view['flowCount'] = flowAutoShow(
  90. NodeDailyDataFlow::where(
  91. 'created_at',
  92. '>=',
  93. date('Y-m-d', strtotime("-30 days"))
  94. )->sum('total')
  95. );
  96. $view['totalFlowCount'] = flowAutoShow(
  97. NodeDailyDataFlow::sum('total')
  98. );
  99. $view['totalCredit'] = User::where('credit', '<>', 0)->sum(
  100. 'credit'
  101. ) / 100;
  102. $view['totalWaitRefAmount'] = ReferralLog::whereIn('status', [0, 1])
  103. ->sum('commission') / 100;
  104. $view['totalRefAmount'] = ReferralApply::whereStatus(2)->sum(
  105. 'amount'
  106. ) / 100;
  107. $view['totalOrder'] = Order::count();
  108. $view['totalOnlinePayOrder'] = Order::where('pay_type', '<>', 0)
  109. ->count();
  110. $view['totalSuccessOrder'] = Order::whereStatus(2)->count();
  111. $view['todaySuccessOrder'] = Order::whereStatus(2)->whereDate(
  112. 'created_at',
  113. date('Y-m-d')
  114. )->count();
  115. // 今日
  116. $view['todayRegister'] = User::whereDate('created_at', date('Y-m-d'))
  117. ->count();
  118. return view('admin.index', $view);
  119. }
  120. // 1小时内流量异常用户
  121. private function trafficAbnormal(): array
  122. {
  123. $result = [];
  124. $userTotalTrafficList = UserHourlyDataFlow::whereNodeId(0)
  125. ->where('total', '>', MB * 50)
  126. ->where(
  127. 'created_at',
  128. '>=',
  129. date(
  130. 'Y-m-d H:i:s',
  131. time() - 3900
  132. )
  133. )
  134. ->groupBy('user_id')
  135. ->selectRaw(
  136. "user_id, sum(total) as totalTraffic"
  137. )
  138. ->get(); // 只统计50M以上的记录,加快速度
  139. foreach ($userTotalTrafficList as $user) {
  140. if ($user->totalTraffic > sysConfig('traffic_ban_value') * GB) {
  141. $result[] = $user->user_id;
  142. }
  143. }
  144. return $result;
  145. }
  146. // 用户列表
  147. public function userList(Request $request)
  148. {
  149. $id = $request->input('id');
  150. $email = $request->input('email');
  151. $wechat = $request->input('wechat');
  152. $qq = $request->input('qq');
  153. $port = $request->input('port');
  154. $status = $request->input('status');
  155. $enable = $request->input('enable');
  156. $online = $request->input('online');
  157. $flowAbnormal = $request->input('flowAbnormal');
  158. $expireWarning = $request->input('expireWarning');
  159. $largeTraffic = $request->input('largeTraffic');
  160. $query = User::with('subscribe');
  161. if (isset($id)) {
  162. $query->whereId($id);
  163. }
  164. if (isset($email)) {
  165. $query->where('email', 'like', '%' . $email . '%');
  166. }
  167. if (isset($wechat)) {
  168. $query->where('wechat', 'like', '%' . $wechat . '%');
  169. }
  170. if (isset($qq)) {
  171. $query->where('qq', 'like', '%' . $qq . '%');
  172. }
  173. if (isset($port)) {
  174. $query->wherePort($port);
  175. }
  176. if (isset($status)) {
  177. $query->whereStatus($status);
  178. }
  179. if (isset($enable)) {
  180. $query->whereEnable($enable);
  181. }
  182. // 流量超过100G的
  183. if ($largeTraffic) {
  184. $query->whereIn('status', [0, 1])->whereRaw(
  185. '(u + d) >= 107374182400'
  186. );
  187. }
  188. // 临近过期提醒
  189. if ($expireWarning) {
  190. $query->whereBetween(
  191. 'expired_at',
  192. [
  193. date('Y-m-d'),
  194. date(
  195. 'Y-m-d',
  196. strtotime("+" . sysConfig('expire_days') . " days")
  197. ),
  198. ]
  199. );
  200. }
  201. // 当前在线
  202. if ($online) {
  203. $query->where('t', '>=', strtotime("-10 minutes"));
  204. }
  205. // 不活跃用户
  206. if ($request->input('unActive')) {
  207. $query->whereBetween(
  208. 't',
  209. [
  210. 1,
  211. strtotime(
  212. "-" . sysConfig(
  213. 'expire_days'
  214. ) . " days"
  215. ),
  216. ]
  217. )->whereEnable(1);
  218. }
  219. // 1小时内流量异常用户
  220. if ($flowAbnormal) {
  221. $query->whereIn('id', $this->trafficAbnormal());
  222. }
  223. $userList = $query->latest()->paginate(15)->appends(
  224. $request->except('page')
  225. );
  226. foreach ($userList as $user) {
  227. $user->transfer_enable = flowAutoShow($user->transfer_enable);
  228. $user->used_flow = flowAutoShow($user->u + $user->d);
  229. if ($user->expired_at < date('Y-m-d')) {
  230. $user->expireWarning = -1; // 已过期
  231. } elseif ($user->expired_at == date('Y-m-d')) {
  232. $user->expireWarning = 0; // 今天过期
  233. } elseif ($user->expired_at > date(
  234. 'Y-m-d'
  235. ) && $user->expired_at <= date(
  236. 'Y-m-d',
  237. strtotime("+30 days")
  238. )) {
  239. $user->expireWarning = 1; // 最近一个月过期
  240. } else {
  241. $user->expireWarning = 2; // 大于一个月过期
  242. }
  243. // 流量异常警告
  244. $totalTraffic = UserHourlyDataFlow::userRecentUsed(
  245. $user->id
  246. )->sum(
  247. 'total'
  248. );
  249. $user->trafficWarning = $totalTraffic > (sysConfig(
  250. 'traffic_ban_value'
  251. ) * GB) ? 1 : 0;
  252. // 订阅地址
  253. $user->link = (sysConfig('subscribe_domain') ?: sysConfig(
  254. 'website_url'
  255. )) . '/s/' . $user->subscribe->code;
  256. }
  257. $view['userList'] = $userList;
  258. return view('admin.user.userList', $view);
  259. }
  260. // 添加账号
  261. public function addUser(Request $request)
  262. {
  263. if ($request->isMethod('POST')) {
  264. // 校验email是否已存在
  265. $exists = User::whereEmail($request->input('email'))->first();
  266. if ($exists) {
  267. return Response::json(
  268. ['status' => 'fail', 'message' => '用户名已存在,请重新输入']
  269. );
  270. }
  271. $user = new User();
  272. $user->username = $request->input('username');
  273. $user->email = $request->input('email');
  274. $user->password = Hash::make(
  275. $request->input('password') ?: Str::random()
  276. );
  277. $user->port = $request->input('port') ?: $this->makePort(
  278. );
  279. $user->passwd = $request->input('passwd') ?: Str::random();
  280. $user->vmess_id = $request->input('uuid') ?: Str::uuid();
  281. $user->transfer_enable = toGB(
  282. $request->input('transfer_enable') ?: 0
  283. );
  284. $user->enable = $request->input('enable') ?: 0;
  285. $user->method = $request->input('method');
  286. $user->protocol = $request->input('protocol');
  287. $user->obfs = $request->input('obfs');
  288. $user->speed_limit = $request->input('speed_limit') * Mbps;
  289. $user->wechat = $request->input('wechat');
  290. $user->qq = $request->input('qq');
  291. $user->expired_at = $request->input('expired_at') ?: date(
  292. 'Y-m-d',
  293. strtotime("+365 days")
  294. );
  295. $user->remark = str_replace(
  296. ["atob", "eval"],
  297. "",
  298. $request->input('remark')
  299. );
  300. $user->level = $request->input('level') ?: 0;
  301. $user->group_id = $request->input('group_id') ?: 0;
  302. $user->reg_ip = getClientIp();
  303. $user->reset_time = $request->input('reset_time') > date(
  304. 'Y-m-d'
  305. ) ? $request->input('reset_time') : null;
  306. $user->invite_num = $request->input('invite_num') ?: 0;
  307. $user->status = $request->input('status') ?: 0;
  308. $user->save();
  309. if ($user->id) {
  310. // 写入用户流量变动记录
  311. Helpers::addUserTrafficModifyLog(
  312. $user->id,
  313. 0,
  314. 0,
  315. toGB($request->input('transfer_enable', 0)),
  316. '后台手动添加用户'
  317. );
  318. return Response::json(
  319. ['status' => 'success', 'message' => '添加成功']
  320. );
  321. }
  322. return Response::json(['status' => 'fail', 'message' => '添加失败']);
  323. }
  324. // 生成一个可用端口
  325. $view['methodList'] = Helpers::methodList();
  326. $view['protocolList'] = Helpers::protocolList();
  327. $view['obfsList'] = Helpers::obfsList();
  328. $view['levelList'] = Level::orderBy('level')->get();
  329. $view['groupList'] = UserGroup::orderBy('id')->get();
  330. return view('admin.user.userInfo', $view);
  331. }
  332. // 生成端口
  333. public function makePort(): int
  334. {
  335. return Helpers::getPort();
  336. }
  337. // 批量生成账号
  338. public function batchAddUsers(Request $request): ?JsonResponse
  339. {
  340. $amount = $request->input('amount');
  341. try {
  342. DB::beginTransaction();
  343. for ($i = 0; $i < $amount; $i++) {
  344. $uid = Helpers::addUser(
  345. Str::random(8) . '@auto.generate',
  346. Hash::make(Str::random()),
  347. toGB(1024),
  348. 365
  349. );
  350. // 生成一个可用端口
  351. if ($uid) {
  352. // 写入用户流量变动记录
  353. Helpers::addUserTrafficModifyLog(
  354. $uid,
  355. 0,
  356. 0,
  357. toGB(1024),
  358. '后台批量生成用户'
  359. );
  360. }
  361. }
  362. DB::commit();
  363. return Response::json(
  364. ['status' => 'success', 'message' => '批量生成账号成功']
  365. );
  366. } catch (Exception $e) {
  367. DB::rollBack();
  368. return Response::json(
  369. [
  370. 'status' => 'fail',
  371. 'message' => '批量生成账号失败:' . $e->getMessage(),
  372. ]
  373. );
  374. }
  375. }
  376. // 编辑账号
  377. public function editUser(Request $request, $id)
  378. {
  379. $user = User::find($id);
  380. if ($request->isMethod('POST')) {
  381. $email = $request->input('email');
  382. $password = $request->input('password');
  383. $port = $request->input('port');
  384. $transfer_enable = $request->input('transfer_enable');
  385. $is_admin = $request->input('is_admin');
  386. $status = $request->input('status');
  387. // 校验email是否已存在
  388. if (User::where('id', '<>', $id)->whereEmail($email)->exists()) {
  389. return Response::json(
  390. ['status' => 'fail', 'message' => '用户名已存在,请重新输入']
  391. );
  392. }
  393. // 校验端口是否已存在
  394. if (User::where('id', '<>', $id)->where('port', '>', 0)->wherePort(
  395. $port
  396. )->exists()) {
  397. return Response::json(
  398. ['status' => 'fail', 'message' => '端口已存在,请重新输入']
  399. );
  400. }
  401. // 禁止取消默认管理员
  402. if ($id == 1 && $is_admin == 0) {
  403. return Response::json(
  404. ['status' => 'fail', 'message' => '系统默认管理员不可取消']
  405. );
  406. }
  407. try {
  408. DB::beginTransaction();
  409. $data = [
  410. 'username' => $request->input('username'),
  411. 'email' => $email,
  412. 'port' => $port,
  413. 'passwd' => $request->input(
  414. 'passwd'
  415. ) ?: Str::random(),
  416. 'vmess_id' => $request->input('uuid') ?: Str::uuid(),
  417. 'transfer_enable' => toGB($transfer_enable ?: 0),
  418. 'enable' => $status < 0 ? 0 : $request->input(
  419. 'enable'
  420. ),
  421. 'method' => $request->input('method'),
  422. 'protocol' => $request->input('protocol'),
  423. 'obfs' => $request->input('obfs'),
  424. 'speed_limit' => $request->input('speed_limit') * Mbps,
  425. 'wechat' => $request->input('wechat'),
  426. 'qq' => $request->input('qq'),
  427. 'expired_at' => $request->input('expired_at') ?: date(
  428. 'Y-m-d',
  429. strtotime("+365 days")
  430. ),
  431. 'remark' => str_replace(
  432. "eval",
  433. "",
  434. str_replace(
  435. "atob",
  436. "",
  437. $request->input(
  438. 'remark'
  439. )
  440. )
  441. ),
  442. 'level' => $request->input('level'),
  443. 'group_id' => $request->input('group_id'),
  444. 'reset_time' => $request->input('reset_time'),
  445. 'invite_num' => $request->input('invite_num'),
  446. 'status' => $status,
  447. ];
  448. // 只有admin才有权限操作管理员属性
  449. if (Auth::getUser()->is_admin == 1) {
  450. $data['is_admin'] = (int)$is_admin;
  451. }
  452. // 非演示环境才可以修改管理员密码
  453. if ( ! empty($password) && ! (config('app.demo') && $id == 1)) {
  454. $data['password'] = Hash::make($password);
  455. }
  456. $user->update($data);
  457. // 写入用户流量变动记录
  458. if ($user->transfer_enable != toGB($transfer_enable)) {
  459. Helpers::addUserTrafficModifyLog(
  460. $id,
  461. 0,
  462. $user->transfer_enable,
  463. toGB($transfer_enable),
  464. '后台手动编辑用户'
  465. );
  466. }
  467. DB::commit();
  468. return Response::json(
  469. ['status' => 'success', 'message' => '编辑成功']
  470. );
  471. } catch (Exception $e) {
  472. DB::rollBack();
  473. Log::error('编辑用户信息异常:' . $e->getMessage());
  474. return Response::json(
  475. ['status' => 'fail', 'message' => '编辑失败']
  476. );
  477. }
  478. } else {
  479. if ($user) {
  480. $user->transfer_enable = flowToGB($user->transfer_enable);
  481. }
  482. $view['user'] = $user->load('inviter:id,email');
  483. $view['methodList'] = Helpers::methodList();
  484. $view['protocolList'] = Helpers::protocolList();
  485. $view['obfsList'] = Helpers::obfsList();
  486. $view['levelList'] = Level::orderBy('level')->get();
  487. $view['groupList'] = UserGroup::orderBy('id')->get();
  488. return view('admin.user.userInfo', $view);
  489. }
  490. }
  491. // 删除用户
  492. public function delUser(Request $request): ?JsonResponse
  493. {
  494. $id = $request->input('id');
  495. if ($id <= 1) {
  496. return Response::json(
  497. ['status' => 'fail', 'message' => '系统管理员不可删除']
  498. );
  499. }
  500. try {
  501. DB::beginTransaction();
  502. User::find($id)->delete();
  503. DB::commit();
  504. return Response::json(['status' => 'success', 'message' => '删除成功']);
  505. } catch (Exception $e) {
  506. Log::error('删除用户信息异常:' . $e->getMessage());
  507. DB::rollBack();
  508. return Response::json(['status' => 'fail', 'message' => '删除失败']);
  509. }
  510. }
  511. // 文章列表
  512. public function articleList(Request $request)
  513. {
  514. $view['list'] = Article::orderByDesc('sort')->paginate(15)->appends(
  515. $request->except('page')
  516. );
  517. return view('admin.article.articleList', $view);
  518. }
  519. // 添加文章
  520. public function addArticle(Request $request)
  521. {
  522. if ($request->isMethod('POST')) {
  523. $article = new Article();
  524. $article->title = $request->input('title');
  525. $article->type = $request->input('type', 1);
  526. $article->summary = $request->input('summary');
  527. // LOGO
  528. if ($article->type == 4) {
  529. $article->logo = $request->input('logo');
  530. } else {
  531. $logo = '';
  532. if ($request->hasFile('logo')) {
  533. $logo = $this->uploadFile($request->file('logo'));
  534. if ( ! $logo) {
  535. Session::flash('errorMsg', 'LOGO不合法');
  536. return Redirect::back()->withInput();
  537. }
  538. }
  539. $article = new Article();
  540. $article->title = $request->input('title');
  541. $article->type = $request->input('type', 1);
  542. $article->summary = $request->input('summary');
  543. $article->logo = $logo;
  544. }
  545. $article->content = $request->input('content');
  546. $article->sort = $request->input('sort', 0);
  547. $article->save();
  548. if ($article->id) {
  549. Session::flash('successMsg', '添加成功');
  550. } else {
  551. Session::flash('errorMsg', '添加失败');
  552. }
  553. return Redirect::to('admin/articleList');
  554. }
  555. return view('admin.article.addArticle');
  556. }
  557. // 编辑文章
  558. public function editArticle(Request $request)
  559. {
  560. $id = $request->input('id');
  561. if ($request->isMethod('POST')) {
  562. $title = $request->input('title');
  563. $type = $request->input('type');
  564. $summary = $request->input('summary');
  565. $content = $request->input('content');
  566. $sort = $request->input('sort');
  567. // 商品LOGO
  568. if ($type == 4) {
  569. $logo = $request->input('logo');
  570. } else {
  571. $logo = '';
  572. if ($request->hasFile('logo')) {
  573. $logo = $this->uploadFile($request->file('logo'));
  574. if ( ! $logo) {
  575. Session::flash('errorMsg', 'LOGO不合法');
  576. return Redirect::back()->withInput();
  577. }
  578. }
  579. }
  580. $data = [
  581. 'type' => $type,
  582. 'title' => $title,
  583. 'summary' => $summary,
  584. 'content' => $content,
  585. 'sort' => $sort,
  586. ];
  587. if ($logo) {
  588. $data['logo'] = $logo;
  589. }
  590. $ret = Article::whereId($id)->update($data);
  591. if ($ret) {
  592. Session::flash('successMsg', '编辑成功');
  593. } else {
  594. Session::flash('errorMsg', '编辑失败');
  595. }
  596. return Redirect::to('admin/editArticle?id=' . $id);
  597. }
  598. $view['article'] = Article::find($id);
  599. return view('admin.article.editArticle', $view);
  600. }
  601. // 删除文章
  602. public function delArticle(Request $request): ?JsonResponse
  603. {
  604. $id = $request->input('id');
  605. $ret = Article::whereId($id)->delete();
  606. if ($ret) {
  607. return Response::json(['status' => 'success', 'message' => '删除成功']);
  608. }
  609. return Response::json(['status' => 'fail', 'message' => '删除失败']);
  610. }
  611. // 流量日志
  612. public function trafficLog(Request $request)
  613. {
  614. $port = $request->input('port');
  615. $user_id = $request->input('user_id');
  616. $email = $request->input('email');
  617. $nodeId = $request->input('nodeId');
  618. $startTime = $request->input('startTime');
  619. $endTime = $request->input('endTime');
  620. $query = UserDataFlowLog::with(['user', 'node']);
  621. if (isset($port)) {
  622. $query->whereHas(
  623. 'user',
  624. static function ($q) use ($port) {
  625. $q->wherePort($port);
  626. }
  627. );
  628. }
  629. if (isset($user_id)) {
  630. $query->whereUserId($user_id);
  631. }
  632. if (isset($email)) {
  633. $query->whereHas(
  634. 'user',
  635. static function ($q) use ($email) {
  636. $q->where('email', 'like', '%' . $email . '%');
  637. }
  638. );
  639. }
  640. if (isset($nodeId)) {
  641. $query->whereNodeId($nodeId);
  642. }
  643. if (isset($startTime)) {
  644. $query->where('log_time', '>=', strtotime($startTime));
  645. }
  646. if (isset($endTime)) {
  647. $query->where('log_time', '<=', strtotime($endTime));
  648. }
  649. // 已使用流量
  650. $view['totalTraffic'] = flowAutoShow(
  651. $query->sum('u') + $query->sum('d')
  652. );
  653. $list = $query->latest('log_time')->paginate(20)->appends(
  654. $request->except('page')
  655. );
  656. foreach ($list as $vo) {
  657. $vo->u = flowAutoShow($vo->u);
  658. $vo->d = flowAutoShow($vo->d);
  659. $vo->log_time = date('Y-m-d H:i:s', $vo->log_time);
  660. }
  661. $view['list'] = $list;
  662. $view['nodeList'] = Node::whereStatus(1)
  663. ->orderByDesc('sort')
  664. ->latest()
  665. ->get();
  666. return view('admin.logs.trafficLog', $view);
  667. }
  668. // 导出配置信息
  669. public function export(Request $request, $id)
  670. {
  671. if (empty($id)) {
  672. return Redirect::to('admin/userList');
  673. }
  674. $user = User::find($id);
  675. if (empty($user)) {
  676. return Redirect::to('admin/userList');
  677. }
  678. if ($request->isMethod('POST')) {
  679. $infoType = $request->input('type');
  680. $node = Node::find($request->input('id'));
  681. if ($node->type == 1) {
  682. if ($node->compatible) {
  683. $proxyType = 'SS';
  684. } else {
  685. $proxyType = 'SSR';
  686. }
  687. } else {
  688. $proxyType = 'V2Ray';
  689. }
  690. $data = $this->getUserNodeInfo(
  691. $id,
  692. $node->id,
  693. $infoType !== 'text' ? 0 : 1
  694. );
  695. return Response::json(
  696. ['status' => 'success', 'data' => $data, 'title' => $proxyType]
  697. );
  698. }
  699. $view['nodeList'] = Node::whereStatus(1)
  700. ->orderByDesc('sort')
  701. ->orderBy('id')
  702. ->paginate(15)
  703. ->appends($request->except('page'));
  704. $view['user'] = $user;
  705. return view('admin.user.export', $view);
  706. }
  707. // 导出原版SS用户配置信息
  708. public function exportSSJson()
  709. {
  710. $userList = User::where('port', '>', 0)->get();
  711. $json = '';
  712. if ( ! $userList->isEmpty()) {
  713. $userPassword = [];
  714. foreach ($userList as $user) {
  715. $userPassword[] = $user->port . ":" . $user->passwd;
  716. }
  717. $json = json_encode(
  718. [
  719. 'server' => '0.0.0.0',
  720. 'local_address' => '127.0.0.1',
  721. 'local_port' => 1080,
  722. 'port_password' => $userPassword,
  723. 'timeout' => 300,
  724. 'method' => Helpers::getDefaultMethod(),
  725. 'fast_open' => false,
  726. ]
  727. );
  728. }
  729. // 生成JSON文件
  730. $fileName = Str::random() . '_shadowsocks.json';
  731. $filePath = public_path('downloads/' . $fileName);
  732. file_put_contents($filePath, $json);
  733. if ( ! is_file($filePath)) {
  734. exit('文件生成失败,请检查目录权限');
  735. }
  736. return Response::download($filePath);
  737. }
  738. // 修改个人资料
  739. public function profile(Request $request)
  740. {
  741. if ($request->isMethod('POST')) {
  742. $new_password = $request->input('new_password');
  743. if ( ! Hash::check(
  744. $request->input('old_password'),
  745. Auth::getUser()->password
  746. )) {
  747. return Redirect::back()->withErrors('旧密码错误,请重新输入');
  748. }
  749. if (Hash::check($new_password, Auth::getUser()->password)) {
  750. return Redirect::back()->withErrors('新密码不可与旧密码一样,请重新输入');
  751. }
  752. $ret = Auth::getUser()->update(
  753. ['password' => Hash::make($new_password)]
  754. );
  755. if ( ! $ret) {
  756. return Redirect::back()->withErrors('修改失败');
  757. }
  758. return Redirect::back()->with('successMsg', '修改成功');
  759. }
  760. return view('admin.config.profile');
  761. }
  762. // 用户流量监控
  763. public function userMonitor(Request $request)
  764. {
  765. $id = $request->input('id');
  766. if (empty($id)) {
  767. return Redirect::to('admin/userList');
  768. }
  769. $user = User::find($id);
  770. if (empty($user)) {
  771. return Redirect::to('admin/userList');
  772. }
  773. $view['email'] = $user->email;
  774. $view = array_merge($view, $this->dataFlowChart($user->id));
  775. return view('admin.logs.userMonitor', $view);
  776. }
  777. // 加密方式、混淆、协议、等级、国家地区
  778. public function config(Request $request)
  779. {
  780. if ($request->isMethod('POST')) {
  781. $name = $request->input('name');
  782. $type = $request->input(
  783. 'type',
  784. 1
  785. ); // 类型:1-加密方式(method)、2-协议(protocol)、3-混淆(obfs)
  786. $is_default = $request->input('is_default', 0);
  787. $sort = $request->input('sort', 0);
  788. if (empty($name)) {
  789. return Response::json(
  790. ['status' => 'fail', 'message' => '配置名称不能为空']
  791. );
  792. }
  793. // 校验是否已存在
  794. $config = SsConfig::type($type)->whereName($name)->first();
  795. if ($config) {
  796. return Response::json(
  797. ['status' => 'fail', 'message' => '配置已经存在,请勿重复添加']
  798. );
  799. }
  800. $ssConfig = new SsConfig();
  801. $ssConfig->name = $name;
  802. $ssConfig->type = $type;
  803. $ssConfig->is_default = $is_default;
  804. $ssConfig->sort = $sort;
  805. $ssConfig->save();
  806. return Response::json(['status' => 'success', 'message' => '添加成功']);
  807. }
  808. $view['methodList'] = SsConfig::type(1)->get();
  809. $view['protocolList'] = SsConfig::type(2)->get();
  810. $view['obfsList'] = SsConfig::type(3)->get();
  811. $view['countryList'] = Country::all();
  812. $view['levelList'] = Level::all();
  813. $view['labelList'] = Label::with('nodes')->get();
  814. return view('admin.config.config', $view);
  815. }
  816. // 删除配置
  817. public function delConfig(Request $request): ?JsonResponse
  818. {
  819. $id = $request->input('id');
  820. $ret = SsConfig::whereId($id)->delete();
  821. if ($ret) {
  822. return Response::json(['status' => 'success', 'message' => '删除成功']);
  823. }
  824. return Response::json(['status' => 'fail', 'message' => '删除失败']);
  825. }
  826. // 设置默认配置
  827. public function setDefaultConfig(Request $request): JsonResponse
  828. {
  829. $id = $request->input('id');
  830. if (empty($id)) {
  831. return Response::json(['status' => 'fail', 'message' => '非法请求']);
  832. }
  833. $config = SsConfig::find($id);
  834. if ( ! $config) {
  835. return Response::json(['status' => 'fail', 'message' => '配置不存在']);
  836. }
  837. // 去除该配置所属类型的默认值
  838. SsConfig::default()->type($config->type)->update(['is_default' => 0]);
  839. // 将该ID对应记录值置为默认值
  840. SsConfig::whereId($id)->update(['is_default' => 1]);
  841. return Response::json(['status' => 'success', 'message' => '操作成功']);
  842. }
  843. // 设置系统扩展信息,例如客服、统计代码
  844. public function setExtend(Request $request): ?RedirectResponse
  845. {
  846. $websiteAnalytics = $request->input('website_analytics');
  847. $websiteCustomerService = $request->input('website_customer_service');
  848. try {
  849. DB::beginTransaction();
  850. // 首页LOGO
  851. if ($request->hasFile('website_home_logo')) {
  852. $ret = $this->uploadFile($request->file('website_home_logo'));
  853. if ( ! $ret) {
  854. Session::flash('errorMsg', 'LOGO不合法');
  855. return Redirect::back();
  856. }
  857. Config::find('website_home_logo')->update(['value' => $ret]);
  858. }
  859. // 站内LOGO
  860. if ($request->hasFile('website_logo')) {
  861. $ret = $this->uploadFile($request->file('website_logo'));
  862. if ( ! $ret) {
  863. Session::flash('errorMsg', 'LOGO不合法');
  864. return Redirect::back();
  865. }
  866. Config::find('website_logo')->update(['value' => $ret]);
  867. }
  868. Config::find('website_analytics')->update(
  869. ['value' => $websiteAnalytics]
  870. );
  871. Config::find('website_customer_service')->update(
  872. ['value' => $websiteCustomerService]
  873. );
  874. Session::flash('successMsg', '更新成功');
  875. DB::commit();
  876. return Redirect::back();
  877. } catch (Exception $e) {
  878. DB::rollBack();
  879. Session::flash('errorMsg', '更新失败');
  880. return Redirect::back();
  881. }
  882. }
  883. // 添加等级
  884. public function addLevel(Request $request): ?JsonResponse
  885. {
  886. $validator = Validator::make(
  887. $request->all(),
  888. [
  889. 'level' => 'required|numeric|unique:level,level',
  890. 'level_name' => 'required',
  891. ]
  892. );
  893. if ($validator->fails()) {
  894. return Response::json(
  895. ['status' => 'fail', 'message' => $validator->errors()->all()]
  896. );
  897. }
  898. $obj = new Level();
  899. $obj->level = $request->input('level');
  900. $obj->name = $request->input('level_name');
  901. $obj->save();
  902. if ($obj->id) {
  903. return Response::json(['status' => 'success', 'message' => '提交成功']);
  904. }
  905. return Response::json(['status' => 'fail', 'message' => '操作失败']);
  906. }
  907. // 编辑等级
  908. public function updateLevel(Request $request): JsonResponse
  909. {
  910. $id = $request->input('id');
  911. $level = $request->input('level');
  912. $validator = Validator::make(
  913. $request->all(),
  914. [
  915. 'id' => 'required|numeric',
  916. 'level' => 'required|numeric',
  917. 'level_name' => 'required',
  918. ]
  919. );
  920. if ($validator->fails()) {
  921. return Response::json(
  922. ['status' => 'fail', 'message' => $validator->errors()->all()]
  923. );
  924. }
  925. // 校验该等级下是否存在关联账号
  926. $levelCheck = Level::where('id', '<>', $id)->whereLevel($level)->exists(
  927. );
  928. if ($levelCheck) {
  929. return Response::json(['status' => 'fail', 'message' => '该等级已存在!']);
  930. }
  931. // 校验该等级下是否存在关联账号
  932. $userCount = User::whereLevel($level)->count();
  933. if ($userCount) {
  934. return Response::json(
  935. ['status' => 'fail', 'message' => '该等级下存在关联账号,请先取消关联!']
  936. );
  937. }
  938. Level::whereId($id)->update(
  939. ['level' => $level, 'name' => $request->input('level_name')]
  940. );
  941. return Response::json(['status' => 'success', 'message' => '操作成功']);
  942. }
  943. // 删除等级
  944. public function delLevel(Request $request): ?JsonResponse
  945. {
  946. $id = $request->input('id');
  947. $validator = Validator::make(
  948. $request->all(),
  949. [
  950. 'id' => 'required|numeric|exists:level,id',
  951. ]
  952. );
  953. if ($validator->fails()) {
  954. return Response::json(
  955. ['status' => 'fail', 'message' => $validator->errors()->all()]
  956. );
  957. }
  958. $level = Level::find($id);
  959. // 校验该等级下是否存在关联账号
  960. $userCount = User::whereLevel($level->level)->count();
  961. if ($userCount) {
  962. return Response::json(
  963. ['status' => 'fail', 'message' => '该等级下存在关联账号,请先取消关联']
  964. );
  965. }
  966. $ret = false;
  967. try {
  968. $ret = Level::whereId($id)->delete();
  969. } catch (Exception $e) {
  970. Log::error('删除等级时报错:' . $e->getMessage());
  971. }
  972. if ($ret) {
  973. return Response::json(['status' => 'success', 'message' => '操作成功']);
  974. }
  975. return Response::json(['status' => 'fail', 'message' => '操作失败']);
  976. }
  977. // 添加国家/地区
  978. public function addCountry(Request $request): JsonResponse
  979. {
  980. $code = $request->input('code');
  981. $name = $request->input('name');
  982. if (empty($code)) {
  983. return Response::json(
  984. ['status' => 'fail', 'message' => '国家/地区代码不能为空']
  985. );
  986. }
  987. if (empty($name)) {
  988. return Response::json(
  989. ['status' => 'fail', 'message' => '国家/地区名称不能为空']
  990. );
  991. }
  992. $exists = Country::find($code);
  993. if ($exists) {
  994. return Response::json(
  995. ['status' => 'fail', 'message' => '该国家/地区名称已存在,请勿重复添加']
  996. );
  997. }
  998. $obj = new Country();
  999. $obj->code = $code;
  1000. $obj->name = $name;
  1001. if ($obj->save()) {
  1002. return Response::json(['status' => 'success', 'message' => '提交成功']);
  1003. }
  1004. return Response::json(['status' => 'fail', 'message' => '操作失败']);
  1005. }
  1006. // 编辑国家/地区
  1007. public function updateCountry(Request $request): JsonResponse
  1008. {
  1009. $code = $request->input('code');
  1010. $name = $request->input('name');
  1011. if (empty($name)) {
  1012. return Response::json(
  1013. ['status' => 'fail', 'message' => '国家/地区名称不能为空']
  1014. );
  1015. }
  1016. if (empty($code)) {
  1017. return Response::json(
  1018. ['status' => 'fail', 'message' => '国家/地区代码不能为空']
  1019. );
  1020. }
  1021. $country = Country::find($code);
  1022. if ( ! $country) {
  1023. return Response::json(
  1024. ['status' => 'fail', 'message' => '国家/地区不存在']
  1025. );
  1026. }
  1027. // 校验该国家/地区下是否存在关联节点
  1028. if (Node::whereCountryCode($country->code)->exists()) {
  1029. return Response::json(
  1030. ['status' => 'fail', 'message' => '该国家/地区下存在关联节点,请先取消关联']
  1031. );
  1032. }
  1033. $ret = $country->update(['name' => $name, 'code' => $code]);
  1034. if ($ret) {
  1035. return Response::json(['status' => 'success', 'message' => '操作成功']);
  1036. }
  1037. return Response::json(['status' => 'fail', 'message' => '操作失败']);
  1038. }
  1039. // 删除国家/地区
  1040. public function delCountry(Request $request): ?JsonResponse
  1041. {
  1042. $code = $request->input('code');
  1043. if (empty($id)) {
  1044. return Response::json(['status' => 'fail', 'message' => 'ID不能为空']);
  1045. }
  1046. $country = Country::find($code);
  1047. if ( ! $country) {
  1048. return Response::json(
  1049. ['status' => 'fail', 'message' => '国家/地区不存在']
  1050. );
  1051. }
  1052. // 校验该国家/地区下是否存在关联节点
  1053. if (Node::whereCountryCode($country->code)->exists()) {
  1054. return Response::json(
  1055. ['status' => 'fail', 'message' => '该国家/地区下存在关联节点,请先取消关联']
  1056. );
  1057. }
  1058. if ($country->delete()) {
  1059. return Response::json(['status' => 'success', 'message' => '操作成功']);
  1060. }
  1061. return Response::json(['status' => 'fail', 'message' => '操作失败']);
  1062. }
  1063. // 系统设置
  1064. public function system()
  1065. {
  1066. $view = Config::pluck('value', 'name')->toArray();
  1067. $view['labelList'] = Label::orderByDesc('sort')->orderBy('id')->get();
  1068. return view('admin.config.system', $view);
  1069. }
  1070. // 设置某个配置项
  1071. public function setConfig(Request $request): JsonResponse
  1072. {
  1073. $name = $request->input('name');
  1074. $value = $request->input('value');
  1075. if ( ! $name) {
  1076. return Response::json(
  1077. ['status' => 'fail', 'message' => '设置失败:请求参数异常']
  1078. );
  1079. }
  1080. // 屏蔽异常配置
  1081. if ( ! in_array($name, Config::pluck('name')->toArray())) {
  1082. return Response::json(
  1083. ['status' => 'fail', 'message' => '设置失败:配置不存在']
  1084. );
  1085. }
  1086. // 如果开启用户邮件重置密码,则先设置网站名称和网址
  1087. if ($value !== '0'
  1088. && in_array(
  1089. $name,
  1090. [
  1091. 'is_reset_password',
  1092. 'is_activate_account',
  1093. 'expire_warning',
  1094. 'traffic_warning',
  1095. ],
  1096. true
  1097. )) {
  1098. $config = Config::find('website_name');
  1099. if ( ! $config->value) {
  1100. return Response::json(
  1101. ['status' => 'fail', 'message' => '设置失败:启用该配置需要先设置【网站名称】']
  1102. );
  1103. }
  1104. $config = Config::find('website_url');
  1105. if ( ! $config->value) {
  1106. return Response::json(
  1107. ['status' => 'fail', 'message' => '设置失败:启用该配置需要先设置【网站地址】']
  1108. );
  1109. }
  1110. }
  1111. // 支付设置判断
  1112. if ($value !== null && in_array(
  1113. $name,
  1114. [
  1115. 'is_AliPay',
  1116. 'is_QQPay',
  1117. 'is_WeChatPay',
  1118. 'is_otherPay',
  1119. ],
  1120. true
  1121. )) {
  1122. switch ($value) {
  1123. case 'f2fpay':
  1124. if ( ! sysConfig('f2fpay_app_id') || ! sysConfig(
  1125. 'f2fpay_private_key'
  1126. ) || ! sysConfig('f2fpay_public_key')) {
  1127. return Response::json(
  1128. [
  1129. 'status' => 'fail',
  1130. 'message' => '请先设置【支付宝F2F】必要参数',
  1131. ]
  1132. );
  1133. }
  1134. break;
  1135. case 'codepay':
  1136. if ( ! sysConfig('codepay_url') || ! sysConfig(
  1137. 'codepay_id'
  1138. ) || ! sysConfig('codepay_key')) {
  1139. return Response::json(
  1140. ['status' => 'fail', 'message' => '请先设置【码支付】必要参数']
  1141. );
  1142. }
  1143. break;
  1144. case 'epay':
  1145. if ( ! sysConfig('epay_url') || ! sysConfig(
  1146. 'epay_mch_id'
  1147. ) || ! sysConfig('epay_key')) {
  1148. return Response::json(
  1149. ['status' => 'fail', 'message' => '请先设置【易支付】必要参数']
  1150. );
  1151. }
  1152. break;
  1153. case 'payjs':
  1154. if ( ! sysConfig('payjs_mch_id') || ! sysConfig(
  1155. 'payjs_key'
  1156. )) {
  1157. return Response::json(
  1158. ['status' => 'fail', 'message' => '请先设置【PayJs】必要参数']
  1159. );
  1160. }
  1161. break;
  1162. case 'bitpayx':
  1163. if ( ! sysConfig('bitpay_secret')) {
  1164. return Response::json(
  1165. ['status' => 'fail', 'message' => '请先设置【麻瓜宝】必要参数']
  1166. );
  1167. }
  1168. break;
  1169. case 'paypal':
  1170. if ( ! sysConfig('paypal_username') || ! sysConfig(
  1171. 'paypal_password'
  1172. ) || ! sysConfig('paypal_secret')) {
  1173. return Response::json(
  1174. [
  1175. 'status' => 'fail',
  1176. 'message' => '请先设置【PayPal】必要参数',
  1177. ]
  1178. );
  1179. }
  1180. break;
  1181. default:
  1182. return Response::json(
  1183. ['status' => 'fail', 'message' => '未知支付渠道']
  1184. );
  1185. }
  1186. }
  1187. // 演示环境禁止修改特定配置项
  1188. if (env('APP_DEMO')) {
  1189. $denyConfig = [
  1190. 'website_url',
  1191. 'min_rand_traffic',
  1192. 'max_rand_traffic',
  1193. 'push_bear_send_key',
  1194. 'push_bear_qrcode',
  1195. 'is_forbid_china',
  1196. 'website_security_code',
  1197. ];
  1198. if (in_array($name, $denyConfig, true)) {
  1199. return Response::json(
  1200. ['status' => 'fail', 'message' => '演示环境禁止修改该配置']
  1201. );
  1202. }
  1203. }
  1204. // 如果是返利比例,则需要除100
  1205. if ($name === 'referral_percent') {
  1206. $value = (int)$value / 100;
  1207. }
  1208. // 更新配置
  1209. Config::find($name)->update(['value' => $value]);
  1210. return Response::json(['status' => 'success', 'message' => '操作成功']);
  1211. }
  1212. // 推送通知测试
  1213. public function sendTestNotification(): JsonResponse
  1214. {
  1215. if (sysConfig('is_notification')) {
  1216. $result = PushNotification::send('这是测试的标题', 'ProxyPanel测试内容');
  1217. if ($result === false) {
  1218. return Response::json(
  1219. ['status' => 'fail', 'message' => '发送失败,请重新尝试!']
  1220. );
  1221. }
  1222. switch (sysConfig('is_notification')) {
  1223. case 'serverChan':
  1224. if ( ! $result['errno']) {
  1225. return Response::json(
  1226. [
  1227. 'status' => 'success',
  1228. 'message' => '发送成功,请查看手机是否收到推送消息',
  1229. ]
  1230. );
  1231. }
  1232. return Response::json(
  1233. [
  1234. 'status' => 'fail',
  1235. 'message' => $result ? $result['errmsg'] : '未知',
  1236. ]
  1237. );
  1238. case 'bark':
  1239. if ($result['code'] == 200) {
  1240. return Response::json(
  1241. [
  1242. 'status' => 'success',
  1243. 'message' => '发送成功,请查看手机是否收到推送消息',
  1244. ]
  1245. );
  1246. }
  1247. return Response::json(
  1248. ['status' => 'fail', 'message' => $result['message']]
  1249. );
  1250. default:
  1251. }
  1252. }
  1253. return Response::json(
  1254. ['status' => 'fail', 'message' => '请先选择【日志通知】渠道']
  1255. );
  1256. }
  1257. // 邀请码列表
  1258. public function inviteList(Request $request)
  1259. {
  1260. $view['inviteList'] = Invite::with(
  1261. ['invitee:id,email', 'inviter:id,email']
  1262. )
  1263. ->orderBy('status')
  1264. ->latest()
  1265. ->paginate(15)
  1266. ->appends($request->except('page'));
  1267. return view('admin.inviteList', $view);
  1268. }
  1269. // 生成邀请码
  1270. public function makeInvite(): JsonResponse
  1271. {
  1272. for ($i = 0; $i < 10; $i++) {
  1273. $obj = new Invite();
  1274. $obj->inviter_id = 0;
  1275. $obj->invitee_id = 0;
  1276. $obj->code = strtoupper(
  1277. substr(md5(microtime() . Str::random(6)), 8, 12)
  1278. );
  1279. $obj->status = 0;
  1280. $obj->dateline = date(
  1281. 'Y-m-d H:i:s',
  1282. strtotime(
  1283. "+" . sysConfig(
  1284. 'admin_invite_days'
  1285. ) . " days"
  1286. )
  1287. );
  1288. $obj->save();
  1289. }
  1290. return Response::json(['status' => 'success', 'message' => '生成成功']);
  1291. }
  1292. // 导出邀请码
  1293. public function exportInvite(): void
  1294. {
  1295. $inviteList = Invite::whereStatus(0)->orderBy('id')->get();
  1296. $filename = '邀请码' . date('Ymd') . '.xlsx';
  1297. $spreadsheet = new Spreadsheet();
  1298. $spreadsheet->getProperties()
  1299. ->setCreator('ProxyPanel')
  1300. ->setLastModifiedBy('ProxyPanel')
  1301. ->setTitle('邀请码')
  1302. ->setSubject('邀请码')
  1303. ->setDescription('')
  1304. ->setKeywords('')
  1305. ->setCategory('');
  1306. try {
  1307. $spreadsheet->setActiveSheetIndex(0);
  1308. $sheet = $spreadsheet->getActiveSheet();
  1309. $sheet->setTitle('邀请码');
  1310. $sheet->fromArray(['邀请码', '有效期'], null);
  1311. foreach ($inviteList as $k => $vo) {
  1312. $sheet->fromArray(
  1313. [$vo->code, $vo->dateline],
  1314. null,
  1315. 'A' . ($k + 2)
  1316. );
  1317. }
  1318. header(
  1319. 'Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  1320. ); // 输出07Excel文件
  1321. //header('Content-Type:application/vnd.ms-excel'); // 输出Excel03版本文件
  1322. header(
  1323. 'Content-Disposition: attachment;filename="' . $filename . '"'
  1324. );
  1325. header('Cache-Control: max-age=0');
  1326. $writer = new Xlsx($spreadsheet);
  1327. $writer->save('php://output');
  1328. } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
  1329. Log::error('导出优惠券时报错' . $e->getMessage());
  1330. }
  1331. }
  1332. // 订单列表
  1333. public function orderList(Request $request)
  1334. {
  1335. $email = $request->input('email');
  1336. $order_sn = $request->input('order_sn');
  1337. $is_coupon = $request->input('is_coupon');
  1338. $is_expire = $request->input('is_expire');
  1339. $pay_way = $request->input('pay_way');
  1340. $status = $request->input('status');
  1341. $range_time = $request->input('range_time');
  1342. $sort = $request->input('sort'); // 0-按创建时间降序、1-按创建时间升序
  1343. $order_id = $request->input('id');
  1344. $query = Order::with(
  1345. ['user:id,email', 'goods:id,name', 'coupon:id,name,sn']
  1346. );
  1347. if (isset($email)) {
  1348. $query->whereHas(
  1349. 'user',
  1350. static function ($q) use ($email) {
  1351. $q->where('email', 'like', '%' . $email . '%');
  1352. }
  1353. );
  1354. }
  1355. if (isset($order_sn)) {
  1356. $query->where('order_sn', 'like', '%' . $order_sn . '%');
  1357. }
  1358. if (isset($is_coupon)) {
  1359. if ($is_coupon) {
  1360. $query->where('coupon_id', '<>', 0);
  1361. } else {
  1362. $query->whereCouponId(0);
  1363. }
  1364. }
  1365. if (isset($is_expire)) {
  1366. $query->whereIsExpire($is_expire);
  1367. }
  1368. if (isset($pay_way)) {
  1369. $query->wherePayWay($pay_way);
  1370. }
  1371. if (isset($status)) {
  1372. $query->whereStatus($status);
  1373. }
  1374. if (isset($range_time) && $range_time !== ',') {
  1375. $range_time = explode(',', $range_time);
  1376. $query->where('created_at', '>=', $range_time[0])->where(
  1377. 'created_at',
  1378. '<=',
  1379. $range_time[1]
  1380. );
  1381. }
  1382. if (isset($order_id)) {
  1383. $query->whereId($order_id);
  1384. }
  1385. if ($sort) {
  1386. $query->orderBy('id');
  1387. } else {
  1388. $query->orderByDesc('id');
  1389. }
  1390. $view['orderList'] = $query->paginate(15)->appends(
  1391. $request->except('page')
  1392. );
  1393. return view('admin.logs.orderList', $view);
  1394. }
  1395. // 重置用户流量
  1396. public function resetUserTraffic(Request $request): JsonResponse
  1397. {
  1398. User::find($request->input('id'))->update(['u' => 0, 'd' => 0]);
  1399. return Response::json(['status' => 'success', 'message' => '操作成功']);
  1400. }
  1401. // 操作用户余额
  1402. public function handleUserCredit(Request $request)
  1403. {
  1404. if ($request->isMethod('POST')) {
  1405. $userId = $request->input('user_id');
  1406. $amount = $request->input('amount');
  1407. if (empty($userId) || empty($amount)) {
  1408. return Response::json(
  1409. ['status' => 'fail', 'message' => '充值异常']
  1410. );
  1411. }
  1412. $user = User::find($userId);
  1413. // 写入余额变动日志
  1414. Helpers::addUserCreditLog(
  1415. $userId,
  1416. 0,
  1417. $user->credit,
  1418. $user->credit + $amount,
  1419. $amount,
  1420. '后台手动充值'
  1421. );
  1422. // 加减余额
  1423. if ((new UserService($user))->updateCredit($amount)) {
  1424. return Response::json(
  1425. ['status' => 'success', 'message' => '充值成功']
  1426. );
  1427. }
  1428. return Response::json(['status' => 'fail', 'message' => '充值失败']);
  1429. }
  1430. return view('admin.handleUserCredit');
  1431. }
  1432. // 用户余额变动记录
  1433. public function userCreditLogList(Request $request)
  1434. {
  1435. $email = $request->input('email');
  1436. $query = UserCreditLog::with('user:id,email')->latest();
  1437. if (isset($email)) {
  1438. $query->whereHas(
  1439. 'user',
  1440. static function ($q) use ($email) {
  1441. $q->where('email', 'like', '%' . $email . '%');
  1442. }
  1443. );
  1444. }
  1445. $view['list'] = $query->paginate(15)->appends($request->except('page'));
  1446. return view('admin.logs.userCreditLogList', $view);
  1447. }
  1448. // 用户封禁记录
  1449. public function userBanLogList(Request $request)
  1450. {
  1451. $email = $request->input('email');
  1452. $query = UserBanedLog::with('user:id,email,t')->latest();
  1453. if (isset($email)) {
  1454. $query->whereHas(
  1455. 'user',
  1456. static function ($q) use ($email) {
  1457. $q->where('email', 'like', '%' . $email . '%');
  1458. }
  1459. );
  1460. }
  1461. $view['list'] = $query->paginate(15)->appends($request->except('page'));
  1462. return view('admin.logs.userBanLogList', $view);
  1463. }
  1464. // 用户流量变动记录
  1465. public function userTrafficLogList(Request $request)
  1466. {
  1467. $email = $request->input('email');
  1468. $query = UserDataModifyLog::with(
  1469. ['user:id,email', 'order.goods:id,name']
  1470. );
  1471. if (isset($email)) {
  1472. $query->whereHas(
  1473. 'user',
  1474. static function ($q) use ($email) {
  1475. $q->where('email', 'like', '%' . $email . '%');
  1476. }
  1477. );
  1478. }
  1479. $view['list'] = $query->latest()->paginate(15)->appends(
  1480. $request->except('page')
  1481. );
  1482. return view('admin.logs.userTrafficLogList', $view);
  1483. }
  1484. // 用户在线IP记录
  1485. public function userOnlineIPList(Request $request)
  1486. {
  1487. $email = $request->input('email');
  1488. $port = $request->input('port');
  1489. $wechat = $request->input('wechat');
  1490. $qq = $request->input('qq');
  1491. $query = User::activeUser();
  1492. if (isset($email)) {
  1493. $query->where('email', 'like', '%' . $email . '%');
  1494. }
  1495. if (isset($wechat)) {
  1496. $query->where('wechat', 'like', '%' . $wechat . '%');
  1497. }
  1498. if (isset($qq)) {
  1499. $query->where('qq', 'like', '%' . $qq . '%');
  1500. }
  1501. if (isset($port)) {
  1502. $query->wherePort($port);
  1503. }
  1504. $userList = $query->paginate(15)->appends($request->except('page'));
  1505. $nodeOnlineIPs = NodeOnlineUserIp::with('node:id,name')
  1506. ->where(
  1507. 'created_at',
  1508. '>=',
  1509. strtotime("-10 minutes")
  1510. )
  1511. ->latest()
  1512. ->distinct();
  1513. // Todo 优化查询
  1514. foreach ($userList as $user) {
  1515. // 最近5条在线IP记录,如果后端设置为60秒上报一次,则为10分钟内的在线IP
  1516. $user->onlineIPList = $nodeOnlineIPs->wherePort($user->port)->limit(
  1517. 5
  1518. )->get();
  1519. }
  1520. $view['userList'] = $userList;
  1521. return view('admin.logs.userOnlineIPList', $view);
  1522. }
  1523. // 转换成某个用户的身份
  1524. public function switchToUser(Request $request): JsonResponse
  1525. {
  1526. $id = $request->input('user_id');
  1527. $user = User::find($id);
  1528. if ( ! $user) {
  1529. return Response::json(['status' => 'fail', 'message' => "用户不存在"]);
  1530. }
  1531. // 存储当前管理员ID,并将当前登录信息改成要切换的用户的身份信息
  1532. Session::put('admin', Auth::id());
  1533. Auth::login($user);
  1534. return Response::json(['status' => 'success', 'message' => "身份切换成功"]);
  1535. }
  1536. // 添加标签
  1537. public function addLabel(Request $request)
  1538. {
  1539. if ($request->isMethod('POST')) {
  1540. $name = $request->input('name');
  1541. $sort = $request->input('sort');
  1542. $label = new Label();
  1543. $label->name = $name;
  1544. $label->sort = $sort;
  1545. $label->save();
  1546. return Response::json(['status' => 'success', 'message' => '添加成功']);
  1547. }
  1548. return view('admin.label.addLabel');
  1549. }
  1550. // 编辑标签
  1551. public function editLabel(Request $request)
  1552. {
  1553. $id = $request->input('id');
  1554. if ($request->isMethod('POST')) {
  1555. $name = $request->input('name');
  1556. $sort = $request->input('sort');
  1557. Label::whereId($id)->update(['name' => $name, 'sort' => $sort]);
  1558. return Response::json(['status' => 'success', 'message' => '添加成功']);
  1559. }
  1560. $view['label'] = Label::find($id);
  1561. return view('admin.label.editLabel', $view);
  1562. }
  1563. // 删除标签
  1564. public function delLabel(Request $request): ?JsonResponse
  1565. {
  1566. $id = $request->input('id');
  1567. try {
  1568. DB::beginTransaction();
  1569. Label::whereId($id)->delete();
  1570. NodeLabel::whereLabelId($id)->delete(); // 删除节点关联
  1571. DB::commit();
  1572. return Response::json(['status' => 'success', 'message' => '删除成功']);
  1573. } catch (Exception $e) {
  1574. DB::rollBack();
  1575. return Response::json(
  1576. ['status' => 'fail', 'message' => '删除失败:' . $e->getMessage()]
  1577. );
  1578. }
  1579. }
  1580. // 邮件发送日志列表
  1581. public function notificationLog(Request $request)
  1582. {
  1583. $email = $request->input('email');
  1584. $type = $request->input('type');
  1585. $query = NotificationLog::query();
  1586. if (isset($email)) {
  1587. $query->where('address', 'like', '%' . $email . '%');
  1588. }
  1589. if (isset($type)) {
  1590. $query->whereType($type);
  1591. }
  1592. $view['list'] = $query->latest()->paginate(15)->appends(
  1593. $request->except('page')
  1594. );
  1595. return view('admin.logs.notificationLog', $view);
  1596. }
  1597. // 在线IP监控(实时)
  1598. public function onlineIPMonitor(Request $request)
  1599. {
  1600. $ip = $request->input('ip');
  1601. $email = $request->input('email');
  1602. $port = $request->input('port');
  1603. $nodeId = $request->input('nodeId');
  1604. $userId = $request->input('id');
  1605. $query = NodeOnlineUserIp::with(['node:id,name', 'user:id,email'])
  1606. ->where(
  1607. 'created_at',
  1608. '>=',
  1609. strtotime("-2 minutes")
  1610. );
  1611. if (isset($ip)) {
  1612. $query->whereIp($ip);
  1613. }
  1614. if (isset($email)) {
  1615. $query->whereHas(
  1616. 'user',
  1617. static function ($q) use ($email) {
  1618. $q->where('email', 'like', '%' . $email . '%');
  1619. }
  1620. );
  1621. }
  1622. if (isset($port)) {
  1623. $query->whereHas(
  1624. 'user',
  1625. static function ($q) use ($port) {
  1626. $q->wherePort($port);
  1627. }
  1628. );
  1629. }
  1630. if (isset($nodeId)) {
  1631. $query->whereHas(
  1632. 'node',
  1633. static function ($q) use ($nodeId) {
  1634. $q->whereId($nodeId);
  1635. }
  1636. );
  1637. }
  1638. if (isset($userId)) {
  1639. $query->whereHas(
  1640. 'user',
  1641. static function ($q) use ($userId) {
  1642. $q->whereId($userId);
  1643. }
  1644. );
  1645. }
  1646. $onlineIPLogs = $query->groupBy('user_id', 'node_id')
  1647. ->latest()
  1648. ->paginate(20)
  1649. ->appends($request->except('page'));
  1650. foreach ($onlineIPLogs as $log) {
  1651. // 跳过上报多IP的
  1652. if ($log->ip == null || strpos($log->ip, ',') !== false) {
  1653. continue;
  1654. }
  1655. $ipInfo = QQWry::ip($log->ip);
  1656. if (isset($ipInfo['error'])) {
  1657. // 用IPIP的库再试一下
  1658. $ipip = IPIP::ip($log->ip);
  1659. $ipInfo = [
  1660. 'country' => $ipip['country_name'],
  1661. 'province' => $ipip['region_name'],
  1662. 'city' => $ipip['city_name'],
  1663. ];
  1664. }
  1665. $log->ipInfo = $ipInfo['country'] . ' ' . $ipInfo['province'] . ' ' . $ipInfo['city'];
  1666. }
  1667. $view['list'] = $onlineIPLogs;
  1668. $view['nodeList'] = Node::whereStatus(1)
  1669. ->orderByDesc('sort')
  1670. ->latest()
  1671. ->get();
  1672. return view('admin.logs.onlineIPMonitor', $view);
  1673. }
  1674. }