Product.php 16 KB

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