Compare commits

..

1 Commits

Author SHA1 Message Date
p3irbfynx b25022ffd8 Add duyunpeng
8 months ago

3234
122

File diff suppressed because it is too large Load Diff

3234
444

File diff suppressed because it is too large Load Diff

@ -1,47 +1,44 @@
<?php
namespace App\Admin\Extensions;
use Illuminate\Contracts\Support\Renderable; // 引入 Renderable 接口
use Illuminate\Contracts\Support\Renderable;
class Div implements Renderable
{
protected $id; // div 元素的 ID
protected $width = '100%'; // div 元素的宽度,默认值为 100%
protected $height = '300px'; // div 元素的高度,默认值为 300px
protected $id;
protected $width = '100%';
protected $height = '300px';
/**
* Div constructor.
*
* @param string $id div 元素的 ID
* @param string|null $width div 元素的宽度,默认为 null
* @param string|null $height div 元素的高度,默认为 null
* @param $id
* @param string $width
* @param string $height
*/
public function __construct($id, $width = null, $height = null)
{
$this->id = $id; // 设置 div 的 ID
$this->id = $id;
// 如果提供了宽度,则设置宽度
if (!is_null($width)) {
$this->width = $width; // 更新 div 的宽度
if (! is_null($width)) {
$this->width = $width;
}
// 如果提供了高度,则设置高度
if (!is_null($height)) {
$this->height = $height; // 更新 div 的高度
if (! is_null($height)) {
$this->height = $height;
}
}
/**
* 渲染 div 元素的 HTML.
*
* @return string 返回包含 div 的 HTML 字符串
*/
public function render()
{
// 使用 heredoc 语法返回包含 div 的 HTML 代码
return <<<div
<div id="{$this->id}" style="width: {$this->width}; height: {$this->height}"></div>
div;
}
}

@ -1,69 +1,56 @@
<?php
namespace App\Admin\Extensions;
use Illuminate\Support\Collection; // 引入集合类
use Encore\Admin\Form\Field; // 引入表单字段基类
use Illuminate\Support\Collection;
use Encore\Admin\Form\Field;
class WangEditor extends Field
{
protected $view = 'admin.wang-editor'; // 定义视图文件路径
protected $view = 'admin.wang-editor';
// 引入 WangEditor 的 CSS 文件
protected static $css = [
'/vendor/wangEditor-3.1.1/release/wangEditor.css',
];
// 引入 WangEditor 的 JS 文件
protected static $js = [
'/vendor/wangEditor-3.1.1/release/wangEditor.js',
];
/**
* 渲染 WangEditor 编辑器.
*
* @return string 返回渲染后的 HTML
*/
public function render()
{
// 格式化字段名称
$name = $this->formatName($this->column);
// 获取 CSRF 令牌
$token = csrf_token();
// 定义上传图片的服务器地址
$url = admin_base_path('upload/editor');
// 定义 JavaScript 代码,初始化 WangEditor
$this->script = <<<EOT
var E = window.wangEditor; // 获取 WangEditor 对象
var editor = new E('#{$this->id}'); // 创建编辑器实例,绑定到指定的 HTML 元素
editor.customConfig.zIndex = 0; // 设置编辑器的 z-index
editor.customConfig.uploadFileName = 'pictures[]'; // 设置上传文件的名称
var E = window.wangEditor
var editor = new E('#{$this->id}');
editor.customConfig.zIndex = 0
editor.customConfig.uploadFileName = 'pictures[]'
// 配置服务器端地址
editor.customConfig.uploadImgServer = '{$url}'; // 设置图片上传的服务器地址
editor.customConfig.uploadImgServer = '{$url}'
editor.customConfig.uploadImgParams = {
_token: '{$token}' // 设置上传参数,包括 CSRF 令牌
};
// 文件改变时,将 HTML 内容添加到隐藏域
_token: '{$token}'
}
// 文件改变添加内容到隐藏域
editor.customConfig.onchange = function (html) {
$('input[name=\'$name\']').val(html); // 更新隐藏域的值为编辑器内容
$('input[name=\'$name\']').val(html);
}
// 监听上传错误
editor.customConfig.uploadImgHooks = {
fail: function (xhr, editor) {
var response = $.parseJSON(xhr.response); // 解析服务器返回的 JSON
alert(response.msg); // 弹出错误信息
var response = $.parseJSON(xhr.response);
alert(response.msg);
}
};
editor.create(); // 创建编辑器实例
}
editor.create()
EOT;
return parent::render(); // 调用父类的 render 方法,返回最终的 HTML
return parent::render();
}
}

@ -1,37 +1,26 @@
<?php
namespace App\Admin\Transforms; // 定义命名空间
namespace App\Admin\Transforms;
use App\Enums\OrderPayTypeEnum; // 引入订单支付类型枚举类
use App\Enums\OrderShipStatusEnum; // 引入订单发货状态枚举类(未使用)
use App\Enums\OrderStatusEnum; // 引入订单状态枚举类(未使用)
use App\Enums\OrderTypeEnum; // 引入订单类型枚举类(未使用)
use App\Models\Order; // 引入订单模型(未使用)
/**
* 订单支付类型转换类
*
* 该类用于将订单支付类型的枚举值转换为可读的字符串形式。
*/
use App\Enums\OrderPayTypeEnum;
use App\Enums\OrderShipStatusEnum;
use App\Enums\OrderStatusEnum;
use App\Enums\OrderTypeEnum;
use App\Models\Order;
class OrderPayTypeTransform implements Transform
{
/**
* 将支付类型转换为对应的可读字符串
*
* @param mixed $type 支付类型的枚举值
* @return string 返回对应的支付方式名称
*/
public static function trans($type)
{
$text = ''; // 初始化返回的字符串
$text = '';
// 根据支付类型的枚举值进行判断并设置对应的字符串
if ($type == OrderPayTypeEnum::ALI) {
$text = '支付宝'; // 支付宝支付
$text = '支付宝';
} elseif ($type == OrderPayTypeEnum::WECHAT) {
$text = '微信'; // 微信支付
$text = '微信';
}
return $text; // 返回转换后的支付方式名称
return $text;
}
}

@ -1,43 +1,33 @@
<?php
namespace App\Admin\Transforms; // 定义命名空间
namespace App\Admin\Transforms;
use App\Enums\OrderShipStatusEnum; // 引入订单发货状态枚举类
use App\Enums\OrderStatusEnum; // 引入订单状态枚举类(未使用)
use App\Enums\OrderTypeEnum; // 引入订单类型枚举类(未使用)
use App\Models\Order; // 引入订单模型(未使用)
/**
* 订单发货状态转换类
*
* 该类用于将订单发货状态的枚举值转换为可读的字符串形式。
*/
use App\Enums\OrderShipStatusEnum;
use App\Enums\OrderStatusEnum;
use App\Enums\OrderTypeEnum;
use App\Models\Order;
class OrderShipStatusTransform implements Transform
{
/**
* 将发货状态转换为对应的可读字符串
*
* @param mixed $status 发货状态的枚举值
* @return string 返回对应的发货状态名称
*/
public static function trans($status)
{
// 根据发货状态的枚举值进行判断并设置对应的字符串
switch ($status) {
case OrderShipStatusEnum::PENDING:
$text = '未发货'; // 订单尚未发货
$text = '未发货';
break;
case OrderShipStatusEnum::DELIVERED:
$text = '待收货'; // 订单已发货,等待用户收货
$text = '待收货';
break;
case OrderShipStatusEnum::RECEIVED:
$text = '已收货'; // 用户已确认收货
$text = '已收货';
break;
default:
$text = '未知状态'; // 未知的发货状态
$text = '未知状态';
break;
}
return $text; // 返回转换后的发货状态名称
return $text;
}
}

@ -1,55 +1,45 @@
<?php
namespace App\Admin\Transforms; // 定义命名空间
namespace App\Admin\Transforms;
use App\Enums\OrderShipStatusEnum; // 引入订单发货状态枚举类(未使用)
use App\Enums\OrderStatusEnum; // 引入订单状态枚举类
use App\Enums\OrderTypeEnum; // 引入订单类型枚举类(未使用)
use App\Models\Order; // 引入订单模型(未使用)
/**
* 订单状态转换类
*
* 该类用于将订单状态的枚举值转换为可读的字符串形式。
*/
use App\Enums\OrderShipStatusEnum;
use App\Enums\OrderStatusEnum;
use App\Enums\OrderTypeEnum;
use App\Models\Order;
class OrderStatusTransform implements Transform
{
/**
* 将订单状态转换为对应的可读字符串
*
* @param mixed $status 订单状态的枚举值
* @return string 返回对应的订单状态名称
*/
public static function trans($status)
{
// 根据订单状态的枚举值进行判断并设置对应的字符串
switch ($status) {
case OrderStatusEnum::UN_PAY_CANCEL:
$text = '取消'; // 订单已被取消
$text = '取消';
break;
case OrderStatusEnum::REFUND:
$text = '退款'; // 订单已退款
$text = '退款';
break;
case OrderStatusEnum::APPLY_REFUND:
$text = '申请退款'; // 用户已申请退款
$text = '申请退款';
break;
case OrderStatusEnum::UN_PAY:
$text = '未支付'; // 订单尚未支付
$text = '未支付';
break;
case OrderStatusEnum::PAID:
$text = '已支付'; // 订单已成功支付
$text = '已支付';
break;
case OrderStatusEnum::TIMEOUT_CANCEL:
$text = '超时未付款系统自动取消'; // 订单因超时未付款被系统自动取消
$text = '超时未付款系统自动取消';
break;
case OrderStatusEnum::COMPLETED:
$text = '完成'; // 订单已完成
$text = '完成';
break;
default:
$text = '未知状态'; // 未知的订单状态
$text = '未知状态';
break;
}
return $text; // 返回转换后的订单状态名称
return $text;
}
}

@ -1,37 +1,25 @@
<?php
namespace App\Admin\Transforms; // 定义命名空间
namespace App\Admin\Transforms;
use App\Enums\OrderShipStatusEnum; // 引入订单发货状态枚举类(未使用)
use App\Enums\OrderStatusEnum; // 引入订单状态枚举类(未使用)
use App\Enums\OrderTypeEnum; // 引入订单类型枚举类
use App\Models\Order; // 引入订单模型(未使用)
/**
* 订单类型转换类
*
* 该类用于将订单类型的枚举值转换为可读的字符串形式。
*/
use App\Enums\OrderShipStatusEnum;
use App\Enums\OrderStatusEnum;
use App\Enums\OrderTypeEnum;
use App\Models\Order;
class OrderTypeTransform implements Transform
{
/**
* 将订单类型转换为对应的可读字符串
*
* @param mixed $type 订单类型的枚举值
* @return string 返回对应的订单类型名称
*/
public static function trans($type)
{
// 默认状态为'未知'
$text = '未知';
// 根据订单类型的枚举值进行判断并设置对应的字符串
if ($type == OrderTypeEnum::COMMON) {
$text = '普通订单'; // 订单类型为普通订单
$text = '普通订单';
} elseif ($type == OrderTypeEnum::SEC_KILL) {
$text = '秒杀订单'; // 订单类型为秒杀订单
$text = '秒杀订单';
}
return $text; // 返回转换后的订单类型名称
return $text;
}
}

@ -1,19 +1,9 @@
<?php
namespace App\Admin\Transforms; // 定义命名空间
namespace App\Admin\Transforms;
/**
* 转换接口
*
* 该接口定义了一个转换方法,所有实现此接口的类都需提供具体的转换逻辑。
*/
interface Transform
{
/**
* 转换方法
*
* @param mixed $trans 需要转换的值,可以是任何类型
* @return mixed 返回转换后的值,类型可以根据具体实现而定
*/
public static function trans($trans);
}

@ -1,36 +1,24 @@
<?php
namespace App\Admin\Transforms; // 定义命名空间
namespace App\Admin\Transforms;
use App\Enums\UserSexEnum; // 引入用户性别枚举类
use App\Enums\UserStatusEnum; // 引入用户状态枚举类(未使用)
use App\Models\User; // 引入用户模型(未使用)
/**
* 用户性别转换类
*
* 该类用于将用户性别的枚举值转换为可读的字符串形式。
*/
use App\Enums\UserSexEnum;
use App\Enums\UserStatusEnum;
use App\Models\User;
class UserSexTransform implements Transform
{
/**
* 将用户性别转换为对应的可读字符串
*
* @param mixed $sex 用户性别的枚举值
* @return string 返回对应的用户性别名称
*/
public static function trans($sex)
{
// 默认状态为'未知'
$text = '未知';
// 根据用户性别的枚举值进行判断并设置对应的字符串
if ($sex == UserSexEnum::MAN) {
$text = '男'; // 用户性别为男性
$text = '男';
} elseif ($sex == UserSexEnum::WOMAN) {
$text = '女'; // 用户性别为女性
$text = '女';
}
return $text; // 返回转换后的用户性别名称
return $text;
}
}

