caipan_shop_admin/app/controller/manager/mall/Order.php

793 lines
29 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\exception\RepositoryException;
use app\exception\TraitException;
use app\model\OrderSku;
use app\model\Express;
use app\repository\OrderRepository;
use app\service\Math;
use Exception;
use PhpOffice\PhpSpreadsheet\IOFactory;
use \PhpOffice\PhpSpreadsheet\Shared\Date as PDate;
use PhpOffice\PhpSpreadsheet\Style\Alignment; //设置对齐方式
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use app\model\Order as OrderModel;
use think\facade\Filesystem;
use think\response\Json;
use think\response\View;
class Order extends Base
{
protected $noNeedLogin = ['getOrderSpu', 'checkSku', 'exportOrderList', 'importOrder'];
/**
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 20);
$searchParams = $this->request->param('searchParams/a', []);
if(!empty(input("get.status/s"))){
$searchParams["status"]= input("get.status/s");
}
$res = $this->getOrderList($searchParams, $page, $size);
return $this->json(0, 'success', $res);
}
if(!empty(input("get.status/s"))){
$this->data['dataUrl'] = "/manager/mall/order/index?status=" . input("get.status/s");
}
$this->data['statusList'] = OrderModel::statusTextList();
return $this->view();
}
/**
* 积分订单
*
* @throws Exception
*/
public function score()
{
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 20);
$searchParams = $this->request->param('searchParams/a', []);
$searchParams['is_score'] = OrderModel::COMMON_ON;
$res = $this->getOrderList($searchParams, $page, $size);
return $this->json(0, 'success', $res);
}
$this->data['statusList'] = OrderModel::statusTextList();
$this->data['dataUrl'] = '/manager/mall/order/score';
$this->data['isScore'] = OrderModel::COMMON_ON;
return $this->view('manager/mall/order/index');
}
/**
* @param array $searchParams
* @param int $page
* @param int $size
* @return array
* @throws Exception
*/
protected function getOrderList(array $searchParams, int $page, int $size): array
{
// 主动触发:自动检测处理过期订单
// OrderRepository::getInstance()->autoCheckInvalidOrders();
$status = $searchParams['status'] ?? '';
$startAt = $searchParams['start_at'] ?? '';
$endAt = $searchParams['end_at'] ?? '';
$hasVirtual = $searchParams['has_virtual'] ?? '';
$isScore = $searchParams['is_score'] ?? 0;
$whereMap = [];
$userWhereMap = [];
$order = ['id' => 'desc'];
$whereMap[] = ['is_score', '=', $isScore];
if (!empty($status) && in_array($status, array_keys(OrderRepository::getInstance()->orderStatusTextList()))) {
$whereMap[] = ['status', '=', $status];
}
if ($hasVirtual != '') {
$whereMap[] = ['has_virtual', '=', $hasVirtual];
}
if (!empty($startAt) && strtotime($startAt)) {
$whereMap[] = ['created_at', '>= TIME', $startAt];
}
if (!empty($endAt) && strtotime($endAt)) {
$whereMap[] = ['created_at', '<= TIME', $endAt];
}
if (isset($searchParams['coding']) && !empty($searchParams['coding'])) {
$whereMap[] = ['coding', '=', $searchParams['coding']];
}
if (isset($searchParams['contacts']) && !empty($searchParams['contacts'])) {
$whereMap[] = ['contacts', 'like', "%".$searchParams['contacts']."%"];
}
if (isset($searchParams['phone']) && !empty($searchParams['phone'])) {
$whereMap[] = ['phone', 'like', "%".$searchParams['phone']."%"];
}
if (isset($searchParams['nickname']) && !empty($searchParams['nickname'])) {
$userWhereMap[] = ['nickname', 'like', "%".$searchParams['nickname']."%"];
}
$res = OrderModel::findList($whereMap, [], $page, $size, function ($q)use($userWhereMap) {
if (!empty($userWhereMap)) {
return $q->hasWhere('account', function ($query) use ($userWhereMap) {
$query->where($userWhereMap);
});
} else {
return $q->with(['account']);
}
}, $order);
$res['list'] = $res['list']->each(function ($item) {
$item->status_text = OrderModel::statusTextList()[$item->status] ?? '';
//$item->price = Math::fen2Yuan($item->price);
$item->nickname = $item->account->nickname ?? '';
$item->real_name = $item->account->real_name ?? '';
$item->pick_self_text = $item->pick_self == 1 ? '自提' : '邮寄';
$item->is_score_text = $item->is_score == 1 ? '是' : '否';
});
return $res;
}
/**
* 获取订单商品列表
*
* @return Json
* @throws Exception
*/
public function getOrderSpu(): Json
{
$coding = input('coding/s', '');
$where = [];
$where[] = ['order_coding', '=', $coding];
$res = OrderSku::findList($where);
$res['list'] = $res['list']->each(function ($item) {
$item->activity_text = \app\model\Spu::activityTextList()[$item->activity_type] ?? '';
});
return $this->json(0, '操作成功', $res);
}
/**
* 核销订单
*
* @return Json
* @throws RepositoryException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function checkSku(): Json
{
$coding = input('coding/s', '');
$id = input('id/d', 0);
$num = input('num/d', 1);
try {
$checkUserInfo = sprintf("ID:%d 昵称:%s", $this->auth['user_id'], $this->auth['nickname']);
OrderRepository::getInstance()->checkBase($coding, $id, $num, $checkUserInfo, OrderModel::CHECK_TYPE_BACKEND);
event('OrderSpuCheck', $coding);
return $this->json(0, '核销成功');
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
/**
* 订单发货、重发(填写快递信息)
* 已有快递记录则覆盖
*
*/
public function send()
{
$repo = OrderRepository::getInstance();
$orderId = $this->request->param('id/d', 0);
try {
$order = $repo->findByid($orderId, [], function ($q) {
return $q->with([
'account', 'skus' => function ($qr) {
$qr->with(['sku']);
}
]);
});
if (empty($order)) {
return $this->json(4004, '没有相关的订单信息!');
}
} catch (RepositoryException $e) {
return $this->json(4004, '没有相关的订单信息!');
}
if ($this->request->isPost()) {
$data = [
'express_number' => trim($this->request->param('express_number/s', '')),
'express_id' => $this->request->param('express_id/d', 0),
'business_remarks' => trim($this->request->param('business_remarks/s', ''))
];
Db::startTrans();
try {
$nowDate = date('Y-m-d H:i:s');
if ($order->pick_self == 1) {
$sendData = [
'shipped_at' => $nowDate,
'business_remarks' => $data['business_remarks'],
];
if (empty($order['shipped_at'])) {
$sendData['shipped_at'] = $nowDate;
}
$sendData['status'] = OrderModel::STATUS_SHIPPED;
} else {
$this->validate($data, [
'express_number|快递单号' => 'require|max:100',
'express_id|快递公司' => 'require|gt:0',
'business_remarks|卖家备注' => 'max:2000',
]);
if (!$express = Express::findById($data['express_id'])) {
throw new ValidateException('没有相关的快递配置信息');
}
if (!in_array($order['status'], [OrderModel::STATUS_PAID, OrderModel::STATUS_SHIPPED])) {
throw new ValidateException('该订单当前状态不支持配送信息的录入');
}
$sendData = [
'express_number' => $data['express_number'],
'express_code' => $express['code'],
'express_name' => $express['name'],
'business_remarks' => $data['business_remarks'],
];
if (empty($order['shipped_at']) || $order['express_number'] != $data['express_number']) {
$sendData['shipped_at'] = $nowDate;
}
if ($order['status'] == OrderModel::STATUS_PAID) {
$sendData['status'] = OrderModel::STATUS_SHIPPED;
}
}
$order->save($sendData);
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(4001, $e->getMessage());
} catch (TraitException | RepositoryException $e) {
Db::rollback();
return $this->json(4002, '配送信息保存失败!');
}
return $this->json();
}
$this->data['id'] = $orderId;
$this->data['item'] = $order;
$this->data['expressJson'] = $this->handleXmExpress([$order->express_code]);
$this->data['statusList'] = OrderModel::statusTextList();
return $this->view();
}
/**
* 订单详情
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function info()
{
$id = input('id/d', 0);
$item = OrderModel::findById($id, [], function ($q) {
return $q->with(['account', 'skus']);
});
if ($this->request->isPost()) {
$data = input("item/a",[]);
$data["shipped_at"] = empty($data["shipped_at"])?null:$data["shipped_at"];
$data["accepted_at"] = empty($data["accepted_at"])?null:$data["accepted_at"];
$data["wedding_date"] = empty($data["wedding_date"])?null:$data["wedding_date"];
$data["expected_delivery_date"] = empty($data["expected_delivery_date"])?null:$data["expected_delivery_date"];
$item->save($data);
return $this->json();
}
$this->data['id'] = $id;
$this->data['item'] = $item;
$this->data['statusList'] = OrderModel::statusTextList();
return $this->view();
}
private function handleXmOrderStatus(array $selected = [], array $disabled = [])
{
$list = [];
$statusList = OrderRepository::getInstance()->orderStatusTextList();
foreach ($statusList as $key => $val) {
$list[] = [
'name' => $val,
'value' => $key,
'selected' => in_array($key, $selected),
'disabled' => in_array($key, $disabled),
];
}
return json_encode($list, JSON_UNESCAPED_UNICODE);
}
private function handleXmExpress(array $selected = [], array $disabled = [])
{
$selected = array_filter($selected);
$disabled = array_filter($disabled);
$list = [];
$items = OrderRepository::getInstance()->allExpress();
foreach ($items as $item) {
$list[] = [
'name' => $item->name,
'value' => $item->id,
'selected' => in_array($item->code, $selected),
'disabled' => in_array($item->code, $disabled),
];
}
return json_encode($list, JSON_UNESCAPED_UNICODE);
}
/**
* 导出订单列表
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function exportOrderList(): Json
{
if ($this->request->isPost()) {
$ids = input('ids/a', []);
$type = input('type/s', 'all');
$isScore = input('is_score/d', 0);
$where = [];
$where[] = ['status', '=', OrderModel::STATUS_PAID];
$where[] = ['pick_self', '=', OrderModel::COMMON_OFF];
if ($type !== 'all') {
//获取指定订单列表
$where[] = ['id', 'in', $ids];
}
$where[] = ['is_score', '=', $isScore];
$list = OrderModel::where($where)->order('id', 'desc')->select();
$result['header'] = [
'订单编号', '订单状态', '下单时间', '付款时间', '配送方式', '收货信息', '买家选择快递', '快递编码', '快递单号', '注1.若快递公司变更,请一定更改快递编码 2.上传物流时请直接在快递单号一栏填写,切勿变更列的顺序'
];
if ($list->isEmpty()) {
return $this->json(200, '没有相应订单');
}
$result['data'] = [];
$statusList = OrderModel::statusTextList();
$list->each(function ($item) use (&$result, $statusList) {
$arr = [
"'".$item['coding'],//订单编号
$statusList[$item['status']] ?? '其他',//订单状态
"'".$item['created_at'],//下单时间
"'".$item['paid_at'],//付款时间
$item['pick_self'] == 1 ? '自提' : '邮寄',//配送方式
$item['pick_self'] == 1 ? '自提' : $item['address'],//收货信息
$item['pick_self'] == 1 ? '自提' : $item['express_name'],//买家选择的快递公司
"'".$item['pick_self'] == 1 ? '' : $item['express_code'],//买家选择等快递编码
'',//快递单号
];
$result['data'][] = $arr;
});
return $this->json(0, 'success', $result);
}
return $this->json(4000, '请求错误');
}
/**
* 导入物流
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function importOrder(): Json
{
if ($this->request->isPost()) {
$fileSrc = input('file/s', '');
$path = public_path().$fileSrc;
if (!file_exists($path)) {
return $this->json(4000, '文件地址错误'.$path);
}
$inputFileType = IOFactory::identify($path);
$reader = IOFactory::createReader($inputFileType);
$spreadsheet = $reader->load($path);
$expressList = Express::column('name', 'code');//快递公司列表
$expressCodeList = array_keys($expressList);//快递编号列表
$sheetData = $spreadsheet->getActiveSheet()->removeRow(1)->toArray(null, true, true, true);
$orderCodingList = [];
$update = [];
$now = date('Y-m-d H:i:s');
foreach ($sheetData as $key => $data) {
$currentKey = $key + 1;
if (!isset($data['A']) || empty($data['A'])) {
return $this->json(4000, '第'.$currentKey.'行记录订单号不存在');
}
if (!isset($data['H']) || !in_array($data['H'], $expressCodeList)) {
return $this->json(4000, '第'.$currentKey.'行快递公司编号不存在');
}
if (!isset($data['I']) || empty($data['I'])) {
continue;
// 快递单号不存在则不更新
// return $this->json(4000, '第'.$currentKey.'行快递单号不存在');
}
$orderCoding = $data['A'];
$expressCode = $data['H'];
$expressNumber = $data['I'];
$orderCodingList[] = $orderCoding;
$update[$orderCoding] = [
'coding' => $orderCoding,
'status' => OrderModel::STATUS_SHIPPED,
'express_code' => $expressCode,
'express_number' => $expressNumber,
'shipped_at' => $now,
'express_name' => $expressList[$expressCode] ?? '',
];
}
$orderList = OrderModel::whereIn('coding', $orderCodingList)->column('id,coding,status', 'coding');
$notUpdateStatus = [];//不更新状态都记录
$existCodings = array_keys($orderList);//数据库存在的订单编号
$coding2Id = [];
// 仅修改快递信息,不修改订单状态的列表 即仅状态=付款的订单修改状态
foreach ($orderList as $order) {
if ($order['status'] != OrderModel::STATUS_PAID) {
$notUpdateStatus[] = $order['coding'];
}
$coding2Id[$order['coding']] = $order['id'];
}
// 存在差异的订单号(数据库不存在的)
$diff = array_diff($orderCodingList, $existCodings);
foreach ($update as $coding => $item) {
if (in_array($coding, $diff) || !isset($coding2Id[$coding])) {
unset($update[$coding]);
continue;
}
$update[$coding]['id'] = $coding2Id[$coding];
if (in_array($coding, $notUpdateStatus)) {
unset($update[$coding]['status']);
}
}
(new OrderModel())->saveAll($update);
return $this->json(0, '操作成功', ['count' => count($update)]);
}
return $this->json(4000, '请求错误');
}
/**
* 修改订单里面的sku信息
*
* @return Json
* @throws Exception
*/
public function editOrderSku(): Json
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'id' => 'require',
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
$orderRep = OrderRepository::getInstance();
if (!$orderSkuInfo = $orderRep->getOrderSku($item['id'])) {
return $this->json(4001, '订单商品记录不存在');
}
if (!$order = OrderModel::findOne([["coding","=",$orderSkuInfo["order_coding"]]])) {
return $this->json(4001, '订单记录不存在');
}
Db::startTrans();
try {
$orderSkuInfo[$item['field']] = $item['value'];
//如果是 修改的字段时 数量或者单价 就要调整
$orderSkuInfo["subtotal"] = $orderSkuInfo["num"] * $orderSkuInfo["price"];
$orderSkuInfo->save();
$orderOriginalPrice = $orderRep->getOrderOriginalPrice($orderSkuInfo["order_coding"],$orderSkuInfo["coding"]);
$orderOriginalPrice += $orderSkuInfo["subtotal"];
$order->save(["original_price"=>$orderOriginalPrice]);
Db::commit();
return $this->json(0,"修改成功",["original_price"=>$orderOriginalPrice]);
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
/**
* 修改订单里面的sku信息
*
* @return Json
* @throws Exception
*/
public function editStatus()
{
$id = input('id/d');
if(!$order = OrderModel::findById($id)){
return $this->json(4001,"订单不存在");
}
if ($this->request->isPost()) {
$status = input("status/s");
Db::startTrans();
try {
$order->save(["status"=>$status]);
Db::commit();
return $this->json();
} catch (Exception $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['statusList'] = OrderModel::statusTextList();
$this->data['status'] = $order["status"];
$this->data['id'] = $id;
return $this->view();
}
/**
* 导出订单信息
* */
public function exportOrderInfo()
{
$id = input("id/d");
$type = input("type/s","save");
$order = OrderModel::findById($id, [], function ($q) {
return $q->with([ 'skus']);
});
// 水平居中对齐
$styleArray = [
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
],
];
// Create new Spreadsheet object
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$spreadsheet->getActiveSheet()->setTitle('订单信息');
//设置默认行高。
$sheet->getDefaultRowDimension()->setRowHeight(30);
//需要设置单元格 上下 水平居中的单元格
$needSetapplyFromArray = [
"A1","C1","D1","A2","B2","C2","D2","E2",
];
// 需要设置的单元格宽度
$setColWidth = [
"A"=>"45",
"B"=>"30",
"C"=>"30",
"D"=>"30",
"E"=>"30",
];
foreach ($setColWidth as $key=>$citem){
$spreadsheet->getActiveSheet()->getColumnDimension($key)->setWidth($citem);
}
//合并单元格
$spreadsheet->getActiveSheet()->mergeCells('A1:B1');
$sheet->setCellValue('A1', "日 期 : ".$order->created_at);
$sheet->setCellValue('C1', "订单号 : ");
$sheet->setCellValue('D1', " " . $order->coding);
$sheet->setCellValue('A2', "商 品 名称");
$sheet->setCellValue('B2', "单 位");
$sheet->setCellValue('C2', "数 量");
$sheet->setCellValue('D2', "单 价");
$sheet->setCellValue('E2', "小 计");
//开始写入订单商品数据
$startRow = 3;//从第四行开始写入
$skusCount = count($order->skus->toArray());
foreach ($order->skus as $key => $item){
$sheet->setCellValue('A'.($startRow+$key), $item["spu_name"]);
$sheet->setCellValue('B'.($startRow+$key), $item["sku_unit"]);
$sheet->setCellValue('C'.($startRow+$key), $item["num"]);
$sheet->setCellValue('D'.($startRow+$key), $item["price"]);
$sheet->setCellValue('E'.($startRow+$key), $item["subtotal"]);
$needSetapplyFromArray[]='A'.($startRow+$key);
$needSetapplyFromArray[]='B'.($startRow+$key);
$needSetapplyFromArray[]='C'.($startRow+$key);
$needSetapplyFromArray[]='D'.($startRow+$key);
$needSetapplyFromArray[]='E'.($startRow+$key);
}
$needSetapplyFromArray[] = 'D' . ($startRow + $skusCount);
$needSetapplyFromArray[] = 'E' . ($startRow + $skusCount);
$sheet->setCellValue('D' . ($startRow + $skusCount), "合 计");
$sheet->setCellValue('E' . ($startRow + $skusCount), $order->original_price);
//单元格文字样式设置
// getStyle 获取单元格样式
// getFont 获取单元格文字样式
// setBold 设置文字粗细
// setName 设置文字字体
// setSize 设置文字大小
$sheet->getStyle('D' . ($startRow + $skusCount))->getFont()->setBold(true)->setSize(18);
$sheet->getStyle('E' . ($startRow + $skusCount))->getFont()->setBold(true)->setSize(18);
$sheet->getStyle('D1')->getFont()->setBold(true);
$sheet->getStyle('A2:E2')->getFont()->setBold(true);
$sheet->getStyle('C3:E' . ($startRow + $skusCount - 1))->getFont()->setBold(true);
$sheet->getStyle('A1:E' . ($startRow + $skusCount - 1))->getFont()->setSize(12);
$sheet->getStyle('A' . (($startRow + $skusCount + 1)))->getFont()->setSize(12);
//设置字体颜色
$sheet->getStyle('D' . ($startRow + $skusCount))->getFont()->getColor()->setARGB(Color::COLOR_RED);
$sheet->getStyle('E' . ($startRow + $skusCount))->getFont()->getColor()->setARGB(Color::COLOR_RED);
$spreadsheet->getActiveSheet()->mergeCells('A' . ($startRow + $skusCount + 1) . ":" . 'E'.($startRow + $skusCount + 2));
$sheet->setCellValue('A' . (($startRow + $skusCount + 1)),
"婚 期:" . $order->wedding_date
. " ,希望到货时间:" . $order->expected_delivery_date
. " ,收 货 地 址:" . $order->address
. " ,联 系 人 :" . $order->contacts
. " ,联 系 人 电 话:" . $order->phone
);
$needSetapplyFromArray[] = 'A' . (($startRow + $skusCount + 1));
//设置单元格 上下 水平居中
foreach ($needSetapplyFromArray as $key => $item) {
$sheet->getStyle($item)->applyFromArray($styleArray);
}
//第一种保存方式
$writer = new Xlsx($spreadsheet);
//保存的路径可自行设置
$path = "storage/order_excel/" ;
if(!is_writable($path)){
return $this->json(4001,"上传文件夹需要写入权限");
}
$path.=date("Ymd")."/";
if(!is_dir(public_path().$path)){
mkdir(public_path().$path);
}
$downloadFileName = $order->contacts . "_" . $order->phone . "_" . $order->coding ;
$fileName = $downloadFileName . ".xlsx";
$filepath = public_path().$path. $fileName;
$writer->save($filepath);
if($type =="save"){
return download($filepath,$downloadFileName );
}else{
return $this->json(0,"success",["url"=>$path.$fileName]);
}
}
/**
* 导出订单信息
* */
public function exportOrderInfoZip()
{
$urls = input("urls/s",'');
$urls = array_filter(explode(",", $urls));
if(empty($urls)){
return $this->json("4001","空的文件");
} if(count($urls)>10){
return $this->json("4001","最多支持10个文件");
}
$zipdir = date("Ymd_His_") .randomStr();
$downloadZipFilename = $zipdir. '.zip';
$zipFilename = public_path() . 'storage/order_excel_zip/' . $downloadZipFilename;
$zip = new \ZipArchive();
// 打开一个zip文档ZipArchive::OVERWRITE如果存在这样的文档则覆盖ZipArchive::CREATE如果不存在则创建
$res = $zip->open($zipFilename, $zip::OVERWRITE | $zip::CREATE);
$zip->addEmptyDir($zipdir);
if($res){
foreach ($urls as $url) {
$filrnamearr = explode("/",$url);
$filrname = end($filrnamearr);
$zip->addFile(public_path() . $url, $zipdir . "/" . $filrname);
}
$zip->close();
return download($zipFilename, $downloadZipFilename);
}
return $this->json(5001,"创建压缩文件失败");
}
public function del()
{
$id = input("id/d",0);
$order = OrderModel::findById($id);
if(empty($order)){
return $this->json(4001,"订单不存在");
}
Db::startTrans();
try {
OrderSku::where("order_coding",$order["coding"])->delete();
$order->delete();
Db::commit();
return $this->json();
}catch (Exception $e){
Db::rollback();
return $this->json(5001,"删除订单失败");
}
}
}