Product.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. <?php
  2. namespace app\api\controller;
  3. use app\common\controller\Api;
  4. use think\Db;
  5. class Product extends Api
  6. {
  7. protected $noNeedLogin = ['*'];
  8. protected $noNeedRight = ['*'];
  9. /**
  10. * 本地文件同步到 OSS(配置齐全时);失败不影响主流程
  11. */
  12. protected function maybeUploadToOss($localFullPath, $objectKey)
  13. {
  14. return Common::uploadLocalFileToOss((string)$localFullPath, (string)$objectKey);
  15. }
  16. /**
  17. * 商户菜单
  18. * @return void
  19. * @throws \think\db\exception\DataNotFoundException
  20. * @throws \think\db\exception\ModelNotFoundException
  21. * @throws \think\exception\DbException
  22. */
  23. public function merchantGetab()
  24. {
  25. if (!$this->request->isGet()) {
  26. $this->error('请求错误');
  27. }
  28. $list = \db('product_merchant')
  29. ->where([
  30. 'status' => 1,
  31. 'deleteTime' => null,
  32. ])
  33. ->field('merchant_code,merchant_name')
  34. ->select();
  35. foreach ($list as $k => $v) {
  36. $list[$k]['tab'] = $v['merchant_name'].'('.$v['merchant_code'].')';
  37. }
  38. if (empty($list)) {
  39. $this->error('未找到商户数据');
  40. }else{
  41. $this->success('成功', $list);
  42. }
  43. }
  44. /**
  45. * 产品列表
  46. * @return void
  47. * @throws \think\db\exception\DataNotFoundException
  48. * @throws \think\db\exception\ModelNotFoundException
  49. * @throws \think\exception\DbException
  50. */
  51. public function productList()
  52. {
  53. // 1. 请求方法验证优化
  54. if (!$this->request->isGet()) {
  55. $this->error('请求方法错误');
  56. }
  57. $param = $this->request->param();
  58. // 2. 参数验证优化
  59. if (empty($param['code']) || !is_string($param['code'])) {
  60. $this->error('商户编码参数错误');
  61. }
  62. // 3. 参数安全处理
  63. $merchantCode = trim($param['code']);
  64. $searchKeyword = isset($param['search']) ? trim($param['search']) : '';
  65. // 4. 分页参数处理
  66. $page = isset($param['page']) ? intval($param['page']) : 1;
  67. $pageSize = isset($param['pageSize']) ? intval($param['pageSize']) : 15;
  68. // 验证分页参数
  69. if ($page < 1) $page = 1;
  70. if ($pageSize < 1 || $pageSize > 100) $pageSize = 15; // 限制最大每页100条
  71. // 5. 构建查询条件
  72. $where = [
  73. 'b.merchant_code' => $merchantCode
  74. ];
  75. if (!empty($searchKeyword)) {
  76. // 使用更安全的查询方式
  77. $where['a.product_name|a.product_code'] = ['like', '%' . addslashes($searchKeyword) . '%'];
  78. }
  79. // 6. 查询数据(带分页)
  80. try {
  81. // 首先获取总记录数
  82. $total = \db('product')
  83. ->alias('a')
  84. ->join('product_merchant b', 'a.merchant_id = b.id')
  85. ->where('a.deleteTime', null)
  86. ->where($where)
  87. ->count();
  88. // 分页查询
  89. $list = \db('product')
  90. ->alias('a')
  91. ->join('product_merchant b', 'a.merchant_id = b.id')
  92. ->where($where)
  93. ->field([
  94. 'a.product_name as 产品名称',
  95. 'a.product_code as 产品编码',
  96. 'a.product_img',
  97. 'a.deleteTime',
  98. 'a.product_new_img',
  99. 'a.createTime as 创建时间',
  100. 'a.create_name as 创建人',
  101. 'b.merchant_code as 商户编码',
  102. 'a.id'
  103. ])
  104. ->where('a.deleteTime', null)
  105. ->order('a.createTime', 'desc')
  106. ->page($page, $pageSize)
  107. ->select();
  108. } catch (\Exception $e) {
  109. $this->error('查询数据失败:' . $e->getMessage());
  110. }
  111. // 7. 优化数据处理逻辑
  112. if (!empty($list)) {
  113. foreach ($list as &$item) {
  114. // 产品图片
  115. if (!empty($item['product_img'])) {
  116. $item['产品图片'] = ltrim($item['product_img'], '/');
  117. unset($item['product_img']); // 移除原始字段
  118. } else {
  119. $item['产品图片'] = ''; // 设置默认值
  120. }
  121. // 产品效果图
  122. if (!empty($item['product_new_img'])) {
  123. $item['产品效果图'] = ltrim($item['product_new_img'], '/');
  124. unset($item['product_new_img']); // 移除原始字段
  125. } else {
  126. $item['产品效果图'] = ''; // 设置默认值
  127. }
  128. }
  129. unset($item); // 解除引用
  130. }
  131. foreach ($list as &$item) {
  132. $item['产品图片'] = Common::ossFullUrl((string)$item['产品图片']);
  133. $item['产品效果图'] = Common::ossFullUrl((string)$item['产品效果图']);
  134. }
  135. unset($item);
  136. $result = [
  137. 'list' => $list,
  138. 'total' => $total,
  139. ];
  140. // 10. 统一返回格式
  141. $this->success('查询成功', $result);
  142. }
  143. /**
  144. * 产品详情
  145. * @return void
  146. * @throws \think\db\exception\DataNotFoundException
  147. * @throws \think\db\exception\ModelNotFoundException
  148. * @throws \think\exception\DbException
  149. */
  150. public function productDetail()
  151. {
  152. if (!$this->request->isGet()) {
  153. $this->error('只支持GET请求');
  154. }
  155. $param = $this->request->param();
  156. if (empty($param['id']) || !is_numeric($param['id'])) {
  157. $this->error('产品ID参数错误');
  158. }
  159. $productId = intval($param['id']);
  160. if ($productId <= 0) {
  161. $this->error('产品ID必须为正整数');
  162. }
  163. try {
  164. $product = \db('product')
  165. ->field([
  166. 'id',
  167. 'product_name as 产品名称',
  168. 'product_code as 产品编码',
  169. 'product_img',
  170. 'product_new_img',
  171. 'createTime as 创建时间',
  172. 'create_name as 创建人',
  173. ])
  174. ->where('id', $productId)
  175. ->whereNull('deleteTime')
  176. ->order('id desc')
  177. ->find();
  178. } catch (\Exception $e) {
  179. $this->error('查询产品详情失败:' . $e->getMessage());
  180. }
  181. if (empty($product)) {
  182. $this->error('产品不存在或已被删除');
  183. }
  184. // 6. 优化图片路径处理
  185. // 产品图片
  186. if (!empty($product['product_img'])) {
  187. $product['产品图片'] = ltrim($product['product_img']);
  188. } else {
  189. $product['产品图片'] = ''; // 设置默认空值
  190. }
  191. // 产品效果图 - 修复变量名错误(原代码中使用了$v)
  192. if (!empty($product['product_new_img'])) {
  193. $product['产品效果图'] = ltrim($product['product_new_img']);
  194. } else {
  195. $product['产品效果图'] = ''; // 设置默认空值
  196. }
  197. $newImg = \db('product_new_img')
  198. ->where('product_id', $productId)
  199. ->whereNull('deleteTime')
  200. ->column('img_address');
  201. $product['newImg'] = $newImg;
  202. // 7. 移除原始图片字段,保持返回数据整洁
  203. unset($product['product_img'], $product['product_new_img']);
  204. $product_image = \db('product_image')->alias('a')
  205. ->field('
  206. a.id,
  207. a.product_id,
  208. a.template_id,
  209. a.product_new_img,
  210. a.product_content,
  211. a.createTime,
  212. b.thumbnail_image,
  213. b.template_name
  214. ')
  215. ->join('product_template b', 'a.template_id = b.id', 'LEFT')
  216. ->where('a.product_id', $productId)
  217. ->whereNull('a.mod_rq')
  218. ->order('a.id desc')
  219. ->select();
  220. foreach ($product_image as &$item) {
  221. if (!empty($item['product_new_img']) || !empty($item['thumbnail_image'])) {
  222. $item['product_new_img'] = Common::ossFullUrl((string)$item['product_new_img']);
  223. $item['thumbnail_image'] = Common::ossFullUrl((string)$item['thumbnail_image']);
  224. }
  225. }
  226. unset($item);
  227. // $product 是单条记录,不是列表,直接处理
  228. if (!empty($product['产品图片'])) {
  229. $product['产品图片'] = Common::ossFullUrl((string)$product['产品图片']);
  230. }
  231. if (!empty($product['产品效果图'])) {
  232. $product['产品效果图'] = Common::ossFullUrl((string)$product['产品效果图']);
  233. }
  234. return json([
  235. 'code' => 0,
  236. 'msg' => '获取产品详情成功',
  237. 'image' => $product_image,//历史图片
  238. 'data' => $product
  239. ]);
  240. }
  241. /**
  242. * 获取单条产品数据信息
  243. */
  244. public function GetProductFind(){
  245. if (!$this->request->isGet()) {
  246. $this->error('只支持GET请求');
  247. }
  248. $param = $this->request->param();
  249. if (empty($param['id']) || !is_numeric($param['id'])) {
  250. $this->error('产品ID参数错误');
  251. }
  252. $product = Db::name('product')->where('id', $param['id'])->find();
  253. if (empty($product)) {
  254. return json([
  255. 'code' => 1,
  256. 'msg' => '产品不存在',
  257. 'data' => null
  258. ]);
  259. }
  260. $this->success('获取成功', $product);
  261. }
  262. /**
  263. * 新增产品
  264. * @return void
  265. */
  266. public function productAdd()
  267. {
  268. // 请求方法验证(可提取为公共方法)
  269. if (!$this->request->isPost()) {
  270. throw new \Exception('非法请求');
  271. }
  272. // 获取并验证参数
  273. $param = $this->request->post();
  274. $this->validateProductParams($param, ['product_name', 'product_code']);
  275. //处理产品图片 base64 -> 保存到 uploads/merchant/{product_code前段}/{product_code}/{product_name}.png
  276. $productImgPath = '';
  277. if (!empty($param['product_img'])) {
  278. $base64Data = $param['product_img'];
  279. if (preg_match('/data:image\/(png|jpg|jpeg);base64,([A-Za-z0-9+\/=]+)/i', $base64Data, $m)) {
  280. $imageType = strtolower($m[1]);
  281. $imageData = base64_decode($m[2]);
  282. if ($imageData !== false && strlen($imageData) >= 100) {
  283. $productCode = $param['product_code'];
  284. $prefix = strlen($productCode) >= 4 ? substr($productCode, 0, -4) : $productCode; // 后四位前面的数值
  285. $safeName = preg_replace('/[\\\\\/:*?"<>|]/u', '_', $param['product_name']);
  286. $ext = ($imageType === 'jpeg') ? 'jpg' : $imageType;
  287. $fileName = $safeName . '.' . $ext;
  288. $saveDir = str_replace('\\', '/', ROOT_PATH . 'public/uploads/merchant/' . $prefix . '/' . $productCode . '/'. 'oldimg/');
  289. if (!is_dir($saveDir)) {
  290. mkdir($saveDir, 0755, true);
  291. }
  292. if (file_put_contents($saveDir . $fileName, $imageData)) {
  293. $productImgPath = 'uploads/merchant/' . $prefix . '/' . $productCode . '/' . 'oldimg/'. $fileName;
  294. // 本地保存成功后,尝试同步 OSS(失败不阻塞新增)
  295. $this->maybeUploadToOss($saveDir . $fileName, $productImgPath);
  296. }
  297. }
  298. }
  299. }
  300. $data = [
  301. 'product_name' => $param['product_name'],
  302. 'product_code' => $param['product_code'],
  303. 'createTime' => date('Y-m-d H:i:s', time()),
  304. 'create_name' => isset($param['create_name']) ? $param['create_name'] : '',
  305. 'merchant_id' => isset($param['merchant_id']) ? intval($param['merchant_id']) : 0,
  306. 'product_img' => $productImgPath,
  307. ];
  308. //验证产品编码唯一性
  309. if (Db::name('product')->where('product_code', $data['product_code'])->count() > 0) {
  310. $this->error('产品编码已存在');
  311. }
  312. $result = Db::name('product')->insert($data);
  313. if ($result) {
  314. $this->success('新增成功');
  315. } else {
  316. $this->error('新增失败');
  317. }
  318. }
  319. /**
  320. * 修改产品数据
  321. * @return void
  322. */
  323. public function productEdit()
  324. {
  325. // 1. 请求方法验证
  326. if (!$this->request->isPost()) {
  327. throw new \Exception('非法请求');
  328. }
  329. // 2. 获取并验证参数
  330. $param = $this->request->post();
  331. $this->validateProductParams($param, ['id', 'product_name', 'product_code']);
  332. // 3. 检查产品是否存在
  333. $product = Db::name('product')->where('id', intval($param['id']))->find();
  334. if (!$product) {
  335. $this->error('产品不存在');
  336. }
  337. // 4. 准备更新数据
  338. $updateData = [
  339. 'product_name' => $param['product_name'],
  340. 'product_code' => $param['product_code'],
  341. ];
  342. // 5. 可选字段更新
  343. $optionalFields = ['create_name', 'merchant_id', 'product_img', 'product_new_img'];
  344. foreach ($optionalFields as $field) {
  345. if (isset($param[$field])) {
  346. $updateData[$field] = $param[$field];
  347. }
  348. }
  349. // 6. 验证产品编码唯一性(排除自身)
  350. $codeExists = Db::name('product')
  351. ->where('product_code', $updateData['product_code'])
  352. ->where('id', '<>', intval($param['id']))
  353. ->count();
  354. if ($codeExists > 0) {
  355. $this->error('产品编码已被其他产品使用');
  356. }
  357. // 7. 使用事务更新
  358. $result = Db::name('product')
  359. ->where('id', intval($param['id']))
  360. ->update($updateData);
  361. if ($result !== false) {
  362. $this->success('更新成功');
  363. } else {
  364. $this->error('更新失败');
  365. }
  366. }
  367. /**
  368. * 验证产品参数(私有方法,供内部使用)
  369. * @param array $param 参数数组
  370. * @param array $requiredFields 必要字段
  371. * @throws \Exception
  372. */
  373. private function validateProductParams($param, $requiredFields)
  374. {
  375. foreach ($requiredFields as $field) {
  376. if (!isset($param[$field]) || trim($param[$field]) === '') {
  377. throw new \Exception("缺少必要参数:{$field}");
  378. }
  379. }
  380. }
  381. /**
  382. * 获取商户ID
  383. * @return void
  384. */
  385. public function getMerchantId()
  386. {
  387. if (!$this->request->isGet()) {
  388. $this->error('请求错误');
  389. }
  390. $param = $this->request->param();
  391. if (empty($param['merchant_code'])) {
  392. $this->error('参数错误');
  393. }
  394. $id = \db('product_merchant')
  395. ->where('merchant_code', $param['merchant_code'])
  396. ->value('id');
  397. if (!empty($id)) {
  398. $this->success('成功', $id);
  399. }else{
  400. $this->error('失败');
  401. }
  402. }
  403. /**
  404. * 产品删除
  405. */
  406. public function Product_Del()
  407. {
  408. $params = $this->request->param();
  409. $record['deleteTime'] = date('Y-m-d H:i:s');
  410. $res = Db::name('product')->where('id', $params['id'])->update($record);
  411. if (!$res) {
  412. return json([
  413. 'code' => 1,
  414. 'msg' => '删除失败',
  415. 'data' => ''
  416. ]);
  417. }
  418. return json([
  419. 'code' => 0,
  420. 'msg' => '删除成功'
  421. ]);
  422. }
  423. }