diff --git a/app/Console/Commands/CreateTodayOrderItems.php b/app/Console/Commands/CreateTodayOrderItems.php index d2122c0..be0261f 100644 --- a/app/Console/Commands/CreateTodayOrderItems.php +++ b/app/Console/Commands/CreateTodayOrderItems.php @@ -44,8 +44,12 @@ class CreateTodayOrderItems extends Command $threshold = 50; DB::enableQueryLog(); + // 获取所有状态为1的项目 + $projects = Project::where("status", 1)->pluck("id")->toArray(); + //获取正在进行中的订单,即使已经到了截止日,只要状态是在进行中的都继续生成 $unGeneratedOrders = (new Orders())->whereIn("status", [Orders::STATUS_ONGOING]) + ->whereIn("project_id", $projects) ->whereRaw("DATEDIFF(`from_date`, now()) <= 0") ->whereDoesntHave("orderItems", function ($query) { $query->whereRaw("DATEDIFF(`service_date`, now()) = 0"); diff --git a/app/Forms/ProjectForm.php b/app/Forms/ProjectForm.php index 511851d..2deeef1 100755 --- a/app/Forms/ProjectForm.php +++ b/app/Forms/ProjectForm.php @@ -26,6 +26,8 @@ class ProjectForm extends Form $this->add("complaint_mobile", Field::TEXT, ["label" => "投诉电话"]); // 是否需要签订协议 $this->add("agreement", Field::SELECT, ["label" => "是否需要签订协议", "choices" => [0 => "否", 1 => "是"]]); + // 状态 + $this->add("status", Field::SELECT, ["label" => "状态", "choices" => [1 => "启用", 0 => "禁用"], "default_value" => 1]); // 协议 $this->add("content", Field::TEXTAREA, ["label" => "协议"]); $this->add("profile", Field::TEXTAREA, ["label" => "简介"]); diff --git a/app/Http/Controllers/Admin/ProjectController.php b/app/Http/Controllers/Admin/ProjectController.php index 43d149d..b4eb58b 100755 --- a/app/Http/Controllers/Admin/ProjectController.php +++ b/app/Http/Controllers/Admin/ProjectController.php @@ -616,4 +616,32 @@ class ProjectController extends CommonController return $this->success("新增成功", '', $model); } + /** + * 快速切换项目状态 + */ + public function toggleStatus(Request $request) + { + try { + $id = $request->id; + $status = $request->status; + + if (!in_array($status, [0, 1])) { + return $this->error("状态值不正确"); + } + + $project = $this->model->find($id); + if (!$project) { + return $this->error("项目不存在"); + } + + $project->status = $status; + $project->save(); + + $statusText = $status == 1 ? "启用" : "禁用"; + return $this->success("项目已{$statusText}"); + } catch (\Exception $exception) { + return $this->error("操作失败:" . $exception->getMessage()); + } + } + } diff --git a/app/Models/OrderItems.php b/app/Models/OrderItems.php index 3a2a8b9..adab973 100755 --- a/app/Models/OrderItems.php +++ b/app/Models/OrderItems.php @@ -218,9 +218,13 @@ class OrderItems extends SoftDeletesModel $last_id = cache("last_auto_checkout_order_item_id", 0); Log::channel("daily_auto_checkout")->info("Last id:" . $last_id); + // 获取所有状态为1的项目 + $projects = Project::where("status", 1)->pluck("id")->toArray(); + $unpaid_order_items = (new OrderItems()) - ->whereHas("order", function ($query) { - $query->where("status", Orders::STATUS_ONGOING); + ->whereHas("order", function ($query) use ($projects) { + $query->where("status", Orders::STATUS_ONGOING) + ->whereIn("project_id", $projects); }) ->whereNull("paid_at") ->where("id", ">", $last_id) diff --git a/database/migrations/2020_06_05_121826_create_project.php b/database/migrations/2020_06_05_121826_create_project.php index 3a014ab..9e586f4 100644 --- a/database/migrations/2020_06_05_121826_create_project.php +++ b/database/migrations/2020_06_05_121826_create_project.php @@ -20,7 +20,7 @@ class CreateProject extends Migration $table->text("profile")->nullable(); $table->string("logo")->nullable(); $table->text("banners")->nullable(); - $table->string("status")->default(0); + $table->string("status")->default(1)->comment("1:启用,0:禁用"); $table->timestamps(); $table->softDeletes(); }); diff --git a/resources/views/admin/project/index.blade.php b/resources/views/admin/project/index.blade.php index e77052a..5c37d27 100755 --- a/resources/views/admin/project/index.blade.php +++ b/resources/views/admin/project/index.blade.php @@ -19,6 +19,7 @@ 地址 项目结算比例 + 状态 微信支付账号 支付宝账号 操作 @@ -32,6 +33,19 @@ {{ $row->address }} {{ $row->percent_first_party }} + + @if($row->status == 1) + 启用 + @else + 禁用 + @endif + + {{ $row->wechatpayAccount ? $row->wechatpayAccount->mchid : ""}}
{{ $row->wechatpayAccount ? $row->wechatpayAccount->name : ""}} {{ $row->alipayAccount ? $row->alipayAccount->appid : ""}}
{{ $row->alipayAccount ? $row->alipayAccount->name : ""}} @@ -61,6 +75,49 @@ @push("footer") @endpush diff --git a/routes/web.php b/routes/web.php index 364d470..f306309 100644 --- a/routes/web.php +++ b/routes/web.php @@ -62,6 +62,7 @@ Route::group(["namespace" => "Admin", "prefix" => "admin"], function () { Route::post("project/edit-department", 'ProjectController@editDepartment'); Route::post("project/delete-department", 'ProjectController@deleteDepartment'); Route::get("project/export-beds", 'ProjectController@exportBeds'); + Route::post("project/toggle-status", 'ProjectController@toggleStatus'); \App\Models\CommonModel::generateCurdRouter("FactorController", "factor"); \App\Models\CommonModel::generateCurdRouter("TrainingTypeController", "training-type"); diff --git a/自动扣款任务诊断报告.md b/自动扣款任务诊断报告.md new file mode 100644 index 0000000..fd0bda5 --- /dev/null +++ b/自动扣款任务诊断报告.md @@ -0,0 +1,141 @@ +# 自动扣款任务诊断报告 + +## 问题描述 +`admin/project` 页面的自动扣款任务(`autoCheckout`)有两天没有按照预期得到结果。 + +## 发现的问题 + +### 1. **日志文件缺失** +- 最新的日志文件是 `storage/logs/daily_auto_checkout-2021-04-11.log` +- 说明最近没有执行记录,可能的原因: + - Laravel 调度器没有运行 + - 任务执行时发生异常但没有记录 + +### 2. **代码缺少异常处理** +- 原代码中 `autoCheckout()` 方法没有 try-catch 包裹 +- 如果执行过程中出现异常,会导致任务静默失败,且没有错误日志 + +### 3. **潜在的空指针问题** +- 代码中 `$customer = $item->customer` 可能为 null +- 如果 customer 为 null,访问 `$customer->balance` 会导致致命错误 + +### 4. **缺少详细的执行日志** +- 原代码只在开始和成功时记录日志 +- 缺少失败、跳过等情况的详细记录 + +## 已实施的改进 + +### 1. **添加完整的异常处理** +- 在 `autoCheckout()` 方法外层添加 try-catch +- 在调度器调用处也添加异常处理 +- 所有异常都会记录到日志 + +### 2. **添加空值检查** +- 检查 `$customer` 是否为 null +- 如果为空,记录警告日志并跳过该订单项 + +### 3. **增强日志记录** +- 记录任务开始和结束 +- 记录处理的订单项数量 +- 记录成功、跳过、失败的统计 +- 记录每个订单项的详细处理情况 +- 异常时记录完整的堆栈跟踪 + +### 4. **添加数据库事务** +- 每个订单项的扣款操作使用事务包裹 +- 确保数据一致性 + +## 排查步骤 + +### 1. **检查 Laravel 调度器是否运行** +```bash +# 检查 crontab 是否配置了调度器 +crontab -l | grep "schedule:run" + +# 应该看到类似这样的配置: +# * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 +``` + +如果没有配置,需要添加: +```bash +* * * * * cd /Users/weizongsong/www/tiantian2 && php artisan schedule:run >> /dev/null 2>&1 +``` + +### 2. **检查调度器状态** +```bash +# 查看调度器列表 +php artisan schedule:list + +# 手动运行一次调度器(测试) +php artisan schedule:run +``` + +### 3. **检查日志文件** +```bash +# 查看最新的自动扣款日志 +ls -lah storage/logs/daily_auto_checkout*.log + +# 查看日志内容 +tail -f storage/logs/daily_auto_checkout-$(date +%Y-%m-%d).log +``` + +### 4. **手动测试任务** +```bash +# 在 PHP 交互式环境中测试 +php artisan tinker +>>> (new \App\Models\OrderItems())->autoCheckout(); +``` + +### 5. **检查缓存** +```bash +# 检查缓存中的 last_id +php artisan tinker +>>> cache('last_auto_checkout_order_item_id') +``` + +## 可能的原因总结 + +1. **调度器未运行**(最可能) + - crontab 未配置或配置错误 + - 服务器时间不正确 + - 调度器进程被杀死 + +2. **代码执行异常** + - 数据库连接问题 + - 数据完整性问题(customer 为 null) + - 其他运行时错误 + +3. **时间范围问题** + - 任务只在 14:00-23:59 之间执行 + - 如果当前时间不在这个范围内,任务不会执行 + +4. **没有待处理的订单** + - 所有订单都已处理 + - 查询条件不匹配 + +## 建议的监控措施 + +1. **添加任务执行监控** + - 可以添加一个健康检查接口 + - 记录最后一次成功执行的时间 + +2. **设置告警** + - 如果超过一定时间没有执行,发送告警 + - 监控日志中的错误数量 + +3. **定期检查** + - 定期检查日志文件 + - 确认任务正常执行 + +## 改进后的代码位置 + +- `app/Models/OrderItems.php` - `autoCheckout()` 方法 +- `app/Console/Kernel.php` - 调度器配置 + +## 下一步行动 + +1. ✅ 已添加异常处理和详细日志 +2. ⏳ 检查并配置 Laravel 调度器 +3. ⏳ 查看改进后的日志输出 +4. ⏳ 确认任务正常执行 +