Setting.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. <?php
  2. /**
  3. * 配置管理
  4. *
  5. */
  6. namespace app\admin\controller;
  7. use app\common\library\FileUpload;
  8. use app\common\model\PaySetting;
  9. use app\common\model\PayWarningRule;
  10. use think\Db;
  11. use think\Exception;
  12. class Setting extends Admin
  13. {
  14. protected function _initialize()
  15. {
  16. parent::_initialize();
  17. }
  18. /**
  19. * 配置列表
  20. * @return mixed
  21. * @throws \think\exception\DbException
  22. */
  23. public function index()
  24. {
  25. $condition = [];
  26. $title = $this->request->get('title', '', 'trim');
  27. $name = $this->request->get('name', '', 'trim');
  28. !empty($title) && $condition['title'] = ['like', "%{$title}%"];
  29. !empty($name) && $condition['name'] = $name;
  30. $settingModel = model('Common/Setting');
  31. $settingList = $settingModel->where($condition)->order('id desc')->paginate(20);
  32. // 拦截超长配置或特殊白名单配置的展示
  33. $settingList->each(function ($item, $key) {
  34. if ($item['name'] == 'game_channel_by_mobild_whitelist' || mb_strlen((string)$item['value']) > 100) {
  35. $displayValue = '-';
  36. // 解析白名单配置的JSON数据
  37. if ($item['name'] == 'game_channel_by_mobild_whitelist') {
  38. $configData = json_decode($item['value'], true);
  39. if ($configData && is_array($configData)) {
  40. $displayParts = [];
  41. // 游戏显示
  42. if (!empty($configData['game_ids'])) {
  43. if ($configData['game_ids'] === 'all') {
  44. $displayParts[] = '游戏: 全部';
  45. } else {
  46. $gameIds = array_slice(explode(',', $configData['game_ids']), 0, 3);
  47. $gameNames = Db::name('cy_game')
  48. ->where(['id' => ['in', $gameIds]])
  49. ->column('name');
  50. if (!empty($gameNames)) {
  51. $displayParts[] = '游戏: ' . implode('、', $gameNames) . '...';
  52. }
  53. }
  54. }
  55. // 渠道显示
  56. if (!empty($configData['channel_ids'])) {
  57. if ($configData['channel_ids'] === 'all') {
  58. $displayParts[] = '渠道: 全部';
  59. } else {
  60. $channelIds = array_slice(explode(',', $configData['channel_ids']), 0, 3);
  61. $channelNames = Db::name('nw_channel')
  62. ->where(['id' => ['in', $channelIds]])
  63. ->column('name');
  64. if (!empty($channelNames)) {
  65. $displayParts[] = '渠道: ' . implode('、', $channelNames) . '...';
  66. }
  67. }
  68. }
  69. if (!empty($displayParts)) {
  70. $displayValue = implode('<br>', $displayParts);
  71. }
  72. }
  73. } else {
  74. // 超长配置,默认截取前100个字符,多的用省略号
  75. $displayValue = mb_substr((string)$item['value'], 0, 100) . '...';
  76. }
  77. $item['value'] = $displayValue;
  78. }
  79. return $item;
  80. });
  81. $this->assign('list', $settingList);
  82. $this->assign('title', $title);
  83. $this->assign('name', $name);
  84. $this->assign('page', $settingList->render());
  85. return $this->fetch();
  86. }
  87. /**
  88. * 新增配置
  89. * @return mixed
  90. */
  91. public function add()
  92. {
  93. if ($this->request->isPost()) {
  94. $data = $this->request->param();
  95. // 针对白名单配置单独组装 JSON
  96. if (isset($data['name']) && $data['name'] == 'game_channel_by_mobild_whitelist') {
  97. $gameIds = isset($data['game_ids']) ? $data['game_ids'] : '';
  98. $channelIds = isset($data['channel_ids']) ? $data['channel_ids'] : '';
  99. $data['value'] = json_encode([
  100. 'game_ids' => $gameIds,
  101. 'channel_ids' => $channelIds,
  102. 'status' => 1
  103. ], JSON_UNESCAPED_UNICODE);
  104. // 删除不在数据库表中的字段
  105. unset($data['game_ids']);
  106. unset($data['channel_ids']);
  107. // 删除 xm-select 组件可能生成的字段
  108. unset($data['select']);
  109. }
  110. if (true !== ($res = $this->validate($data, 'Setting'))) {
  111. $this->error($res);
  112. }
  113. $settingModel = model('Common/Setting');
  114. if ($settingModel->where(['name' => input('post.name', '', 'trim')])->find()) {
  115. $this->error('配置项已经存在,不能重复添加');
  116. }
  117. if ($settingModel->allowField(true)->save($data)) {
  118. $this->success('添加成功');
  119. }
  120. $this->error($settingModel->getError() ?: '添加失败');
  121. }
  122. return $this->fetch();
  123. }
  124. /**
  125. * 编辑配置
  126. */
  127. public function edit()
  128. {
  129. $data = $this->checkData();
  130. if ($this->request->isPost()) {
  131. $data = $this->request->param();
  132. // 确保 id 字段存在
  133. if (!isset($data['id']) || empty($data['id'])) {
  134. $this->error('缺少ID参数');
  135. }
  136. // 针对白名单配置单独组装 JSON
  137. if (isset($data['name']) && $data['name'] == 'game_channel_by_mobild_whitelist') {
  138. $gameIds = isset($data['game_ids']) ? $data['game_ids'] : '';
  139. $channelIds = isset($data['channel_ids']) ? $data['channel_ids'] : '';
  140. $data['value'] = json_encode([
  141. 'game_ids' => $gameIds,
  142. 'channel_ids' => $channelIds,
  143. ], JSON_UNESCAPED_UNICODE);
  144. // 删除不在数据库表中的字段
  145. unset($data['game_ids']);
  146. unset($data['channel_ids']);
  147. // 删除 xm-select 组件可能生成的字段
  148. unset($data['select']);
  149. unset($data['game_select']);
  150. unset($data['channel_select']);
  151. // 删除所有可能的xm-select生成的字段
  152. foreach ($data as $key => $value) {
  153. if (strpos($key, 'select') !== false || strpos($key, 'xm') !== false) {
  154. unset($data[$key]);
  155. }
  156. }
  157. }
  158. if (true !== ($res = $this->validate($data, 'Setting'))) {
  159. $this->error($res);
  160. }
  161. $settingModel = model('Common/Setting');
  162. if ($settingModel->where(['name' => $data['name'], 'id' => ['<>', $data['id']]])->find()) {
  163. $this->error('配置项已经存在,不能重复配置');
  164. }
  165. $result = $settingModel->allowField(true)->save($data, ['id' => $data['id']]);
  166. if ($result !== false) {
  167. $this->success('编辑成功', url('setting/index'));
  168. }
  169. $this->error('编辑失败或未编辑;' . $settingModel->getError());
  170. }
  171. // 如果是白名单配置,单独准备前端联动组件需要的数据
  172. if ($data['name'] == 'game_channel_by_mobild_whitelist') {
  173. // 获取游戏数据(按创建时间倒序)
  174. $gameList = \think\Db::name('cy_game')
  175. ->where(['isdelete' => 0])
  176. ->field('id as value, name')
  177. ->order('create_time', 'desc')
  178. ->select();
  179. $this->assign('gameList', json_encode($gameList, JSON_UNESCAPED_UNICODE));
  180. // 获取完整渠道树(包含所有层级,用于搜索功能)
  181. $tree = \app\common\library\ChannelUtil::getFullChannelTree();
  182. $this->assign('channelTree', json_encode($tree, JSON_UNESCAPED_UNICODE));
  183. // 获取一级渠道列表(用于判断是否一级全选)
  184. $channelList = \think\Db::name('nw_channel')
  185. ->where(['parent_id' => 0, 'status' => 1])
  186. ->field('id as value, name')
  187. ->order('id', 'asc')
  188. ->select();
  189. // 解析当前拥有的配置,传给前端用于初始选中
  190. $currentConfig = json_decode($data['value'], true);
  191. $gameIds = isset($currentConfig['game_ids']) ? $currentConfig['game_ids'] : '';
  192. $channelIds = isset($currentConfig['channel_ids']) ? $currentConfig['channel_ids'] : '';
  193. $this->assign('gameIds', $gameIds);
  194. $this->assign('channelIds', $channelIds);
  195. // 获取已选中的渠道的详细信息,用于前台渲染 initValue 标签
  196. $selectedChannels = [];
  197. if (!empty($channelIds) && $channelIds !== 'all') {
  198. $ids = explode(',', $channelIds);
  199. // 判断是否是一级全选模式(所有一级渠道ID都在选中列表中)
  200. $level1Ids = array_column($channelList, 'value');
  201. $isLevel1All = !empty(array_diff($level1Ids, array_map('intval', $ids))) === false;
  202. if ($isLevel1All) {
  203. // 一级全选模式,只返回一级渠道信息
  204. foreach ($channelList as $level1) {
  205. $selectedChannels[] = [
  206. 'name' => $level1['name'],
  207. 'value' => (int)$level1['value'],
  208. 'selected' => true
  209. ];
  210. }
  211. } else {
  212. // 部分选中模式,返回具体选中的渠道信息
  213. $selectedChannelList = \think\Db::name('nw_channel')
  214. ->where(['id' => ['in', $ids]])
  215. ->field('id as value, name, parent_id')
  216. ->select();
  217. foreach ($selectedChannelList as $sc) {
  218. $item = [
  219. 'name' => $sc['name'],
  220. 'value' => (int)$sc['value'],
  221. 'selected' => true
  222. ];
  223. // 检查是否有子节点,如果有则添加 children 属性(用于树形组件识别父节点)
  224. $hasChildren = \think\Db::name('nw_channel')
  225. ->where(['parent_id' => $sc['value'], 'status' => 1])
  226. ->count() > 0;
  227. if ($hasChildren) {
  228. $item['children'] = [];
  229. }
  230. $selectedChannels[] = $item;
  231. }
  232. }
  233. }
  234. $this->assign('selectedChannels', json_encode($selectedChannels, JSON_UNESCAPED_UNICODE));
  235. }
  236. $this->assign('data', $data);
  237. return $this->fetch();
  238. }
  239. /**
  240. * 删除
  241. */
  242. public function delete()
  243. {
  244. $data = $this->checkData();
  245. if ($data->delete()) {
  246. $this->success('删除成功!');
  247. }
  248. $this->error('删除失败');
  249. }
  250. /**
  251. * 检查礼包信息
  252. * @return array|false|\PDOStatement|string|\think\Model
  253. * @throws \think\db\exception\DataNotFoundException
  254. * @throws \think\db\exception\ModelNotFoundException
  255. * @throws \think\exception\DbException
  256. */
  257. protected function checkData()
  258. {
  259. $id = $this->request->param('id', 0, 'intval');
  260. $settingModel = model('Common/Setting');
  261. $data = [];
  262. if (empty($id) || !($data = $settingModel->find($id))) {
  263. $this->error('参数错误,不存在该配置信息');
  264. }
  265. return $data;
  266. }
  267. /**
  268. * 平台币说明设置
  269. *
  270. */
  271. public function coinInstruction()
  272. {
  273. $instructionModel = model('Common/CoinInstruction');
  274. $instructionInfo = $instructionModel->find();
  275. //表单提交
  276. if ($this->request->isPost()) {
  277. $content = input('post.content');
  278. if ($content == '') {
  279. $this->error('平台币劵说明,不能为空');
  280. }
  281. if ($instructionInfo) {
  282. $instructionModel->allowField(true)->save(['content' => $content], ['id' => $instructionInfo['id']]);
  283. $this->success('提交成功');
  284. } else {
  285. if ($instructionModel->allowField(true)->save(['content' => $content])) {
  286. $this->success('提交成功');
  287. }
  288. }
  289. $this->error('提交失败');
  290. }
  291. $this->assign('data', $instructionInfo);
  292. return $this->fetch('coin_instruction');
  293. }
  294. /**
  295. * 安卓SDK版本配置
  296. * @return mixed
  297. */
  298. public function android()
  299. {
  300. $condition = [];
  301. $version = $this->request->get('version', '', 'trim');
  302. !empty($version) && $condition['version'] = ['like', "%{$version}%"];
  303. $sdkModel = model('Common/AndroidSdk');
  304. $configList = $sdkModel
  305. ->field(['id', 'version', 'num', 'filename', 'content', 'update_time', 'create_time'])
  306. ->where($condition)
  307. ->order('create_time', 'desc')
  308. ->paginate(10, false, ['query' => $condition]);
  309. // dump($configList->toArray());
  310. $this->assign('list', $configList);
  311. $this->assign('version', $version);
  312. $this->assign('page', $configList->render());
  313. return $this->fetch();
  314. }
  315. // 添加安卓sdk
  316. public function sdkAdd()
  317. {
  318. if ($this->request->isPost()) {
  319. $data = $this->request->param();
  320. if (true !== ($res = $this->validate($data, 'AndroidSdk.add'))) {
  321. $this->error($res);
  322. }
  323. //filename是完整URL时
  324. if (filter_var($data['filename'], FILTER_VALIDATE_URL)) {
  325. //去除掉域名部分
  326. $data['filename'] = str_replace(STATIC_DOMAIN, "", $data['filename']);
  327. }
  328. $sdkModel = model('Common/AndroidSdk');
  329. if ($sdkModel->allowField(true)->save($data)) {
  330. $this->success('添加成功', url('setting/android'));
  331. }
  332. $this->error($sdkModel->getError() ?: '添加失败');
  333. }
  334. return $this->fetch('sdk_add');
  335. }
  336. // 修改安卓SDK
  337. public function sdkEdit()
  338. {
  339. $id = $this->request->param('id', 0, 'intval');
  340. $sdkModel = model('Common/AndroidSdk');
  341. $data = [];
  342. if (empty($id) || !($data = $sdkModel->find($id))) {
  343. $this->error('参数错误,不存在该SDK信息');
  344. }
  345. if ($this->request->isPost()) {
  346. $data = $this->request->param();
  347. if (true !== ($res = $this->validate($data, 'AndroidSdk.edit'))) {
  348. $this->error($res);
  349. }
  350. //去除掉域名部分
  351. if ($data['filename']) {
  352. $data['filename'] = str_replace(STATIC_DOMAIN, "", $data['filename']);
  353. }
  354. if ($sdkModel->allowField(true)->save($data, ['id' => $data['id']])) {
  355. $this->success('编辑成功', url('setting/android'));
  356. }
  357. $this->error($sdkModel->getError() ?: '编辑失败');
  358. }
  359. $this->assign('data', $data);
  360. return $this->fetch('sdk_edit');
  361. }
  362. // 更新SDK:状态,时间
  363. /*
  364. public function sdkUpdate()
  365. {
  366. $id = $this->request->param('id', 0, 'intval');
  367. $sdkModel = model('Common/AndroidSdk');
  368. if (empty($id) || !($sdkModel->find($id))) {
  369. $this->error('参数错误,不存在该SDK信息');
  370. }
  371. $data = [
  372. 'status' => 1,
  373. 'update_status_time' => request()->time()
  374. ];
  375. if (true !== ($res = $this->validate($data, 'AndroidSdk.update'))) {
  376. $this->error($res);
  377. }
  378. if ($sdkModel->allowField(true)->save($data, ['id' => $id])) {
  379. $this->success('更新成功', url('setting/android'));
  380. }
  381. $this->error($sdkModel->getError() ?: '更新失败');
  382. }*/
  383. // 上传文件
  384. public function uploadFile()
  385. {
  386. $file = request()->file('file');
  387. if (!$file) {
  388. $this->error('上传文件不能为空! ');
  389. }
  390. $fileObject = new FileUpload();
  391. $fileObject->set('allowExt', 'jar'); // 设置允许上传类型
  392. $path = $fileObject->upload($file);
  393. if ($path) {
  394. $this->success('上传成功', null, ['filename' => $path]);
  395. } else {
  396. $this->error('上传失败! ' . $fileObject->getError());
  397. };
  398. }
  399. /**
  400. * 充值预警设置页
  401. *
  402. */
  403. public function pay()
  404. {
  405. $paySettingModel = new PaySetting;
  406. $payWarningRuleModel = new PayWarningRule;
  407. //配置信息
  408. $settingInfo = $paySettingModel->getPaySetting();
  409. //规则列表
  410. $rule_list = $payWarningRuleModel->getPaySetting();
  411. //外部渠道列表
  412. $channel_list = model('Common/Channel')->getAllByCondition('id,name', ['flag' => 3], 'name asc');
  413. $this->assign('channel_list', $channel_list);
  414. $this->assign('rule_list', $rule_list);
  415. $this->assign('settingInfo', $settingInfo);
  416. $this->assign('whitelist_channels', !empty($settingInfo['whitelist_channel']) ? explode(',', $settingInfo['whitelist_channel']) : []);
  417. return $this->fetch();
  418. }
  419. /**
  420. * 充值预警保存处理
  421. *
  422. */
  423. public function payPost()
  424. {
  425. if ($this->request->isAjax()) {
  426. $status = input('post.status');
  427. $whitelist_channel = input('post.whitelist_channel/a');
  428. $emails = input('post.emails/a');
  429. //以下为规则参数
  430. $warning_time = input('post.warning_time/a');
  431. $order_count = input('post.order_count/a');
  432. $min_amount = input('post.min_amount/a');
  433. $max_amount = input('post.max_amount/a');
  434. $is_six = input('post.is_six/a');
  435. $paySettingModel = new PaySetting;
  436. $payWarningRuleModel = new PayWarningRule;
  437. $validate = new \app\common\library\ValidateExtend();
  438. $result = $this->validate(
  439. [
  440. 'status' => input('post.status'),
  441. 'emails' => $emails,
  442. ],
  443. [
  444. ['status', 'require|integer', '请选择功能状态|功能状态必须为整型'],
  445. ['emails', 'require', '请填写收件人邮箱'],
  446. ]
  447. );
  448. if (true !== $result) {
  449. $this->error($result);
  450. } else {
  451. //邮箱格式验证
  452. foreach ($emails as $value) {
  453. if (!$validate->is($value, 'email')) {
  454. $this->error('收件人邮箱格式不正确');
  455. exit;
  456. }
  457. }
  458. $rule_count = count($warning_time);
  459. Db::startTrans();
  460. try {
  461. $payWarningRuleModel->where('id<>0')->delete();
  462. //有规则时
  463. if ($rule_count > 0) {
  464. $ruleData = [];
  465. for ($i = 0; $i < $rule_count; $i++) {
  466. $tmp = [];
  467. $tmp['warning_time'] = $warning_time[$i];
  468. $tmp['order_count'] = $order_count[$i];
  469. $tmp['min_amount'] = $min_amount[$i];
  470. $tmp['max_amount'] = $max_amount[$i];
  471. $tmp['is_six'] = $is_six[$i];
  472. //规则参数验证
  473. $ruleResult = $this->validate(
  474. $tmp,
  475. [
  476. ['warning_time', 'require|positiveInteger', '预警时间阈值不能为空|功能状态必须为整型'],
  477. ['order_count', 'require|positiveInteger', '连续充值成功订单阈值不能为空|连续充值成功订单阈值必须为整型'],
  478. ['min_amount', 'require|positiveInteger', '金额预警最小值不能为空|金额预警最小值必须为整型'],
  479. ['max_amount', 'require|positiveInteger|>:min_amount', '金额预警最大值不能为空|金额预警最大值必须为整型|金额预警最大值须大于最小值'],
  480. ['is_six', 'require|integer', '请选择是否包括6元|是否包括6元必须为整型'],
  481. ]
  482. );
  483. if (true !== $ruleResult) {
  484. throw new Exception($ruleResult);
  485. }
  486. $tmp['create_time'] = NOW_TIMESTAMP;
  487. $ruleData[] = $tmp;
  488. }
  489. $payWarningRuleModel->saveAll($ruleData, false);
  490. }
  491. $settingData = ['status' => $status, 'emails' => implode(";", $emails), 'update_time' => NOW_TIMESTAMP];
  492. $settingData['whitelist_channel'] = !empty($whitelist_channel) ? implode(",", $whitelist_channel) : '';
  493. //保存充值预警配置表
  494. $paySettingModel->save($settingData, ['id' => 1]);
  495. Db::commit();
  496. } catch (\Exception $e) {
  497. Db::rollback();
  498. $this->error("充值预警设置失败;原因: " . $e->getMessage());
  499. }
  500. $this->success('充值预警设置成功');
  501. }
  502. } else {
  503. $this->error('非法请求');
  504. }
  505. }
  506. /**
  507. * 获取下级渠道数据(用于异步懒加载)
  508. */
  509. public function getChannels()
  510. {
  511. $parentId = $this->request->param('parent_id', 0, 'intval');
  512. $channelList = \think\Db::name('nw_channel')
  513. ->where(['parent_id' => $parentId, 'status' => 1])
  514. ->field('id as value, name, level')
  515. ->order('id', 'asc')
  516. ->select();
  517. $data = [];
  518. foreach ($channelList as $v) {
  519. // 基于 parent_id 检查是否有启用状态的下层渠道
  520. $hasChildren = \think\Db::name('nw_channel')
  521. ->where(['parent_id' => $v['value'], 'status' => 1])
  522. ->count() > 0;
  523. $data[] = [
  524. 'name' => $v['name'],
  525. 'value' => (int)$v['value'],
  526. 'children' => $hasChildren ? [] : null,
  527. ];
  528. }
  529. return json(['code' => 1, 'data' => $data]);
  530. }
  531. /**
  532. * 搜索渠道(支持远程搜索,用于懒加载树的搜索功能)
  533. */
  534. public function searchChannels()
  535. {
  536. $keyword = $this->request->param('keyword', '', 'trim');
  537. if (empty($keyword)) {
  538. return json(['code' => 1, 'data' => []]);
  539. }
  540. $channelList = Db::name('nw_channel')
  541. ->where('name', 'like', "%{$keyword}%")
  542. ->where('status', 1)
  543. ->field('id as value, name, parent_id')
  544. ->order('id', 'asc')
  545. ->limit(50)
  546. ->select();
  547. $data = [];
  548. foreach ($channelList as $v) {
  549. $hasChildren = Db::name('nw_channel')
  550. ->where(['parent_id' => $v['value'], 'status' => 1])
  551. ->count() > 0;
  552. $data[] = [
  553. 'name' => $v['name'],
  554. 'value' => (int)$v['value'],
  555. 'children' => $hasChildren ? [] : null,
  556. ];
  557. }
  558. return json(['code' => 1, 'data' => $data]);
  559. }
  560. }