Product.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. <?php
  2. /**
  3. * 产品管理控制器
  4. */
  5. namespace app\admin\controller\package;
  6. use app\common\controller\Backend;
  7. use think\facade\Db;
  8. class Product extends Backend
  9. {
  10. protected array $noNeedLogin = ['*'];
  11. /**
  12. * 产品列表
  13. * @return void
  14. */
  15. public function index():void
  16. {
  17. $where = [];
  18. // 产品名称搜索
  19. if (!empty($this->request->get('name'))) {
  20. $name = $this->request->get('name');
  21. $where[] = ['p.name', 'like', '%' . $name . '%'];
  22. }
  23. // 产品编码搜索
  24. if (!empty($this->request->get('code'))) {
  25. $code = $this->request->get('code');
  26. $where[] = ['p.code', 'like', '%' . $code . '%'];
  27. }
  28. // 分类筛选
  29. if (!empty($this->request->get('category_id'))) {
  30. $categoryId = $this->request->get('category_id');
  31. $where['p.category_id'] = $categoryId;
  32. }
  33. // 库存状态筛选
  34. if (!empty($this->request->get('stock_status'))) {
  35. $stockStatus = $this->request->get('stock_status');
  36. $where['p.stock_status'] = $stockStatus;
  37. }
  38. // 产品状态筛选
  39. if (!empty($this->request->get('status'))) {
  40. $status = $this->request->get('status');
  41. $where['p.status'] = $status;
  42. }
  43. // 排序
  44. $order = [];
  45. if (!empty($this->request->get('order'))) {
  46. $order_a = $this->request->get('order');
  47. $order_a = explode(',', $order_a);
  48. $order[$order_a[0]] = $order_a[1];
  49. } else {
  50. $order['p.sort'] = 'asc';
  51. $order['p.id'] = 'desc';
  52. }
  53. // 分页
  54. $page = $this->request->get('page', 1);
  55. $limit = $this->request->get('limit', 20);
  56. // 查询数据
  57. $productList = Db::name('product')
  58. ->alias('p')
  59. ->leftJoin('product_category pc', 'p.category_id = pc.id')
  60. ->field('p.*, pc.name as category_name')
  61. ->where($where)
  62. ->order($order)
  63. ->paginate([
  64. 'list_rows' => $limit,
  65. 'page' => $page
  66. ]);
  67. // 转换图片字段
  68. $list = $productList->items();
  69. foreach ($list as &$item) {
  70. if (!empty($item['image'])) {
  71. $item['image'] = json_decode($item['image'], true);
  72. } else {
  73. $item['image'] = [];
  74. }
  75. if (!empty($item['key_attributes'])) {
  76. $item['key_attributes'] = json_decode($item['key_attributes'], true);
  77. } else {
  78. $item['key_attributes'] = [];
  79. }
  80. }
  81. $this->success('查询成功', [
  82. 'list' => $list,
  83. 'total' => $productList->total()
  84. ]);
  85. }
  86. /**
  87. * 新增/编辑产品
  88. * @return void
  89. */
  90. public function edit():void
  91. {
  92. $id = $this->request->post('id');
  93. $name = $this->request->post('name');
  94. $code = $this->request->post('code');
  95. $categoryId = $this->request->post('category_id');
  96. $description = $this->request->post('description', '');
  97. $detail = $this->request->post('detail', '');
  98. $keyAttributes = $this->request->post('key_attributes', []);
  99. $image = $this->request->post('image');
  100. $stockStatus = $this->request->post('stock_status', 1);
  101. $status = $this->request->post('status', 1);
  102. $sort = $this->request->post('sort', 0);
  103. if (empty($name)) {
  104. $this->error(__('产品名称不能为空'));
  105. }
  106. if (empty($code)) {
  107. $this->error(__('产品编码不能为空'));
  108. }
  109. // 验证产品编码是否已存在(编辑时排除自己)
  110. $checkCode = Db::name('product')
  111. ->where('code', $code);
  112. if (!empty($id)) {
  113. $checkCode->where('id', '<>', $id);
  114. }
  115. if ($checkCode->find()) {
  116. $this->error(__('产品编码已存在'));
  117. }
  118. // 处理图片和属性字段
  119. if (empty($image)) {
  120. $image = "";
  121. }
  122. if (!is_array($keyAttributes)) {
  123. $keyAttributes = [];
  124. }
  125. $data = [
  126. 'name' => $name,
  127. 'code' => $code,
  128. 'category_id' => $categoryId ?: null,
  129. 'description' => $description,
  130. 'detail' => $detail,
  131. 'key_attributes' => json_encode($keyAttributes, JSON_UNESCAPED_UNICODE),
  132. 'image' => $image,
  133. 'stock_status' => $stockStatus,
  134. 'status' => $status,
  135. 'sort' => $sort,
  136. 'update_time' => date('Y-m-d H:i:s')
  137. ];
  138. try {
  139. if (!empty($id)) {
  140. // 编辑
  141. $productInfo = Db::name('product')
  142. ->where('id', $id)
  143. ->find();
  144. if (!$productInfo) {
  145. $this->error(__('产品不存在'));
  146. }
  147. $result = Db::name('product')
  148. ->where('id', $id)
  149. ->update($data);
  150. $message = '编辑成功';
  151. } else {
  152. // 新增
  153. $data['create_time'] = date('Y-m-d H:i:s');
  154. $result = Db::name('product')
  155. ->insert($data);
  156. $id = Db::name('product')->getLastInsID();
  157. $message = '新增成功';
  158. }
  159. } catch (\Exception $e) {
  160. $this->error('操作失败: ' . $e->getMessage());
  161. }
  162. if ($result) {
  163. $this->success($message, ['id' => $id]);
  164. } else {
  165. $this->error(__('操作失败'));
  166. }
  167. }
  168. /**
  169. * 删除产品
  170. * @return void
  171. */
  172. public function delete():void
  173. {
  174. $id = $this->request->post('id');
  175. if (empty($id)) {
  176. $this->error(__('参数错误'));
  177. }
  178. // 检查是否有套餐关联
  179. $hasPackage = Db::name('package')
  180. ->where('product_id', $id)
  181. ->count();
  182. if ($hasPackage > 0) {
  183. $this->error(__('该产品下有关联的套餐,请先删除套餐'));
  184. }
  185. // 检查是否有订单关联
  186. $hasOrder = Db::name('order_item')
  187. ->where('product_id', $id)
  188. ->count();
  189. if ($hasOrder > 0) {
  190. $this->error(__('该产品已有订单记录,不能删除'));
  191. }
  192. try {
  193. Db::startTrans();
  194. // 删除产品
  195. $result = Db::name('product')
  196. ->where('id', $id)
  197. ->delete();
  198. // 删除产品规格
  199. Db::name('product_spec')
  200. ->where('product_id', $id)
  201. ->delete();
  202. Db::commit();
  203. } catch (\Exception $e) {
  204. Db::rollback();
  205. $this->error('删除失败: ' . $e->getMessage());
  206. }
  207. if ($result) {
  208. $this->success('删除成功');
  209. } else {
  210. $this->error(__('删除失败'));
  211. }
  212. }
  213. /**
  214. * 产品详情
  215. * @return void
  216. */
  217. public function detail():void
  218. {
  219. $id = $this->request->get('id');
  220. if (empty($id)) {
  221. $this->error(__('参数错误'));
  222. }
  223. // 获取产品基础信息
  224. $productInfo = Db::name('product')
  225. ->alias('p')
  226. ->leftJoin('product_category pc', 'p.category_id = pc.id')
  227. ->where('p.id', $id)
  228. ->field('p.*, pc.name as category_name')
  229. ->find();
  230. if (!$productInfo) {
  231. $this->error(__('产品不存在'));
  232. }
  233. if (empty($productInfo['image'])) {
  234. $productInfo['image'] = [];
  235. }
  236. if (!empty($productInfo['key_attributes'])) {
  237. $productInfo['key_attributes'] = json_decode($productInfo['key_attributes'], true);
  238. } else {
  239. $productInfo['key_attributes'] = [];
  240. }
  241. // 获取产品规格
  242. $specList = Db::name('product_spec')
  243. ->where('product_id', $id)
  244. ->order('sort', 'asc')
  245. ->select()
  246. ->toArray();
  247. foreach ($specList as &$spec) {
  248. if (!empty($spec['value'])) {
  249. $spec['value'] = json_decode($spec['value'], true);
  250. } else {
  251. $spec['value'] = [];
  252. }
  253. }
  254. // 获取关联套餐数量
  255. $packageCount = Db::name('package')
  256. ->where('product_id', $id)
  257. ->count();
  258. // 获取订单数量
  259. $orderCount = Db::name('order_item')
  260. ->where('product_id', $id)
  261. ->count();
  262. $result = [
  263. 'product_info' => $productInfo,
  264. 'spec_list' => $specList,
  265. 'package_count' => $packageCount,
  266. 'order_count' => $orderCount
  267. ];
  268. $this->success('查询成功', $result);
  269. }
  270. /**
  271. * 批量更新产品状态
  272. * @return void
  273. */
  274. public function batchStatus():void
  275. {
  276. $ids = $this->request->post('ids');
  277. $status = $this->request->post('status');
  278. if (empty($ids) || empty($status)) {
  279. $this->error(__('参数错误'));
  280. }
  281. if (!is_array($ids)) {
  282. $ids = explode(',', $ids);
  283. }
  284. try {
  285. $result = Db::name('product')
  286. ->whereIn('id', $ids)
  287. ->update([
  288. 'status' => $status,
  289. 'update_time' => date('Y-m-d H:i:s')
  290. ]);
  291. } catch (\Exception $e) {
  292. $this->error('操作失败: ' . $e->getMessage());
  293. }
  294. if ($result) {
  295. $statusText = $status == 1 ? '上架' : '下架';
  296. $this->success("成功{$statusText}{$result}个产品");
  297. } else {
  298. $this->error(__('操作失败'));
  299. }
  300. }
  301. /**
  302. * 批量更新库存状态
  303. * @return void
  304. */
  305. public function batchStockStatus():void
  306. {
  307. $ids = $this->request->post('ids');
  308. $stockStatus = $this->request->post('stock_status');
  309. if (empty($ids) || empty($stockStatus)) {
  310. $this->error(__('参数错误'));
  311. }
  312. if (!is_array($ids)) {
  313. $ids = explode(',', $ids);
  314. }
  315. try {
  316. $result = Db::name('product')
  317. ->whereIn('id', $ids)
  318. ->update([
  319. 'stock_status' => $stockStatus,
  320. 'update_time' => date('Y-m-d H:i:s')
  321. ]);
  322. } catch (\Exception $e) {
  323. $this->error('操作失败: ' . $e->getMessage());
  324. }
  325. if ($result) {
  326. $statusText = [
  327. 1 => '充足',
  328. 2 => '缺货',
  329. 3 => '下架'
  330. ][$stockStatus] ?? '未知';
  331. $this->success("成功更新{$result}个产品的库存状态为{$statusText}");
  332. } else {
  333. $this->error(__('操作失败'));
  334. }
  335. }
  336. /**
  337. * 添加/编辑产品规格
  338. * @return void
  339. */
  340. public function editSpec():void
  341. {
  342. $id = $this->request->post('id');
  343. $productId = $this->request->post('product_id');
  344. $name = $this->request->post('name');
  345. $value = $this->request->post('value', []);
  346. $sort = $this->request->post('sort', 0);
  347. if (empty($productId) || empty($name)) {
  348. $this->error(__('参数不完整'));
  349. }
  350. // 检查产品是否存在
  351. $productInfo = Db::name('product')
  352. ->where('id', $productId)
  353. ->find();
  354. if (!$productInfo) {
  355. $this->error(__('产品不存在'));
  356. }
  357. // 处理规格值
  358. if (!is_array($value)) {
  359. $value = [];
  360. }
  361. $data = [
  362. 'product_id' => $productId,
  363. 'name' => $name,
  364. 'value' => json_encode($value, JSON_UNESCAPED_UNICODE),
  365. 'sort' => $sort
  366. ];
  367. try {
  368. if (!empty($id)) {
  369. // 编辑
  370. $result = Db::name('product_spec')
  371. ->where('id', $id)
  372. ->update($data);
  373. $message = '编辑成功';
  374. } else {
  375. // 新增
  376. $data['create_time'] = date('Y-m-d H:i:s');
  377. $result = Db::name('product_spec')
  378. ->insert($data);
  379. $id = Db::name('product_spec')->getLastInsID();
  380. $message = '新增成功';
  381. }
  382. } catch (\Exception $e) {
  383. $this->error('操作失败: ' . $e->getMessage());
  384. }
  385. if ($result) {
  386. $this->success($message, ['id' => $id]);
  387. } else {
  388. $this->error(__('操作失败'));
  389. }
  390. }
  391. /**
  392. * 删除产品规格
  393. * @return void
  394. */
  395. public function deleteSpec():void
  396. {
  397. $id = $this->request->post('id');
  398. if (empty($id)) {
  399. $this->error(__('参数错误'));
  400. }
  401. try {
  402. $result = Db::name('product_spec')
  403. ->where('id', $id)
  404. ->delete();
  405. } catch (\Exception $e) {
  406. $this->error('删除失败: ' . $e->getMessage());
  407. }
  408. if ($result) {
  409. $this->success('删除成功');
  410. } else {
  411. $this->error(__('删除失败'));
  412. }
  413. }
  414. /**
  415. * 获取产品规格列表
  416. * @return void
  417. */
  418. public function specList():void
  419. {
  420. $productId = $this->request->get('product_id');
  421. if (empty($productId)) {
  422. $this->error(__('参数错误'));
  423. }
  424. $specList = Db::name('product_spec')
  425. ->where('product_id', $productId)
  426. ->order('sort', 'asc')
  427. ->select()
  428. ->toArray();
  429. foreach ($specList as &$spec) {
  430. if (!empty($spec['value'])) {
  431. $spec['value'] = json_decode($spec['value'], true);
  432. } else {
  433. $spec['value'] = [];
  434. }
  435. }
  436. $this->success('查询成功', $specList);
  437. }
  438. /**
  439. * 获取产品状态选项
  440. * @return void
  441. */
  442. public function statusOptions():void
  443. {
  444. $options = [
  445. ['value' => 1, 'label' => '上架'],
  446. ['value' => 2, 'label' => '下架']
  447. ];
  448. $this->success('获取成功', $options);
  449. }
  450. /**
  451. * 获取库存状态选项
  452. * @return void
  453. */
  454. public function stockStatusOptions():void
  455. {
  456. $options = [
  457. ['value' => 1, 'label' => '充足'],
  458. ['value' => 2, 'label' => '缺货'],
  459. ['value' => 3, 'label' => '下架']
  460. ];
  461. $this->success('获取成功', $options);
  462. }
  463. /**
  464. * 导出产品列表
  465. * @return void
  466. */
  467. public function export():void
  468. {
  469. $where = [];
  470. // 根据筛选条件构建查询条件
  471. if (!empty($this->request->get('name'))) {
  472. $name = $this->request->get('name');
  473. $where[] = ['p.name', 'like', '%' . $name . '%'];
  474. }
  475. if (!empty($this->request->get('status'))) {
  476. $status = $this->request->get('status');
  477. $where['p.status'] = $status;
  478. }
  479. // 查询数据
  480. $productList = Db::name('product')
  481. ->alias('p')
  482. ->leftJoin('product_category pc', 'p.category_id = pc.id')
  483. ->field('p.id, p.name, p.code, pc.name as category_name,
  484. p.stock_status, p.status, p.sort, p.create_time')
  485. ->where($where)
  486. ->order('p.sort', 'asc')
  487. ->order('p.id', 'desc')
  488. ->select()
  489. ->toArray();
  490. // 状态映射
  491. $stockStatusMap = [1 => '充足', 2 => '缺货', 3 => '下架'];
  492. $statusMap = [1 => '上架', 2 => '下架'];
  493. foreach ($productList as &$item) {
  494. $item['stock_status'] = $stockStatusMap[$item['stock_status']] ?? '未知';
  495. $item['status'] = $statusMap[$item['status']] ?? '未知';
  496. }
  497. $this->success('查询成功', $productList);
  498. }
  499. }