pull/2/head
chen 8 months ago
parent 784a200288
commit cbc671aaeb

@ -29,6 +29,8 @@ class AddShopToEsSearchCommand extends Command
*
* @return void
*/
public function __construct()
{
parent::__construct();
@ -41,51 +43,75 @@ class AddShopToEsSearchCommand extends Command
*/
public function handle()
{
try {
ElasticSearchTrait::client()->ping([
'client' => [
'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;
}
// 新建商品索引
if (Product::indexExists()) {
Product::deleteIndex();
$this->info('删除索引');
}
Product::createIndex();
$this->info('新建索引成功');
// 开始导入数据
$query = Product::query();
$count = $query->count();
$handle = 0;
$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";
});
});
echo PHP_EOL;
$this->info('索引生成完毕');
}
}

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

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

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

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

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

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

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

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

@ -18,38 +18,43 @@ class AuthController extends Controller
/**
* 登录的接口
*
* @param LoginRequest $request
* @return \Illuminate\Http\JsonResponse
* @param LoginRequest $request 登录请求数据
* @return \Illuminate\Http\JsonResponse 返回登录结果的 JSON 响应
*/
public function login(LoginRequest $request)
{
// 获取用户名和密码
$username = $request->input('username');
$password = $request->input('password');
// 查询用户
$user = User::query()
->whereNotNull('name')
->where('name', $username)
->first();
// 检查用户是否存在
if (is_null($user)) {
return responseJsonAsBadRequest('用户名或者密码错误');
}
// 验证密码是否正确
if (! Hash::check($password, $user->getAuthPassword())) {
return responseJsonAsBadRequest('用户名或者密码错误');
}
// 返回成功响应和 token
return responseJson(200, '登录成功', $this->getToken($user));
}
/**
* 注销的接口
*
* @return \Illuminate\Http\JsonResponse
* @return \Illuminate\Http\JsonResponse 返回注销结果的 JSON 响应
*/
public function logout()
{
// 注销用户
auth('api')->logout();
return responseJsonAsDeleted('注销成功');
@ -58,47 +63,46 @@ class AuthController extends Controller
/**
* 注册的接口
*
* @param RegisterUserRequest $request
* @return \Illuminate\Http\JsonResponse
* @param RegisterUserRequest $request 注册请求数据
* @return \Illuminate\Http\JsonResponse 返回注册结果的 JSON 响应
*/
public function register(RegisterUserRequest $request)
{
// 获取用户名和密码
$username = $request->input('username');
$password = $request->input('password');
// 检查用户名是否已存在
if (User::query()->where('name', $username)->exists()) {
return responseJsonAsBadRequest('用户名已经存在, 请换一个用户名');
}
// 创建新用户
$user = new User();
$user->name = $username;
$user->password = $password;
$user->sex = UserSexEnum::MAN;
$user->is_init_email = 1;
// api 注册的用户默认激活
$user->is_active = UserStatusEnum::ACTIVE;
$user->save();
$user->password = Hash::make($password); // 确保密码被哈希处理
$user->sex = UserSexEnum::MAN; // 默认性别
$user->is_init_email = 1; // 初始化邮箱状态
$user->is_active = UserStatusEnum::ACTIVE; // 默认激活状态
$user->save(); // 保存用户信息
// 返回成功响应和 token
return responseJson(201, '注册成功', $this->getToken($user));
}
/**
* 拼接 token
*
* @param User $user
* @return array
* @param User $user 用户模型
* @return array 返回包含 token 的数组
*/
protected function getToken(User $user)
{
// 换取 token
// 生成 token
$prefix = 'Bearer';
$token = auth('api')->login($user);
$me = new OwnResource($user);
$token = auth('api')->login($user); // 登录并获取 token
$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
{
/**
* 获取分类列表
*
* @param PageServe $serve 分页服务
* @return \Illuminate\Http\JsonResponse 返回分类列表的 JSON 响应
*/
public function index(PageServe $serve)
{
// 获取分页参数
list($limit, $offset) = $serve->getPageParameters();
// 构建分类查询
$query = Category::query();
// 根据名称过滤分类
if ($title = $serve->input('name')) {
$query->where('title', 'like', "%{$title}%");
}
// 获取总数和分类数据
$count = $query->count();
$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)
{
// 获取分页参数
list($limit, $offset) = $serve->getPageParameters();
// 排序的字段和排序的值
$orderField = $serve->input('order_field');
$orderValue = $serve->input('order_value');
// 查找指定分类
/**
* @var $category Category
*/
$category = Category::query()->findOrFail($category);
// 获取该分类下的产品查询
$query = $category->products();
// 根据名称过滤产品
if ($name = $serve->input('name')) {
$query->where('name', 'like', "%{$name}%");
}
@ -56,18 +72,17 @@ class CategoryController extends Controller
$allFields = ['created_at', 'sale_count', 'view_count'];
$orderField = in_array($orderField, $allFields) ?
$orderField :
array_first($allFields);
$orderValue = $orderValue === 'asc' ? 'asc' : 'desc';
array_first($allFields); // 默认排序字段
$orderValue = $orderValue === 'asc' ? 'asc' : 'desc'; // 默认排序方式
// 获取数据
$count = $query->count();
$count = $query->count(); // 获取产品总数
$products = $query->orderBy($orderField, $orderValue)
->limit($limit)
->offset($offset)
->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
{
/**
* 获取当前用户的信息
*
* @return \Illuminate\Http\JsonResponse 返回当前用户信息的 JSON 响应
*/
public function me()
{
// 获取当前认证用户
$me = auth()->user();
// 返回成功响应和用户信息
return responseJson(200, 'success', new OwnResource($me));
}
/**
* 获取当前用户的积分日志
*
* @param PageServe $serve 分页服务
* @return \Illuminate\Http\JsonResponse 返回积分日志的 JSON 响应
*/
public function scoreLogs(PageServe $serve)
{
// 获取分页参数
list($limit, $offset) = $serve->getPageParameters();
/**
* @var $me User
*/
// 获取当前认证用户
$me = auth()->user();
// 获取用户的积分日志查询
$query = $me->scoreLogs();
// 获取积分日志总数
$count = $query->count();
// 获取分页后的积分日志数据
$scoreLogs = $me->scoreLogs()
->latest()
->offset($offset)
->limit($limit)
->latest() // 按时间降序排列
->offset($offset) // 设置偏移量
->limit($limit) // 设置限制
->get();
// 返回成功响应和积分日志
return responseJson(200, 'success', ScoreLogResource::collection($scoreLogs), compact('count'));
}
}

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

@ -6,35 +6,41 @@ use App\Http\Controllers\Controller;
use App\Models\Category;
use Illuminate\Http\Request;
class CategoryController extends Controller
{
/**
* 前台分类列表
* 显示前台分类列表
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回分类列表视图
*/
public function index()
{
// 获取最新的分类,分页显示,每页 30 条
$categories = Category::query()->latest()->paginate(30);
// 返回分类列表视图,并传递分类数据
return view('categories.index', compact('categories'));
}
/**
* 分类详情
* 显示分类详情
*
* @param Request $request
* @param Category $category
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @param Request $request 请求对象
* @param Category $category 分类模型实例
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 返回分类详情视图
*/
public function show(Request $request, Category $category)
{
// 获取排序字段,默认为 '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'));
}
}

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

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

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

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

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

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

@ -81,7 +81,7 @@
}
}
}

Loading…
Cancel
Save