Cart.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. <?php
  2. namespace app\controller\api;
  3. use app\extra\basic\Base;
  4. use app\extra\tools\CodeExtend;
  5. use app\middleware\WxMiddleware;
  6. use app\model\saas\SaasCart;
  7. use app\model\saas\SaasCombo;
  8. use app\model\saas\SaasDiscount;
  9. use app\model\saas\SaasPrice;
  10. use app\model\saas\SaasPrintClient;
  11. use app\model\saas\SaasShop;
  12. use app\model\saas\SaasUser;
  13. use app\model\saas\SaasUserBuy;
  14. use app\model\saas\SaasWordChange;
  15. use LinFly\Annotation\Route\Controller;
  16. use LinFly\Annotation\Route\Middleware;
  17. use LinFly\Annotation\Route\Route;
  18. use Qcloud\Cos\Client;
  19. use support\Request;
  20. use support\Response;
  21. use Webman\RedisQueue\Redis;
  22. use yzh52521\EasyHttp\Http;
  23. #[Controller(prefix: "/wx_api/cart"),Middleware(WxMiddleware::class)]
  24. class Cart extends Base
  25. {
  26. protected array $noNeedLogin = [];
  27. /**
  28. * 颜色
  29. * @var array|string[]
  30. */
  31. protected array $color = ["1" => "彩色", "2" => "黑白"];
  32. /**
  33. * 单双面
  34. * @var array|string[]
  35. */
  36. protected array $duplex = ["1" => "单面", "2" => "双面"];
  37. /**
  38. * 打印方向
  39. * @var array|string[]
  40. */
  41. protected array $direction = ["1" => "长边翻转","2" => "短边翻转",'3' => ""];
  42. // protected array $direction = ["1" => "自适应","2" => "横向", "3" => "竖向"];
  43. /**
  44. * 配送方式
  45. * @var array|string[]
  46. */
  47. protected array $package = ["1" => "店内打印", "2" => '远程自取' , "3" => "商家配送"];
  48. protected array $types = [
  49. '1_1_1' => ['name' => '彩色-单面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
  50. '1_1_2' => ['name' => '彩色-单面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
  51. '1_2_1' => ['name' => '彩色-双面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
  52. '2_1_1' => ['name' => '黑白-单面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
  53. '2_2_1' => ['name' => '黑白-双面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
  54. '2_1_2' => ['name' => '黑白-双面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
  55. ];
  56. /**
  57. * 获取打印购物车列表
  58. * @return Response
  59. */
  60. #[Route(path: "list",methods: "get")]
  61. public function getCartList(Request $request): Response
  62. {
  63. try {
  64. $param = $this->_valid([
  65. "shop.require" => trans("empty.require"),
  66. "print.require" => trans("empty.require"),
  67. "type.default" => 1
  68. ]);
  69. if (!is_array($param)) return error($param);
  70. $cart = (new SaasCart)->where("shop_id",$param['shop'])->where("print_id",$param['print'])->where("openid",$request->user['openid'])->order("create_at desc")->select();
  71. if ($cart->isEmpty()) return success('ok',['cart' => []]);
  72. $totalAmount = $totalDiscount = 0;
  73. foreach ($cart as $k=>$v){
  74. $key = $v['color'] . '_' . $v['duplex'] . '_' . $v['source'];
  75. if (isset($this->types[$key])) {
  76. $this->types[$key]['quantity'] += $v['page'];
  77. $this->types[$key]['amount'] += $v['money'];
  78. }
  79. $cart[$k] = $v;
  80. $cart[$k]['money'] = format_money($v['money'] / 100,2);
  81. $cart[$k]['name'] = msubstr($v['name'],0,12);
  82. $cart[$k]['jobStatus'] = 'success';
  83. }
  84. $printData = (new SaasPrintClient)->where("shop_id",$param['shop'])->where("code",$param['print'])->select();
  85. if ($printData->isEmpty()) return error('无可用打印机');
  86. $rule = [];
  87. foreach ($printData as $key=>$value) {
  88. $rule[$key]['check'] = 0;
  89. $nameColor = "";
  90. if (empty($value['rule'])) return error("尚未配置打印机~");
  91. foreach ($value['rule']['color'] as $key2=>$val) {
  92. $rule[$key]['color'][$key2][$val] = $this->color[$val];
  93. $nameColor .= $this->color[$val];
  94. }
  95. foreach ($value['rule']['direction'] as $key2=>$val) {
  96. $rule[$key]['direction'][$key2][$val] = $this->direction[$val];
  97. }
  98. foreach ($value['rule']['duplex'] as $key2=>$val) {
  99. $rule[$key]['duplex'][$key2][$val] = $this->duplex[$val];
  100. }
  101. foreach ($value['rule']['package'] as $key2=>$val) {
  102. $rule[$key]['package'][$key2][$val] = $this->package[$val];
  103. }
  104. foreach ($value['rule']['paper_size'] as $key2=>$val) {
  105. $rule[$key]['paper_size'][$key2][$val] = $val;
  106. }
  107. if ($param['print'] == $value['code']) {
  108. $rule[$key]['check'] = 1;
  109. }
  110. $rule[$key]['name'] = $nameColor."-".$value['name'];
  111. $rule[$key]['code'] = $value['code'];
  112. }
  113. // 计算折扣
  114. foreach ($this->types as $k=>$v) {
  115. $discount = (new SaasDiscount)->where("shop_id",$param['shop'])->where("keys",$k)->where("number",'<',$v['quantity'])->findOrEmpty();
  116. if (!$discount->isEmpty()) {
  117. $v['discount'] = round($v['amount'] * $discount['rate']);
  118. $this->types[$k]['discount'] = $v['discount'];
  119. }
  120. $totalAmount += $v['amount'];
  121. $totalDiscount += $v['discount'];
  122. }
  123. $totalAmount = format_money($totalAmount / 100,2);
  124. $totalDiscount = format_money($totalDiscount / 100,2);
  125. if ($param['type'] <> 1) {
  126. $shop = (new SaasShop)->where("shop_id",$param['shop'])->field("shop_name,shop_address,user_card,user_card_price")->find();
  127. $isRecharge = (new SaasUserBuy)->where("shop_id",$param['shop'])->where("openid",$request->user['openid'])->where("status",1)->sum("money");;
  128. $cardPrice = [];
  129. if ($shop['user_card'] < 3) {
  130. if ($shop['user_card'] == 2) { // 自定义套餐
  131. $cardPrice = array_values($shop['user_card_price']);
  132. } else {
  133. $cardPrice = (new SaasCombo)->where("type",2)->field("id,name,ROUND(money/100,2) as money,ROUND(old_money/100,2) as old_money,is_first")->where("status",1)->select()->toArray();
  134. }
  135. $cardPrice = array_filter($cardPrice, function($item) use ($isRecharge) {
  136. if ($isRecharge > 0) {
  137. return $item['is_first'] != '1'; // 注意:这里使用松散比较,因为数据中有字符串'1'
  138. } else {
  139. return $item;
  140. }
  141. });
  142. foreach ($cardPrice as $key=>$val) {
  143. $cardPrice[$key] = $val;
  144. $cardPrice[$key]['money'] = $val['money'];
  145. $cardPrice[$key]['old_money'] = $val['old_money'];
  146. }
  147. $cardPrice = array_values($cardPrice);
  148. }
  149. $card = (new SaasUser)->where("openid",$request->user['openid'])->where("shop_id",$param['shop'])->field("ROUND(balance/100,2) as money")->findOrEmpty();
  150. if ($card->isEmpty()) {
  151. $card = null;
  152. }
  153. $package = $this->package;
  154. return success("",compact('rule','totalAmount','totalDiscount','shop','cardPrice','package','card'));
  155. }
  156. return success("",compact('cart','rule','totalAmount','totalDiscount'));
  157. } catch (\Throwable $th) {
  158. return error($th->getMessage());
  159. }
  160. }
  161. /**
  162. * 删除购物
  163. * @param Request $request
  164. * @return Response
  165. */
  166. #[Route(path: "del",methods: "post")]
  167. public function delCart(Request $request): Response
  168. {
  169. try {
  170. $param = $this->_valid([
  171. "shop.require" => trans("empty.require"),
  172. "id.require" => trans("empty.require"),
  173. "print.require" => trans("empty.require"),
  174. ],"post");
  175. if (!is_array($param)) return error($param);
  176. $cart = (new SaasCart)->where("id",$param['id'])->findOrEmpty();
  177. if ($cart->isEmpty()) return error("操作失败");
  178. if ($cart['openid'] <> $request->user['openid']) return error("操作失败");
  179. $state = $cart->delete();
  180. if (!$state) return error("操作失败");
  181. return success("操作成功");
  182. } catch (\Throwable $exception) {
  183. return error($exception->getMessage());
  184. }
  185. }
  186. /**
  187. * 更新购物
  188. * @param Request $request
  189. * @return Response
  190. */
  191. #[Route(path: "update",methods: "post")]
  192. public function updateCart(Request $request): Response
  193. {
  194. try {
  195. $param = $request->post();
  196. $cart = (new SaasCart)->where("id",$param['id'])->where("openid",$request->user['openid'])->findOrEmpty();
  197. if ($cart->isEmpty()) return error('数据格式错误');
  198. if ($param['end_page'] > $cart['total_page']) return error('打印范围不能大于总页数');
  199. // 查询默认打印机是否有额外收费规则
  200. $printData = (new SaasPrintClient)->where("shop_id",$param['shop_id'])->where("code",$param['print'])->findOrEmpty();
  201. $extraMoney = 0;
  202. $moneyMode = (new SaasPrice)->where([
  203. "shop_id" => $param['shop_id'],
  204. "paper_size" => $param['paper_size'],
  205. "type" => $param['source'],
  206. "color" => $param['color'],
  207. "duplex" => $param['duplex'],
  208. ])->findOrEmpty();
  209. if ($moneyMode->isEmpty()) return error("尚未设置收费规则");
  210. if (!empty($printData['price']) && $printData['is_price'] == 1) {
  211. $priceRule = isset($printData['price'][$moneyMode['id']]['price']) ? $printData['price'][$moneyMode['id']]['price'] : 0;
  212. $extraMoney = $priceRule * 100;
  213. }
  214. if ($param['duplex'] == 2) {
  215. $updateData['page'] = ceil(($param['end_page'] - $param['start_page'] + 1) / 2); // 双面
  216. } else {
  217. $updateData['page'] = $param['end_page'] - $param['start_page'] + 1;
  218. }
  219. $updateData['extra_money'] = $extraMoney;
  220. $updateData['money'] = ($moneyMode['price']*100 + $extraMoney) * $updateData['page'] * $param['number'];
  221. $updateData['single_money'] = $moneyMode['price']*100;
  222. $updateData['single_id'] = $moneyMode['id'];
  223. $updateData['paper_size'] = $param['paper_size'];
  224. $updateData['number'] = $param['number'];
  225. $updateData['duplex'] = $param['duplex'];
  226. $updateData['direction'] = $param['direction']??1;
  227. $updateData['color'] = $param['color'];
  228. $updateData['start_page'] = $param['start_page'];
  229. $updateData['end_page'] = $param['end_page'];
  230. $state = $cart->save($updateData);
  231. if (!$state) return error("数据操作失败");
  232. return success("更新成功");
  233. } catch (\Throwable $exception) {
  234. return error($exception->getMessage());
  235. }
  236. }
  237. /**
  238. * 预览
  239. * @return Response
  240. */
  241. #[Route(path: "preview",methods: "post")]
  242. public function wordPreview(): Response
  243. {
  244. try {
  245. return success("",[
  246. "host" => "https://".sConf("storage.cos_http_domain")."/",
  247. "query" => "?ci-process=doc-preview&dstType=jpg&imageDpi=120&page="
  248. ]);
  249. } catch (\Throwable $th) {
  250. return error($th->getMessage());
  251. }
  252. }
  253. /**
  254. * 图片打印
  255. * @return Response
  256. */
  257. #[Route(path: "image",methods: "post")]
  258. public function uploadMultiImage(): Response
  259. {
  260. try {
  261. return success("");
  262. } catch (\Throwable $th) {
  263. return error($th->getMessage());
  264. }
  265. }
  266. /**
  267. * 文档打印
  268. * @return Response
  269. */
  270. #[Route(path: "word",methods: "post")]
  271. public function uploadWord(Request $request): Response
  272. {
  273. try {
  274. $param = $this->_valid([
  275. "shop.require" => trans("empty.require"),
  276. "word.require" => trans("empty.require"),
  277. "print.require" => trans("empty.require"),
  278. "type.default" => 1, // 1打印 2复印
  279. "size.default" => "", // 纸张类型
  280. ],$request->method());
  281. if (!is_array($param)) return error($param);
  282. $wordData = json_decode($param["word"], true);
  283. $printData = (new SaasPrintClient)->where(['shop_id' => $param['shop'],'code' => $param['print']])->findOrEmpty();
  284. if ($printData->isEmpty()) return error('无可用打印机');
  285. if ($printData['status'] <> 1) return error('当前打印机不可用');
  286. $paperRule = is_string($printData['rule'])?json_decode($printData['rule'],true):$printData['rule'];
  287. $paperSize = count($paperRule['paper_size'])==1?$paperRule['paper_size'][0]:'A4';
  288. $colorSize = count($paperRule['color'])==1?$paperRule['color'][0]:2;
  289. $paperSize = empty($param['size'])?$paperSize:$param['size'];
  290. $moneyMode = (new SaasPrice)->where(['shop_id' => $param['shop'],'paper_size' => $paperSize,'color' => $colorSize,'type' => $param['type']])->findOrEmpty();
  291. if ($moneyMode->isEmpty()) return error("店铺未设置收费规则");
  292. $extraMoney = 0;
  293. // 计算额外收费
  294. if (!empty($printData['price']) && $printData['is_price'] == 1) {
  295. $priceRule = isset($printData['price'][$moneyMode['id']]['price']) ? $printData['price'][$moneyMode['id']]['price'] : 0;
  296. $extraMoney = $priceRule * 100 ;
  297. }
  298. $cartData = [];
  299. foreach ($wordData as $key=>$val) {
  300. if ($val['total'] <= 0) {
  301. return error("请检查上传文档是否有显示页数");
  302. }
  303. $cartData[$key] = [
  304. "money" => ($val['total'] * $moneyMode['price'] * 100) + ($extraMoney * $val['total']),
  305. "extra_money" => $extraMoney,
  306. "number" => 1,
  307. "duplex" => 1,
  308. "color" => $colorSize,
  309. "page" => $val['total'],
  310. "total_page" => $val['total'],
  311. "end_page" => $val['total'],
  312. "name" => $val['name'],
  313. "openid" => $request->user['openid'],
  314. "shop_id" => $param['shop'],
  315. "paper_size" => $paperSize,
  316. "extension" => $val['ext'],
  317. "source" => $param['type'],
  318. "icon" => "https://inmei-print.oss-cn-guangzhou.aliyuncs.com/extension/{$val['ext']}.png", // 图标
  319. "single_money" => $moneyMode['price'] * 100,
  320. "single_id" => $moneyMode['id'],
  321. "path" => $val['cosKey'],
  322. "print_id" => $param['print'],
  323. "print_name" => $printData['name'],
  324. ];
  325. }
  326. if (empty($cartData)) return error('上传失败');
  327. $state = (new SaasCart)->insertAll($cartData);
  328. if (!$state) return error("解析文档失败,请重试");
  329. return success("ok");
  330. } catch (\Throwable $th) {
  331. return error($th->getMessage());
  332. }
  333. }
  334. /**
  335. * 读取页码
  336. * ?ci-process=doc-preview&page=1&dstType=jpg&imageDpi=120
  337. * ?ci-process=doc-preview&page=2&sheet=1&excelPaperDirection=0
  338. */
  339. #[Route(path: "total",methods: ['post','get'])]
  340. public function checkTotal(Request $request): Response
  341. {
  342. try {
  343. $param = $this->_valid([
  344. "cosKey.require" => trans("empty.require"),
  345. "type.default" => "",
  346. "ext.default" => ""
  347. ],$request->method());
  348. if (!is_array($param)) return error($param);
  349. $suffix = pathinfo($param['cosKey'], PATHINFO_EXTENSION);
  350. if (empty($suffix)) return error("empty.data.suffix");
  351. return error("请重启小程序");
  352. $cosClient = new Client([
  353. 'region' => sConf("storage.cos_region"),
  354. 'schema' => 'https', // 协议头部,默认为 http
  355. 'credentials' => array(
  356. 'secretId' => sConf("storage.cos_access_key"),
  357. 'secretKey' => sConf("storage.cos_secret_key"),
  358. ),
  359. "verify" => false
  360. ]);
  361. $url = $cosClient->getObjectUrl(sConf("storage.cos_bucket"), $param['cosKey']);
  362. $params = array(
  363. 'ci-process' => 'doc-preview',
  364. 'page' => 1,
  365. 'dstType' => 'jpg',
  366. 'imageDpi' => '120',
  367. );
  368. $query = http_build_query($params);
  369. $path = $url.$query;
  370. $resp = Http::get($path)->headers();
  371. if (!isset($resp['X-Total-Page'])) return error("文档可能需要密码,请先删除后再确认");
  372. $page = $resp['X-Total-Page']?$resp['X-Total-Page'][0]:1;
  373. $path = $param['cosKey'];
  374. return success("ok",compact('page','path'));
  375. } catch (\Throwable $th) {
  376. return error($th->getMessage());
  377. }
  378. }
  379. #[Route(path: "change_total",methods: ['post','get'])]
  380. public function checkChangeStep(Request $request)
  381. {
  382. try {
  383. $param = $this->_valid([
  384. "cosKey.require" => trans("empty.require"),
  385. "type.default" => "",
  386. "ext.default" => ""
  387. ],$request->method());
  388. if (!is_array($param)) return error($param);
  389. $suffix = pathinfo($param['cosKey'], PATHINFO_EXTENSION);
  390. if (empty($suffix)) return error("empty.data.suffix");
  391. $cosClient = new Client([
  392. 'region' => sConf("storage.cos_region"),
  393. 'schema' => 'https', // 协议头部,默认为 http
  394. 'credentials' => array(
  395. 'secretId' => sConf("storage.cos_access_key"),
  396. 'secretKey' => sConf("storage.cos_secret_key"),
  397. ),
  398. "verify" => false
  399. ]);
  400. $url = $cosClient->getObjectUrl(sConf("storage.cos_bucket"), $param['cosKey']);
  401. $params = array(
  402. 'ci-process' => 'doc-preview',
  403. 'page' => 1,
  404. 'dstType' => 'jpg',
  405. 'imageDpi' => '120',
  406. );
  407. $query = http_build_query($params);
  408. $pathUri = $url.$query;
  409. $resp = Http::get($pathUri)->headers();
  410. if (!isset($resp['X-Total-Page'])) return error("文档可能需要密码,请先删除后再确认");
  411. $page = $resp['X-Total-Page']?$resp['X-Total-Page'][0]:1;
  412. if ($param['ext'] !== 'pdf') {
  413. $change = (new SaasWordChange)->where("key",md5($param['cosKey']))->findOrEmpty();
  414. if ($change->isEmpty())
  415. {
  416. $result = $cosClient->CreateDocProcessJobs([
  417. 'Bucket' => sConf("storage.cos_bucket"), // 存储桶名称,由 BucketName-Appid 组成,可以在 COS 控制台查看 https://console.cloud.tencent.com/cos5/bucket
  418. 'Tag' => 'DocProcess', //任务的 Tag:DocProcess 固定值
  419. 'Input' => array(
  420. 'Object' => $param['cosKey'] //待操作的文件对象
  421. ),
  422. 'Operation' => array(
  423. 'DocProcess' => array(
  424. 'SheetId' => 0, //表格文件参数,转换第 X 个表,默认为1
  425. 'StartPage' => 1, //从第 X 页开始转换,默认为1
  426. 'Quality' => 100, //生成预览图的图片质量,取值范围 [1-100],默认值100
  427. 'Zoom' => 100, //预览图片的缩放参数,取值范围[10-200], 默认值100
  428. "ImageDpi" => 300
  429. ),
  430. 'Output' => array(
  431. 'Region' => sConf("storage.cos_region"), //存储桶的地域
  432. 'Bucket' => sConf("storage.cos_bucket"), // 存储结果的存储桶
  433. 'Object' => 'out/'.date('Ymd').'/'.time().md5($param['cosKey']).'-${Number}.pdf', //输出文件路径
  434. ),
  435. ),
  436. ])->toArray();
  437. $change->insertGetId([
  438. "key" => md5($param['cosKey']),
  439. "task_id" => $result['JobsDetail']['JobId']??'',
  440. "total" => $page
  441. ]);
  442. return error("error");
  443. }
  444. $result = $cosClient->describeDocProcessJob(array(
  445. 'Bucket' => sConf("storage.cos_bucket"), // 存储桶名称,由 BucketName-Appid 组成,可以在 COS 控制台查看 https://console.cloud.tencent.com/cos5/bucket
  446. 'Key' => $change['task_id'], // JobId
  447. ))->toArray();
  448. if ($result['JobsDetail']['State'] == 'Success') {
  449. $change->status = 1;
  450. $change->path = $result['JobsDetail']['Operation']['DocProcessResult']['PageInfo'][0]['TgtUri']??'';
  451. $change->end_at = getDateFull();
  452. $change->save();
  453. $page = $change['total'];
  454. $path = $change['path'];
  455. return success("ok",compact("path","page"));
  456. }
  457. return error("Error");
  458. } else {
  459. $path = $param['cosKey'];
  460. return success("ok",compact("path","page"));
  461. }
  462. } catch (\Throwable $throwable) {
  463. return error($throwable->getMessage());
  464. }
  465. }
  466. /**
  467. * 获取打印机支持的纸张
  468. * @param Request $request
  469. * @return Response
  470. */
  471. #[Route(path: "size",methods: ['post','get'])]
  472. public function getPageSize(Request $request): Response
  473. {
  474. try {
  475. $param = $this->_valid([
  476. "shop.require" => trans("empty.require"),
  477. "print.require" => trans("empty.require")
  478. ],$request->method());
  479. if (!is_array($param)) return error($param);
  480. $printData = (new SaasPrintClient)->where(['shop_id' => $param['shop'],'code' => $param['print']])->findOrEmpty();
  481. if ($printData->isEmpty()) return error('无可用打印机');
  482. $paperRule = is_string($printData['rule'])?json_decode($printData['rule'],true):$printData['rule'];
  483. return success("ok",['size' => $paperRule['paper_size']]);
  484. } catch (\Throwable $th) {
  485. return error($th->getMessage());
  486. }
  487. }
  488. }