@ -1,27 +1,16 @@
<?php
namespace App\Admin\Transforms; // 定义命名空间
namespace App\Admin\Transforms;
use App\Enums\UserStatusEnum; // 引入用户状态枚举类(未使用)
/**
* 是/否转换类
*
* 该类用于将布尔值转换为相应的图标表示形式。
*/
use App\Enums\UserStatusEnum;
class YesNoTransform implements Transform
{
/**
* 将布尔值转换为对应的图标表示
*
* @param bool $is 布尔值,用于表示是或否
* @return string 返回相应的 HTML 图标字符串
*/
public static function trans($is)
{
// 返回对应的图标,绿色勾表示“是”,红色叉表示“否”
return $is
? "<i style='color: green;' class=\"fa fa-check-circle\" aria-hidden=\"true\"></i>" // 当 $is 为 true 时,返回绿色勾图标
: "<i style='color: red;' class=\"fa fa-times\" aria-hidden=\"true\"></i>"; // 当 $is 为 false 时,返回红色叉图标
? "<i style='color: green;' class=\"fa fa-check-circle\" aria-hidden=\"true\"></i>"
: "<i style='color: red;' class=\"fa fa-times\" aria-hidden=\"true\"></i>";
}
}

@ -1,83 +1,76 @@
<?php
use App\Admin\Controllers\CouponLogController; // 引入优惠券日志控制器
use Illuminate\Routing\Router; // 引入路由器类
use App\Admin\Controllers\CouponLogController;
use Illuminate\Routing\Router;
// 注册管理后台的认证路由
Admin::registerAuthRoutes();
// 定义路由组
Route::group([
'prefix' => config('admin.route.prefix'), // 路由前缀
'namespace' => config('admin.route.namespace'), // 控制器命名空间
'middleware' => config('admin.route.middleware'), // 中间件
'prefix' => config('admin.route.prefix'),
'namespace' => config('admin.route.namespace'),
'middleware' => config('admin.route.middleware'),
], function (Router $router) {
// 设置默认的 404 错误页面
$router->fallback('HomeController@noFound');
// 首页路由
$router->get('/', 'HomeController@index');
// 覆盖默认的用户管理路由
// 覆盖默认的用户管理
$router->get('auth/users', 'AdminController@index');
// 覆盖默认的操作日志路由
// 覆盖默认的操作日志
$router->get('auth/logs', 'AdminController@indexLogs');
// 系统配置相关路由
// 系统配置
$router->resource('settings', 'SettingController')->only('index', 'store');
// 商品上架下架操作
// 商品上架下架
$router->get('products/{id}/push', 'ProductController@pushProduct');
// 分类管理
// 商品管理
// 秒杀商品管理
$router->resource('categories', 'CategoryController'); // 分类资源路由
$router->resource('products', 'ProductController'); // 商品资源路由
$router->resource('seckills', 'SeckillController')->only('index', 'create', 'store', 'destroy'); // 秒杀活动的资源路由
// 分类
// 商品
// 秒杀商品管理
$router->resource('categories', 'CategoryController');
$router->resource('products', 'ProductController');
$router->resource('seckills', 'SeckillController')->only('index', 'create', 'store', 'destroy');
// 订单发货相关路由
// 管理员确认订单发
// 商品发货
// 管理员帮忙确认收
$router->post('orders/{order}/ship', 'OrderController@ship');
// 确认发货状态
$router->patch('orders/{order}/shipped', 'OrderController@confirmShip');
// 退款相关路由
// 订单相关路由
// 评论管理路由
$router->get('orders/{order}/refund', 'OrderController@refund'); // 退款请求
$router->resource('orders', 'OrderController'); // 订单资源路由
$router->resource('comments', 'CommentController'); // 评论资源路由
// 退款
// 订单
// 评论
$router->get('orders/{order}/refund', 'OrderController@refund');
$router->resource('orders', 'OrderController');
$router->resource('comments', 'CommentController');
// 会员管理相关路由
$router->resource('users', 'UserController'); // 用户资源路由
// 会员管理
$router->resource('users', 'UserController');
// 积分日志
$router->get('score_logs', 'ScoreLogController@index'); // 积分日志查看
$router->get('score_logs', 'ScoreLogController@index');
// 用户购物车数据
$router->get('cars', 'CarController@index'); // 购物车数据查看
$router->get('cars', 'CarController@index');
// 用户收藏数据
$router->get('user_like_products', 'ProductLikeController@index'); // 用户收藏的产品
$router->get('user_like_products', 'ProductLikeController@index');
// 积分规则和积分等级管理
$router->resource('score_rules', 'ScoreRuleController'); // 积分规则资源路由
$router->resource('levels', 'LevelController'); // 积分等级资源路由
// 优惠券管理
$router->resource('coupon_templates', 'CouponTemplateController'); // 优惠券模板资源路由
// 优惠券日志
$router->resource('coupon_logs', 'CouponLogController')->only('index'); // 优惠券日志查看
// 优惠券兑换码管理
$router->resource('coupon_codes', 'CouponCodeController')->only('index', 'create', 'store', 'destroy'); // 兑换码资源路由
// 积分规则, 积分等级
$router->resource('score_rules', 'ScoreRuleController');
$router->resource('levels', 'LevelController');
// 文章通知管理
$router->resource('article_notifications', 'ArticleNotificationController')->only('index', 'create', 'store', 'show', 'destroy'); // 文章通知资源路由
// 优惠券管理
$router->resource('coupon_templates', 'CouponTemplateController');
// 优惠券
$router->resource('coupon_logs', 'CouponLogController')->only('index');
// 优惠券兑换码
$router->resource('coupon_codes', 'CouponCodeController')->only('index', 'create', 'store', 'destroy');
// 富文本编辑器图片上传
$router->post('upload/editor', 'UploadController@uploadByEditor'); // 图片上传路由
// 发布文章通知
$router->resource('article_notifications', 'ArticleNotificationController')->only('index', 'create', 'store', 'show', 'destroy');
// 富文本图片上传
$router->post('upload/editor', 'UploadController@uploadByEditor');
// 通过分类异步加载商品下拉列表
$router->get('api/products', 'CategoryController@getProducts'); // 获取产品列表的 API 路由
$router->get('api/products', 'CategoryController@getProducts');
});

@ -1,98 +1,117 @@
<?php
namespace App\Console\Commands; // 定义命名空间
use App\Models\Product; // 引入商品模型
use App\Models\SearchAble\ElasticSearchTrait; // 引入 Elasticsearch 特性
use Elasticsearch\ClientBuilder; // 引入 Elasticsearch 客户端构建器
use Illuminate\Console\Command; // 引入 Laravel 命令基类
use Illuminate\Database\Eloquent\Collection; // 引入 Eloquent 集合类
/**
* 将商品添加到 Elasticsearch 搜索的控制台命令
*
* 该命令用于将商品数据添加到 Elasticsearch 的全文索引中。
*/
namespace App\Console\Commands;
use App\Models\Product;
use App\Models\SearchAble\ElasticSearchTrait;
use Elasticsearch\ClientBuilder;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Collection;
class AddShopToEsSearchCommand extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'add:shop-to-search';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '把商品添加到全文索引';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令.
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
try {
// 检查 Elasticsearch 服务器是否可用
ElasticSearchTrait::client()->ping([
'client' => [
'timeout' => 5, // 设置请求超时时间
'connect_timeout' => 5 // 设置连接超时时间
'timeout' => 5,
'connect_timeout' => 5
]
]);
} catch (\Exception $exception) {
// 捕获连接异常并输出错误信息
$this->info($exception->getMessage());
$this->info('无法连接到 Elasticsearch 服务器,请配置 config/elasticsearch.php 文件');
$this->info('无法连接到 elasticse
arch 服务器,请配置 config/elasticsearch.php 文件');
$this->info('默认使用 MySQL 的模糊搜索');
$this->info('配置完毕后可运行: php artisan add:shop-to-search 添加索引');
return; // 退出命令
return;
}
// 新建商品索引
if (Product::indexExists()) {
// 如果索引已存在,则删除索引
Product::deleteIndex();
$this->info('删除索引');
}
// 创建新的索引
Product::createIndex();
$this->info('新建索引成功');
// 开始导入数据
$query = Product::query(); // 创建商品查询
$query = Product::query();
$count = $query->count(); // 获取商品总数
$handle = 0; // 初始化处理计数
$count = $query->count();
$handle = 0;
// 使用 chunk 方法分批处理商品数据
$query->with('category')->chunk(1000, function (Collection $models) use ($count, &$handle) {
// 遍历每个商品模型
$models->map(function (Product $product) use ($count, &$handle) {
// 将商品添加到索引
$product->addToIndex($product->getSearchData());
++$handle; // 处理计数加一
echo "\r {$handle}/$count"; // 输出当前处理进度
++ $handle;
echo "\r {$handle}/$count";
});
});
echo PHP_EOL; // 换行
$this->info('索引生成完毕'); // 输出完成信息
echo PHP_EOL;
$this->info('索引生成完毕');
}
}

@ -1,54 +1,40 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
use Illuminate\Console\Command; // 引入 Laravel 命令基类
use Illuminate\Console\Command;
/**
* 基础控制台命令类
*
* 该类提供了一个基础命令实现,包含执行 shell 命令并打印输出的功能。
*/
class BaseCommand extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:base'; // 定义命令的名称和签名
protected $signature = 'moon:base';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = 'don`t use'; // 描述该命令的用途,当前设置为“不建议使用”
protected $description = 'don`t use';
/**
* 创建一个新的命令实例.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行 shell 命令并打印输出
*
* @param string $command 要执行的 shell 命令
* @return void
*/
public function execShellWithPrint($command)
{
$this->info('----------'); // 打印分隔线
$this->info($command); // 打印要执行的命令
// 执行 shell 命令并获取输出
$this->info('----------');
$this->info($command);
$output = shell_exec($command);
$this->info($output); // 打印命令的输出结果
$this->info($output);
}
}

@ -1,51 +1,44 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
/**
* 缓存优化控制台命令
*
* 该命令用于优化配置和路由缓存,以提高网站的运行速度。
*/
class CacheOptimize extends BaseCommand
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:cache'; // 定义命令的名称和签名
protected $signature = 'moon:cache';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = 'Cache routing, configure information, to speed up the website running speed'; // 描述该命令的用途
protected $description = 'Cache routing, configure information, to speed up the website running speed';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行缓存命令
*
* 该方法用于处理缓存优化的逻辑,包括优化配置和路由缓存。
*
* @return mixed
*/
public function handle()
{
// 优化配置并缓存配置文件
$this->call('config:cache'); // 调用 Laravel 的 config:cache 命令
// 优化路由并缓存路由信息
$this->call('route:cache'); // 调用 Laravel 的 route:cache 命令
// 优化配置
$this->call('config:cache');
// 优化路由
$this->call('route:cache');
}
}

@ -1,54 +1,42 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
/**
* 清除缓存控制台命令
*
* 该命令用于清除应用程序中的缓存信息,包括配置缓存、路由缓存和视图缓存。
*/
class ClearCache extends BaseCommand
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:clear'; // 定义命令的名称和签名
protected $signature = 'moon:clear';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = 'clear moon:cache cache information'; // 描述该命令的用途
protected $description = 'clear moon:cache cache information';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 清除所有缓存
*
* 该方法用于处理缓存清除的逻辑,包括清除配置缓存、路由缓存和视图缓存。
*
* @return mixed
*/
public function handle()
{
// 清除配置缓存
$this->call('config:clear'); // 调用 Laravel 的 config:clear 命令
// 清除路由缓存
$this->call('route:clear'); // 调用 Laravel 的 route:clear 命令
// 清除视图缓存
$this->call('view:clear'); // 调用 Laravel 的 view:clear 命令
$this->call('config:clear');
$this->call('route:clear');
$this->call('view:clear');
}
}

