| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- <?php
- namespace App\Module\Farm\Commands;
- use App\Module\Farm\Enums\GROWTH_STAGE;
- use App\Module\Farm\Enums\LAND_STATUS;
- use App\Module\Farm\Models\FarmCrop;
- use App\Module\Farm\Models\FarmLand;
- use Illuminate\Console\Command;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Log;
- /**
- * 修复农场土地状态命令
- *
- * 修复土地状态与作物生长阶段不一致的问题
- * php artisan farm:fix-land-status --dry-run
- */
- class FixLandStatusCommand extends Command
- {
- /**
- * 命令签名
- *
- * @var string
- */
- protected $signature = 'farm:fix-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 {
- // 获取需要修复的数据
- $inconsistentData = $this->getInconsistentData($userId, $limit);
- if ($inconsistentData->isEmpty()) {
- $this->info('没有发现需要修复的数据');
- return 0;
- }
- $this->info("发现 {$inconsistentData->count()} 条需要修复的数据");
- // 按问题类型分组
- $groupedData = $inconsistentData->groupBy('problem_type');
- foreach ($groupedData as $problemType => $items) {
- $this->info("\n=== {$problemType} ({$items->count()} 条) ===");
-
- if ($dryRun) {
- $this->displayProblems($items);
- } else {
- $this->fixProblems($items, $problemType);
- }
- }
- 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 getInconsistentData(?string $userId, int $limit): \Illuminate\Support\Collection
- {
- $query = DB::table('farm_land_users as fl')
- ->join('farm_crops as fc', 'fl.id', '=', 'fc.land_id')
- ->select([
- 'fl.id as land_id',
- 'fl.user_id',
- 'fl.status as land_status',
- 'fc.id as crop_id',
- 'fc.growth_stage',
- 'fc.disasters'
- ])
- ->whereNull('fc.deleted_at') // 排除软删除的作物
- ->where(function ($query) {
- $query
- // 作物成熟但土地状态不是可收获
- ->where(function ($q) {
- $q->where('fc.growth_stage', GROWTH_STAGE::MATURE->value)
- ->where('fl.status', '!=', LAND_STATUS::HARVESTABLE->value);
- })
- // 作物枯萎但土地状态不是枯萎
- ->orWhere(function ($q) {
- $q->where('fc.growth_stage', GROWTH_STAGE::WITHERED->value)
- ->where('fl.status', '!=', LAND_STATUS::WITHERED->value);
- })
- // 作物未成熟但土地状态是可收获
- ->orWhere(function ($q) {
- $q->whereNotIn('fc.growth_stage', [GROWTH_STAGE::MATURE->value, GROWTH_STAGE::WITHERED->value])
- ->where('fl.status', LAND_STATUS::HARVESTABLE->value);
- })
- // 作物未枯萎但土地状态是枯萎
- ->orWhere(function ($q) {
- $q->where('fc.growth_stage', '!=', GROWTH_STAGE::WITHERED->value)
- ->where('fl.status', LAND_STATUS::WITHERED->value);
- });
- });
- if ($userId) {
- $query->where('fl.user_id', $userId);
- }
- $results = $query->limit($limit)->get();
- // 添加问题类型标识
- return $results->map(function ($item) {
- $item->problem_type = $this->getProblemType($item);
- $item->active_disasters_count = $this->countActiveDisasters($item->disasters);
- return $item;
- });
- }
- /**
- * 获取问题类型
- *
- * @param object $item
- * @return string
- */
- private function getProblemType(object $item): string
- {
- if ($item->growth_stage == GROWTH_STAGE::MATURE->value && $item->land_status != LAND_STATUS::HARVESTABLE->value) {
- return '作物成熟但土地非可收获';
- }
-
- if ($item->growth_stage == GROWTH_STAGE::WITHERED->value && $item->land_status != LAND_STATUS::WITHERED->value) {
- return '作物枯萎但土地非枯萎';
- }
-
- if (!in_array($item->growth_stage, [GROWTH_STAGE::MATURE->value, GROWTH_STAGE::WITHERED->value]) && $item->land_status == LAND_STATUS::HARVESTABLE->value) {
- return '作物未成熟但土地可收获';
- }
-
- if ($item->growth_stage != GROWTH_STAGE::WITHERED->value && $item->land_status == LAND_STATUS::WITHERED->value) {
- return '作物未枯萎但土地枯萎';
- }
-
- return '其他不一致';
- }
- /**
- * 统计活跃灾害数量
- *
- * @param string|array|null $disasters
- * @return int
- */
- private function countActiveDisasters($disasters): int
- {
- if (empty($disasters)) {
- return 0;
- }
- // 如果是字符串,解码为数组
- if (is_string($disasters)) {
- $disasterArray = json_decode($disasters, true);
- if (!is_array($disasterArray)) {
- return 0;
- }
- } elseif (is_array($disasters)) {
- $disasterArray = $disasters;
- } else {
- return 0;
- }
- return count(array_filter($disasterArray, function ($disaster) {
- return ($disaster['status'] ?? '') === 'active';
- }));
- }
- /**
- * 显示问题数据
- *
- * @param \Illuminate\Support\Collection $items
- * @return void
- */
- private function displayProblems(\Illuminate\Support\Collection $items): void
- {
- $headers = ['土地ID', '用户ID', '土地状态', '作物ID', '生长阶段', '活跃灾害数'];
- $rows = [];
- foreach ($items as $item) {
- $rows[] = [
- $item->land_id,
- $item->user_id,
- $this->getLandStatusName($item->land_status),
- $item->crop_id,
- $this->getGrowthStageName($item->growth_stage),
- $item->active_disasters_count
- ];
- }
- $this->table($headers, $rows);
- }
- /**
- * 修复问题数据
- *
- * @param \Illuminate\Support\Collection $items
- * @param string $problemType
- * @return void
- */
- private function fixProblems(\Illuminate\Support\Collection $items, string $problemType): void
- {
- $fixedCount = 0;
- $failedCount = 0;
- foreach ($items as $item) {
- try {
- DB::transaction(function () use ($item, $problemType) {
- $this->fixSingleItem($item, $problemType);
- });
-
- $fixedCount++;
- $this->info("✓ 修复土地 {$item->land_id} (用户 {$item->user_id})");
-
- } 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' => $problemType,
- 'error' => $e->getMessage()
- ]);
- }
- }
- $this->info("修复完成: 成功 {$fixedCount} 条,失败 {$failedCount} 条");
- }
- /**
- * 修复单个数据项
- *
- * @param object $item
- * @param string $problemType
- * @return void
- */
- private function fixSingleItem(object $item, string $problemType): void
- {
- $land = FarmLand::lockForUpdate()->find($item->land_id);
- $crop = FarmCrop::lockForUpdate()->find($item->crop_id);
- if (!$land || !$crop) {
- throw new \Exception('土地或作物不存在');
- }
- $newStatus = $this->calculateCorrectLandStatus($crop);
-
- if ($land->status != $newStatus) {
- $oldStatus = $land->status;
- $land->status = $newStatus;
- $land->updateHasCrop();
- $land->save();
- Log::info('土地状态修复', [
- 'land_id' => $land->id,
- 'user_id' => $land->user_id,
- 'crop_id' => $crop->id,
- 'old_status' => $oldStatus,
- 'new_status' => $newStatus,
- 'growth_stage' => $crop->growth_stage,
- 'problem_type' => $problemType,
- 'active_disasters' => $this->countActiveDisasters($crop->disasters)
- ]);
- }else{
- Log::info('土地状态无需修复', [
- 'land_id' => $land->id,
- 'user_id' => $land->user_id,
- 'crop_id' => $crop->id,
- 'status' => $land->status,
- 'growth_stage' => $crop->growth_stage,
- 'problem_type' => $problemType,
- 'active_disasters' => $this->countActiveDisasters($crop->disasters)
- ]);
- }
- }
- /**
- * 计算正确的土地状态
- *
- * @param FarmCrop $crop
- * @return int
- */
- private function calculateCorrectLandStatus(FarmCrop $crop): int
- {
- // 根据作物生长阶段确定土地状态
- switch ($crop->growth_stage) {
- case GROWTH_STAGE::MATURE:
- // 作物成熟,土地状态为可收获
- return LAND_STATUS::HARVESTABLE->value;
-
- case GROWTH_STAGE::WITHERED:
- // 作物枯萎,土地状态为枯萎
- return LAND_STATUS::WITHERED->value;
-
- default:
- // 其他阶段,检查是否有活跃灾害
- $activeDisasters = $this->countActiveDisasters($crop->disasters);
- if ($activeDisasters > 0) {
- return LAND_STATUS::DISASTER->value;
- } else {
- return LAND_STATUS::PLANTING->value;
- }
- }
- }
- /**
- * 获取土地状态名称
- *
- * @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 => '未知'
- };
- }
- /**
- * 获取生长阶段名称
- *
- * @param int $stage
- * @return string
- */
- private function getGrowthStageName(int $stage): string
- {
- return match ($stage) {
- GROWTH_STAGE::SEED->value => '种子期',
- GROWTH_STAGE::SPROUT->value => '发芽期',
- GROWTH_STAGE::GROWTH->value => '生长期',
- GROWTH_STAGE::FRUIT->value => '果实期',
- GROWTH_STAGE::MATURE->value => '成熟期',
- GROWTH_STAGE::WITHERED->value => '枯萎期',
- default => '未知'
- };
- }
- }
|