User.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. <?php
  2. namespace app\admin\controller\userCenter;
  3. use app\common\controller\Backend;
  4. use think\facade\Db;
  5. class User extends Backend
  6. {
  7. // protected array $noNeedLogin = ['*'];
  8. /**
  9. * 用户列表
  10. * @return void
  11. * @throws \think\db\exception\DataNotFoundException
  12. * @throws \think\db\exception\DbException
  13. * @throws \think\db\exception\ModelNotFoundException
  14. */
  15. public function index(): void
  16. {
  17. $where = [];
  18. // 用户名搜索
  19. if (!empty($this->request->get('username'))) {
  20. $username = $this->request->get('username');
  21. $where[] = ['bu.username', 'like', '%' . $username . '%'];
  22. }
  23. // 昵称搜索
  24. if (!empty($this->request->get('nickname'))) {
  25. $nickname = $this->request->get('nickname');
  26. $where[] = ['bu.nickname', 'like', '%' . $nickname . '%'];
  27. }
  28. // 邮箱搜索
  29. if (!empty($this->request->get('email'))) {
  30. $email = $this->request->get('email');
  31. $where[] = ['bu.email', 'like', '%' . $email . '%'];
  32. }
  33. // 手机号搜索
  34. if (!empty($this->request->get('mobile'))) {
  35. $mobile = $this->request->get('mobile');
  36. $where[] = ['bu.mobile', 'like', '%' . $mobile . '%'];
  37. }
  38. // 状态筛选
  39. if ($this->request->has('status') && $this->request->get('status') !== '') {
  40. $status = $this->request->get('status');
  41. $where[] = ['bu.status', '=', $status];
  42. }
  43. // 企业搜索(通过企业成员表关联)
  44. if (!empty($this->request->get('company'))) {
  45. $company = $this->request->get('company');
  46. $companyIds = Db::name('ba_company')
  47. ->where('name', 'like', '%' . $company . '%')
  48. ->column('id');
  49. if (!empty($companyIds)) {
  50. $userIds = Db::name('ba_company_member')
  51. ->where('company_id', 'in', $companyIds)
  52. ->column('user_id');
  53. if (!empty($userIds)) {
  54. $where[] = ['bu.id', 'in', $userIds];
  55. } else {
  56. // 如果没有匹配的用户,返回空结果
  57. $where[] = ['bu.id', '=', 0];
  58. }
  59. } else {
  60. // 如果没有匹配的企业,返回空结果
  61. $where[] = ['bu.id', '=', 0];
  62. }
  63. }
  64. // 排序
  65. $order = [];
  66. if (!empty($this->request->get('order'))) {
  67. $orderParam = $this->request->get('order');
  68. $orderArray = explode(',', $orderParam);
  69. if (count($orderArray) === 2) {
  70. $order[$orderArray[0]] = $orderArray[1];
  71. }
  72. } else {
  73. $order['bu.id'] = 'desc';
  74. }
  75. // 分页
  76. $page = $this->request->get('page', 1);
  77. $limit = $this->request->get('limit', 20);
  78. // 查询用户列表
  79. $query = Db::table('ba_user')
  80. ->alias('bu')
  81. ->field([
  82. 'bu.id',
  83. 'bu.username',
  84. 'bu.nickname',
  85. 'bu.email',
  86. 'bu.mobile',
  87. 'bu.avatar',
  88. 'bu.gender',
  89. 'bu.status',
  90. 'bu.create_time',
  91. 'bu.last_login_time',
  92. 'bu.last_login_ip',
  93. 'bu.join_ip',
  94. 'bu.motto',
  95. 'bu.invitation_code',
  96. 'GROUP_CONCAT(DISTINCT bc.name SEPARATOR ", ") as company_names',
  97. 'GROUP_CONCAT(DISTINCT inviter.username SEPARATOR ", ") as inviter_names'
  98. ])
  99. ->leftJoin('ba_company_member bcm', 'bu.id = bcm.user_id')
  100. ->leftJoin('ba_company bc', 'bcm.company_id = bc.id AND bc.status = 1')
  101. ->leftJoin('ba_user_invitation bui', 'bu.id = bui.invitee_id')
  102. ->leftJoin('ba_user inviter', 'bui.inviter_id = inviter.id')
  103. ->where($where)
  104. ->group('bu.id')
  105. ->order($order);
  106. $userList = $query->paginate([
  107. 'list_rows' => $limit,
  108. 'page' => $page
  109. ]);
  110. // 处理时间戳字段
  111. $items = $userList->items();
  112. foreach ($items as &$item) {
  113. // 转换时间戳为日期时间格式
  114. if (!empty($item['create_time'])) {
  115. $item['create_time'] = date('Y-m-d H:i:s', $item['create_time']);
  116. }
  117. if (!empty($item['last_login_time'])) {
  118. $item['last_login_time'] = date('Y-m-d H:i:s', $item['last_login_time']);
  119. }
  120. if (!empty($item['join_time'])) {
  121. $item['join_time'] = date('Y-m-d H:i:s', $item['join_time']);
  122. }
  123. // 处理性别显示
  124. $genderMap = [
  125. 0 => '未知',
  126. 1 => '男',
  127. 2 => '女'
  128. ];
  129. $item['gender_text'] = $genderMap[$item['gender']] ?? '未知';
  130. // 处理状态显示
  131. if ($item['status'] === 'enable') {
  132. $item['status_text'] = '启用';
  133. } elseif ($item['status'] === 'disable') {
  134. $item['status_text'] = '禁用';
  135. } else {
  136. $item['status_text'] = $item['status'];
  137. }
  138. // 如果企业名为空,设置为空字符串
  139. if (empty($item['company_names'])) {
  140. $item['company_names'] = '';
  141. }
  142. // 如果邀请人为空,设置为空字符串
  143. if (empty($item['inviter_names'])) {
  144. $item['inviter_names'] = '';
  145. }
  146. }
  147. $this->success('查询成功', [
  148. 'list' => $items,
  149. 'total' => $userList->total()
  150. ]);
  151. }
  152. /**
  153. * 新增/编辑用户
  154. * @return void
  155. */
  156. public function edit():void
  157. {
  158. $id = $this->request->post('id');
  159. $username = $this->request->post('name');
  160. $email = $this->request->post('email');
  161. $password = $this->request->post('password');
  162. $company = $this->request->post('company');
  163. $company_id = $this->request->post('company_id');
  164. $status = $this->request->post('status', 1);
  165. if (empty($username) || empty($email)) {
  166. $this->error(__('用户名和邮箱不能为空'));
  167. }
  168. // 验证邮箱格式
  169. if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
  170. $this->error(__('邮箱格式不正确'));
  171. }
  172. // 检查用户名是否已存在(编辑时排除自己)
  173. $checkUsername = Db::name('user')
  174. ->where('username', $username);
  175. if (!empty($id)) {
  176. $checkUsername->where('id', '<>', $id);
  177. }
  178. if ($checkUsername->find()) {
  179. $this->error(__('用户名已存在'));
  180. }
  181. // 检查邮箱是否已存在(编辑时排除自己)
  182. $checkEmail = Db::name('user')
  183. ->where('email', $email);
  184. if (!empty($id)) {
  185. $checkEmail->where('id', '<>', $id);
  186. }
  187. if ($checkEmail->find()) {
  188. $this->error(__('邮箱已存在'));
  189. }
  190. $data = [
  191. 'username' => $username,
  192. 'nickname' => $username,
  193. 'email' => $email,
  194. 'status' => $status,
  195. 'update_time' => time()
  196. ];
  197. // 如果有密码,进行加密
  198. if (!empty($password)) {
  199. $data['password'] = password_hash($password, PASSWORD_DEFAULT);
  200. }
  201. Db::startTrans();
  202. try {
  203. if (!empty($id)) {
  204. // 编辑
  205. $result = Db::name('user')
  206. ->where('id', $id)
  207. ->update($data);
  208. $message = '编辑成功';
  209. } else {
  210. // 新增
  211. $data['join_time'] = time();
  212. $data['create_time'] = time();
  213. // 生成邀请码(可选)
  214. if (empty($data['invitation_code'])) {
  215. $data['invitation_code'] = $this->generateInvitationCode();
  216. }
  217. $result = Db::name('user')
  218. ->insert($data);
  219. $id = Db::name('user')->getLastInsID();
  220. $message = '新增成功';
  221. }
  222. // 处理企业成员关系
  223. if ($company_id > 0) {
  224. // 检查企业是否存在
  225. $company = Db::name('company')
  226. ->where('id', $company_id)
  227. ->where('status', 1)
  228. ->find();
  229. if ($company) {
  230. // 检查是否已经是该企业成员
  231. $member = Db::name('company_member')
  232. ->where('user_id', $id)
  233. ->find();
  234. if ($member) {
  235. // 更新企业成员关系
  236. Db::name('company_member')
  237. ->where('user_id', $id)
  238. ->update([
  239. 'company_id' => $company_id,
  240. 'email' => $email,
  241. 'name' =>$username,
  242. 'update_time' => date('Y-m-d H:i:s')
  243. ]);
  244. } else {
  245. // 新增企业成员关系
  246. Db::name('company_member')
  247. ->insert([
  248. 'company_id' => $company_id,
  249. 'user_id' => $id,
  250. 'email' => $email,
  251. 'name' =>$username,
  252. 'role' => 2, // 普通成员
  253. 'status' => 1,
  254. 'join_time' => date('Y-m-d H:i:s'),
  255. 'create_time' => date('Y-m-d H:i:s'),
  256. 'update_time' => date('Y-m-d H:i:s')
  257. ]);
  258. }
  259. }
  260. } else {
  261. // 如果企业ID为空,删除企业成员关系
  262. Db::name('company_member')
  263. ->where('user_id', $id)
  264. ->delete();
  265. }
  266. Db::commit();
  267. } catch (\Exception $e) {
  268. Db::rollback();
  269. $this->error('操作失败: ' . $e->getMessage());
  270. }
  271. if ($result) {
  272. $this->success($message, ['id' => $id]);
  273. } else {
  274. $this->error(__('操作失败'));
  275. }
  276. }
  277. /**
  278. * 生成邀请码
  279. * @return string
  280. */
  281. private function generateInvitationCode(): string
  282. {
  283. $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  284. $code = '';
  285. for ($i = 0; $i < 6; $i++) {
  286. $code .= $characters[rand(0, strlen($characters) - 1)];
  287. }
  288. // 检查是否已存在
  289. $exists = Db::name('user')
  290. ->where('invitation_code', $code)
  291. ->find();
  292. if ($exists) {
  293. return $this->generateInvitationCode();
  294. }
  295. return $code;
  296. }
  297. /**
  298. * 用户详情
  299. * @return void
  300. * @throws \think\db\exception\DataNotFoundException
  301. * @throws \think\db\exception\DbException
  302. * @throws \think\db\exception\ModelNotFoundException
  303. */
  304. public function detail(): void
  305. {
  306. $id = $this->request->get('id');
  307. if (empty($id)) {
  308. $this->error(__('参数错误'));
  309. }
  310. // 获取用户基础信息
  311. $userInfo = Db::name('user')
  312. ->where('id', $id)
  313. ->find();
  314. if (!$userInfo) {
  315. $this->error(__('用户不存在'));
  316. }
  317. // 转换时间戳字段
  318. if (!empty($userInfo['create_time'])) {
  319. $userInfo['create_time_str'] = date('Y-m-d H:i:s', $userInfo['create_time']);
  320. }
  321. if (!empty($userInfo['last_login_time'])) {
  322. $userInfo['last_login_time_str'] = date('Y-m-d H:i:s', $userInfo['last_login_time']);
  323. }
  324. if (!empty($userInfo['join_time'])) {
  325. $userInfo['join_time_str'] = date('Y-m-d H:i:s', $userInfo['join_time']);
  326. }
  327. if (!empty($userInfo['update_time'])) {
  328. $userInfo['update_time_str'] = date('Y-m-d H:i:s', $userInfo['update_time']);
  329. }
  330. // 获取用户所在企业信息
  331. $companyInfo = Db::name('company_member')
  332. ->alias('bcm')
  333. ->leftJoin('ba_company bc', 'bcm.company_id = bc.id')
  334. ->where('bcm.user_id', $id)
  335. ->field('bc.id, bc.name')
  336. ->find();
  337. $userInfo['company_id'] = $companyInfo['id'] ?? 0;
  338. $userInfo['company_name'] = $companyInfo['name'] ?? '';
  339. // 获取用户佣金信息
  340. $commissionInfo = Db::name('user_commission')
  341. ->where('user_id', $id)
  342. ->find();
  343. if (!$commissionInfo) {
  344. // 如果没有佣金记录,创建默认记录
  345. $commissionInfo = [
  346. 'total_commission' => '0.00',
  347. 'current_commission' => '0.00',
  348. 'frozen_commission' => '0.00',
  349. 'withdrawn_commission' => '0.00',
  350. 'promotion_link' => $this->generatePromotionLink($id),
  351. 'invitation_count' => 0
  352. ];
  353. }
  354. // 获取用户购买的产品列表(包括套餐信息)
  355. $productList = Db::name('user_product_usage')
  356. ->alias('up')
  357. ->leftJoin('ba_product p', 'up.product_id = p.id')
  358. ->leftJoin('ba_package_price pp', 'up.package_price_id = pp.id')
  359. ->leftJoin('ba_package pk', 'pp.package_id = pk.id')
  360. ->where('up.user_id', $id)
  361. ->field('up.*, p.name as product_name, pp.spec_name as package_name, pp.card_type, pk.name as package_full_name')
  362. ->order('up.create_time', 'desc')
  363. ->select()
  364. ->toArray();
  365. // 处理产品状态和显示信息
  366. foreach ($productList as &$product) {
  367. // 确定产品名称
  368. if (!empty($product['package_full_name'])) {
  369. $product['display_name'] = $product['package_full_name'] . ' (' . $product['package_name'] . ')';
  370. } else {
  371. $product['display_name'] = $product['product_name'] ?? '未知产品';
  372. }
  373. // 处理卡类型显示
  374. $cardTypeMap = [
  375. 1 => '次卡',
  376. 2 => '积分卡',
  377. 3 => '日卡',
  378. 4 => '周卡',
  379. 5 => '季卡',
  380. 6 => '月卡',
  381. 7 => '年卡'
  382. ];
  383. $product['card_type_text'] = $cardTypeMap[$product['card_type']] ?? '未知';
  384. // 处理购买时间
  385. if (!empty($product['create_time'])) {
  386. $product['buy_time'] = date('Y-m-d H:i:s', strtotime($product['create_time']));
  387. } else {
  388. $product['buy_time'] = '';
  389. }
  390. // 处理到期时间
  391. if (!empty($product['end_time'])) {
  392. $product['expire_time'] = date('Y-m-d', strtotime($product['end_time']));
  393. } else {
  394. $product['expire_time'] = '';
  395. }
  396. // 判断状态
  397. $currentTime = time();
  398. $endTime = !empty($product['end_time']) ? strtotime($product['end_time']) : 0;
  399. if ($product['status'] == 2) {
  400. $product['status_text'] = '已过期';
  401. } elseif ($product['status'] == 3) {
  402. $product['status_text'] = '已用完';
  403. } elseif ($endTime > 0 && $endTime < $currentTime) {
  404. $product['status_text'] = '已过期';
  405. } elseif ($product['remaining_times'] == 0 && $product['card_type'] == 1) {
  406. $product['status_text'] = '已用完';
  407. } elseif ($product['remaining_points'] == 0 && $product['card_type'] == 2) {
  408. $product['status_text'] = '已用完';
  409. } else {
  410. $product['status_text'] = '正常';
  411. }
  412. }
  413. // 获取下级人员(通过邀请关系)
  414. $subordinateList = Db::name('user_invitation')
  415. ->alias('bui')
  416. ->leftJoin('ba_user u', 'bui.invitee_id = u.id')
  417. ->leftJoin('ba_user_commission uc', 'u.id = uc.user_id')
  418. ->where('bui.inviter_id', $id)
  419. ->field('u.id, u.username, u.nickname, u.email, uc.current_commission, bui.create_time')
  420. ->order('bui.create_time', 'desc')
  421. ->select()
  422. ->toArray();
  423. // 处理下级人员数据
  424. foreach ($subordinateList as &$subordinate) {
  425. if (!empty($subordinate['create_time'])) {
  426. $subordinate['bind_time'] = date('Y-m-d H:i:s', strtotime($subordinate['create_time']));
  427. } else {
  428. $subordinate['bind_time'] = '';
  429. }
  430. $subordinate['name'] = $subordinate['nickname'] ?: $subordinate['username'];
  431. $subordinate['commission_amount'] = $subordinate['current_commission'] ?? '0.00';
  432. $subordinate['status_text'] = '正常';
  433. }
  434. $result = [
  435. 'user_info' => $userInfo,
  436. 'commission_info' => $commissionInfo,
  437. 'product_list' => $productList,
  438. 'subordinate_list' => $subordinateList
  439. ];
  440. $this->success('查询成功', $result);
  441. }
  442. /**
  443. * 删除用户
  444. * @return void
  445. */
  446. public function delete(): void
  447. {
  448. $id = $this->request->post('id');
  449. if (empty($id)) {
  450. $this->error(__('参数错误'));
  451. }
  452. // 检查是否存在
  453. $user = Db::name('user')
  454. ->where('id', $id)
  455. ->find();
  456. if (!$user) {
  457. $this->error(__('用户不存在'));
  458. }
  459. // 检查是否有下级员工(通过邀请关系)
  460. $hasSubordinate = Db::name('user_invitation')
  461. ->where('inviter_id', $id)
  462. ->count();
  463. if ($hasSubordinate > 0) {
  464. $this->error(__('该用户存在下级员工,请先处理下级关系'));
  465. }
  466. Db::startTrans();
  467. try {
  468. // 删除用户
  469. $result = Db::name('user')
  470. ->where('id', $id)
  471. ->delete();
  472. // 删除用户佣金记录
  473. Db::name('user_commission')
  474. ->where('user_id', $id)
  475. ->delete();
  476. // 删除用户产品使用记录
  477. Db::name('user_product_usage')
  478. ->where('user_id', $id)
  479. ->delete();
  480. // 删除企业成员关系
  481. Db::name('company_member')
  482. ->where('user_id', $id)
  483. ->delete();
  484. // 删除邀请关系(如果该用户是别人的下级)
  485. Db::name('user_invitation')
  486. ->where('invitee_id', $id)
  487. ->delete();
  488. // 删除邀请码
  489. Db::name('invitation_code')
  490. ->where('user_id', $id)
  491. ->delete();
  492. // 删除用户产品记录
  493. Db::name('user_product')
  494. ->where('user_id', $id)
  495. ->delete();
  496. // 删除用户关系表记录
  497. Db::name('user_relation')
  498. ->where('user_id', $id)
  499. ->whereOr('subordinate_id', $id)
  500. ->delete();
  501. // 删除佣金流水记录
  502. Db::name('commission_flow')
  503. ->where('user_id', $id)
  504. ->whereOr('from_user_id', $id)
  505. ->delete();
  506. // 删除分销记录
  507. Db::name('distribution_record')
  508. ->where('buyer_id', $id)
  509. ->whereOr('distributor_id', $id)
  510. ->delete();
  511. Db::commit();
  512. } catch (\Exception $e) {
  513. Db::rollback();
  514. $this->error('删除失败: ' . $e->getMessage());
  515. }
  516. if ($result) {
  517. $this->success('删除成功');
  518. } else {
  519. $this->error(__('删除失败'));
  520. }
  521. }
  522. /**
  523. * 解除下级关系
  524. * @return void
  525. */
  526. public function removeSubordinate(): void
  527. {
  528. $inviteeId = $this->request->post('invitee_id');
  529. $inviterId = $this->request->post('inviter_id');
  530. if (empty($inviteeId) || empty($inviterId)) {
  531. $this->error(__('参数错误'));
  532. }
  533. Db::startTrans();
  534. try {
  535. $result = Db::name('ba_user_invitation')
  536. ->where('inviter_id', $inviterId)
  537. ->where('invitee_id', $inviteeId)
  538. ->delete();
  539. Db::commit();
  540. if ($result) {
  541. $this->success('解除成功');
  542. } else {
  543. $this->error(__('解除失败'));
  544. }
  545. } catch (\Exception $e) {
  546. Db::rollback();
  547. $this->error('解除失败: ' . $e->getMessage());
  548. }
  549. }
  550. /**
  551. * 获取用户状态选项
  552. * @return void
  553. */
  554. public function statusOptions():void
  555. {
  556. $options = [
  557. ['value' => 1, 'label' => '启用'],
  558. ['value' => 2, 'label' => '禁用'],
  559. ['value' => 3, 'label' => '待审核'],
  560. ['value' => 4, 'label' => '审核拒绝']
  561. ];
  562. $this->success('获取成功', $options);
  563. }
  564. /**
  565. * 生成推广链接
  566. */
  567. private function generatePromotionLink(int $userId): string
  568. {
  569. // 获取用户的邀请码
  570. $user = Db::table('ba_user')
  571. ->where('id', $userId)
  572. ->field('invitation_code')
  573. ->find();
  574. $invitationCode = $user['invitation_code'] ?? '';
  575. if (empty($invitationCode)) {
  576. // 如果用户还没有邀请码,先生成一个
  577. $invitationCode = $this->generateInvitationCode();
  578. Db::table('ba_user')
  579. ->where('id', $userId)
  580. ->update(['invitation_code' => $invitationCode]);
  581. }
  582. $domain = request()->domain();
  583. return $domain . '/#/user/register?invite=' . $invitationCode;
  584. }
  585. }