@ -1,75 +1,66 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
use Illuminate\Filesystem\Filesystem; // 引入 Filesystem 类
use Illuminate\Support\Facades\Storage; // 引入 Storage 门面(未使用,可以考虑移除)
/**
* 复制文件控制台命令
*
* 该命令用于将所有上传的静态资源复制到公共存储目录。
*/
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
class CopyFile extends BaseCommand
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:copy'; // 定义命令的名称和签名
protected $signature = 'moon:copy';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = 'Copy all upload static resources'; // 描述该命令的用途
protected $description = 'Copy all upload static resources';
/**
* 文件系统实例
*
* @var Filesystem
*/
protected $filesystem; // 声明文件系统实例变量
protected $filesystem;
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @param Filesystem $filesystem 文件系统实例
* @param Filesystem $filesystem
*/
public function __construct(Filesystem $filesystem)
{
$this->filesystem = $filesystem; // 注入 Filesystem 实例
$this->filesystem = $filesystem;
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 把静态资源发布到 public/storage 目录
*
* 该方法执行复制静态资源的逻辑,包括产品图片、默认头像和其他静态图片。
*
* @return mixed
*/
public function handle()
{
// 定义图片的静态目录及目标目录
$from = storage_path('app/resources/products'); // 源目录:产品图片
$to = storage_path('app/public/products'); // 目标目录:公共产品图片
$this->filesystem->copyDirectory($from, $to); // 复制产品图片目录
// 图片的静态目录
$from = storage_path('app/resources/products');
$to = storage_path('app/public/products');
$this->filesystem->copyDirectory($from, $to);
// 复制默认头像
$from = storage_path('app/resources/avatars'); // 源目录:默认头像
$to = storage_path('app/public/avatars'); // 目标目录:公共头像
$this->filesystem->copyDirectory($from, $to); // 复制默认头像目录
// 默认头像
$from = storage_path('app/resources/avatars');
$to = storage_path('app/public/avatars');
$this->filesystem->copyDirectory($from, $to);
// 复制其他静态图片
$from = storage_path('app/resources/images'); // 源目录:其他静态图片
$to = storage_path('app/public/images'); // 目标目录:公共静态图片
$this->filesystem->copyDirectory($from, $to); // 复制其他静态图片目录
// 默认头像
$from = storage_path('app/resources/images');
$to = storage_path('app/public/images');
$this->filesystem->copyDirectory($from, $to);
// 输出成功信息
$this->info('copy file success'); // 命令执行成功后的提示信息
$this->info('copy file success');
}
}

@ -1,70 +1,59 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
use App\Models\SiteCount; // 引入 SiteCount 模型
use App\Services\SiteCountService; // 引入 SiteCountService 服务
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Illuminate\Support\Facades\Cache; // 引入 Cache 门面(未使用,可以考虑移除)
use App\Models\SiteCount;
use App\Services\SiteCountService;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
/**
* 统计站点数据控制台命令
*
* 该命令用于统计站点数据,执行时会将统计数据记录到前一天。
*/
class CountSite extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:count-site'; // 定义命令的名称和签名
protected $signature = 'moon:count-site';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '统计数据,直接执行会统计到昨天'; // 描述该命令的用途
protected $description = '统计数据,直接执行会统计到昨天';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
* Execute the console command.
*
* 该方法执行统计逻辑,统计前一天的数据并保存到数据库。
*
* @param SiteCountService $service 站点统计服务实例
* @param SiteCountService $service
* @return mixed
*/
public function handle(SiteCountService $service)
{
// 获取昨天的日期
$date = Carbon::now()->subDay(1)->toDateString(); // 获取当前日期并减去一天
// 每天凌晨统计昨天的数据
$date = Carbon::now()->subDay(1)->toDateString();
/**
* 防止一天运行多次,所以采用增加
*
* @var $site SiteCount
*/
// 查找或创建一个新的 SiteCount 实例,基于日期
$site = SiteCount::query()->firstOrNew(compact('date')); // 如果没有记录则创建新记录
// 使用服务同步数据并保存
$site = $service->syncByCache($site, true); // 同步数据,传入 $site 实例和强制更新标志
$site->save(); // 保存统计数据到数据库
$site = SiteCount::query()->firstOrNew(compact('date'));
$site = $service->syncByCache($site, true);
$site->save();
// 记录系统日志
createSystemLog('系统统计站点数据', $site->toArray()); // 记录操作日志,包含统计的数据
createSystemLog('系统统计站点数据', $site->toArray());
}
}

@ -1,47 +1,41 @@
<?php
namespace App\Console\Commands; // 定义命名空间
use App\Services\ScoreLogServe; // 引入 ScoreLogServe 服务
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Illuminate\Support\Facades\Cache; // 引入 Cache 门面
/**
* 删除过期积分数据控制台命令
*
* 该命令用于删除昨天过期的积分统计数据。
*/
namespace App\Console\Commands;
use App\Services\ScoreLogServe;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
class DelExpireScoreData extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:del-score-data'; // 定义命令的名称和签名
protected $signature = 'moon:del-score-data';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = 'Delete expired score data from the previous day'; // 描述该命令的用途
protected $description = 'Command description';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
*
* 该方法执行删除过期积分数据的逻辑。
* Execute the console command.
*
* @return mixed
* @throws \Psr\SimpleCache\InvalidArgumentException
@ -49,15 +43,12 @@ class DelExpireScoreData extends Command
public function handle()
{
// 获取昨天的日期
$yesterday = Carbon::today()->subDay()->toDateString(); // 获取昨天的日期字符串
$yesterday = Carbon::today()->subDay()->toDateString();
// 创建 ScoreLogServe 实例
$serve = new ScoreLogServe(); // 实例化服务以处理积分日志
$serve = new ScoreLogServe();
// 删除缓存中的过期积分数据
Cache::delete($serve->loginKey($yesterday)); // 根据昨天的日期生成键并删除对应的缓存数据
Cache::delete($serve->loginKey($yesterday));
// 记录系统日志
createSystemLog("系统删除{$yesterday}过期积分统计数据", ['date' => $yesterday]); // 记录删除操作的日志
createSystemLog("系统删除{$yesterday}过期积分统计数据", ['date' => $yesterday]);
}
}

@ -1,96 +1,92 @@
<?php
namespace App\Console\Commands; // 定义命名空间
use App\Models\Seckill; // 引入 Seckill 模型
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Illuminate\Support\Facades\Redis; // 引入 Redis 门面
/**
* 删除过期秒杀数据控制台命令
*
* 该命令用于删除过期的秒杀数据,并将相关库存数量回滚到商品中。
*/
namespace App\Console\Commands;
use App\Models\Seckill;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
class DelExpireSecKill extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:del-seckills'; // 定义命令的名称和签名
protected $signature = 'moon:del-seckills';
/**
* 控制台命令的描述
* 删除过期的秒杀
*
* @var string
*/
protected $description = 'delete expire seckills'; // 描述该命令的用途
protected $description = 'delete expire seckills';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
*
* 该方法执行删除过期秒杀数据的逻辑,并回滚相关库存。
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$rollbacks = collect(); // 创建一个集合,用于存储回滚的秒杀记录
$rollbacks = collect();
// 查出已经过期且没有回滚过的秒杀
// 查出已经过期确没有回滚过的秒杀,
Seckill::query()
->where('is_rollback', 0) // 选择未回滚的秒杀
->where('end_at', '<', Carbon::now()->toDateTimeString()) // 选择已过期的秒杀
->get() // 获取符合条件的秒杀记录
->where('is_rollback', 0)
->where('end_at', '<', Carbon::now()->toDateTimeString())
->get()
->map(function (Seckill $seckill) use ($rollbacks) {
// 1. 回滚数量到商品
// 2. 设置为过期
$product = $seckill->product()->first(); // 获取与秒杀相关联的商品
$product = $seckill->product()->first();
// 获取 Redis 中的秒杀数量
$jsonSeckill = Redis::get($seckill->getRedisModelKey()); // 从 Redis 获取秒杀数据
$redisSeckill = json_decode($jsonSeckill, true); // 将 JSON 数据解码为数组
// 获取剩余的秒杀量
$surplus = Redis::llen($seckill->getRedisQueueKey()); // 从 Redis 获取剩余秒杀量
// 获取 redis 数量
$jsonSeckill = Redis::get($seckill->getRedisModelKey());
$redisSeckill = json_decode($jsonSeckill, true);
// 获取剩余秒杀量
$surplus = Redis::llen($seckill->getRedisQueueKey());
// 恢复剩余的库存量
// 恢复库存数量
if ($redisSeckill['sale_count'] != 0) { // 如果 Redis 中的销售数量不为零
$product->increment('sale_count', $redisSeckill['sale_count']); // 增加商品的销售数量
if ($redisSeckill['sale_count'] != 0) {
$product->increment('sale_count', $redisSeckill['sale_count']);
}
if ($surplus != 0) { // 如果剩余秒杀量不为零
$product->increment('count', $surplus); // 增加商品的总库存数量
if ($surplus != 0) {
$product->increment('count', $surplus);
}
// 同步 Redis 数据到数据库中
$seckill->sale_count += $redisSeckill['sale_count']; // 更新秒杀的销售数量
$seckill->rollback_count += $surplus; // 更新秒杀的回滚数量
$seckill->is_rollback = 1; // 标记为已回滚
$seckill->save(); // 保存更新后的秒杀记录
$rollbacks->push($seckill); // 将已回滚的秒杀记录添加到集合中
// 同步 redis 数据到数据库中
$seckill->sale_count += $redisSeckill['sale_count'];
$seckill->rollback_count += $surplus;
$seckill->is_rollback = 1;
$seckill->save();
$rollbacks->push($seckill);
// 删除掉秒杀数据
$ids = Redis::connection()->keys("seckills:{$seckill->id}:*"); // 获取与秒杀相关的所有 Redis 键
Redis::del($ids); // 删除这些 Redis 键
$ids = Redis::connection()->keys("seckills:{$seckill->id}:*");
Redis::del($ids);
});
if ($rollbacks->isNotEmpty()) { // 如果有回滚的秒杀记录
createSystemLog('系统回滚秒杀数据', $rollbacks->toArray()); // 记录系统回滚操作的日志
if ($rollbacks->isNotEmpty()) {
createSystemLog('系统回滚秒杀数据', $rollbacks->toArray());
}
}
}

@ -1,56 +1,46 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
use Illuminate\Support\Facades\Storage; // 引入 Storage 门面
/**
* 删除文件控制台命令
*
* 该命令用于删除所有上传的静态资源文件。
*/
use Illuminate\Support\Facades\Storage;
class DeleteFile extends BaseCommand
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:delete'; // 定义命令的名称和签名
protected $signature = 'moon:delete';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = 'Delete all upload static resources'; // 描述该命令的用途
protected $description = 'Delete all upload static resources';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 删除静态资源文件
*
* 该方法执行删除操作,删除指定的静态资源目录及其内容。
*
* @return mixed
*/
public function handle()
{
// 删除上传列表目录
Storage::deleteDirectory('public' . DIRECTORY_SEPARATOR . config('web.upload.list')); // 根据配置删除上传列表目录
// 删除上传详情目录
Storage::deleteDirectory('public' . DIRECTORY_SEPARATOR . config('web.upload.detail')); // 根据配置删除上传详情目录
Storage::deleteDirectory('public' . DIRECTORY_SEPARATOR . config('web.upload.list'));
Storage::deleteDirectory('public' . DIRECTORY_SEPARATOR . config('web.upload.detail'));
// 输出成功信息
$this->info('delete file success'); // 命令执行成功后的提示信息
$this->info('delete file success');
}
}

@ -1,64 +1,56 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
use App\Models\User; // 引入 User 模型
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Illuminate\Database\Eloquent\Model; // 引入 Eloquent 模型基类
use Illuminate\Support\Collection; // 引入 Collection 类
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
/**
* 导出数据库命令
*
* 该命令用于将用户数据导出到 JSON 文件中。
*/
class ExportDatabaseCommand extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:export'; // 定义命令的名称和签名
protected $signature = 'moon:export';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '导出数据到 json 文件'; // 描述该命令的用途
protected $description = '导出数据到 json 文件';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
*
* 该方法执行导出用户数据的逻辑,将数据保存为 JSON 格式。
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// 查询所有用户并按创建时间升序排列
$users = User::query()
->oldest() // 按照创建时间升序排列
->get() // 获取所有用户记录
->oldest()
->get()
->transform(function (User $user) {
// 先排除自身主键
$user->offsetUnset($user->getKeyName()); // 从用户模型中移除主键字段
$user->offsetUnset($user->getKeyName());
return $user; // 返回处理后的用户对象
return $user;
});
// 将用户数据转换为 JSON 格式并写入指定文件
file_put_contents(\UsersTableSeeder::DATA_PATH, $users->toJson(JSON_UNESCAPED_UNICODE)); // 保存 JSON 数据到文件
file_put_contents(\UsersTableSeeder::DATA_PATH, $users->toJson(JSON_UNESCAPED_UNICODE));
}
}

