li 8 months ago
commit 2b995d0e96

@ -29,6 +29,8 @@ class AddShopToEsSearchCommand extends Command
* *
* @return void * @return void
*/ */
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@ -41,19 +43,36 @@ class AddShopToEsSearchCommand extends Command
*/ */
public function handle() public function handle()
{ {
try { try {
ElasticSearchTrait::client()->ping([ ElasticSearchTrait::client()->ping([
'client' => [ 'client' => [
'timeout' => 5, 'timeout' => 5,
'connect_timeout' => 5 'connect_timeout' => 5
] ]
]); ]);
} catch (\Exception $exception) { } catch (\Exception $exception) {
$this->info($exception->getMessage()); $this->info($exception->getMessage());
$this->info('无法连接到 elasticsearch 服务器,请配置 config/elasticsearch.php 文件');
$this->info('无法连接到 elasticse
arch 服务器,请配置 config/elasticsearch.php 文件');
$this->info('默认使用 MySQL 的模糊搜索'); $this->info('默认使用 MySQL 的模糊搜索');
$this->info('配置完毕后可运行: php artisan add:shop-to-search 添加索引'); $this->info('配置完毕后可运行: php artisan add:shop-to-search 添加索引');
return; return;
} }
@ -61,13 +80,17 @@ class AddShopToEsSearchCommand extends Command
// 新建商品索引 // 新建商品索引
if (Product::indexExists()) { if (Product::indexExists()) {
Product::deleteIndex(); Product::deleteIndex();
$this->info('删除索引'); $this->info('删除索引');
} }
Product::createIndex(); Product::createIndex();
$this->info('新建索引成功'); $this->info('新建索引成功');
// 开始导入数据 // 开始导入数据
$query = Product::query(); $query = Product::query();
@ -85,7 +108,10 @@ class AddShopToEsSearchCommand extends Command
}); });
}); });
echo PHP_EOL; echo PHP_EOL;
$this->info('索引生成完毕'); $this->info('索引生成完毕');
} }
} }

@ -19,6 +19,7 @@ class DelExpireScoreData extends Command
/** /**
* The console command description. * The console command description.
* *
* @var string * @var string
*/ */
protected $description = 'Command description'; protected $description = 'Command description';

@ -37,22 +37,42 @@ class SyncProducViewCommand extends Command
* Execute the console command. * Execute the console command.
* *
* @return mixed * @return mixed
*/ */
public function handle() public function handle()
{ {
$yesterday = Carbon::yesterday()->toDateString(); $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) { $products->map(function (Product $product) use ($yesterday) {
$viewCount = Cache::pull($product->getViewCountKey($yesterday), 0); $viewCount = Cache::pull($product->getViewCountKey($yesterday), 0);
$product->view_count += $viewCount; $product->view_count += $vi
ewCount;
$product->today_has_view = 0; $product->today_has_view = 0;
$product->save(); $product->save();
}); });
createSystemLog("系统同步{$yesterday}商品浏览量", ['date' => $yesterday]); createSystemLog("系统同步{$yesterday}商品浏览量", ['date' => $yesterday]);
} }
} }

@ -24,6 +24,9 @@ class UninstallShop extends BaseCommand
* *
* @return void * @return void
*/ */
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();

@ -7,6 +7,8 @@ use App\Models\Category;
use App\Models\Product; use App\Models\Product;
use App\Models\User; use App\Models\User;
use App\Utils\HomeCacheDataUtil; use App\Utils\HomeCacheDataUtil;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
@ -47,9 +49,24 @@ class UpdateCacheHomeData extends Command
// 每分钟有定时任务更新 // 每分钟有定时任务更新
$ttl = 60 * 2; $ttl = 60 * 2;
HomeCacheDataUtil::categories($ttl, true); HomeCacheDataUtil::categories($ttl, true);
HomeCacheDataUtil::hotProducts($ttl, true); HomeCacheDataUtil::hotProducts($ttl, true);
HomeCacheDataUtil::latestProducts($ttl, true); HomeCacheDataUtil::latestProducts($ttl, true);
HomeCacheDataUtil::users($ttl, true); HomeCacheDataUtil::users($ttl, true);
} }
} }

@ -8,3 +8,5 @@ class BadRequestException extends Exception
{ {
// //
} }

