Product.php 16 KB

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