| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- <?php
- namespace App\Module\Farm\Commands;
- use App\Module\Farm\Enums\LAND_STATUS;
- use App\Module\Farm\Models\FarmLand;
- use Illuminate\Console\Command;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Log;
- /**
- * 修复已铲除作物的土地状态命令
- *
- * 修复作物已经铲除(软删除)但土地状态不是空闲的问题
- * php artisan farm:fix-removed-crop-land-status --dry-run
- */
- class FixRemovedCropLandStatusCommand extends Command
- {
- /**
- * 命令签名
- *
- * @var string
- */
- protected $signature = 'farm:fix-removed-crop-land-status
- {--dry-run : 仅显示需要修复的数据,不执行修复}
- {--user= : 指定用户ID,只修复该用户的数据}
- {--limit=100 : 每批处理的数量}';
- /**
- * 命令描述
- *
- * @var string
- */
- protected $description = '修复作物已经铲除(软删除)但土地状态不是空闲的问题';
- /**
- * 执行命令
- *
- * @return int
- */
- public function handle(): int
- {
- $dryRun = $this->option('dry-run');
- $userId = $this->option('user');
- $limit = (int) $this->option('limit');
- $this->info('开始检查已铲除作物的土地状态...');
- if ($dryRun) {
- $this->warn('运行在模拟模式,不会实际修改数据');
- }
- try {
- // 获取需要修复的数据
- $problematicLands = $this->getProblematicLands($userId, $limit);
- if ($problematicLands->isEmpty()) {
- $this->info('没有发现需要修复的数据');
- return 0;
- }
- $this->info("发现 {$problematicLands->count()} 条需要修复的土地");
- if ($dryRun) {
- $this->displayProblems($problematicLands);
- } else {
- $this->fixProblems($problematicLands);
- }
- if (!$dryRun) {
- $this->info("\n修复完成!");
- }
- return 0;
- } catch (\Exception $e) {
- $this->error("修复过程中发生错误: {$e->getMessage()}");
- Log::error('已铲除作物土地状态修复失败', [
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
- return 1;
- }
- }
- /**
- * 获取有问题的土地数据
- * 查找作物已软删除但土地状态不是空闲的情况
- *
- * @param string|null $userId
- * @param int $limit
- * @return \Illuminate\Support\Collection
- */
- private function getProblematicLands(?string $userId, int $limit): \Illuminate\Support\Collection
- {
- // 先获取有软删除作物的土地ID列表
- $landsWithDeletedCrops = DB::table('farm_land_users')
- ->join('farm_crops', 'farm_land_users.id', '=', 'farm_crops.land_id')
- ->select('farm_land_users.id')
- ->whereNotNull('farm_crops.deleted_at')
- ->where('farm_land_users.status', '!=', LAND_STATUS::IDLE->value)
- ->when($userId, function ($query, $userId) {
- return $query->where('farm_land_users.user_id', $userId);
- })
- ->distinct()
- ->pluck('farm_land_users.id');
- // 获取没有未删除作物的土地ID列表
- $landsWithoutActiveCrops = DB::table('farm_land_users')
- ->leftJoin('farm_crops', function ($join) {
- $join->on('farm_land_users.id', '=', 'farm_crops.land_id')
- ->whereNull('farm_crops.deleted_at');
- })
- ->select('farm_land_users.id')
- ->whereNull('farm_crops.id')
- ->where('farm_land_users.status', '!=', LAND_STATUS::IDLE->value)
- ->when($userId, function ($query, $userId) {
- return $query->where('farm_land_users.user_id', $userId);
- })
- ->pluck('farm_land_users.id');
- // 合并所有有问题的土地ID
- $allProblematicLandIds = $landsWithDeletedCrops->concat($landsWithoutActiveCrops)->unique()->take($limit);
- // 获取详细信息
- $results = collect();
- foreach ($allProblematicLandIds as $landId) {
- $land = DB::table('farm_land_users')->where('id', $landId)->first();
- if (!$land) continue;
- // 获取该土地的软删除作物信息
- $deletedCrops = DB::table('farm_crops')
- ->where('land_id', $landId)
- ->whereNotNull('deleted_at')
- ->get();
- $item = (object) [
- 'land_id' => $land->id,
- 'user_id' => $land->user_id,
- 'land_status' => $land->status,
- 'has_crop' => $land->has_crop,
- 'deleted_crops_count' => $deletedCrops->count(),
- 'crop_ids' => $deletedCrops->pluck('id')->implode(','),
- 'first_deleted_at' => $deletedCrops->min('deleted_at'),
- 'last_deleted_at' => $deletedCrops->max('deleted_at'),
- 'seed_ids' => $deletedCrops->pluck('seed_id')->unique()->implode(',')
- ];
- $item->problem_type = $this->getProblemType($item);
- $results->push($item);
- }
- return $results;
- }
- /**
- * 获取问题类型
- *
- * @param object $item
- * @return string
- */
- private function getProblemType(object $item): string
- {
- if ($item->deleted_crops_count > 0) {
- return '作物已软删除但土地非空闲';
- } elseif ($item->deleted_crops_count == 0) {
- return '土地无作物但状态非空闲';
- }
- return '其他问题';
- }
- /**
- * 显示问题数据
- *
- * @param \Illuminate\Support\Collection $items
- * @return void
- */
- private function displayProblems(\Illuminate\Support\Collection $items): void
- {
- // 按问题类型分组显示
- $groupedData = $items->groupBy('problem_type');
- foreach ($groupedData as $problemType => $problemItems) {
- $this->info("\n=== {$problemType} ({$problemItems->count()} 条) ===");
- $headers = ['土地ID', '用户ID', '当前土地状态', 'has_crop', '软删除作物数', '作物IDs', '最后删除时间', '种子IDs'];
- $rows = [];
- foreach ($problemItems as $item) {
- $rows[] = [
- $item->land_id,
- $item->user_id,
- $this->getLandStatusName($item->land_status),
- $item->has_crop ? '是' : '否',
- $item->deleted_crops_count,
- $item->crop_ids ? (strlen($item->crop_ids) > 50 ? substr($item->crop_ids, 0, 47) . '...' : $item->crop_ids) : 'N/A',
- $item->last_deleted_at ?? 'N/A',
- $item->seed_ids ?? 'N/A'
- ];
- }
- $this->table($headers, $rows);
- }
- }
- /**
- * 修复问题数据
- *
- * @param \Illuminate\Support\Collection $items
- * @return void
- */
- private function fixProblems(\Illuminate\Support\Collection $items): void
- {
- $fixedCount = 0;
- $failedCount = 0;
- foreach ($items as $item) {
- try {
- DB::transaction(function () use ($item) {
- $this->fixSingleLand($item);
- });
-
- $fixedCount++;
- $this->info("✓ 修复土地 {$item->land_id} (用户 {$item->user_id}) - {$item->problem_type}");
-
- } catch (\Exception $e) {
- $failedCount++;
- $this->error("✗ 修复土地 {$item->land_id} 失败: {$e->getMessage()}");
-
- Log::error('单个土地状态修复失败', [
- 'land_id' => $item->land_id,
- 'user_id' => $item->user_id,
- 'problem_type' => $item->problem_type,
- 'error' => $e->getMessage()
- ]);
- }
- }
- $this->info("修复完成: 成功 {$fixedCount} 条,失败 {$failedCount} 条");
- }
- /**
- * 修复单个土地
- *
- * @param object $item
- * @return void
- */
- private function fixSingleLand(object $item): void
- {
- $land = FarmLand::lockForUpdate()->find($item->land_id);
- if (!$land) {
- throw new \Exception('土地不存在');
- }
- $oldStatus = $land->status;
- $oldHasCrop = $land->has_crop;
- // 设置土地状态为空闲
- $land->status = LAND_STATUS::IDLE->value;
- $land->updateHasCrop(); // 这会将has_crop设置为false
- $land->save();
- Log::info('已铲除作物土地状态修复', [
- 'land_id' => $land->id,
- 'user_id' => $land->user_id,
- 'deleted_crops_count' => $item->deleted_crops_count,
- 'crop_ids' => $item->crop_ids,
- 'old_status' => $oldStatus,
- 'new_status' => $land->status,
- 'old_has_crop' => $oldHasCrop,
- 'new_has_crop' => $land->has_crop,
- 'problem_type' => $item->problem_type,
- 'first_deleted_at' => $item->first_deleted_at,
- 'last_deleted_at' => $item->last_deleted_at
- ]);
- }
- /**
- * 获取土地状态名称
- *
- * @param int $status
- * @return string
- */
- private function getLandStatusName(int $status): string
- {
- return match ($status) {
- LAND_STATUS::IDLE->value => '空闲',
- LAND_STATUS::PLANTING->value => '种植中',
- LAND_STATUS::DISASTER->value => '灾害',
- LAND_STATUS::HARVESTABLE->value => '可收获',
- LAND_STATUS::WITHERED->value => '枯萎',
- default => '未知'
- };
- }
- }
|