@ -1,80 +1,71 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
use App\Models\Product; // 引入 Product 模型
/**
* 安装商城命令
*
* 该命令用于初始化商城项目,包括数据库迁移、数据填充和资源复制等操作。
*/
use App\Models\Product;
class InstallShop extends BaseCommand
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:install'; // 定义命令的名称和签名
protected $signature = 'moon:install';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '初始化商城项目'; // 描述该命令的用途
protected $description = '初始化商城项目';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行商城安装命令
*
* 该方法执行初始化商城项目的逻辑,包括数据库迁移、数据填充和静态资源处理等。
* 安装商城
*
* @return mixed
*/
public function handle()
{
// 生成应用的加密密钥
$this->call('key:generate');
// 删除上一次保留的数据表
$this->call('migrate:reset'); // 重置数据库迁移,删除所有表
$this->call('migrate:reset');
// 删除上一次保留的文件
$this->call('moon:delete'); // 删除之前的文件或数据
// 禁止在安装过程中将产品添加到搜索索引
$this->call('moon:delete');
Product::$addToSearch = false;
/****************************************
* 1. 迁移数据表
* 2. 数据库迁移
* 3. 复制静态资源
* 4. 创建软链接
****************************************/
$this->call('migrate'); // 执行数据库迁移,创建所需的数据表
$this->call('db:seed'); // 填充数据库,插入初始数据
$this->call('moon:copy'); // 复制静态资源到公共目录
$this->call('storage:link'); // 创建存储目录的软链接,以便访问存储的文件
// 更新首页数据,防止上一次遗留数据的影响
$this->call('moon:update-home'); // 更新首页相关数据
*/
$this->call('migrate');
$this->call('db:seed');
$this->call('moon:copy');
$this->call('storage:link');
// 更新首页数据,防止上一次遗留
$this->call('moon:update-home');
// 生成全文索引
$this->call('add:shop-to-search'); // 为商城产品生成搜索索引
$this->call('add:shop-to-search');
// 直接开启监听队列
// $this->info('queue starting please don`t close cmd windows!!!');
// $this->call('queue:work', ['--tries' => '3']); // 启动队列工作进程,处理队列任务
// $this->call('queue:work', ['--tries' => '3']);
}
}

@ -1,70 +1,55 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
use App\Mail\SubscribesNotice; // 引入订阅通知邮件类
use App\Models\Subscribe; // 引入订阅模型
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Mail; // 引入 Mail 门面
use App\Mail\SubscribesNotice;
use App\Models\Subscribe;
use Illuminate\Console\Command;
use Mail;
/**
* 发送订阅邮件命令
*
* 该命令用于向所有已订阅用户发送通知邮件。
*/
class SendSubscribeEmail extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:send-subscribes'; // 定义命令的名称和签名
protected $signature = 'moon:send-subscribes';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '发送订阅通知邮件'; // 描述该命令的用途
protected $description = 'Command description';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
*
* 该方法执行发送订阅邮件的逻辑,包括查询订阅用户和发送邮件。
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// 查询所有已订阅的用户邮箱
$mails = Subscribe::query()
->where('is_subscribe', 1) // 只选择已订阅的用户
->pluck('email'); // 获取邮箱地址列表
$mails = Subscribe::query()->where('is_subscribe', 1)->pluck('email');
// 遍历每个邮箱并发送通知邮件
$mails->map(function ($realMail) {
// 对邮箱进行加密处理
$email = encrypt($realMail);
// 生成包含加密邮箱的邮件链接
$url = route('site.email', compact('email'));
// 发送邮件给当前用户
// 不要一次发送给多个用户,以免暴露其他人的邮箱
// 不要一次 to 多个用户,会暴露其他人的邮箱
Mail::to($realMail)->send(new SubscribesNotice($url));
});
// 记录系统日志,记录发送的用户信息
createSystemLog('系统发送订阅消息, 发送的用户:' . $mails->implode(', '), $mails->toArray());
}
}

@ -1,69 +1,78 @@
<?php
namespace App\Console\Commands; // 定义命名空间
use App\Models\Product; // 引入 Product 模型
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Illuminate\Support\Facades\Cache; // 引入 Cache 门面
/**
* 同步商品浏览量命令
*
* 该命令用于同步前一天的商品浏览量,并更新数据库中的相关记录。
*/
class SyncProductViewCommand extends Command
namespace App\Console\Commands;
use App\Models\Product;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
class SyncProducViewCommand extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:sync-product_view'; // 定义命令的名称和签名
protected $signature = 'moon:sync-product_view';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '同步商品浏览量'; // 描述该命令的用途
protected $description = 'Command description';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
*
* 该方法执行同步商品浏览量的逻辑,包括获取前一天的浏览量并更新数据库。
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// 获取昨天的日期字符串
$yesterday = Carbon::yesterday()->toDateString(); // 获取昨天的日期,格式为 YYYY-MM-DD
$yesterday = Carbon::yesterday()->toDateStr
ing();
$products = Product::query()->where('today_has_view', 1)->get();
// 查询所有今天有浏览量记录的商品
$products = Product::query()->where('today_has_view', 1)->get(); // 获取所有今天有浏览量的商品
// 遍历每个商品并更新浏览量
$products->map(function (Product $product) use ($yesterday) {
// 从缓存中取出前一天的浏览量,默认值为 0
$viewCount = Cache::pull($product->getViewCountKey($yesterday), 0);
// 更新商品的浏览量
$product->view_count += $viewCount; // 累加前一天的浏览量
$product->today_has_view = 0; // 重置今天的浏览量标记
$product->save(); // 保存更新后的商品信息
$product->view_count += $vi
ewCount;
$product->today_has_view = 0;
$product->save();
});
// 记录系统日志,记录同步操作的信息
createSystemLog("系统同步{$yesterday}商品浏览量", ['date' => $yesterday]); // 记录同步的日期
createSystemLog("系统同步{$yesterday}商品浏览量", ['date' => $yesterday]);
}
}

@ -1,54 +1,48 @@
<?php
namespace App\Console\Commands; // 定义命名空间
namespace App\Console\Commands;
/**
* 卸载商城项目命令
*
* 该命令用于卸载商城项目,包括清理缓存、重置数据库迁移和删除上传的静态资源。
*/
class UninstallShop extends BaseCommand
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:uninstall'; // 定义命令的名称和签名
protected $signature = 'moon:uninstall';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '卸载商城项目'; // 描述该命令的用途
protected $description = '卸载商城项目';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
*
* 该方法执行卸载商城项目的逻辑,依次调用其他命令来完成任务。
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// 调用清理命令,清除缓存
$this->call('moon:clear');
// 调用迁移重置命令,重置数据库迁移
$this->call('migrate:reset');
// 删除所有上传的静态资源
// delete all upload static resources
$this->call('moon:delete');
}
}