@ -1,7 +1,6 @@
<?php <?php
namespace App\Exceptions; namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -9,7 +8,6 @@ use Throwable;
use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenBlacklistedException; use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException; use Tymon\JWTAuth\Exceptions\TokenInvalidException;
class Handler extends ExceptionHandler class Handler extends ExceptionHandler
{ {
/** /**
@ -22,40 +20,38 @@ class Handler extends ExceptionHandler
'password', 'password',
'password_confirmation', 'password_confirmation',
]; ];
/** /**
* Register the exception handling callbacks for the application. * Register the exception handling callbacks for the application.
*/ */
public function register(): void public function register(): void
{ {
// 处理可报告的异常
$this->reportable(function (Throwable $e) { $this->reportable(function (Throwable $e) {
// // 可以在这里记录异常或执行其他操作
}); });
// 处理可渲染的异常
$this->renderable(function (\Exception $exception, Request $request) { $this->renderable(function (\Exception $exception, Request $request) {
// 检查请求是否为 API 请求
if ($request->is('api*')) { if ($request->is('api*')) {
// 处理 JWT 相关的异常
if ($exception instanceof JWTException) { if ($exception instanceof JWTException) {
// 映射 JWT 异常到用户友好的消息
$mapExceptions = [ $mapExceptions = [
TokenInvalidException::class => '无效的token', TokenInvalidException::class => '无效的token',
TokenBlacklistedException::class => 'token 已被加入黑名单,请重新登录' TokenBlacklistedException::class => 'token 已被加入黑名单,请重新登录'
]; ];
// 获取对应的错误消息
$msg = $mapExceptions[get_class($exception)] ?? $exception->getMessage(); $msg = $mapExceptions[get_class($exception)] ?? $exception->getMessage();
return responseJsonAsUnAuthorized($msg); return responseJsonAsUnAuthorized($msg); // 返回未授权的 JSON 响应
} }
// 拦截表单验证错误抛出的异常 // 拦截表单验证错误抛出的异常
elseif ($exception instanceof ValidationException) { elseif ($exception instanceof ValidationException) {
return responseJsonAsBadRequest($exception->validator->errors()->first()); // 返回验证错误的 JSON 响应
return responseJsonAsBadRequest($exception->validator->errors()->first());
} }
// 处理其他服务器错误
return responseJsonAsServerError($exception->getMessage()); return responseJsonAsServerError($exception->getMessage());
} }
// 对于非 API 请求,返回服务器错误信息
return responseJsonAsServerError($exception->getMessage(), null); return responseJsonAsServerError($exception->getMessage(), null);
}); });
} }

@ -5,10 +5,7 @@
* Date: 2018/10/19 * Date: 2018/10/19
* Time: 22:04 * Time: 22:04
*/ */
namespace App\Exceptions; namespace App\Exceptions;
class OrderException extends \Exception class OrderException extends \Exception
{ {

@ -12,4 +12,7 @@ namespace App\Exceptions;
class UploadException extends \Exception class UploadException extends \Exception
{ {
} }

@ -18,38 +18,43 @@ class AuthController extends Controller
/** /**
* 登录的接口 * 登录的接口
* *
* @param LoginRequest $request * @param LoginRequest $request 登录请求数据
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse 返回登录结果的 JSON 响应
*/ */
public function login(LoginRequest $request) public function login(LoginRequest $request)
{ {
// 获取用户名和密码
$username = $request->input('username'); $username = $request->input('username');
$password = $request->input('password'); $password = $request->input('password');
// 查询用户
$user = User::query() $user = User::query()
->whereNotNull('name') ->whereNotNull('name')
->where('name', $username) ->where('name', $username)
->first(); ->first();
// 检查用户是否存在
if (is_null($user)) { if (is_null($user)) {
return responseJsonAsBadRequest('用户名或者密码错误'); return responseJsonAsBadRequest('用户名或者密码错误');
} }
// 验证密码是否正确
if (! Hash::check($password, $user->getAuthPassword())) { if (! Hash::check($password, $user->getAuthPassword())) {
return responseJsonAsBadRequest('用户名或者密码错误'); return responseJsonAsBadRequest('用户名或者密码错误');
} }
// 返回成功响应和 token
return responseJson(200, '登录成功', $this->getToken($user)); return responseJson(200, '登录成功', $this->getToken($user));
} }
/** /**
* 注销的接口 * 注销的接口
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse 返回注销结果的 JSON 响应
*/ */
public function logout() public function logout()
{ {
// 注销用户
auth('api')->logout(); auth('api')->logout();
return responseJsonAsDeleted('注销成功'); return responseJsonAsDeleted('注销成功');
@ -58,47 +63,46 @@ class AuthController extends Controller
/** /**
* 注册的接口 * 注册的接口
* *
* @param RegisterUserRequest $request * @param RegisterUserRequest $request 注册请求数据
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse 返回注册结果的 JSON 响应
*/ */
public function register(RegisterUserRequest $request) public function register(RegisterUserRequest $request)
{ {
// 获取用户名和密码
$username = $request->input('username'); $username = $request->input('username');
$password = $request->input('password'); $password = $request->input('password');
// 检查用户名是否已存在
if (User::query()->where('name', $username)->exists()) { if (User::query()->where('name', $username)->exists()) {
return responseJsonAsBadRequest('用户名已经存在, 请换一个用户名'); return responseJsonAsBadRequest('用户名已经存在, 请换一个用户名');
} }
// 创建新用户
$user = new User(); $user = new User();
$user->name = $username; $user->name = $username;
$user->password = $password; $user->password = Hash::make($password); // 确保密码被哈希处理
$user->sex = UserSexEnum::MAN; $user->sex = UserSexEnum::MAN; // 默认性别
$user->is_init_email = 1; $user->is_init_email = 1; // 初始化邮箱状态
// api 注册的用户默认激活 $user->is_active = UserStatusEnum::ACTIVE; // 默认激活状态
$user->is_active = UserStatusEnum::ACTIVE; $user->save(); // 保存用户信息
$user->save();
// 返回成功响应和 token
return responseJson(201, '注册成功', $this->getToken($user)); return responseJson(201, '注册成功', $this->getToken($user));
} }
/** /**
* 拼接 token * 拼接 token
* *
* @param User $user * @param User $user 用户模型
* @return array * @return array 返回包含 token 的数组
*/ */
protected function getToken(User $user) protected function getToken(User $user)
{ {
// 换取 token // 生成 token
$prefix = 'Bearer'; $prefix = 'Bearer';
$token = auth('api')->login($user); $token = auth('api')->login($user); // 登录并获取 token
$me = new OwnResource($user); $me = new OwnResource($user); // 创建用户资源
return compact('prefix', 'token', 'me'); return compact('prefix', 'token', 'me'); // 返回 token 和用户信息
} }
} }

@ -11,44 +11,60 @@ use App\Http\Controllers\Controller;
class CategoryController extends Controller class CategoryController extends Controller
{ {
/**
* 获取分类列表
*
* @param PageServe $serve 分页服务
* @return \Illuminate\Http\JsonResponse 返回分类列表的 JSON 响应
*/
public function index(PageServe $serve) public function index(PageServe $serve)
{ {
// 获取分页参数
list($limit, $offset) = $serve->getPageParameters(); list($limit, $offset) = $serve->getPageParameters();
// 构建分类查询
$query = Category::query(); $query = Category::query();
// 根据名称过滤分类
if ($title = $serve->input('name')) { if ($title = $serve->input('name')) {
$query->where('title', 'like', "%{$title}%"); $query->where('title', 'like', "%{$title}%");
} }
// 获取总数和分类数据
$count = $query->count(); $count = $query->count();
$categories = $query->orderBy('order')->limit($limit)->offset($offset)->get(); $categories = $query->orderBy('order')->limit($limit)->offset($offset)->get();
$categories = CategoreResource::collection($categories); $categories = CategoreResource::collection($categories); // 转换为资源集合
return responseJson(200, 'success', $categories, compact('count')); return responseJson(200, 'success', $categories, compact('count')); // 返回成功响应
} }
/**
* 获取指定分类下的产品
*
* @param PageServe $serve 分页服务
* @param int $category 分类 ID
* @return \Illuminate\Http\JsonResponse 返回产品列表的 JSON 响应
*/
public function getProducts(PageServe $serve, $category) public function getProducts(PageServe $serve, $category)
{ {
// 获取分页参数
list($limit, $offset) = $serve->getPageParameters(); list($limit, $offset) = $serve->getPageParameters();
// 排序的字段和排序的值 // 排序的字段和排序的值
$orderField = $serve->input('order_field'); $orderField = $serve->input('order_field');
$orderValue = $serve->input('order_value'); $orderValue = $serve->input('order_value');
// 查找指定分类
/** /**
* @var $category Category * @var $category Category
*/ */
$category = Category::query()->findOrFail($category); $category = Category::query()->findOrFail($category);
// 获取该分类下的产品查询
$query = $category->products(); $query = $category->products();
// 根据名称过滤产品
if ($name = $serve->input('name')) { if ($name = $serve->input('name')) {
$query->where('name', 'like', "%{$name}%"); $query->where('name', 'like', "%{$name}%");
} }
@ -56,18 +72,17 @@ class CategoryController extends Controller
$allFields = ['created_at', 'sale_count', 'view_count']; $allFields = ['created_at', 'sale_count', 'view_count'];
$orderField = in_array($orderField, $allFields) ? $orderField = in_array($orderField, $allFields) ?
$orderField : $orderField :
array_first($allFields); array_first($allFields); // 默认排序字段
$orderValue = $orderValue === 'asc' ? 'asc' : 'desc'; $orderValue = $orderValue === 'asc' ? 'asc' : 'desc'; // 默认排序方式
// 获取数据 // 获取数据
$count = $query->count(); $count = $query->count(); // 获取产品总数
$products = $query->orderBy($orderField, $orderValue) $products = $query->orderBy($orderField, $orderValue)
->limit($limit) ->limit($limit)
->offset($offset) ->offset($offset)
->get(); ->get();
$products = ProductResource::collection($products); $products = ProductResource::collection($products); // 转换为资源集合
return responseJson(200, 'success', $products, compact('count')); return responseJson(200, 'success', $products, compact('count')); // 返回成功响应
} }
} }

@ -11,31 +11,51 @@ use App\Http\Controllers\Controller;
class OwnController extends Controller class OwnController extends Controller
{ {
/**
* 获取当前用户的信息
*
* @return \Illuminate\Http\JsonResponse 返回当前用户信息的 JSON 响应
*/
public function me() public function me()
{ {
// 获取当前认证用户
$me = auth()->user(); $me = auth()->user();
// 返回成功响应和用户信息
return responseJson(200, 'success', new OwnResource($me)); return responseJson(200, 'success', new OwnResource($me));
} }
/**
* 获取当前用户的积分日志
*
* @param PageServe $serve 分页服务
* @return \Illuminate\Http\JsonResponse 返回积分日志的 JSON 响应
*/
public function scoreLogs(PageServe $serve) public function scoreLogs(PageServe $serve)
{ {
// 获取分页参数
list($limit, $offset) = $serve->getPageParameters(); list($limit, $offset) = $serve->getPageParameters();
/** /**
* @var $me User * @var $me User
*/ */
// 获取当前认证用户
$me = auth()->user(); $me = auth()->user();
// 获取用户的积分日志查询
$query = $me->scoreLogs(); $query = $me->scoreLogs();
// 获取积分日志总数
$count = $query->count(); $count = $query->count();
// 获取分页后的积分日志数据
$scoreLogs = $me->scoreLogs() $scoreLogs = $me->scoreLogs()
->latest() ->latest() // 按时间降序排列
->offset($offset) ->offset($offset) // 设置偏移量
->limit($limit) ->limit($limit) // 设置限制
->get(); ->get();
// 返回成功响应和积分日志
return responseJson(200, 'success', ScoreLogResource::collection($scoreLogs), compact('count')); return responseJson(200, 'success', ScoreLogResource::collection($scoreLogs), compact('count'));
} }
} }

@ -9,35 +9,40 @@ use Illuminate\Http\Request;
class CarController extends Controller class CarController extends Controller
{ {
// 构造函数,设置中间件
public function __construct() public function __construct()
{ {
// 仅对 store 和 destroy 方法应用用户身份验证中间件
$this->middleware('user.auth')->only('store', 'destroy'); $this->middleware('user.auth')->only('store', 'destroy');
} }
/** /**
* 购物车列表 * 显示购物车列表
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回购物车视图
*/ */
public function index() public function index()
{ {
$cars = collect(); $cars = collect(); // 初始化购物车集合
/** /**
* @var $user User * @var $user User
*/ */
// 获取当前登录用户的购物车
if ($user = \auth()->user()) { if ($user = \auth()->user()) {
// 直接获取当前登录用户的购物车 // 直接获取当前用户的购物车及其关联的产品
$cars = $user->cars()->with('product')->get(); $cars = $user->cars()->with('product')->get();
} }
// 返回购物车视图,并传递购物车数据
return view('cars.index', compact('cars')); return view('cars.index', compact('cars'));
} }
/** /**
* 添加购物车 * 添加商品到购物车
* @param Request $request *
* @return array * @param Request $request 请求对象
* @return array 返回操作结果
*/ */
public function store(Request $request) public function store(Request $request)
{ {
@ -46,43 +51,47 @@ class CarController extends Controller
* @var $product Product * @var $product Product
* @var $user User * @var $user User
*/ */
// 根据产品 UUID 查找产品
$product = Product::query()->where('uuid', $request->input('product_id'))->firstOrFail(); $product = Product::query()->where('uuid', $request->input('product_id'))->firstOrFail();
// 获取当前用户
$user = auth()->user(); $user = auth()->user();
// 查找或新建购物车记录
$car = $user->cars()->firstOrNew([ $car = $user->cars()->firstOrNew([
'user_id' => \auth()->id(), 'user_id' => \auth()->id(),
'product_id' => $product->id 'product_id' => $product->id
]); ]);
// 处理购物车数量变化
// 如果是同步,则只是赋值,如果是添加购物车则是添加 $change = 0; // 数量变化
$change = 0; $number = $request->input('number', 1); // 获取请求中的数量,默认为 1
$number = $request->input('number', 1);
if ($request->input('action') == 'sync') { if ($request->input('action') == 'sync') {
// 如果是同步操作,直接设置数量
$change = $number - $car->number; $change = $number - $car->number;
$car->number = $number; $car->number = $number;
} else { } else {
// 否则,增加数量
$car->number += $number; $car->number += $number;
} }
// 检查库存是否足够
if ($car->number > $product->count) { if ($car->number > $product->count) {
return responseJson(403, '库存不足'); // 返回库存不足的响应
return responseJson(403, '库存不足');
} }
// 保存购物车记录
$car->save(); $car->save();
// 返回成功响应
return responseJson(200, '加入购物车成功', compact('change')); return responseJson(200, '加入购物车成功', compact('change'));
} }
/** /**
* @param $id * 从购物车中删除商品
* @return array *
* @param $id 购物车项的 ID
* @return array 返回操作结果
*/ */
public function destroy($id) public function destroy($id)
{ {
@ -90,16 +99,19 @@ class CarController extends Controller
/** /**
* @var $user User * @var $user User
*/ */
// 获取当前用户
$user = auth()->user(); $user = auth()->user();
// 查找购物车项并删除
$car = $user->cars()->whereKey($id)->firstOrFail(); $car = $user->cars()->whereKey($id)->firstOrFail();
$car->delete(); $car->delete();
} catch (\Exception $e) { } catch (\Exception $e) {
// 捕获异常并返回错误响应
dd($e); dd($e); // 可选:调试输出异常信息
return responseJson(500, '服务器异常,请稍后再试'); return responseJson(500, '服务器异常,请稍后再试');
} }
// 返回成功响应
return responseJson(200, '删除成功'); return responseJson(200, '删除成功');
} }
} }

@ -6,35 +6,41 @@ use App\Http\Controllers\Controller;
use App\Models\Category; use App\Models\Category;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class CategoryController extends Controller class CategoryController extends Controller
{ {
/** /**
* 前台分类列表 * 显示前台分类列表
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回分类列表视图
*/ */
public function index() public function index()
{ {
// 获取最新的分类,分页显示,每页 30 条
$categories = Category::query()->latest()->paginate(30); $categories = Category::query()->latest()->paginate(30);
// 返回分类列表视图,并传递分类数据
return view('categories.index', compact('categories')); return view('categories.index', compact('categories'));
} }
/** /**
* 分类详情 * 显示分类详情
* *
* @param Request $request * @param Request $request 请求对象
* @param Category $category * @param Category $category 分类模型实例
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回分类详情视图
*/ */
public function show(Request $request, Category $category) public function show(Request $request, Category $category)
{ {
// 获取排序字段,默认为 'created_at'
$orderBy = $request->input('orderBy', 'created_at'); $orderBy = $request->input('orderBy', 'created_at');
$categoryProducts = $category->products()->withCount('users')->orderBy($orderBy, 'desc')->paginate(10);
// 获取该分类下的产品,并按指定字段排序,分页显示,每页 10 条
$categoryProducts = $category->products()
->withCount('users') // 计算每个产品的用户数量
->orderBy($orderBy, 'desc')
->paginate(10);
// 返回分类详情视图,并传递分类及其产品数据
return view('categories.show', compact('category', 'categoryProducts')); return view('categories.show', compact('category', 'categoryProducts'));
} }
} }

@ -9,5 +9,8 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController class Controller extends BaseController
{ {
// 使用 Laravel 的授权请求、任务调度和请求验证功能
use AuthorizesRequests, DispatchesJobs, ValidatesRequests; use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
// 此类作为所有控制器的基类,提供通用功能
} }

@ -12,66 +12,73 @@ use Illuminate\Support\Facades\DB;
class CouponController extends Controller class CouponController extends Controller
{ {
/**
* 处理用户领取优惠券的请求
*
* @param Request $request 请求对象
* @return \Illuminate\Http\JsonResponse 返回 JSON 响应
*/
public function store(Request $request) public function store(Request $request)
{ {
// 获取请求中的优惠券模板 ID
$templateId = $request->input('template_id'); $templateId = $request->input('template_id');
/** /**
* @var $user User * @var $user User
*/ */
// 获取当前登录用户
$user = auth()->user(); $user = auth()->user();
// 如果用户未登录,返回未授权响应
if (is_null($user)) { if (is_null($user)) {
return responseJsonAsUnAuthorized('请先登录再领取优惠券'); return responseJsonAsUnAuthorized('请先登录再领取优惠券');
} }
// 查找优惠券模板
$template = CouponTemplate::query()->find($templateId); $template = CouponTemplate::query()->find($templateId);
// 如果模板不存在,返回无效优惠券响应
if (is_null($template)) { if (is_null($template)) {
return responseJsonAsBadRequest('无效的优惠券'); return responseJsonAsBadRequest('无效的优惠券');
} }
// 判断优惠券是否过期 // 判断优惠券是否过期
$today = Carbon::today(); $today = Carbon::today();
$endDate = Carbon::make($template->end_date); $endDate = Carbon::make($template->end_date);
// 如果当前日期大于优惠券结束日期,返回过期响应
if ($today->gt($endDate)) { if ($today->gt($endDate)) {
return responseJsonAsBadRequest('优惠券已过期'); return responseJsonAsBadRequest('优惠券已过期');
} }
// 检查用户积分是否足够
if ($user->score_now < $template->score) { if ($user->score_now < $template->score) {
return responseJsonAsBadRequest("积分不足{$template->score},请先去获取积分"); return responseJsonAsBadRequest("积分不足{$template->score},请先去获取积分");
} }
// 这里我会只让每个用户只能领取一张优惠券 // 确保每个用户只能领取一张优惠券
if ($user->coupons()->where('template_id', $templateId)->exists()) { if ($user->coupons()->where('template_id', $templateId)->exists()) {
return responseJsonAsBadRequest('你已经领取过优惠券了'); return responseJsonAsBadRequest('你已经领取过优惠券了');
} }
// 开始数据库事务
DB::beginTransaction(); DB::beginTransaction();
try { try {
// 用户减少积分 // 用户减少积分
if ($template->score > 0) { if ($template->score > 0) {
$user->score_now -= $template->score; $user->score_now -= $template->score;
$user->save(); $user->save();
// 生成积分日志 // 生成积分日志
$log = new ScoreLog(); $log = new ScoreLog();
$log->user_id = $user->getKey(); $log->user_id = $user->getKey();
$log->description = "领取优惠券"; $log->description = "领取优惠券";
$log->score = -1 * $template->score; $log->score = -1 * $template->score; // 记录减少的积分
$log->save(); $log->save();
} }
// 开始领取优惠券 // 创建用户优惠券记录
$coupon = new UserHasCoupon(); $coupon = new UserHasCoupon();
$coupon->template_id = $template->getKey(); $coupon->template_id = $template->getKey();
$coupon->user_id = $user->getKey(); $coupon->user_id = $user->getKey();
$coupon->title = $template->title; $coupon->title = $template->title;
$coupon->amount = $template->amount; $coupon->amount = $template->amount;
$coupon->full_amount = $template->full_amount; $coupon->full_amount = $template->full_amount;
@ -80,13 +87,15 @@ class CouponController extends Controller
$coupon->save(); $coupon->save();
} catch (\Exception $e) { } catch (\Exception $e) {
// 如果发生异常,回滚事务
DB::rollBack(); DB::rollBack();
return responseJsonAsServerError('服务器异常,请稍后再试'); return responseJsonAsServerError('服务器异常,请稍后再试');
} }
// 提交事务
DB::commit(); DB::commit();
// 返回成功响应
return responseJson(200, '领取成功'); return responseJson(200, '领取成功');
} }
} }

@ -8,21 +8,26 @@ use Illuminate\Http\Request;
class CouponTemplateController extends Controller class CouponTemplateController extends Controller
{ {
/**
* 显示可用的优惠券模板
*
* @return \Illuminate\View\View 返回包含优惠券模板的视图
*/
public function index() public function index()
{ {
// 获取今天的日期,格式为 YYYY-MM-DD
$today = Carbon::today()->toDateString(); $today = Carbon::today()->toDateString();
// 只查询未过期的 // 查询未过期的优惠券模板,并标记用户已领取的优惠券
// 标记已经领取过的
$templates = CouponTemplate::query() $templates = CouponTemplate::query()
->withCount(['coupons' => function ($b) { ->withCount(['coupons' => function ($query) {
// 统计当前用户已领取的优惠券数量
$b->where('user_id', auth()->id()); $query->where('user_id', auth()->id());
}]) }])
->where('end_date', '>=', $today) ->where('end_date', '>=', $today) // 只选择未过期的优惠券模板
->get(); ->get(); // 执行查询并获取结果
// 返回包含优惠券模板的视图
return view('coupons.templates', compact('templates')); return view('coupons.templates', compact('templates'));
} }
} }

@ -10,22 +10,24 @@ use App\Utils\HomeCacheDataUtil;
class HomeController extends Controller class HomeController extends Controller
{ {
/** /**
* 首页显示的数据 * 显示首页的数据
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回首页视图
*/ */
public function index() public function index()
{ {
// 取出后台排序好的九个分类,并且关联出商品的总数 // 设置缓存过期时间,随机增加 10 到 30 秒
// 如没有 key存入缓存中防止用户未配置好任务调度确访问首页
// 数据将不会从首页更新,每分钟任务调度更新,请务必配置好
$ttl = 120 + mt_rand(10, 30); $ttl = 120 + mt_rand(10, 30);
// 获取后台排序好的九个分类,并关联商品总数
// 如果没有缓存的 key存入缓存中防止用户未配置好任务调度时访问首页
// 数据将不会从首页更新,每分钟任务调度更新,请务必配置好
$categories = HomeCacheDataUtil::categories($ttl); $categories = HomeCacheDataUtil::categories($ttl);
$hotProducts = HomeCacheDataUtil::hotProducts($ttl); $hotProducts = HomeCacheDataUtil::hotProducts($ttl);
$latestProducts = HomeCacheDataUtil::latestProducts($ttl); $latestProducts = HomeCacheDataUtil::latestProducts($ttl);
$users = HomeCacheDataUtil::users($ttl); $users = HomeCacheDataUtil::users($ttl);
// 秒杀数据 // 获取秒杀数据
$secKills = HomeCacheDataUtil::getSeckillData(); $secKills = HomeCacheDataUtil::getSeckillData();
/** /**
@ -33,35 +35,45 @@ class HomeController extends Controller
* *
* @var $loginUser User * @var $loginUser User
*/ */
// 如果用户已登录,加载其订阅信息
if ($loginUser = auth()->user()) { if ($loginUser = auth()->user()) {
$loginUser->load('subscribe'); $loginUser->load('subscribe');
} }
// 查询优惠券 // 查询优惠券模板
$couponTemplates = HomeCacheDataUtil::couponTemplates(); $couponTemplates = HomeCacheDataUtil::couponTemplates();
// 检查秒杀功能是否开启
$setting = new SettingKeyEnum(SettingKeyEnum::IS_OPEN_SECKILL); $setting = new SettingKeyEnum(SettingKeyEnum::IS_OPEN_SECKILL);
$isOpenSeckill = setting($setting) == 1; $isOpenSeckill = setting($setting) == 1; // 1 表示开启
// 返回首页视图,并传递所需的数据
return view( return view(
'homes.index', 'homes.index',
compact('categories', 'hotProducts', 'latestProducts', 'users', 'secKills', 'loginUser', 'isOpenSeckill', 'couponTemplates') compact('categories', 'hotProducts', 'latestProducts', 'users', 'secKills', 'loginUser', 'isOpenSeckill', 'couponTemplates')
); );
} }
/**
* 取消订阅功能
*
* @param string $email 加密的邮箱地址
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回取消订阅的结果视图
*/
public function unSubscribe($email) public function unSubscribe($email)
{ {
try { try {
// 解密邮箱地址
$email = decrypt($email); $email = decrypt($email);
} catch (\Exception $e) { } catch (\Exception $e) {
// 如果解密失败,返回错误提示视图
return view('hint.error', ['status' => '未知的账号']); return view('hint.error', ['status' => '未知的账号']);
} }
// 更新订阅状态为未订阅
Subscribe::query()->where('email', $email)->update(['is_subscribe' => 0]); Subscribe::query()->where('email', $email)->update(['is_subscribe' => 0]);
// 返回成功提示视图
return view('hint.success', ['status' => '已取消订阅']); return view('hint.success', ['status' => '已取消订阅']);
} }
} }

@ -16,80 +16,80 @@ class PaymentNotificationController extends Controller
{ {
protected $config; protected $config;
public function __construct() public function __construct()
{ {
// 从配置文件中加载支付宝支付配置
$this->config = config('pay.ali'); $this->config = config('pay.ali');
} }
/** /**
* 后台通知的接口 * 处理支付宝后台通知的接口
* *
* @param Request $request * @param Request $request
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response 返回支付宝的成功响应
*/ */
public function payNotify(Request $request) public function payNotify(Request $request)
{ {
$alipay = Pay::alipay($this->config); $alipay = Pay::alipay($this->config);
// TODO , 加一个轮询接口配合后台通知修改订单状态 // TODO: 加入轮询接口以配合后台通知修改订单状态
// 后台异步通知接口可能网络问题接收不到 // 后台异步通知接口可能因网络问题而未能接收
// 使用轮询插接订单状态,如果支付了停止轮询 // 使用轮询检查订单状态,如果支付成功则停止轮询
try{ try {
$data = $alipay->verify(); // 是的,验签就这么简单! $data = $alipay->verify(); // 验签操作
// 验证 app_id // 验证 app_id
// 可判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额); // 可判断 total_amount 是否为该订单的实际金额
if ($data->get('app_id') == $this->config['app_id']) { if ($data->get('app_id') == $this->config['app_id']) {
// 支付成功 // 支付成功
if ($data->get('trade_status') == 'TRADE_SUCCESS') { if ($data->get('trade_status') == 'TRADE_SUCCESS') {
// 更新订单 // 更新订单信息
$order = Order::query()->where('no', $data->get('out_trade_no'))->firstOrFail(); $order = Order::query()->where('no', $data->get('out_trade_no'))->firstOrFail();
$order->paid_at = $data->get('notify_time'); $order->paid_at = $data->get('notify_time'); // 支付时间
$order->pay_no = $data->get('trade_no'); $order->pay_no = $data->get('trade_no'); // 支付交易号
$order->pay_amount = $data->get('receipt_amount'); $order->pay_amount = $data->get('receipt_amount'); // 实际支付金额
$order->status = OrderStatusEnum::PAID; $order->status = OrderStatusEnum::PAID; // 更新订单状态为已支付
$order->pay_type = OrderPayTypeEnum::ALI; $order->pay_type = OrderPayTypeEnum::ALI; // 支付类型
$order->save(); $order->save(); // 保存订单信息
} }
} }
// 记录支付宝通知日志
Log::debug('Alipay notify', $data->all()); Log::debug('Alipay notify', $data->all());
} catch (\Exception $e) { } catch (\Exception $e) {
// 记录异常日志
Log::debug('Alipay notify', $e->getMessage()); Log::debug('Alipay notify', $e->getMessage());
} }
// 返回支付宝的成功响应
return $alipay->success(); return $alipay->success();
} }
/** /**
* 前台跳转的接口 * 处理前台跳转的接口
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回支付结果视图
*/ */
public function payReturn() public function payReturn()
{ {
// 获取最新的 9 个产品
$latestProducts = Product::query()->latest()->take(9)->get(); $latestProducts = Product::query()->latest()->take(9)->get();
$order = null; $order = null;
try { try {
// 验证支付数据
$data = Pay::alipay($this->config)->verify(); $data = Pay::alipay($this->config)->verify();
// 根据订单号查询订单
$order = Order::query()->where('no', $data->get('out_trade_no'))->firstOrFail(); $order = Order::query()->where('no', $data->get('out_trade_no'))->firstOrFail();
} catch (\Exception $e) { } catch (\Exception $e) {
// 处理异常(可以记录日志或返回错误视图)
} }
// 返回支付结果视图,并传递订单和最新产品数据
return view('user.payments.result', compact('order', 'latestProducts')); return view('user.payments.result', compact('order', 'latestProducts'));
} }
} }

@ -16,51 +16,51 @@ use Illuminate\Support\Facades\Cache;
class ProductController extends Controller class ProductController extends Controller
{ {
/** /**
* 商品列表 * 显示商品列表
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回商品列表视图
*/ */
public function index() public function index()
{ {
// 随机查出一些商品展示 // 随机查出一些商品展示,取前 9 个商品
$products = Product::query()->inRandomOrder()->take(9)->get(['uuid', 'name'])->split(3); $products = Product::query()->inRandomOrder()->take(9)->get(['uuid', 'name'])->split(3);
// 获取所有商品的拼音
$pinyins = ProductPinYin::query()->orderBy('pinyin')->pluck('pinyin'); $pinyins = ProductPinYin::query()->orderBy('pinyin')->pluck('pinyin');
// 返回商品列表视图,并传递商品和拼音数据
return view('products.index', compact('products', 'pinyins')); return view('products.index', compact('products', 'pinyins'));
} }
/** /**
* ajax 通过商品首字母查询商品 * 通过商品首字母查询商品AJAX请求
* @param $pinyin *
* @return mixed * @param string $pinyin 商品首字母
* @return mixed 返回符合条件的商品集合
*/ */
public function getProductsByPinyin($pinyin) public function getProductsByPinyin($pinyin)
{ {
// 查询首字母为指定字母的商品,并返回
$products = Product::query()->where('first_pinyin', $pinyin)->get(['id', 'name'])->split(3); $products = Product::query()->where('first_pinyin', $pinyin)->get(['id', 'name'])->split(3);
return $products; return $products;
} }
/** /**
* 商品搜索 * 商品搜索功能
* *
* @param Request $request * @param Request $request 请求对象
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回搜索结果视图
*/ */
public function search(Request $request) public function search(Request $request)
{ {
$keyword = $request->input('keyword', ''); $keyword = $request->input('keyword', ''); // 获取搜索关键词
$page = abs((int)$request->get('page', 1)); $page = abs((int)$request->get('page', 1)); // 获取当前页码
$limit = (int)$request->get('limit', 20); $limit = (int)$request->get('limit', 20); // 获取每页显示的商品数量
$offset = (int) ($page - 1) * $limit; $offset = (int) ($page - 1) * $limit; // 计算偏移量
// 全文索引 // 尝试使用全文索引搜索商品
try { try {
$parameters = [ $parameters = [
'multi_match' => [ 'multi_match' => [
'query' => $keyword, 'query' => $keyword,
@ -68,46 +68,47 @@ class ProductController extends Controller
] ]
]; ];
// 获取搜索结果数量
$count = Product::searchCount($parameters); $count = Product::searchCount($parameters);
$searchCount = $count['count'] ?? 0; $searchCount = $count['count'] ?? 0; // 搜索结果总数
$searchResult = Product::search($parameters, $limit, $offset); $searchResult = Product::search($parameters, $limit, $offset); // 获取搜索结果
$filterIds = Collection::make($searchResult['hits']['hits'] ?? [])->pluck('_source.id'); $filterIds = Collection::make($searchResult['hits']['hits'] ?? [])->pluck('_source.id'); // 提取商品ID
$models = Product::query()->findMany($filterIds); $models = Product::query()->findMany($filterIds); // 根据ID查询商品
// 创建分页对象
$products = new LengthAwarePaginator($models, $searchCount, $limit, $page); $products = new LengthAwarePaginator($models, $searchCount, $limit, $page);
} catch (\Exception $e) { } catch (\Exception $e) {
// 如果全文索引搜索失败,使用模糊搜索
$products = Product::query()->withCount('users')->where('name', 'like', "%{$keyword}%")->paginate($limit); $products = Product::query()->withCount('users')->where('name', 'like', "%{$keyword}%")->paginate($limit);
} }
// 返回搜索结果视图
return view('products.search', compact('products')); return view('products.search', compact('products'));
} }
/** /**
* 单个商品显示 * 显示单个商品的详细信息
* *
* @param $uuid * @param string $uuid 商品的UUID
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回商品详情视图
*/ */
public function show($uuid) public function show($uuid)
{ {
/** // 查询商品
* @var $user User|null
*/
$product = Product::query()->where('uuid', $uuid)->firstOrFail(); $product = Product::query()->where('uuid', $uuid)->firstOrFail();
// 记录今日是否已查看
if (! $product->today_has_view) { if (!$product->today_has_view) {
$product->today_has_view = true; $product->today_has_view = true;
$product->save(); $product->save();
} }
// 直接使用缓存
// 增加商品浏览次数
$today = Carbon::today()->toDateString(); $today = Carbon::today()->toDateString();
Cache::increment($product->getViewCountKey($today)); Cache::increment($product->getViewCountKey($today));
// 当前登录用户
// 商品浏览次数 + 1
$user = auth()->user(); $user = auth()->user();
// 同类商品推荐 // 同类商品推荐
@ -116,28 +117,30 @@ class ProductController extends Controller
->take(5) ->take(5)
->get(); ->get();
// 加载出详情,收藏的人数, 评论 // 加载商品的详细信息、收藏用户和评论
$product->load([ $product->load([
'detail', 'detail',
'users', 'users',
'comments' => function ($query) { 'comments' => function ($query) {
$query->latest(); $query->latest(); // 按最新评论排序
}, },
'comments.user' 'comments.user'
]); ]);
// 检查当前用户是否已收藏该商品
$product->userIsLike = $product->users()->where('id', auth()->id())->exists(); $product->userIsLike = $product->users()->where('id', auth()->id())->exists();
// 如果登录返回所有地址列表,如果没有,则返回一个空集合 // 如果用户已登录,增加积分
if ($user) { if ($user) {
// 浏览商品增加积分
(new ScoreLogServe)->visitedProductAddScore($user, $product); (new ScoreLogServe)->visitedProductAddScore($user, $product);
} }
// 返回商品详情视图
return view('products.show', compact('product', 'recommendProducts')); return view('products.show', compact('product', 'recommendProducts'));
} }
/** /**
* 获取当前认证用户的守卫
*
* @return \Illuminate\Contracts\Auth\StatefulGuard * @return \Illuminate\Contracts\Auth\StatefulGuard
*/ */
protected function guard() protected function guard()

@ -76,7 +76,12 @@
"repositories": { "repositories": {
"packagist": { "packagist": {
"type": "composer", "type": "composer",
"url": "https://mirrors.aliyun.com/composer/" "url": "https://mirrors.aliyun.com/composer/",
} }
} }
} }

Loading…
Cancel
Save