Files
hyperf_service/app/Service/Admin/Catering/Option/CateringService.php
2025-04-02 10:39:23 +08:00

602 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* This service file is part of item.
*
* @author ctexthuang
* @contact ctexthuang@qq.com
*/
declare(strict_types=1);
namespace App\Service\Admin\Catering\Option;
use App\Constants\Admin\CateringCode;
use App\Constants\Common\OrderCode;
use App\Constants\ConfigCode;
use App\Exception\ErrException;
use App\Extend\StringUtil;
use App\Model\Order;
use App\Model\OrderGood;
use App\Model\OrderOptionCateringLog;
use App\Model\PickupCode;
use App\Model\Site;
use App\Model\User;
use App\Service\Admin\Catering\CateringBaseService;
use App\Service\Admin\Catering\Print\PrintOrderFactory;
use App\Service\ServiceTrait\Admin\Catering\PrintTrait;
use Exception;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class CateringService extends CateringBaseService
{
use PrintTrait;
/**
* @var OrderOptionCateringLog
*/
#[Inject]
protected OrderOptionCateringLog $orderOptionCateringLogModel;
/**
* @var Order
*/
#[Inject]
protected Order $orderModel;
/**
* @var OrderGood
*/
#[Inject]
protected OrderGood $orderGoodModel;
/**
* @var User
*/
#[Inject]
protected User $userModel;
/**
* @var OrderOptionCateringLog
*/
protected OrderOptionCateringLog $logInfo;
/**
* @var Site
*/
protected Site $siteInfo;
/**
* @var array
*/
protected array $orderList = [];
/**
* @var array
*/
protected array $orderIds = [];
/**
* @var int
*/
protected int $totalCopies = 0;
/**
* @var int
*/
protected int $printBoxMaxNum;
/**
* @var int
*/
protected int $partitionCount;
/**
* @var int
*/
protected int $copiesCount;
public function __construct()
{
parent::__construct();
$this->printBoxMaxNum = (int)$this->configCache->getConfigValueByKey(ConfigCode::MAXIMUM_VALUE_IN_FULL_BOX);
$this->partitionCount = (int)$this->configCache->getConfigValueByKey(ConfigCode::MAXIMUM_WHOLE_CASE_SPLIT);
}
/**
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function handle(): array
{
$id = (int)$this->request->input('id');
// 获取数据源
$this->logInfo = $this->orderOptionCateringLogModel->find($id);
$this->siteInfo = $this->siteModel->find($this->logInfo->site_id);
// 检测数据
$this->check();
// 截单 打印按钮置灰
$this->closePrintAndPlaceOrder();
// 获取订单数据
$this->getOrderData();
// 构建取餐码
$this->copiesCount = $this->buildPickupCode();
if (empty($this->orderList)) throw new ErrException('无数据打印');
// 打印或者喷码
$res = match ($this->request->input('type')) {
CateringCode::OPTION_PRINT_YLY => $this->printOrderByYly(),
CateringCode::OPTION_PRINT_CODING => $this->printOrderByCoding(),
};
// 关闭配餐
$this->catering();
return $this->return->success('success', ['list' => $res]);
}
/**
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function catering(): void
{
if ($this->isCateringByCache()) return;
try {
Db::beginTransaction();
$this->logInfo->status = CateringCode::CATERING_STATUS_FINISH;
if (!$this->logInfo->save()) throw new Exception('配餐失败1');
if (!$this->orderModel->isCateringByOrderIds($this->orderIds) || !$this->orderGoodModel->isCateringByOrderIds($this->orderIds)) throw new Exception('配餐失败2');
Db::commit();
$this->joinCateringCache();
} catch (Exception $exception) {
Db::rollback();
throw new ErrException($exception->getMessage());
}
}
/**
* @return int
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function buildPickupCode(): int
{
if ($this->isBuildPickupCode()) {
return $this->getPickupCode();
}
$currentCode = 0;
$prefix = strtoupper(StringUtil::randStr(3));
$codeRanges = [];
$takeFoodCodes = [];
$currentDate = date('Y-m-d H:i:s');
foreach ($this->orderList as &$order) {
foreach ($order['copies_list'] as &$copiesItem) {
if ($currentCode / $this->printBoxMaxNum === 0) {
$startCode = $currentCode + 1;
$endCode = min($currentCode + $this->printBoxMaxNum, $this->totalCopies);
$codeGroups = $this->splitArrayIntoPartitions(range($startCode, $endCode));
$codeRanges = $this->generateCodeRanges($codeGroups, $prefix);
}
$currentCode++;
$paddedCode = str_pad((string)$currentCode, 3, '0', STR_PAD_LEFT);
$fullCode = "$prefix-$paddedCode";
$copiesItem['take_food_code'] = $fullCode;
$copiesItem['heapsort'] = $codeRanges[$currentCode] ?? '';
$takeFoodCodes[] = [
'order_id' => $order['id'],
'copies' => $copiesItem['copies'],
'pickup_code' => $fullCode,
'heapsort' => $copiesItem['heapsort'],
'create_time' => $currentDate
];
}
}
$this->insertTakeFoodCode($takeFoodCodes);
unset($currentCode,$prefix,$codeRanges,$currentDate,$startCode,$endCode,$codeGroups,$paddedCode,$fullCode);
return count($takeFoodCodes);
}
/**
* @var PickupCode
*/
#[Inject]
protected PickupCode $pickupCodeModel;
/**
* @return int
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function getPickupCode(): int
{
$pickupCodeList = $this->pickupCodeModel
->whereIn('order_id',$this->orderIds)
->get(['take_food_code', 'order_id', 'box_num', 'heapsort', 'id'])
->toArray();
if (empty($pickupCodeList)) {
$this->delPickUpCodeByIdCache();
throw new ErrException('找不到数据,请重新生成');
}
$pickupCodeArrList = $this->buildPickupCodeListData($pickupCodeList);
foreach ($this->orderList as &$order) {
foreach ($order['copies_list'] as &$copiesItem) {
$copiesItem['take_food_code'] = $pickupCodeArrList[$copiesItem['id']][$copiesItem]['take_food_code'];
$copiesItem['heapsort'] = $pickupCodeArrList[$copiesItem['id']][$copiesItem]['heapsort'];
}
}
unset($pickupCodeArrList);
return count($pickupCodeList);
}
/**
* @param array $codeRanges
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function insertTakeFoodCode(array $codeRanges): void
{
try {
Db::beginTransaction();
$delRes = $this->pickupCodeModel->whereIn('order_id',$this->orderIds)->delete();
$insertModel = new PickupCode();
$insertRes = $insertModel->insert($codeRanges);
if (!$insertRes || !$delRes) throw new Exception('取餐码生成失败');
$this->buildPickupCodeCache();
Db::commit();
}catch (Exception $e) {
Db::rollback();
throw new ErrException($e->getMessage());
}
}
/**
* 将数组尽可能均匀地分割为指定份数
* @param array $array 输入数组
* @return array
*/
private function splitArrayIntoPartitions(array $array): array
{
$partitionCount = $this->partitionCount;
$totalElements = count($array);
$baseChunkSize = intval($totalElements / $partitionCount);
$remainder = $totalElements % $partitionCount;
$result = [];
$currentIndex = 0;
for ($partition = 0; $partition < $partitionCount; $partition++) {
$chunkSize = $baseChunkSize + ($partition < $remainder ? 1 : 0);
if ($chunkSize === 0) {
$result[] = [];
continue;
}
$result[] = array_slice($array, $currentIndex, $chunkSize);
$currentIndex += $chunkSize;
}
return $result;
}
/**
* 生成编码范围映射表
* @param array $codeGroups 编码分组数组
* @param string $prefix 前缀字符串
* @return array
*/
private function generateCodeRanges(array $codeGroups, string $prefix): array {
$rangeMap = [];
foreach ($codeGroups as $group) {
if (empty($group)) continue;
$minCode = min($group);
$maxCode = max($group);
$rangeValue = sprintf("%03d%03d", $minCode, $maxCode);
foreach ($group as $code) {
$rangeMap[$code] = $rangeValue;
}
}
return $rangeMap;
}
/**
* @return void
*/
private function getOrderData(): void
{
$orderList = $this->orderModel
->where('site_id', $this->siteInfo->id)
->where('cycle_id', $this->cycleId)
->where('type',OrderCode::ORDER_TYPE_OPTIONAL)
->where('status',OrderCode::PAYED)
->orderBy('id')
->select(['id','cycle_id','order_sno','user_id','copies'])
->get();
if ($orderList->isEmpty()) throw new ErrException('该点暂无自选订单');
$orderList = $orderList->toArray();
$orderIds = array_column($orderList,'id');
$orderGoodArr = $this->orderGoodModel->whereIn('order_id',$orderIds)->select(['order_id','sku_id','quantity','copies'])->get();
if ($orderGoodArr->isEmpty()) throw new ErrException('商品数据丢失,请联系管理员');
$orderGoodArr = $orderGoodArr->toArray();
$userIds = array_column($orderList,'user_id');
$userInfos = $this->userModel->whereIn('id',$userIds)->select(['nickname', 'mobile', 'id'])->get();
if ($userInfos->isEmpty()) throw new ErrException('用户数据丢失,请联系管理员');
$userInfos = array_column($userInfos->toArray(),null,'id');
$skuIds = array_unique(array_column($orderGoodArr,'sku_id')); //todo code_number 还没写
$skuCodeNumberArr = $this->skuModel->whereIn('id',$skuIds)->pluck('code_number','id')->toArray();
$orderGoodArr = $this->buildOrderCopiesData($orderGoodArr,$skuCodeNumberArr);
// $adminInfo = $this->adminUserModel->find($this->siteInfo->delivered_id);
$driverInfo = $this->driverSequenceModel->where('driver_id',$this->siteInfo->delivered_id)->first();
if (empty($driverInfo)) throw new ErrException('配送员信息丢失,请联系管理员');
foreach ($orderList as &$one) {
$one['dates'] = date('Y-m-d');
$one['site_text'] = $this->siteInfo->name;
$one['nickname'] = $userInfos[$one['user_id']]['nickname'] ?? '';
$one['mobile'] = $userInfos[$one['user_id']]['mobile'] ?? '';
$one['driver_num'] = $driverInfo->driver_num;
$one['site_order'] = $this->siteInfo->sequence;
$one['real_name'] = $driverInfo->driver_name;
$one['copies_list'] = array_values($orderGoodArr[$one['id']]);
}
$this->orderList = $orderList;
$this->orderIds = $orderIds;
$this->totalCopies = array_sum(array_column($orderList,'copies'));
//节省内存 优化协程内存占用 防止内存泄露
unset($orderGoodArr,$skuCodeNumberArr,$skuIds,$userInfos,$userIds,$orderIds,$orderList);
}
/**
* @param array $data
* @param array $skuInfoArr
* @return array
*/
private function buildOrderCopiesData(array $data, array $skuInfoArr): array
{
$result = [];
foreach ($data as $item) {
// 提取关键字段
$orderId = $item["order_id"];
$copies = $item["copies"];
$skuId = $item["sku_id"];
$quantity= $item["quantity"];
// 按层级构建数组结构
if (!isset($result[$orderId])) {
$result[$orderId] = [];
}
if (!isset($result[$orderId][$copies])) {
$result[$orderId][$copies] = [
'copies' => $copies,
'sku' => []
];
}
$result[$orderId][$copies]['sku'][] = [
'id' => $skuId,
'num' => $quantity,
'code_num' => empty($skuInfoArr[$skuId]) ? 0 : (int)$skuInfoArr[$skuId],
];
}
return $result;
}
/**
* @param array $data
* @return array
*/
private function buildPickupCodeListData(array $data): array
{
$result = [];
foreach ($data as $item) {
// 提取关键字段
$orderId = $item["order_id"];
$copies = $item["copies"];
$pickupCode = $item["pickup_code"];
$heapsort = $item["heapsort"];
// 按层级构建数组结构
if (!isset($result[$orderId])) {
$result[$orderId] = [];
}
if (!isset($result[$orderId][$copies])) {
$result[$orderId][$copies] = [];
}
$result[$orderId][$copies][] = [
'pickup_code' => $pickupCode,
'heapsort' => $heapsort,
];
}
return $result;
}
/**
* 打印置灰和截单
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function closePrintAndPlaceOrder(): void
{
//生成 key
$this->__initRedisKey();
//打印一次后置灰
$this->closePrint();
//点位截单
$this->closeSite();
//如果一条线的所有点位截单 则整线截单
$this->closeWholeLine();
}
/**
* 检测数据
* @return void
*/
private function check(): void
{
if (empty($this->logInfo)) throw new ErrException('配餐数据不存在');
if ($this->logInfo->quantity <= 0) throw new ErrException('该配餐数量为0不可配餐');
}
/**
* @var PrintOrderFactory
*/
#[Inject]
protected PrintOrderFactory $printOrderFactory;
/**
* @return true
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function printOrderByYly(): true
{
$service = $this->printOrderFactory->handle($this->request->input('type'));
$service->printId = (int)$this->request->input('print_id');
$service->handle();
$printBoxNum = 0;
$printCurrentNum = 0;
foreach ($this->orderList as $one) {
foreach ($one['copies_list'] as $copiesItem) {
// 先加当前数量
$printBoxNum++;
//todo 数据加载做一下
$service->data = [
'pickup_code' => $copiesItem['pickup_code'],
'driver_num' => '',
'site_order' => '',
'site_text' => '',
'order_sno' => '',
'username' => '',
'date' => '',
'mobile' => '',
'heapsort' => $copiesItem['heapsort'],
'sku' => $copiesItem['sku'],
];
// 打印单份
$service->printList();
// 当前数量等于最大值 归零
if ($printBoxNum == $this->printBoxMaxNum || $this->printBoxMaxNum * $printCurrentNum+$printBoxNum == $this->copiesCount) {
$printBoxNum = 0;
$printCurrentNum++;
}
//不归零跳出 当前循环
if ($printBoxNum != 0) continue;
$service->data = [
'driver_num' => '',
'site_order' => '',
'site_text' => '',
'driver_name' => '',
'current_num' => $printCurrentNum,
'date_text' => date('Y-m-d'),
'copies_num' => $this->copiesCount,
];
// 打印标签
$service->printBoxLabel();
}
}
return true;
}
/**
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function printOrderByCoding(): array
{
$service = $this->printOrderFactory->handle($this->request->input('type'));
$printBoxNum = 0;
$printCurrentNum = 0;
$data=[];
foreach ($this->orderList as $one) {
foreach ($one['copies_list'] as $copiesItem) {
$service->data = [
'pickup_code' => $copiesItem['pickup_code'],
'driver_num' => '',
'site_order' => '',
'site_text' => '',
'order_sno' => '',
'username' => '',
'date' => '',
'mobile' => '',
'heapsort' => $copiesItem['heapsort'],
'sku' => $copiesItem['sku'],
];
$data[] = $service->printList();
//先加当前数量
$printBoxNum++;
//当前数量等于最大值 归零
if ($printBoxNum == $this->printBoxMaxNum || $this->printBoxMaxNum*$printCurrentNum+$printBoxNum == $this->copiesCount) {
$printBoxNum = 0;
$printCurrentNum++;
}
//不归零跳出 当前循环
if ($printBoxNum != 0) continue;
//归零打印
// $service->printInfo['current_num'] = $printCurrentNum;
// $service->printInfo['date_text'] = date('Y-m-d',$this->todayDate);
// $service->printInfo['box_num'] = $boxNum;
// $service->printBoxLabel();
}
}
return $data;
}
}