@ -1,70 +1,72 @@
<?php
namespace App\Console\Commands; // 定义命名空间
use App\Enums\HomeCacheEnum; // 引入 HomeCacheEnum 枚举
use App\Models\Category; // 引入 Category 模型
use App\Models\Product; // 引入 Product 模型
use App\Models\User; // 引入 User 模型
use App\Utils\HomeCacheDataUtil; // 引入 HomeCacheDataUtil 工具类
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Illuminate\Support\Facades\Cache; // 引入 Cache 门面
/**
* 更新首页缓存数据命令
*
* 该命令用于更新首页的缓存数据,包括分类、热销商品、最新商品和用户信息。
*/
namespace App\Console\Commands;
use App\Enums\HomeCacheEnum;
use App\Models\Category;
use App\Models\Product;
use App\Models\User;
use App\Utils\HomeCacheDataUtil;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
class UpdateCacheHomeData extends Command
{
/**
* 控制台命令的名称和签名
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'moon:update-home'; // 定义命令的名称和签名
protected $signature = 'moon:update-home';
/**
* 控制台命令的描述
* The console command description.
*
* @var string
*/
protected $description = '更新首页缓存数据'; // 描述该命令的用途
protected $description = 'Command description';
/**
* 创建一个新的命令实例.
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 调用父类构造函数
parent::__construct();
}
/**
* 执行控制台命令
*
* 该方法执行更新缓存数据的逻辑,定时更新首页所需的各类数据。
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// 设置缓存的生存时间TTL单位为秒这里设置为 2 分钟
$ttl = 60 * 2; // 每 2 分钟更新一次缓存
// 每分钟有定时任务更新
$ttl = 60 * 2;
HomeCacheDataUtil::categories($ttl, true);
// 更新分类缓存数据
HomeCacheDataUtil::categories($ttl, true); // 更新分类数据并设置 TTL
// 更新热销商品缓存数据
HomeCacheDataUtil::hotProducts($ttl, true); // 更新热销商品数据并设置 TTL
// 更新最新商品缓存数据
HomeCacheDataUtil::latestProducts($ttl, true); // 更新最新商品数据并设置 TTL
// 更新用户信息缓存数据
HomeCacheDataUtil::users($ttl, true); // 更新用户数据并设置 TTL
HomeCacheDataUtil::hotProducts($ttl, true);
HomeCacheDataUtil::latestProducts($ttl, true);
HomeCacheDataUtil::users($ttl, true);
}
}

@ -1,54 +1,45 @@
<?php
namespace App\Console; // 定义命名空间
namespace App\Console;
// 引入需要的控制台命令类
use App\Console\Commands\CountSite; // 引入 CountSite 命令
use App\Console\Commands\DelExpireScoreData; // 引入 DelExpireScoreData 命令
use App\Console\Commands\DelExpireSecKill; // 引入 DelExpireSecKill 命令
use App\Console\Commands\SendSubscribeEmail; // 引入 SendSubscribeEmail 命令
use App\Console\Commands\SyncProducViewCommand; // 引入 SyncProducViewCommand 命令
use App\Console\Commands\UpdateCacheHomeData; // 引入 UpdateCacheHomeData 命令
use Illuminate\Console\Scheduling\Schedule; // 引入 Schedule 类
use Illuminate\Foundation\Console\Kernel as ConsoleKernel; // 引入 ConsoleKernel 基类
use App\Console\Commands\CountSite;
use App\Console\Commands\DelExpireScoreData;
use App\Console\Commands\DelExpireSecKill;
use App\Console\Commands\SendSubscribeEmail;
use App\Console\Commands\SyncProducViewCommand;
use App\Console\Commands\UpdateCacheHomeData;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
/**
* 控制台命令调度内核
*
* 该类负责注册和调度应用程序的 Artisan 命令。
*/
class Kernel extends ConsoleKernel
{
/**
* 应用程序提供的 Artisan 命令
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
// 注册的命令列表
//
];
/**
* 定义应用程序的命令调度
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// 每周六早上 8 点发送订阅邮件
// 每周六点发送订阅邮件
$schedule->command(SendSubscribeEmail::class)->saturdays()->at('8:00');
// 每天凌晨 1 点统计注册人数和销售数量
// 每天统计注册人数, 销售数量
$schedule->command(CountSite::class)->dailyAt('01:00');
// 每小时执行一次,回滚秒杀过期的数据
// 每小时执行一次, 回滚秒杀过期的数据
$schedule->command(DelExpireSecKill::class)->hourly();
// 每天午夜 12 点删除昨天过期的积分数据
// 每天夜里十二点执行删除昨天过期的积分数据
$schedule->command(DelExpireScoreData::class)->dailyAt('00:00');
// 每天午夜 12 点 10 分同步商品浏览量
// 每天夜里十二点同步商品浏览量
$schedule->command(SyncProducViewCommand::class)->dailyAt('00:10');
// 每分钟更新一次首页数据
@ -56,16 +47,14 @@ class Kernel extends ConsoleKernel
}
/**
* 注册应用程序的命令
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
// 加载命令目录下的所有命令
$this->load(__DIR__.'/Commands');
// 引入控制台路由文件
require base_path('routes/console.php');
}
}

@ -1,182 +1,121 @@
<?php
namespace App\Http\Controllers\User; // 定义命名空间
use App\Http\Requests\AddressRequest; // 引入地址请求验证类
use App\Models\Address; // 引入地址模型
use App\Models\User; // 引入用户模型
use Auth; // 引入认证类
use DB; // 引入数据库门面
use Illuminate\Http\Request; // 引入请求类
use App\Http\Controllers\Controller; // 引入基础控制器类
/**
* 地址控制器
*
* 该控制器处理用户地址的增删改查操作。
*/
namespace App\Http\Controllers\User;
use App\Http\Requests\AddressRequest;
use App\Models\Address;
use App\Models\User;
use Auth;
use DB;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class AddressController extends Controller
{
/**
* 显示用户的地址列表
*
* @return \Illuminate\View\View
*/
public function index()
{
// 获取当前用户的地址
$addresses = auth()->user()->addresses;
// 获取省份和城市数据
$provinces = DB::table('provinces')->get(); // 获取所有省份
$cities = DB::table('cities')->where('province_id', $provinces->first()->id)->get(); // 获取第一个省的城市
// Provincial and municipal regions
$provinces = DB::table('provinces')->get();
$cities = DB::table('cities')->where('province_id', $provinces->first()->id)->get();
// 返回地址列表视图
return view('user.addresses.index', compact('addresses', 'provinces', 'cities'));
}
/**
* 存储新地址
*
* @param AddressRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(AddressRequest $request)
{
// 获取格式化的请求数据
$addressesData = $this->getFormatRequest($request);
//
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$user->addresses()->create($addressesData); // 创建新地址
$user = auth()->user();
$user->addresses()->create($addressesData);
// 返回带有成功消息的重定向
return back()->with('status', '创建成功');
}
/**
* 显示指定地址的详细信息
*
* @param Address $address
* @return Address
*/
public function show(Address $address)
{
return $address; // 返回地址实例
return $address;
}
/**
* 显示编辑地址的表单
*
* @param Address $address
* @return \Illuminate\View\View
*/
public function edit(Address $address)
{
// 检查当前用户是否有权限编辑该地址
if (auth()->id() != $address->user_id) {
abort(403, '非法操作'); // 如果没有权限返回403错误
abort(403, '非法操作');
}
// 获取当前用户的地址
$addresses = auth()->user()->addresses;
// 获取省份和城市数据
$provinces = DB::table('provinces')->get(); // 获取所有省份
$cities = DB::table('cities')->where('province_id', $address->province_id)->get(); // 获取指定省的城市
// Provincial and municipal regions
$provinces = DB::table('provinces')->get();
$cities = DB::table('cities')->where('province_id', $address->province_id)->get();
// 返回编辑地址的视图
return view('user.addresses.edit', compact('addresses', 'address', 'provinces', 'cities'));
}
/**
* 更新指定地址
*
* @param AddressRequest $request
* @param Address $address
* @return \Illuminate\Http\RedirectResponse
*/
public function update(AddressRequest $request, Address $address)
{
// 检查当前用户是否有权限更新该地址
if (auth()->id() != $address->user_id) {
abort(403, '非法操作'); // 如果没有权限返回403错误
abort(403, '非法操作');
}
// 获取格式化的请求数据
$addressesData = $this->getFormatRequest($request);
$address->update($addressesData); // 更新地址
$address->update($addressesData);
// 返回带有成功消息的重定向
return back()->with('status', '修改成功');
}
/**
* 删除指定地址
*
* @param Address $address
* @return \Illuminate\Http\JsonResponse
*/
public function destroy(Address $address)
{
// 检查当前用户是否有权限删除该地址
if (auth()->id() != $address->user_id) {
return responseJson(400, '非法操作'); // 如果没有权限返回400错误
return responseJson(400, '非法操作');
}
$address->delete(); // 删除地址
$address->delete();
// 返回成功的 JSON 响应
return responseJson(200, '删除成功');
}
/**
* 设置默认地址
*
* @param Address $address
* @return \Illuminate\Http\JsonResponse
*/
public function setDefaultAddress(Address $address)
{
// 检查当前用户是否有权限设置该地址为默认
if (auth()->id() != $address->user_id) {
return responseJson(400, '非法操作'); // 如果没有权限返回400错误
return responseJson(400, '非法操作');
}
// 将当前用户的所有地址设置为非默认
Address::query()->where('user_id', $address->user_id)->update(['is_default' => 0]);
$address->is_default = 1; // 将指定地址设置为默认
$address->is_default = 1;
if ($address->save()) {
return responseJson(0, '设置成功'); // 返回成功的 JSON 响应
return responseJson(0, '设置成功');
}
return responseJson(400, '请稍后再试!'); // 返回失败的 JSON 响应
return responseJson(400, '请稍后再试!');
}
/**
* 格式化请求数据
*
* @param Request $request
* @return array
*/
protected function getFormatRequest(Request $request)
{
// 只获取指定的请求参数
return $request->only(['name', 'phone', 'province_id', 'city_id', 'detail_address']);
return $request->only(['name', 'phone', 'province_id', 'city_id','detail_address']);
}
/**
* 根据省份 ID 获取城市
*
* @param Request $request
* @return \Illuminate\Support\Collection
*/
public function getCities(Request $request)
{
// 根据省份 ID 查询城市
return DB::table('cities')->where('province_id', $request->input('province_id'))->get();
}
}

@ -1,89 +1,65 @@
<?php
namespace App\Http\Controllers\User; // 定义命名空间
use App\Models\Product; // 引入产品模型
use App\Models\User; // 引入用户模型
use Auth; // 引入认证类
use Illuminate\Http\Request; // 引入请求类
use App\Http\Controllers\Controller; // 引入基础控制器类
/**
* 用户收藏控制器
*
* 该控制器处理用户对产品的收藏(点赞)功能。
*/
namespace App\Http\Controllers\User;
use App\Models\Product;
use App\Models\User;
use Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class LikesController extends Controller
{
/**
* 默认响应信息
*
* @var array
*/
protected $response = [
'code' => 1,
'msg' => '服务器异常,请稍后再试',
];
/**
* 显示用户收藏的产品列表
*
* @return \Illuminate\View\View
*/
public function index()
{
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$user = auth()->user();
// 获取用户收藏的产品,按最新排序并分页
$likesProducts = $user->products()
->where('user_id', auth()->id())
->withCount('users') // 统计每个产品的收藏用户数
->latest() // 按照创建时间降序排列
->paginate(10); // 每页显示10个产品
->withCount('users')
->latest()
->paginate(10);
// 返回用户收藏产品的视图
return view('user.products.likes', compact('likesProducts'));
}
/**
* 切换产品的收藏状态
*
* @param string $uuid 产品的唯一标识符
* @return \Illuminate\Http\JsonResponse
*/
public function toggle($uuid)
{
/**
* @var $product Product
*/
$product = Product::query()
->where('uuid', $uuid) // 根据 UUID 查找产品
->firstOrFail(); // 如果未找到,抛出 404 异常
->where('uuid', $uuid)
->firstOrFail();
$user = auth()->id(); // 获取当前用户的 ID
// 检查当前用户是否已经收藏该产品
$user = auth()->id();
if ($product->users()->where('user_id', $user)->exists()) {
// 如果已收藏,则取消收藏
$product->users()->detach($user);
// 返回 JSON 响应,表示已取消收藏
return response()->json([
'code' => 200,
'msg' => '欢迎下次收藏'
]);
}
// 如果未收藏,则添加收藏
$product->users()->attach($user);
// 返回 JSON 响应,表示已成功收藏
return response()->json([
'code' => 201,
'msg' => '收藏成功'
]);
}
}
}

@ -1,184 +1,149 @@
<?php
namespace App\Http\Controllers\User; // 定义命名空间
use App\Http\Controllers\Controller; // 引入基础控制器类
use App\Models\User; // 引入用户模型
use App\Notifications\CouponCodeNotification; // 引入优惠券通知类(未使用)
use App\Services\NotificationServe; // 引入通知服务类
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Http\Request; // 引入请求类
use Illuminate\Notifications\DatabaseNotification; // 引入数据库通知模型
/**
* 用户通知控制器
*
* 该控制器处理用户通知的展示、阅读和管理功能。
*/
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Notifications\CouponCodeNotification;
use App\Services\NotificationServe;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Notifications\DatabaseNotification;
class NotificationController extends Controller
{
/**
* 显示用户的通知列表
*
* @param Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$user = auth()->user();
// 根据请求的 tab 参数确定查询的通知类型
switch ($request->input('tab', 1)) {
case 2:
$query = $user->notifications(); // 所有通知
$query = $user->notifications();
break;
case 3:
$query = $user->readNotifications(); // 已读通知
$query = $user->readNotifications();
break;
case 1:
default:
$query = $user->unreadNotifications(); // 未读通知
$query = $user->unreadNotifications();
break;
}
// 分页获取通知
$notifications = $query->paginate();
// 为每个通知设置标题
foreach ($notifications as $notification) {
$notification->title = NotificationServe::getTitle($notification);
}
// 获取未读和已读通知的数量
$unreadCount = $user->unreadNotifications()->count();
$readCount = $user->readNotifications()->count();
// 返回通知列表视图
return view('user.notifications.index', compact('notifications', 'unreadCount', 'readCount'));
}
/**
* 标记指定通知为已读
*
* @param int $id 通知的 ID
* @return \Illuminate\Http\JsonResponse
*/
public function read($id)
{
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$user = auth()->user();
/**
* @var $notification DatabaseNotification
*/
$notification = $user->notifications()->find($id); // 查找指定通知
$notification = $user->notifications()->find($id);
if (is_null($notification)) {
return responseJsonAsBadRequest('无效的通知'); // 返回无效通知的错误
return responseJsonAsBadRequest('无效的通知');
}
$notification->markAsRead(); // 标记通知为已读
$notification->markAsRead();
return responseJson(); // 返回成功的 JSON 响应
return responseJson();
}
/**
* 标记所有未读通知为已读
*
* @return \Illuminate\Http\JsonResponse
*/
public function readAll()
{
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$user = auth()->user();
// 更新所有未读通知为已读
$count = $user->unreadNotifications()->update(['read_at' => Carbon::now()]);
// 返回已读通知数量的 JSON 响应
return responseJson(200, "本次已读{$count}条消息");
}
/**
* 显示指定通知的详细信息
*
* @param int $id 通知的 ID
* @return \Illuminate\View\View|\Illuminate\Http\Response
*/
public function show($id)
{
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$user = auth()->user();
/**
* @var $notification DatabaseNotification
*/
$notification = $user->notifications()->find($id); // 查找指定通知
$notification = $user->notifications()->find($id);
if (is_null($notification)) {
return abort(403, '无效的通知'); // 返回403错误
return abort(403, '无效的通知');
}
// 查找上一条和下一条通知
// 查看是否有上一条下一条
$last = $user->notifications()->where('created_at', '<', $notification->created_at)->first();
$next = $user->notifications()->where('created_at', '>', $notification->created_at)->first();
// 标记通知为已读
// 标记为已读
$notification->markAsRead();
// 获取通知的视图
$view = NotificationServe::getView($notification);
if (! view()->exists($view)) {
abort(404, '未知的的消息'); // 如果视图不存在返回404错误
abort(404, '未知的的消息');
}
// 设置通知的标题
$notification->title = NotificationServe::getTitle($notification);
$data = $notification->data; // 获取通知的数据
$data = $notification->data;
// 返回通知详细信息的视图
return view('user.notifications.show', compact('last', 'next', 'notification', 'view', 'data'));
}
/**
* 获取未读通知的数量
*
* @return \Illuminate\Http\JsonResponse
*/
public function getUnreadCount()
{
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$user = auth()->user();
/**
* @var $notification DatabaseNotification
*/
$count = $user->unreadNotifications()->count(); // 获取未读通知的数量
$count = $user->unreadNotifications()->count();
// 初始化通知的标题、内容和 ID
$title = '';
$content = '';
$id = null;
if ($count > 0) {
$notification = $user->unreadNotifications()->first(); // 获取第一条未读通知
$notification = $user->unreadNotifications()->first();
// 前端弹窗内容和标题相反显示,所以变量名会有点怪
$id = $notification->id; // 获取通知 ID
$title = NotificationServe::getContent($notification); // 获取通知内容
$content = NotificationServe::getTitle($notification); // 获取通知标题
$id = $notification->id;
$title = NotificationServe::getContent($notification);
$content = NotificationServe::getTitle($notification);
}
// 返回未读通知数量的 JSON 响应
return responseJson(200, 'success', compact('count', 'title', 'content', 'id'));
}
}

@ -1,37 +1,28 @@
<?php
namespace App\Http\Controllers\User; // 定义命名空间
use App\Admin\Transforms\OrderShipStatusTransform; // 引入订单发货状态转换类
use App\Admin\Transforms\OrderStatusTransform; // 引入订单状态转换类
use App\Enums\OrderShipStatusEnum; // 引入订单发货状态枚举
use App\Enums\OrderStatusEnum; // 引入订单状态枚举
use App\Enums\ScoreRuleIndexEnum; // 引入积分规则索引枚举
use App\Http\Controllers\Controller; // 引入基础控制器类
use App\Models\Address; // 引入地址模型
use App\Models\Comment; // 引入评论模型
use App\Models\Order; // 引入订单模型
use App\Models\OrderDetail; // 引入订单详情模型
use App\Models\ScoreRule; // 引入积分规则模型
use App\Models\User; // 引入用户模型
use App\Services\OrderStatusButtonServe; // 引入订单状态按钮服务类
use App\Services\ScoreLogServe; // 引入积分日志服务类
use Illuminate\Http\Request; // 引入请求类
use Illuminate\Support\Facades\DB; // 引入数据库门面
use Yansongda\Pay\Pay; // 引入支付类
/**
* 用户订单控制器
*
* 该控制器处理用户的订单相关操作,包括订单列表展示、订单详情、完成订单、确认收货等功能。
*/
namespace App\Http\Controllers\User;
use App\Admin\Transforms\OrderShipStatusTransform;
use App\Admin\Transforms\OrderStatusTransform;
use App\Enums\OrderShipStatusEnum;
use App\Enums\OrderStatusEnum;
use App\Enums\ScoreRuleIndexEnum;
use App\Http\Controllers\Controller;
use App\Models\Address;
use App\Models\Comment;
use App\Models\Order;
use App\Models\OrderDetail;
use App\Models\ScoreRule;
use App\Models\User;
use App\Services\OrderStatusButtonServe;
use App\Services\ScoreLogServe;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Yansongda\Pay\Pay;
class OrderController extends Controller
{
/**
* 显示用户的订单列表
*
* @return \Illuminate\View\View
*/
public function index()
{
// 获取积分比例
@ -40,162 +31,153 @@ class OrderController extends Controller
/**
* @var $user User
*/
$user = auth()->user(); // 获取当前用户
$query = $user->orders(); // 获取用户的订单查询构建器
$user = auth()->user();
$query = $user->orders();
// 根据请求的 tab 参数筛选订单状态
switch (request('tab', 0)) {
case 0:
break; // 默认显示所有订单
break;
case 1:
// 待付款
$query->where('status', OrderStatusEnum::UN_PAY);
break;
case 2:
// 未发货
$query->where('status', OrderStatusEnum::PAID)
->where('ship_status', OrderShipStatusEnum::PENDING);
$query->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::PENDING);
break;
case 3:
// 待收货
$query->where('status', OrderStatusEnum::PAID)
->where('ship_status', OrderShipStatusEnum::DELIVERED);
$query->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::DELIVERED);
break;
case 4:
// 待评价
$query->where('status', OrderStatusEnum::PAID)
->where('ship_status', OrderShipStatusEnum::RECEIVED);
$query->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::RECEIVED);
break;
}
// 获取订单列表并进行处理
$orders = $query->latest() // 按照创建时间降序排列
->with('details', 'details.product') // 预加载订单详情和产品
$orders = $query->latest()
->with('details', 'details.product')
->get()
->map(
function (Order $order) use ($scoreRatio) {
// 计算可以获得的积分
$order->score = ceil($order->amount * $scoreRatio);
// 设置订单状态文本
// 可以或得到的积分
$order->score = ceil($order->amount*$scoreRatio);
// 完成按钮必须是已经支付和确认收货
$order->status_text = OrderStatusTransform::trans($order->status);
// 如果订单已付款,则显示发货状态
// 如果订单是付款了则显示发货状态
if ($order->status == OrderStatusEnum::PAID) {
// 如果已发货,则显示发货信息
// 如果发货了,则显示发货信息
$order->status_text = OrderShipStatusTransform::trans($order->ship_status);
}
// 创建订单状态按钮服务实例
$buttonServe = new OrderStatusButtonServe($order);
switch ($order->status) {
// 未支付的订单
// 未支付的
case OrderStatusEnum::UN_PAY:
$buttonServe->payButton()->cancelOrderButton(); // 添加支付和取消订单按钮
$buttonServe->payButton()->cancelOrderButton();
break;
case OrderStatusEnum::PAID:
// 已确认收货
// 已经确认收获了
if ($order->ship_status == OrderShipStatusEnum::RECEIVED) {
$buttonServe->completeButton(); // 添加完成按钮
$buttonServe->completeButton();
} elseif ($order->ship_status == OrderShipStatusEnum::DELIVERED) {
$buttonServe->shipButton(); // 添加发货按钮
$buttonServe->shipButton();
} else {
$buttonServe->refundButton(); // 添加退款按钮
$buttonServe->refundButton();
}
break;
// 手动取消的订单、已完成的订单、超时取消的订单
// 手动取消的订单
// 已经完成的订单
// 超时取消的订单
case OrderStatusEnum::UN_PAY_CANCEL:
case OrderStatusEnum::COMPLETED:
case OrderStatusEnum::TIMEOUT_CANCEL:
$buttonServe->replyBuyButton()->deleteButton(); // 添加重新购买和删除按钮
$buttonServe->replyBuyButton()->deleteButton();
break;
}
// 获取按钮集合
$order->buttons = $buttonServe->getButtons();
return $order; // 返回处理后的订单
return $order;
}
);
// 查询不同状态的订单数量
// 查询订单总量
$unPayCount = $user->orders()->where('status', OrderStatusEnum::UN_PAY)->count();
$shipPendingCount = $user->orders()->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::PENDING)->count();
$shipDeliveredCount = $user->orders()->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::DELIVERED)->count();
$shipReceivedCount = $user->orders()->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::RECEIVED)->count();
$ordersCount = $user->orders()->count(); // 获取订单总数
$ordersCount = $user->orders()->count();
// 返回订单列表视图
return view('user.orders.index', compact('orders', 'unPayCount', 'shipPendingCount', 'shipDeliveredCount', 'shipReceivedCount', 'ordersCount'));
}
/**
* 显示指定订单的详细信息
*
* @param Order $order
* @return \Illuminate\View\View|\Illuminate\Http\Response
*/
public function show(Order $order)
{
// 检查当前用户是否有权限查看该订单
if ($order->isNotUser(auth()->id())) {
abort(403, '你没有权限'); // 返回403错误
abort(403, '你没有权限');
}
// 设置发货和确认收货状态
$order->ship_send = $order->ship_status == OrderShipStatusEnum::DELIVERED;
$order->confirm_ship = $order->ship_status == OrderShipStatusEnum::RECEIVED;
// 如果已确认收货,标记为已发货
if ($order->confirm_ship) {
$order->ship_send = true;
}
// 设置订单是否已完成
$order->completed = $order->status == OrderStatusEnum::RECEIVED;
$order->completed = $order->status == OrderShipStatusEnum::RECEIVED;
// 返回订单详情视图
return view('user.orders.show', compact('order'));
}
/**
* 完成订单并添加评论
*
* @param Order $order
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function completeOrder(Order $order, Request $request)
{
// 获取评分和评论内容
$star = intval($request->input('star'));
$content = $request->input('content');
// 验证评分范围
if ($star < 0 || $star > 5) {
return responseJsonAsBadRequest('无效的评分'); // 返回错误响应
return responseJsonAsBadRequest('无效的评分');
}
// 验证评论内容
if (empty($content)) {
return responseJsonAsBadRequest('请至少写一些内容吧'); // 返回错误响应
return responseJsonAsBadRequest('请至少些一些内容吧');
}
// 检查当前用户是否有权限完成该订单
// 判断是当前用户的订单才可以删除
$user = auth()->user();
if ($order->isNotUser($user->id)) {
return responseJsonAsBadRequest('你没有权限'); // 返回错误响应
return responseJsonAsBadRequest('你没有权限');
}
// 只有已付款且确认收货的订单才能完成
if (!($order->status == OrderStatusEnum::PAID && $order->ship_status == OrderShipStatusEnum::RECEIVED)) {
return responseJsonAsBadRequest('订单当前状态不能完成'); // 返回错误响应
// 只有付完款的订单,而且必须是未完成的, 确认收货
if (
// 必须是已经付款,且已经确认收货的
! ($order->status == OrderStatusEnum::PAID && $order->ship_status == OrderShipStatusEnum::RECEIVED)
) {
return responseJsonAsBadRequest('订单当前状态不能完成');
}
// 获取订单详情
$orderDetails = $order->details()->get();
// 构建评论数据
$comments = $orderDetails->map(function (OrderDetail $orderDetail) use ($user, $content, $star) {
return [
'order_id' => $orderDetail->order_id,
'order_detail_id' => $orderDetail->id,
@ -206,95 +188,89 @@ class OrderController extends Controller
];
});
DB::beginTransaction(); // 开始数据库事务
DB::beginTransaction();
try {
// 更新订单状态为完成
// 订单完成
$order->status = OrderStatusEnum::COMPLETED;
$order->save();
// 保存评论内容
// 评论内容
Comment::query()->insert($comments->all());
// 更新订单详情为已评论
OrderDetail::query()->where('order_id', $order->id)->update(['is_commented' => true]);
// 完成订单增加积分
(new ScoreLogServe)->completeOrderAddScore($order);
DB::commit(); // 提交事务
DB::commit();
} catch (\Exception $e) {
DB::rollBack(); // 回滚事务
return responseJsonAsServerError('服务器异常,请稍后再试'); // 返回错误响应
return responseJsonAsServerError('服务器异常,请稍后再试');
}
return responseJson(200, '完成订单已增加积分'); // 返回成功响应
return responseJson(200, '完成订单已增加积分');
}
/**
* 确认收货
*
* @param Order $order
* @return \Illuminate\Http\RedirectResponse
*/
public function confirmShip(Order $order)
{
// 检查当前用户是否有权限确认收货
// 判断是当前用户的订单才可以删除
if ($order->isNotUser(auth()->id())) {
abort(403, '你没有权限'); // 返回403错误
abort(403, '你没有权限');
}
// 验证订单状态
if ($order->status != OrderStatusEnum::PAID) {
return back()->withErrors('订单未付款'); // 返回错误
return back()->withErrors('订单未付款');
}
if ($order->ship_status != OrderShipStatusEnum::DELIVERED) {
return back()->withErrors('订单未发货'); // 返回错误
return back()->withErrors('订单未发货');
}
// 更新订单发货状态为已收货
$order->ship_status = OrderShipStatusEnum::RECEIVED;
$order->save();
return back()->with('status', '收货成功'); // 返回成功消息
return back()->with('status', '收货成功');
}
/**
* 取消订单
*
* @param Order $order
* @return \Illuminate\Http\RedirectResponse
*/
public function cancelOrder(Order $order)
{
// 检查当前用户是否有权限取消订单
// 判断是当前用户的订单才可以删除
if ($order->isNotUser(auth()->id())) {
abort(403, '你没有权限'); // 返回403错误
abort(403, '你没有权限');
}
// 验证订单状态
if ($order->status != OrderStatusEnum::UN_PAY) {
return back()->withErrors('未付款的订单才能取消'); // 返回错误
return back()->withErrors('未付款的订单才能取消');
}
$pay = Pay::alipay(config('pay.ali')); // 获取支付宝支付实例
$pay = Pay::alipay(config('pay.ali'));
try {
// 取消订单
$orderData = [
'out_trade_no' => $order->no,
];
$result = $pay->cancel($orderData); // 调用支付取消接口
$result = $pay->cancel($orderData);
// 更新订单状态为未支付取消
$order->status = OrderStatusEnum::UN_PAY_CANCEL;
$order->save();
} catch (\Exception $e) {
return back()->withErrors('服务器异常,请稍后再试'); // 返回错误
return back()->withErrors('服务器异常,请稍后再试');
}
return back()->with('status', '取消成功'); // 返回成功消息
return back()->with('status', '取消成功');
}
/**
@ -304,50 +280,47 @@ class OrderController extends Controller
*/
protected function getScoreRatio()
{
// 查询积分规则
$scoreRule = ScoreRule::query()->where('index_code', ScoreRuleIndexEnum::COMPLETE_ORDER)->firstOrFail();
return $scoreRule->score ?? 1; // 返回积分比例默认为1
return $scoreRule->score ?? 1;
}
/**
* 检查地址是否存在
* 地址是否存在
*
* @param $address
* @return bool
*/
protected function hasAddress($address)
{
// 查询用户是否拥有该地址
return Address::query()
->where('user_id', auth()->id())
->where('id', $address)
->exists();
}
/**
* 删除订单
*
* @param int $id 订单 ID
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($id)
{
/**
* @var $order Order
*/
$order = Order::query()->findOrFail($id); // 查找订单
// 检查当前用户是否有权限删除该订单
$order = Order::query()->findOrFail($id);
// 判断是当前用户的订单才可以删除
if ($order->isNotUser(auth()->id())) {
abort(403, '你没有权限'); // 返回403错误
abort(403, '你没有权限');
}
// 检查订单状态,支付的订单不能删除
if (!in_array($order->status, [OrderStatusEnum::UN_PAY_CANCEL, OrderStatusEnum::TIMEOUT_CANCEL, OrderStatusEnum::COMPLETED])) {
abort(403, '订单不能删除'); // 返回403错误
// 支付的订单不能删除
if (! in_array($order->status, [OrderStatusEnum::UN_PAY_CANCEL, OrderStatusEnum::TIMEOUT_CANCEL, OrderStatusEnum::COMPLETED])) {
abort(403, '订单不能删除');
}
$order->delete(); // 删除订单
$order->delete();
return back()->with('status', '删除成功'); // 返回成功消息
return back()->with('status', '删除成功');
}
}

