Compare commits

..

No commits in common. 'main' and 'hanyoujun' have entirely different histories.

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);
editor.create(); // 创建编辑器实例
alert(response.msg);
}
}
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; // 定义命名空间
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 集合类
use App\Models\Product;
use App\Models\SearchAble\ElasticSearchTrait;
use Elasticsearch\ClientBuilder;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Collection;
/**
* 将商品添加到 Elasticsearch 搜索的控制台命令
*
* 该命令用于将商品数据添加到 Elasticsearch 的全文索引中。
*/
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; // 定义命名空间
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 门面
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; // 定义命名空间
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 门面
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,59 +1,50 @@
<?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;
/****************************************
@ -61,20 +52,20 @@ class InstallShop extends BaseCommand
* 2. 数据库迁移
* 3. 复制静态资源
* 4. 创建软链接
****************************************/
$this->call('migrate'); // 执行数据库迁移,创建所需的数据表
$this->call('db:seed'); // 填充数据库,插入初始数据
$this->call('moon:copy'); // 复制静态资源到公共目录
$this->call('storage:link'); // 创建存储目录的软链接,以便访问存储的文件
*/
$this->call('migrate');
$this->call('db:seed');
$this->call('moon:copy');
$this->call('storage:link');
// 更新首页数据,防止上一次遗留数据的影响
$this->call('moon:update-home'); // 更新首页相关数据
// 更新首页数据,防止上一次遗留
$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; // 定义命名空间
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 门面
use App\Models\Product;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
/**
* 同步商品浏览量命令
*
* 该命令用于同步前一天的商品浏览量,并更新数据库中的相关记录。
*/
class SyncProductViewCommand extends Command
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; // 定义命名空间
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 App\Enums\HomeCacheEnum;
use App\Models\Category;
use App\Models\Product;
use App\Models\User;
use App\Utils\HomeCacheDataUtil;
use Carbon\Carbon; // 引入 Carbon 日期处理库
use Illuminate\Console\Command; // 引入 Laravel 的 Command 基类
use Illuminate\Support\Facades\Cache; // 引入 Cache 门面
/**
* 更新首页缓存数据命令
*
* 该命令用于更新首页的缓存数据,包括分类、热销商品、最新商品和用户信息。
*/
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 // 设置数量
'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];
}
}

Loading…
Cancel
Save