Merge branch 'master' of /mnt/git/v2.tiantianxinye.365care

master
songweizong 4 months ago
commit 0e07540b34

@ -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");

@ -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" => "简介"]);

@ -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());
}
}
}

@ -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)

@ -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();
});

@ -19,6 +19,7 @@
</th>
<th>地址</th>
<th>项目结算比例</th>
<th>状态</th>
<th>微信支付账号</th>
<th>支付宝账号</th>
<th>操作</th>
@ -32,6 +33,19 @@
</td>
<td>{{ $row->address }}</td>
<td>{{ $row->percent_first_party }}</td>
<td>
@if($row->status == 1)
<span class="badge badge-success">启用</span>
@else
<span class="badge badge-danger">禁用</span>
@endif
<button type="button" class="btn btn-sm btn-link btn-toggle-status"
data-id="{{$row->id}}"
data-status="{{$row->status}}"
title="点击切换状态">
<i class="mdi {{$row->status == 1 ? 'mdi-check-circle' : 'mdi-close-circle'}}"></i>
</button>
</td>
<td>{{ $row->wechatpayAccount ? $row->wechatpayAccount->mchid : ""}}<br>{{ $row->wechatpayAccount ? $row->wechatpayAccount->name : ""}}</td>
<td>{{ $row->alipayAccount ? $row->alipayAccount->appid : ""}}<br>{{ $row->alipayAccount ? $row->alipayAccount->name : ""}}</td>
<td>
@ -61,6 +75,49 @@
@push("footer")
<script>
$(document).ready(function() {
// 切换状态
$(document).on('click', '.btn-toggle-status', function() {
var $btn = $(this);
var id = $btn.data('id');
var currentStatus = $btn.data('status');
var newStatus = currentStatus == 1 ? 0 : 1;
if (confirm('确定要' + (newStatus == 1 ? '启用' : '禁用') + '该项目吗?')) {
$.ajax({
url: "{{url($urlPrefix.'/toggle-status')}}",
type: 'POST',
data: {
id: id,
status: newStatus,
_token: '{{csrf_token()}}'
},
success: function(response) {
if (response.status == 1) {
// 更新按钮状态
$btn.data('status', newStatus);
var $row = $btn.closest('tr');
var $badge = $row.find('td:eq(3)').find('.badge');
if (newStatus == 1) {
$badge.removeClass('badge-danger').addClass('badge-success').text('启用');
$btn.find('i').removeClass('mdi-close-circle').addClass('mdi-check-circle');
} else {
$badge.removeClass('badge-success').addClass('badge-danger').text('禁用');
$btn.find('i').removeClass('mdi-check-circle').addClass('mdi-close-circle');
}
alert(response.msg || '操作成功');
} else {
alert(response.msg || '操作失败');
}
},
error: function() {
alert('操作失败,请稍后重试');
}
});
}
});
});
</script>
@endpush

@ -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");

@ -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. ⏳ 确认任务正常执行
Loading…
Cancel
Save