@ -2,179 +2,183 @@
namespace App\Http\Controllers\User;
use App\Enums\OrderTypeEnum; // 引入订单类型枚举
use App\Enums\SettingKeyEnum; // 引入设置键枚举
use App\Jobs\CancelUnPayOrder; // 引入取消未支付订单的任务
use App\Models\Address; // 引入地址模型
use App\Models\Order; // 引入订单模型
use App\Models\OrderDetail; // 引入订单详情模型
use App\Models\Product; // 引入产品模型
use App\Models\Seckill; // 引入秒杀模型
use App\Models\User; // 引入用户模型
use App\Utils\OrderUtil; // 引入订单工具类
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Auth\SessionGuard; // 引入会话守卫
use Illuminate\Http\Request; // 引入请求类
use Illuminate\Support\Facades\DB; // 引入数据库门面
use Illuminate\Support\Facades\Redis; // 引入 Redis 门面
use Jenssegers\Agent\Agent; // 引入用户代理类
/**
* 秒杀控制器
*
* 该控制器处理与秒杀活动相关的操作,包括展示秒杀商品、参与秒杀、获取参与秒杀的用户等。
*/
use App\Enums\OrderTypeEnum;
use App\Enums\SettingKeyEnum;
use App\Jobs\CancelUnPayOrder;
use App\Models\Address;
use App\Models\Order;
use App\Models\OrderDetail;
use App\Models\Product;
use App\Models\Seckill;
use App\Models\User;
use App\Utils\OrderUtil;
use Carbon\Carbon;
use Illuminate\Auth\SessionGuard;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Jenssegers\Agent\Agent;
class SeckillController extends PaymentController
{
protected $redisSeckill; // 存储秒杀活动的 Redis 数据
protected $redisSeckill;
/**
* 显示秒杀活动的详细信息
*
* @param int $id 秒杀活动的 ID
* @return \Illuminate\View\View
*/
public function show($id)
{
$seckill = new Seckill(compact('id')); // 创建秒杀活动实例
$redisSeckill = $this->getSeckill($seckill); // 从 Redis 获取秒杀活动数据
$seckill = new Seckill(compact('id'));
$redisSeckill = $this->getSeckill($seckill);
$product = $redisSeckill->product; // 获取秒杀商品信息
$product = $redisSeckill->product;
/**
* @var $user User
* 如果用户已登录,则返回该用户的所有地址列表;否则返回一个空集合
* 如果登录返回所有地址列表,如果没有,则返回一个空集合
*/
$addresses = collect(); // 初始化地址集合
$addresses = collect();
if ($user = auth()->user()) {
$addresses = $user->addresses()->get(); // 获取用户地址
$addresses = $user->addresses()->get();
}
return view('seckills.show', compact('redisSeckill', 'product', 'addresses')); // 返回视图
return view('seckills.show', compact('redisSeckill', 'product', 'addresses'));
}
/**
* 抢购秒杀商品
* 抢购秒杀
*
* @param Request $request 请求对象
* @param int $id 秒杀活动的 ID
* @param Request $request
* @param $id
* @return \Illuminate\Http\JsonResponse
* @throws \Exception
*/
public function storeSeckill(Request $request, $id)
{
/**
* 直接从 session 中读取用户 ID,不经过数据库
* 直接从 session 中读取 id,不经过数据库
*
* @var $user User
* @var $auth SessionGuard
*/
$seckill = new Seckill(compact('id')); // 创建秒杀活动实例
$auth = auth('web'); // 获取当前会话
$userId = session()->get($auth->getName()); // 获取用户 ID
$seckill = new Seckill(compact('id'));
$auth = auth('web');
$userId = session()->get($auth->getName());
try {
// 检查请求中是否包含地址 ID
if (!$request->has('address_id')) {
throw new \Exception('必须选择一个地址'); // 抛出异常
if (! $request->has('address_id')) {
throw new \Exception('必须选择一个地址');
}
// 验证秒杀活动是否存在及是否已开始
// 验证是否有这个秒杀
// 验证秒杀活动是否已经结束
$redisSeckill = $this->redisSeckill = $this->getSeckill($seckill);
if (!$redisSeckill->is_start) {
throw new \Exception('秒杀未开始'); // 抛出异常
if (! $redisSeckill->is_start) {
throw new \Exception('秒杀未开始');
}
} catch (\Exception $e) {
return responseJson(402, $e->getMessage()); // 返回错误响应
return responseJson(402, $e->getMessage());
}
// 开始抢购逻辑,检查队列中是否还有可抢购的商品
// // 返回 0代表之前已经设置过了代表已经抢过
// if (0 == Redis::hset($seckill->getUsersKey($userId), 'id', $userId)) {
//
// return responseJson(403, '你已经抢购过了');
// }
// 开始抢购逻辑,如果从队列中读取不到了,代表已经抢购完成
if (is_null(Redis::lpop($seckill->getRedisQueueKey()))) {
return responseJson(403, '已经抢购完了'); // 返回错误响应
return responseJson(403, '已经抢购完了');
}
DB::beginTransaction(); // 开始数据库事务
DB::beginTransaction();
try {
$product = $redisSeckill->product; // 获取秒杀商品
$product = $redisSeckill->product;
if (is_null($product)) {
return responseJson(400, '商品已下架'); // 返回错误响应
return responseJson(400, '商品已下架');
}
// 验证用户的收货地址是否有效
// 已经通过抢购请求,可以查询数据库
// 在这里验证一下地址是不是本人的
$user = auth()->user();
$address = Address::query()->where('user_id', $user->id)->find($request->input('address_id'));
if (is_null($address)) {
return responseJson(400, '无效的收货地址'); // 返回错误响应
return responseJson(400, '无效的收货地址');
}
// 创建秒杀主表订单和明细表订单,默认数量为 1
// 创建一个秒杀主表订单和明细表订单,默认数量一个
$masterOrder = ($orderUtil = new OrderUtil([['product' => $product]]))->make($user->id, $address);
$masterOrder->type = OrderTypeEnum::SEC_KILL; // 设置订单类型为秒杀
$masterOrder->amount = $redisSeckill->price; // 设置订单金额
$masterOrder->save(); // 保存订单
$masterOrder->type = OrderTypeEnum::SEC_KILL;
$masterOrder->amount = $redisSeckill->price;
$masterOrder->save();
// 创建订单明细
$details = $orderUtil->getDetails();
data_set($details, '*.order_id', $masterOrder->id); // 设置订单 ID
OrderDetail::query()->insert($details); // 保存订单明细
data_set($details, '*.order_id', $masterOrder->id);
OrderDetail::query()->insert($details);
// 设置未支付订单的自动取消时间
// 当订单超过三十分钟未付款,自动取消订单
$setting = new SettingKeyEnum(SettingKeyEnum::UN_PAY_CANCEL_TIME);
$delay = Carbon::now()->addMinute(setting($setting, 30)); // 设置延迟时间
CancelUnPayOrder::dispatch($masterOrder)->delay($delay); // 调度取消任务
$delay = Carbon::now()->addMinute(setting($setting, 30));
CancelUnPayOrder::dispatch($masterOrder)->delay($delay);
// 生成支付信息
$form = $this->buildPayForm($masterOrder, (new Agent)->isMobile())->getContent();
} catch (\Exception $e) {
DB::rollBack(); // 回滚事务
// 回滚秒杀数量
DB::rollBack();
// 回滚一个秒杀数量
Redis::lpush($seckill->getRedisQueueKey(), 9);
// 从 Redis 中删除当前用户的抢购记录,允许用户重新抢购
// 把当前用户踢出,给他继续抢购
Redis::del($seckill->getUsersKey($userId));
return responseJson(403, $e->getMessage()); // 返回错误响应
return responseJson(403, $e->getMessage());
}
DB::commit(); // 提交事务
// 更新秒杀活动的剩余数量
$redisSeckill->sale_count += 1; // 增加销售数量
$redisSeckill->number -= 1; // 减少库存数量
Redis::set($seckill->getRedisModelKey(), json_encode($redisSeckill)); // 更新 Redis 中的秒杀活动数据
DB::commit();
// 数量减 -
$redisSeckill->sale_count += 1;
$redisSeckill->number -= 1;
Redis::set($seckill->getRedisModelKey(), json_encode($redisSeckill));
// 存储抢购成功的用户名
$user = auth()->user();
Redis::hset($seckill->getUsersKey($userId), 'name', $user->hidden_name);
return responseJson(200, '抢购成功', compact('form')); // 返回成功响应
return responseJson(200, '抢购成功', compact('form'));
}
/**
* 获取参与秒杀活动的用户
*
* @param int $id 秒杀活动的 ID
* @return \Illuminate\Http\JsonResponse
*/
public function getSeckillUsers($id)
{
$seckill = new Seckill(compact('id')); // 创建秒杀活动实例
$keys = Redis::keys($seckill->getUsersKey('*')); // 获取所有参与用户的键
$seckill = new Seckill(compact('id'));
$keys = Redis::keys($seckill->getUsersKey('*'));
$users = collect(); // 初始化用户集合
$users = collect();
foreach ($keys as $key) {
$users->push(Redis::hget($key, 'name')); // 获取用户名称并添加到集合
$users->push(Redis::hget($key, 'name'));
}
return responseJson(200, 'success', $users); // 返回成功响应
return responseJson(200, 'success', $users);
}
/**
* 从 Redis 中获取秒杀活动的数据
* 从 redis 中获取秒杀的数据
*
* @param Seckill $seckill 秒杀活动实例
* @param Seckill $seckill
* @return mixed
*/
protected function getSeckill(Seckill $seckill)
@ -182,48 +186,50 @@ class SeckillController extends PaymentController
/**
* @var $product Product
*/
$json = Redis::get($seckill->getRedisModelKey()); // 从 Redis 获取秒杀活动数据
$redisSeckill = json_decode($json); // 解析 JSON 数据
$json = Redis::get($seckill->getRedisModelKey());
$redisSeckill = json_decode($json);
if (is_null($redisSeckill)) {
abort(403, "没有这个秒杀活动"); // 返回 403 错误
abort(403, "没有这个秒杀活动");
}
// 获取当前时间和秒杀结束时间
// 得到这些时间
$now = Carbon::now();
$endAt = Carbon::make($redisSeckill->end_at);
if ($now->gt($endAt)) {
abort(403, "秒杀已经结束"); // 返回 403 错误
abort(403, "秒杀已经结束");
}
// 检查秒杀是否已经开始
// 秒杀是否已经开始
$startAt = Carbon::make($redisSeckill->start_at);
$redisSeckill->is_start = $now->gt($startAt); // 设置活动是否开始
// 计算倒计时
$redisSeckill->is_start = $now->gt($startAt);
// 开始倒计时
$redisSeckill->diff_time = $startAt->getTimestamp() - time();
return $redisSeckill; // 返回秒杀活动数据
return $redisSeckill;
}
/**
* 重写父类的构建订单明细
*
* @param Product $product 产品实例
* @param int $number 数量
* @return array 订单明细数组
* @param Product $product
* @param $number
* @return array
*/
protected function buildOrderDetail(Product $product, $number)
{
$attribute = [
'product_id' => $product->id, // 设置产品 ID
'number' => $number // 设置数量
$attribute = [
'product_id' => $product->id,
'number' => $number
];
// 价格为秒杀的价格,直接从 Redis 中读取
$attribute['price'] = ceilTwoPrice($this->redisSeckill->price); // 设置价格
$attribute['total'] = ceilTwoPrice($attribute['price'] * $attribute['number']); // 计算总价
// 价格为秒杀的价格, 直接从 redis 中读取
$attribute['price'] = ceilTwoPrice($this->redisSeckill->price);
$attribute['total'] = ceilTwoPrice($attribute['price'] * $attribute['number']);
return $attribute; // 返回订单明细数组
return $attribute;
}
}

@ -2,219 +2,217 @@
namespace App\Http\Controllers\User;
use App\Enums\OrderStatusEnum; // 引入订单状态枚举
use App\Enums\OrderTypeEnum; // 引入订单类型枚举
use App\Enums\SettingKeyEnum; // 引入设置键枚举
use App\Exceptions\OrderException; // 引入订单异常
use App\Http\Controllers\Controller; // 引入基础控制器
use App\Jobs\CancelUnPayOrder; // 引入取消未支付订单的任务
use App\Models\Address; // 引入地址模型
use App\Models\Car; // 引入购物车模型
use App\Models\Order; // 引入订单模型
use App\Models\OrderDetail; // 引入订单详情模型
use App\Models\Product; // 引入产品模型
use App\Models\User; // 引入用户模型
use App\Utils\OrderUtil; // 引入订单工具类
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Http\Request; // 引入请求类
use Illuminate\Support\Collection; // 引入集合类
use Illuminate\Support\Facades\DB; // 引入数据库门面
use Symfony\Component\HttpFoundation\Response; // 引入响应类
use App\Enums\OrderStatusEnum;
use App\Enums\OrderTypeEnum;
use App\Enums\SettingKeyEnum;
use App\Exceptions\OrderException;
use App\Http\Controllers\Controller;
use App\Jobs\CancelUnPayOrder;
use App\Models\Address;
use App\Models\Car;
use App\Models\Order;
use App\Models\OrderDetail;
use App\Models\Product;
use App\Models\User;
use App\Utils\OrderUtil;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Response;
class StoreOrderController extends Controller
{
/**
* 创建订单页面
*
* @param Request $request 请求对象
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
/**
* @var $user User
* 获取当前登录用户
*/
$user = auth()->user();
// 从请求中获取购物车信息和商品 ID
$cars = $request->input('cars', []);
$ids = $request->input('ids');
$numbers = $request->input('numbers');
// 验证商品 ID 和数量的有效性
if (count($ids) === 0 || count($ids) !== count($numbers)) {
return back()->with('error', '无效的商品'); // 返回错误信息
return back()->with('error', '无效的商品');
}
// 查询数据库中存在的商品
$products = Product::query()->whereIn('uuid', $ids)->get();
if ($products->count() === 0 || $products->count() !== count($numbers)) {
return back()->with('error', '无效的商品'); // 返回错误信息
}
$totalAmount = 0; // 初始化总金额
return back()->with('error', '无效的商品');
}
// 计算每个商品的总金额并更新商品数量
$totalAmount = 0;
$products->transform(function (Product $product, $i) use ($numbers, &$totalAmount) {
$product->number = $numbers[$i]; // 设置商品数量
$product->total_amount = round($product->price * $product->number); // 计算商品总金额
$totalAmount += $product->total_amount; // 累加总金额
return $product; // 返回更新后的商品
$product->number = $numbers[$i];
$product->total_amount = round($product->price * $product->number);
$totalAmount += $product->total_amount;
return $product;
});
// 增加邮费
$postAmount = \setting(new SettingKeyEnum(SettingKeyEnum::POST_AMOUNT));
$totalAmount += $postAmount; // 将邮费加到总金额
$totalAmount += $postAmount;
// 获取用户的收货地址
/**
* @var $user User
*/
$user = auth()->user();
$addresses = $user->addresses()->latest()->get();
// 获取可用的优惠券
// 可用的优惠券
$today = Carbon::today()->toDateString();
$coupons = $user->coupons()
->where('start_date', '<=', $today) // 优惠券开始日期
->where('end_date', '>=', $today) // 优惠券结束日期
->whereNull('used_at') // 未使用的优惠券
->where('full_amount', '<=', $totalAmount) // 满足使用条件的优惠券
->where('start_date', '<=', $today)
->where('end_date', '>=', $today)
->whereNull('used_at')
->where('full_amount', '<=', $totalAmount)
->latest()
->get();
// 返回创建订单页面的视图
return view('orders.create', compact('products', 'cars', 'addresses', 'totalAmount', 'coupons', 'postAmount'));
}
/**
* 存储订单
*
* @param Request $request 请求对象
* @return \Illuminate\Http\JsonResponse
*/
public function store(Request $request)
{
// 验证请求数据的有效性
if (($response = $this->validateRequest($request)) instanceof Response) {
return $response; // 返回响应
return $response;
}
list($ids, $numbers, $productMap, $address, $couponModel) = $response; // 解构响应数据
// 构建订单详情数据
list($ids, $numbers, $productMap, $address, $couponModel) = $response;
// 构建出订单所需的详情
$detailsData = Collection::make($ids)->map(function ($id, $index) use ($numbers, $productMap) {
return [
'number' => $numbers[$index], // 商品数量
'product' => $productMap[$id] // 商品信息
'number' => $numbers[$index],
'product' => $productMap[$id]
];
});
DB::beginTransaction(); // 开始数据库事务
DB::beginTransaction();
try {
// 创建主订单
$masterOrder = ($orderUtil = new OrderUtil($detailsData))->make(auth()->id(), $address);
// 验证优惠券的使用条件
if (!is_null($couponModel) && $masterOrder->amount < $couponModel->full_amount) {
throw new \Exception('优惠券门槛金额为 ' . $couponModel->full_amount); // 抛出异常
if (! is_null($couponModel) && $masterOrder->amount < $couponModel->full_amount) {
throw new \Exception('优惠券门槛金额为 ' . $couponModel->full_amount);
}
// 计算订单金额
if (!is_null($couponModel)) {
// 订单价格等于原价 - 优惠价格
if (! is_null($couponModel)) {
$masterOrder->amount = $masterOrder->amount > $couponModel->amount ?
($masterOrder->amount - $couponModel->amount) : 0.01; // 计算实际支付金额
($masterOrder->amount - $couponModel->amount) : 0.01;
$couponModel->used_at = Carbon::now()->toDateTimeString(); // 标记优惠券为已使用
$couponModel->save(); // 保存优惠券状态
$couponModel->used_at = Carbon::now()->toDateTimeString();
$couponModel->save();
$masterOrder->coupon_id = $couponModel->id; // 关联优惠券 ID
$masterOrder->coupon_amount = $couponModel->amount; // 记录优惠券金额
$masterOrder->coupon_id = $couponModel->id;
$masterOrder->coupon_amount = $couponModel->amount;
}
$masterOrder->save(); // 保存主订单
$masterOrder->save();
// 创建订单明细
$details = $orderUtil->getDetails(); // 获取订单明细数据
data_set($details, '*.order_id', $masterOrder->id); // 设置订单 ID
OrderDetail::query()->insert($details); // 保存订单明细
$details = $orderUtil->getDetails();
data_set($details, '*.order_id', $masterOrder->id);
OrderDetail::query()->insert($details);
// 如果存在购物车,删除购物车中的商品
// 如果存在购物车,把购物车删除
$cars = $request->input('cars');
if (is_array($cars) && !empty($cars)) {
Car::query()->where('user_id', auth()->id())->where('id', $cars)->delete(); // 删除购物车商品
if (is_array($cars) && ! empty($cars)) {
Car::query()->where('user_id', auth()->id())->where('id', $cars)->delete();
}
// 设置未支付订单的自动取消时间
// 当订单超过三十分钟未付款,自动取消订单
$settingKey = new SettingKeyEnum(SettingKeyEnum::UN_PAY_CANCEL_TIME);
$delay = Carbon::now()->addMinute(setting($settingKey, 30)); // 设置延迟时间
CancelUnPayOrder::dispatch($masterOrder)->delay($delay); // 调度取消任务
$delay = Carbon::now()->addMinute(setting($settingKey, 30));
CancelUnPayOrder::dispatch($masterOrder)->delay($delay);
DB::commit(); // 提交事务
DB::commit();
} catch (\Exception $e) {
DB::rollBack(); // 回滚事务
return responseJsonAsBadRequest($e->getMessage()); // 返回错误响应
DB::rollBack();
return responseJsonAsBadRequest($e->getMessage());
}
return responseJson(200, '创建订单成功', ['order_id' => $masterOrder->id]); // 返回成功响应
return responseJson(200, '创建订单成功', ['order_id' => $masterOrder->id]);
}
/**
* 验证请求数据的有效性
*
* @param Request $request 请求对象
* @return array|Response 验证结果
*/
private function validateRequest(Request $request)
{
/**
* @var $user User
* @var $address Address
*/
$user = auth()->user(); // 获取当前用户
$user = auth()->user();
$ids = $request->input('ids'); // 获取商品 ID
$numbers = $request->input('numbers'); // 获取商品数量
$ids = $request->input('ids');
$numbers = $request->input('numbers');
// 验证商品 ID 和数量的有效性
if (count($ids) === 0 || count($ids) !== count($numbers)) {
return responseJsonAsBadRequest('无效的商品'); // 返回错误响应
return responseJsonAsBadRequest('无效的商品');
}
// 查询数据库中存在的商品
$productMap = Product::query()->whereIn('uuid', $ids)->get()->mapWithKeys(function (Product $product) {
return [$product->uuid => $product]; // 将商品映射为 ID 和商品对象
});
return [$product->uuid => $product];
});
if (count($productMap) === 0 || count($productMap) !== count($numbers)) {
return responseJsonAsBadRequest('无效的商品.'); // 返回错误响应
return responseJsonAsBadRequest('无效的商品.');
}
// 验证收货地址的有效性
$address = $user->addresses()->find($request->input('address_id'));
if (is_null($address)) {
return responseJsonAsBadRequest('请选择收货地址'); // 返回错误响应
return responseJsonAsBadRequest('请选择收货地址');
}
// 验证优惠券的有效性
// 查看是否有优惠券的价格
$couponModel = null;
if ($couponId = $request->input('coupon_id')) {
$couponModel = $user->coupons()->find($couponId); // 查询优惠券
$couponModel = $user->coupons()->find($couponId);
if (is_null($couponModel)) {
return responseJsonAsBadRequest('无效的优惠券'); // 返回错误响应
return responseJsonAsBadRequest('无效的优惠券');
}
$today = Carbon::today();
$startDate = Carbon::make($couponModel->start_date) ?? Carbon::tomorrow(); // 优惠券开始日期
$endDate = Carbon::make($couponModel->end_date) ?? Carbon::yesterday(); // 优惠券结束日期
$startDate = Carbon::make($couponModel->start_date) ?? Carbon::tomorrow();
$endDate = Carbon::make($couponModel->end_date) ?? Carbon::yesterday();
if ($today->lt($startDate) || $today->gt($endDate)) {
return responseJsonAsBadRequest('优惠券已过使用期'); // 返回错误响应
return responseJsonAsBadRequest('优惠券已过使用期');
}
if (!is_null($couponModel->used_at)) {
return responseJsonAsBadRequest('优惠券已使用过'); // 返回错误响应
if (! is_null($couponModel->used_at)) {
return responseJsonAsBadRequest('优惠券已使用过');
}
}
return [$ids, $numbers, $productMap, $address, $couponModel]; // 返回验证结果
return [$ids, $numbers, $productMap, $address, $couponModel];
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save