CropLogic.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  1. <?php
  2. namespace App\Module\Farm\Logics;
  3. use App\Module\Farm\Dtos\CropInfoDto;
  4. use App\Module\Farm\Dtos\HarvestResultDto;
  5. use App\Module\Farm\Enums\GROWTH_STAGE;
  6. use App\Module\Farm\Enums\LAND_STATUS;
  7. use App\Module\Farm\Events\CropGrowthStageChangedEvent;
  8. use App\Module\Farm\Events\CropHarvestedEvent;
  9. use App\Module\Farm\Events\CropPlantedEvent;
  10. use App\Module\Farm\Events\DisasterClearedEvent;
  11. use App\Module\Farm\Events\LandStatusChangedEvent;
  12. use App\Module\Farm\Models\FarmCrop;
  13. use App\Module\Farm\Models\FarmHarvestLog;
  14. use App\Module\Farm\Models\FarmLand;
  15. use App\Module\Farm\Models\FarmSeed;
  16. use App\Module\Farm\Models\FarmSeedOutput;
  17. use App\Module\Farm\Models\FarmSowLog;
  18. use App\Module\GameItems\Services\ItemService;
  19. use Illuminate\Support\Facades\DB;
  20. use Illuminate\Support\Facades\Log;
  21. use UCore\Db\Helper;
  22. use UCore\Dto\Res;
  23. /**
  24. * 作物管理逻辑
  25. */
  26. class CropLogic
  27. {
  28. /**
  29. * 获取作物信息
  30. *
  31. * @param int $cropId
  32. * @return CropInfoDto|null
  33. */
  34. public function getCropInfo(int $cropId): ?CropInfoDto
  35. {
  36. try {
  37. $crop = FarmCrop::find($cropId);
  38. if (!$crop) {
  39. return null;
  40. }
  41. return CropInfoDto::fromModel($crop);
  42. } catch (\Exception $e) {
  43. Log::error('获取作物信息失败', [
  44. 'crop_id' => $cropId,
  45. 'error' => $e->getMessage(),
  46. 'trace' => $e->getTraceAsString()
  47. ]);
  48. return null;
  49. }
  50. }
  51. /**
  52. * 获取土地上的作物信息
  53. *
  54. * @param int $landId
  55. * @return CropInfoDto|null
  56. */
  57. public function getCropByLandId(int $landId): ?CropInfoDto
  58. {
  59. try {
  60. $crop = FarmCrop::where('land_id', $landId)->first();
  61. if (!$crop) {
  62. return null;
  63. }
  64. return CropInfoDto::fromModel($crop);
  65. } catch (\Exception $e) {
  66. Log::error('获取土地作物信息失败', [
  67. 'land_id' => $landId,
  68. 'error' => $e->getMessage(),
  69. 'trace' => $e->getTraceAsString()
  70. ]);
  71. return null;
  72. }
  73. }
  74. /**
  75. * 种植作物
  76. *
  77. * @param int $userId
  78. * @param int $landId
  79. * @param int $itemId 种子物品ID
  80. * @return array|null 返回包含CropInfoDto和日志ID的数组,格式为['crop' => CropInfoDto, 'log_id' => int]
  81. * @throws \Exception
  82. */
  83. public function plantCrop(int $userId, int $landId, int $itemId): ?array
  84. {
  85. try {
  86. // 检查是否已开启事务
  87. \UCore\Db\Helper::check_tr();
  88. // 获取土地信息
  89. $land = FarmLand::where('id', $landId)
  90. ->where('user_id', $userId)
  91. ->first();
  92. if (!$land) {
  93. throw new \Exception('土地不存在');
  94. }
  95. // 检查土地状态
  96. if ($land->status !== LAND_STATUS::IDLE->value) {
  97. throw new \Exception('土地状态不允许种植');
  98. }
  99. // 根据物品ID获取种子配置信息
  100. $seed = FarmSeed::where('item_id', $itemId)->first();
  101. if (!$seed) {
  102. throw new \Exception('种子配置不存在');
  103. }
  104. $seedId = $seed->id;
  105. // 创建作物记录
  106. $crop = new FarmCrop();
  107. $crop->land_id = $landId;
  108. $crop->user_id = $userId;
  109. $crop->seed_id = $seedId;
  110. $crop->plant_time = now();
  111. $crop->growth_stage = GROWTH_STAGE::SEED->value;
  112. $crop->stage_start_time = now(); // 设置当前阶段开始时间
  113. $crop->stage_end_time = now()->addSeconds($seed->seed_time);
  114. $crop->disasters = [];
  115. $crop->fertilized = false;
  116. $crop->last_disaster_check_time = null; // 初始化灾害检查时间
  117. $crop->can_disaster = false; // 种子期不能产生灾害
  118. $crop->save();
  119. // 创建种植日志
  120. $sowLog = new FarmSowLog();
  121. $sowLog->user_id = $userId;
  122. $sowLog->land_id = $landId;
  123. $sowLog->crop_id = $crop->id;
  124. $sowLog->seed_id = $seedId;
  125. $sowLog->sow_time = now();
  126. $sowLog->save();
  127. // 更新土地状态
  128. $land->status = LAND_STATUS::PLANTING->value;
  129. $land->save();
  130. // 触发作物种植事件
  131. event(new CropPlantedEvent($userId, $land, $crop));
  132. Log::info('作物种植成功', [
  133. 'user_id' => $userId,
  134. 'land_id' => $landId,
  135. 'seed_id' => $seedId,
  136. 'crop_id' => $crop->id,
  137. 'sow_log_id' => $sowLog->id
  138. ]);
  139. return [
  140. 'crop' => CropInfoDto::fromModel($crop),
  141. 'log_id' => $sowLog->id
  142. ];
  143. } catch (\Exception $e) {
  144. // 回滚事务
  145. DB::rollBack();
  146. Log::error('作物种植失败', [
  147. 'user_id' => $userId,
  148. 'land_id' => $landId,
  149. 'seed_id' => $seedId,
  150. 'error' => $e->getMessage(),
  151. 'trace' => $e->getTraceAsString()
  152. ]);
  153. return null;
  154. }
  155. }
  156. /**
  157. * 收获作物
  158. *
  159. * @param int $userId
  160. * @param int $landId
  161. * @return HarvestResultDto|null
  162. */
  163. public function harvestCrop(int $userId, int $landId): Res
  164. {
  165. try {
  166. // 事务检查
  167. Helper::check_tr();
  168. // 获取土地信息
  169. /**
  170. * @var FarmLand $land
  171. */
  172. $land = FarmLand::where('id', $landId)
  173. ->where('user_id', $userId)
  174. ->first();
  175. if (!$land) {
  176. throw new \Exception('土地不存在');
  177. }
  178. // 检查土地状态
  179. // if ($land->status !== LAND_STATUS::HARVESTABLE->value) {
  180. // throw new \Exception('土地状态不允许收获');
  181. // }
  182. // 获取作物信息
  183. $crop = FarmCrop::where('land_id', $landId)->first();
  184. if (!$crop) {
  185. throw new \Exception('作物不存在');
  186. }
  187. // 检查作物生长阶段
  188. if ($crop->growth_stage !== GROWTH_STAGE::MATURE) {
  189. throw new \Exception('作物未成熟,不能收获');
  190. }
  191. // 获取种子信息
  192. $seed = $crop->seed;
  193. if (!$seed) {
  194. throw new \Exception('种子信息不存在');
  195. }
  196. // 使用发芽期确定的最终产出果实ID,如果没有则随机选择
  197. if ($crop->final_output_item_id) {
  198. $outputItemId = $crop->final_output_item_id;
  199. // 获取对应的产出配置来确定数量范围
  200. $outputInfo = $this->getOutputInfoByItemId($seed->id, $outputItemId);
  201. $outputAmount = mt_rand($outputInfo['min_amount'], $outputInfo['max_amount']);
  202. Log::info('使用发芽期确定的最终产出果实', [
  203. 'crop_id' => $crop->id,
  204. 'final_output_item_id' => $outputItemId,
  205. 'output_amount' => $outputAmount
  206. ]);
  207. } else {
  208. // 兼容旧数据:如果没有预设的最终产出果实ID,则随机选择
  209. $outputInfo = $this->getRandomOutput($seed->id);
  210. $outputItemId = $outputInfo['item_id'];
  211. $outputAmount = mt_rand($outputInfo['min_amount'], $outputInfo['max_amount']);
  212. Log::warning('作物没有预设最终产出果实ID,使用随机选择', [
  213. 'crop_id' => $crop->id,
  214. 'seed_id' => $seed->id,
  215. 'random_output_item_id' => $outputItemId
  216. ]);
  217. }
  218. // 创建收获记录
  219. $harvestLog = new FarmHarvestLog();
  220. $harvestLog->user_id = $userId;
  221. $harvestLog->land_id = $landId;
  222. $harvestLog->crop_id = $crop->id;
  223. $harvestLog->seed_id = $seed->id;
  224. $harvestLog->output_amount = $outputAmount;
  225. $harvestLog->harvest_time = now();
  226. $harvestLog->created_at = now();
  227. $harvestLog->save();
  228. // 收获后作物进入枯萎期,而不是直接删除
  229. $oldStage = $crop->growth_stage;
  230. $crop->growth_stage = GROWTH_STAGE::WITHERED;
  231. $crop->stage_start_time = now();
  232. $crop->stage_end_time = null; // 枯萎期没有结束时间
  233. $crop->fertilized = false; // 重置施肥状态
  234. $crop->save();
  235. // 更新土地状态为枯萎状态
  236. $land->status = LAND_STATUS::WITHERED;
  237. $land->save();
  238. // 触发作物生长阶段变更事件(从成熟期到枯萎期)
  239. event(new CropGrowthStageChangedEvent($userId, $crop, $oldStage->value, GROWTH_STAGE::WITHERED->value));
  240. // 触发作物收获事件
  241. event(new CropHarvestedEvent($userId, $land, $crop, $harvestLog, $outputItemId, $outputAmount));
  242. Log::info('作物收获成功,进入枯萎期', [
  243. 'user_id' => $userId,
  244. 'land_id' => $landId,
  245. 'crop_id' => $crop->id,
  246. 'seed_id' => $seed->id,
  247. 'output_item_id' => $outputItemId,
  248. 'output_amount' => $outputAmount,
  249. 'harvest_log_id' => $harvestLog->id,
  250. 'old_stage' => $oldStage->value,
  251. 'new_stage' => GROWTH_STAGE::WITHERED->value,
  252. 'land_status' => LAND_STATUS::WITHERED->value
  253. ]);
  254. // 物品入包
  255. ItemService::addItem($userId, $outputItemId, $outputAmount,[
  256. 'source'=>'FarmHarve',
  257. 'FarmHarvestLog'=>$harvestLog->id
  258. ]);
  259. return Res::success();
  260. } catch (\Exception $e) {
  261. // 回滚事务
  262. Log::error('作物收获失败', [
  263. 'user_id' => $userId,
  264. 'land_id' => $landId,
  265. 'error' => $e->getMessage(),
  266. 'trace' => $e->getTraceAsString()
  267. ]);
  268. return Res::error('');
  269. }
  270. }
  271. /**
  272. * 使用化肥
  273. *
  274. * @param int $userId
  275. * @param int $landId
  276. * @param int $crop_growth_time
  277. * @return Res
  278. */
  279. public function useFertilizer(int $userId, int $landId, int $crop_growth_time): Res
  280. {
  281. try {
  282. Helper::check_tr();
  283. // 获取土地信息(防错误机制:确保土地存在)
  284. /**
  285. * @var FarmLand $land
  286. */
  287. $land = FarmLand::where('id', $landId)
  288. ->where('user_id', $userId)
  289. ->first();
  290. if (!$land) {
  291. throw new \Exception('土地不存在');
  292. }
  293. // 获取作物信息(防错误机制:确保作物存在)
  294. /**
  295. * @var FarmCrop $crop
  296. */
  297. $crop = FarmCrop::where('land_id', $landId)->first();
  298. if (!$crop) {
  299. throw new \Exception('作物不存在');
  300. }
  301. // 防错误机制:基本状态检查,避免意外执行
  302. // 不进行土地状态验证,只进行作物状态验证
  303. // if ($land->status !== LAND_STATUS::PLANTING->valueInt()) {
  304. // Log::warning('土地状态异常,但继续执行施肥', [
  305. // 'land_id' => $landId,
  306. // 'expected_status' => LAND_STATUS::PLANTING->valueInt(),
  307. // 'actual_status' => $land->status
  308. // ]);
  309. // }
  310. if ($crop->fertilized) {
  311. throw new \Exception('作物已施肥');
  312. }
  313. // 更新作物信息
  314. $crop->fertilized = true;
  315. // 根据 crop_growth_time 参数减少当前阶段时间
  316. if ($crop->stage_end_time) {
  317. $currentTime = now();
  318. $endTime = $crop->stage_end_time;
  319. $remainingTime = $currentTime->diffInSeconds($endTime, false);
  320. if ($remainingTime > 0) {
  321. // 确保减少的时间不超过剩余时间
  322. $reducedTime = min($crop_growth_time, $remainingTime);
  323. // 使用copy()方法创建副本,避免修改原始对象
  324. $newEndTime = $endTime->copy()->subSeconds($reducedTime);
  325. $crop->stage_end_time = $newEndTime;
  326. Log::info('化肥减少生长时间', [
  327. 'crop_id' => $crop->id,
  328. 'reduced_time' => $reducedTime,
  329. 'original_end_time' => $endTime->toDateTimeString(),
  330. 'new_end_time' => $crop->stage_end_time->toDateTimeString(),
  331. 'stage_start_time' => $crop->stage_start_time->toDateTimeString()
  332. ]);
  333. } else {
  334. Log::warning('作物已经到达或超过结束时间,无法减少生长时间', [
  335. 'crop_id' => $crop->id,
  336. 'current_time' => $currentTime->toDateTimeString(),
  337. 'stage_end_time' => $endTime->toDateTimeString(),
  338. 'remaining_time' => $remainingTime
  339. ]);
  340. }
  341. }
  342. $crop->save();
  343. Log::info('使用化肥成功', [
  344. 'user_id' => $userId,
  345. 'land_id' => $landId,
  346. 'crop_id' => $crop->id,
  347. 'growth_stage' => $crop->growth_stage,
  348. 'stage_end_time' => $crop->stage_end_time
  349. ]);
  350. return Res::success('',[
  351. 'crop_id' => $crop->id,
  352. ]);
  353. } catch (\Exception $e) {
  354. Log::error('使用化肥失败', [
  355. 'user_id' => $userId,
  356. 'land_id' => $landId,
  357. 'error' => $e->getMessage(),
  358. 'trace' => $e->getTraceAsString()
  359. ]);
  360. return Res::error('使用化肥失败');
  361. }
  362. }
  363. /**
  364. * 清理灾害
  365. *
  366. * @param int $userId
  367. * @param int $landId
  368. * @param int $disasterType
  369. * @return bool
  370. */
  371. public function clearDisaster(int $userId, int $landId, int $disasterType): bool
  372. {
  373. try {
  374. // 获取土地信息
  375. $land = FarmLand::where('id', $landId)
  376. ->where('user_id', $userId)
  377. ->first();
  378. if (!$land) {
  379. throw new \Exception('土地不存在');
  380. }
  381. // 检查土地状态
  382. if ($land->status !== LAND_STATUS::DISASTER->value) {
  383. throw new \Exception('土地没有灾害');
  384. }
  385. // 获取作物信息
  386. $crop = FarmCrop::where('land_id', $landId)->first();
  387. if (!$crop) {
  388. throw new \Exception('作物不存在');
  389. }
  390. // 检查灾害是否存在
  391. $disasters = $crop->disasters ?? [];
  392. $disasterIndex = -1;
  393. $disasterInfo = null;
  394. foreach ($disasters as $index => $disaster) {
  395. if (($disaster['type'] ?? 0) == $disasterType && ($disaster['status'] ?? '') === 'active') {
  396. $disasterIndex = $index;
  397. $disasterInfo = $disaster;
  398. break;
  399. }
  400. }
  401. if ($disasterIndex === -1 || !$disasterInfo) {
  402. throw new \Exception('指定类型的灾害不存在');
  403. }
  404. // 更新灾害状态
  405. $disasters[$disasterIndex]['status'] = 'cleared';
  406. $disasters[$disasterIndex]['cleared_at'] = now()->toDateTimeString();
  407. $crop->disasters = $disasters;
  408. // 检查是否还有其他活跃灾害
  409. $hasActiveDisaster = false;
  410. foreach ($disasters as $disaster) {
  411. if (($disaster['status'] ?? '') === 'active') {
  412. $hasActiveDisaster = true;
  413. break;
  414. }
  415. }
  416. // 如果没有其他活跃灾害,更新土地状态
  417. $oldLandStatus = $land->status;
  418. if (!$hasActiveDisaster) {
  419. $land->status = LAND_STATUS::PLANTING->value;
  420. }
  421. // 保存更改
  422. $crop->save();
  423. $land->save();
  424. // 如果土地状态发生了变化,触发土地状态变更事件
  425. if ($oldLandStatus !== $land->status) {
  426. event(new LandStatusChangedEvent($userId, $landId, $oldLandStatus, $land->status));
  427. }
  428. // 触发灾害清理事件
  429. event(new DisasterClearedEvent($userId, $crop, $disasterType, $disasterInfo));
  430. Log::info('灾害清理成功', [
  431. 'user_id' => $userId,
  432. 'land_id' => $landId,
  433. 'crop_id' => $crop->id,
  434. 'disaster_type' => $disasterType
  435. ]);
  436. return true;
  437. } catch (\Exception $e) {
  438. Log::error('灾害清理失败', [
  439. 'user_id' => $userId,
  440. 'land_id' => $landId,
  441. 'disaster_type' => $disasterType,
  442. 'error' => $e->getMessage(),
  443. 'trace' => $e->getTraceAsString()
  444. ]);
  445. return false;
  446. }
  447. }
  448. /**
  449. * 铲除作物
  450. *
  451. * @param int $userId
  452. * @param int $landId
  453. * @return bool
  454. */
  455. public function removeCrop(int $userId, int $landId): bool
  456. {
  457. try {
  458. // 检查是否已开启事务
  459. \UCore\Db\Helper::check_tr();
  460. // 获取土地信息
  461. $land = FarmLand::where('id', $landId)
  462. ->where('user_id', $userId)
  463. ->first();
  464. if (!$land) {
  465. throw new \Exception('土地不存在');
  466. }
  467. // 检查土地状态
  468. if ($land->status === LAND_STATUS::IDLE->value) {
  469. throw new \Exception('土地上没有作物');
  470. }
  471. // 获取作物信息
  472. $crop = FarmCrop::where('land_id', $landId)->first();
  473. if (!$crop) {
  474. // 如果没有作物但土地状态不是空闲,修正土地状态
  475. $oldLandStatus = $land->status;
  476. $land->status = LAND_STATUS::IDLE->value;
  477. $land->save();
  478. // 记录状态变更信息,由调用方处理事件触发
  479. Log::info('土地状态已修正', [
  480. 'user_id' => $userId,
  481. 'land_id' => $landId,
  482. 'old_status' => $oldLandStatus,
  483. 'new_status' => $land->status
  484. ]);
  485. return true;
  486. }
  487. // 删除作物记录
  488. $crop->delete();
  489. // 记录旧状态
  490. $oldLandStatus = $land->status;
  491. // 更新土地状态
  492. $land->status = LAND_STATUS::IDLE->value;
  493. $land->save();
  494. // 记录状态变更信息,由调用方处理事件触发和事务提交
  495. Log::info('铲除作物成功', [
  496. 'user_id' => $userId,
  497. 'land_id' => $landId,
  498. 'crop_id' => $crop->id,
  499. 'old_status' => $oldLandStatus,
  500. 'new_status' => $land->status
  501. ]);
  502. return true;
  503. } catch (\Exception $e) {
  504. Log::error('铲除作物失败', [
  505. 'user_id' => $userId,
  506. 'land_id' => $landId,
  507. 'error' => $e->getMessage(),
  508. 'trace' => $e->getTraceAsString()
  509. ]);
  510. throw $e; // 重新抛出异常,由调用方处理事务回滚
  511. }
  512. }
  513. /**
  514. * 更新作物生长阶段
  515. *
  516. * @param int $cropId
  517. * @return bool
  518. */
  519. public function updateGrowthStage(int $cropId): bool
  520. {
  521. try {
  522. // 获取作物信息
  523. $crop = FarmCrop::find($cropId);
  524. if (!$crop) {
  525. throw new \Exception('作物不存在');
  526. }
  527. // 检查是否需要更新
  528. if (!$crop->stage_end_time || $crop->stage_end_time > now()) {
  529. return false;
  530. }
  531. // 获取当前生长阶段
  532. $oldStage = $crop->growth_stage;
  533. // 计算新的生长阶段
  534. $newStage = $this->calculateNextStage($crop);
  535. // 如果阶段没有变化,不需要更新
  536. if ($newStage === $oldStage) {
  537. return false;
  538. }
  539. // 计算新阶段的结束时间
  540. $stageEndTime = $this->calculateStageEndTime($crop, $newStage);
  541. // 更新作物信息
  542. $crop->growth_stage = $newStage;
  543. $crop->stage_start_time = now(); // 设置新阶段的开始时间
  544. $crop->stage_end_time = $stageEndTime;
  545. $crop->fertilized = false; // 重置施肥状态
  546. // 如果进入发芽期,必须确定最终产出果实ID
  547. if ($newStage === GROWTH_STAGE::SPROUT->value) {
  548. if (!$crop->final_output_item_id) {
  549. $outputInfo = $this->getRandomOutput($crop->seed_id);
  550. $crop->final_output_item_id = $outputInfo['item_id'];
  551. Log::info('作物进入发芽期,确定最终产出果实', [
  552. 'crop_id' => $crop->id,
  553. 'user_id' => $crop->user_id,
  554. 'seed_id' => $crop->seed_id,
  555. 'final_output_item_id' => $crop->final_output_item_id
  556. ]);
  557. }
  558. }
  559. // 验证:如果进入成熟期但没有final_output_item_id,这是一个严重错误
  560. if ($newStage === GROWTH_STAGE::MATURE->value && !$crop->final_output_item_id) {
  561. Log::error('严重错误:作物进入成熟期但没有确定最终产出果实ID', [
  562. 'crop_id' => $crop->id,
  563. 'user_id' => $crop->user_id,
  564. 'seed_id' => $crop->seed_id,
  565. 'current_stage' => $oldStage,
  566. 'new_stage' => $newStage
  567. ]);
  568. throw new \Exception("作物ID {$crop->id} 进入成熟期但没有确定最终产出果实ID,这是系统错误");
  569. }
  570. $crop->save();
  571. // 如果进入枯萎期,需要更新土地状态
  572. if ($newStage === GROWTH_STAGE::WITHERED->value) {
  573. $land = $crop->land;
  574. if ($land) {
  575. $land->status = LAND_STATUS::WITHERED;
  576. $land->save();
  577. Log::info('作物进入枯萎期,更新土地状态', [
  578. 'crop_id' => $crop->id,
  579. 'land_id' => $land->id,
  580. 'land_status' => LAND_STATUS::WITHERED->value
  581. ]);
  582. }
  583. }
  584. // 触发生长阶段变更事件
  585. event(new CropGrowthStageChangedEvent($crop->user_id, $crop, $oldStage->value, $newStage));
  586. Log::info('作物生长阶段更新成功', [
  587. 'crop_id' => $cropId,
  588. 'user_id' => $crop->user_id,
  589. 'old_stage' => $oldStage,
  590. 'new_stage' => $newStage,
  591. 'stage_end_time' => $stageEndTime
  592. ]);
  593. return true;
  594. } catch (\Exception $e) {
  595. Log::error('作物生长阶段更新失败', [
  596. 'crop_id' => $cropId,
  597. 'error' => $e->getMessage(),
  598. 'trace' => $e->getTraceAsString()
  599. ]);
  600. return false;
  601. }
  602. }
  603. /**
  604. * 计算下一个生长阶段
  605. *
  606. * @param FarmCrop $crop
  607. * @return int
  608. */
  609. public function calculateNextStage(FarmCrop $crop): int
  610. {
  611. $currentStage = $crop->growth_stage;
  612. // 如果当前是成熟期,检查是否应该进入枯萎期
  613. $currentStageValue = is_object($currentStage) ? $currentStage->value : $currentStage;
  614. if ($currentStageValue === GROWTH_STAGE::MATURE->value) {
  615. // 如果成熟期已经超过结束时间,则进入枯萎期
  616. if ($crop->stage_end_time && now() >= $crop->stage_end_time) {
  617. return GROWTH_STAGE::WITHERED->value;
  618. }
  619. // 否则保持成熟期
  620. return GROWTH_STAGE::MATURE->value;
  621. }
  622. // 使用阶段映射确定下一个阶段
  623. $stageMap = [
  624. GROWTH_STAGE::SEED->value => GROWTH_STAGE::SPROUT->value,
  625. GROWTH_STAGE::SPROUT->value => GROWTH_STAGE::GROWTH->value,
  626. GROWTH_STAGE::GROWTH->value => GROWTH_STAGE::MATURE->value,
  627. GROWTH_STAGE::MATURE->value => GROWTH_STAGE::WITHERED->value,
  628. GROWTH_STAGE::WITHERED->value => GROWTH_STAGE::WITHERED->value, // 枯萎期保持不变
  629. ];
  630. // 确保返回整数值
  631. $currentStageValue = is_object($currentStage) ? $currentStage->value : $currentStage;
  632. return $stageMap[$currentStageValue] ?? GROWTH_STAGE::WITHERED->value;
  633. }
  634. /**
  635. * 计算阶段结束时间
  636. *
  637. * @param FarmCrop $crop
  638. * @param int $stage
  639. * @return \Carbon\Carbon|null
  640. */
  641. private function calculateStageEndTime(FarmCrop $crop, int $stage)
  642. {
  643. $seed = $crop->seed;
  644. if (!$seed) {
  645. return null;
  646. }
  647. $now = now();
  648. switch ($stage) {
  649. case GROWTH_STAGE::SEED->value:
  650. return $now->addSeconds($seed->seed_time);
  651. case GROWTH_STAGE::SPROUT->value:
  652. return $now->addSeconds($seed->sprout_time);
  653. case GROWTH_STAGE::GROWTH->value:
  654. return $now->addSeconds($seed->growth_time);
  655. case GROWTH_STAGE::MATURE->value:
  656. // 成熟期持续24小时后进入枯萎期
  657. return $now->addHours(24);
  658. case GROWTH_STAGE::WITHERED->value:
  659. // 枯萎期没有结束时间
  660. return null;
  661. default:
  662. return null;
  663. }
  664. }
  665. /**
  666. * 获取随机产出
  667. *
  668. * @param int $seedId
  669. * @return array
  670. */
  671. public function getRandomOutput(int $seedId): array
  672. {
  673. // 获取种子的所有产出配置
  674. $outputs = FarmSeedOutput::where('seed_id', $seedId)->get();
  675. if ($outputs->isEmpty()) {
  676. // 如果没有产出配置,使用种子的默认产出
  677. $seed = FarmSeed::find($seedId);
  678. return [
  679. 'item_id' => $seed->item_id,
  680. 'min_amount' => $seed->min_output,
  681. 'max_amount' => $seed->max_output,
  682. ];
  683. }
  684. // 按概率排序
  685. $outputs = $outputs->sortByDesc('probability');
  686. // 获取默认产出
  687. $defaultOutput = $outputs->firstWhere('is_default', true);
  688. // 随机选择产出
  689. $random = mt_rand(1, 100);
  690. $cumulativeProbability = 0;
  691. foreach ($outputs as $output) {
  692. $cumulativeProbability += $output->probability;
  693. if ($random <= $cumulativeProbability) {
  694. return [
  695. 'item_id' => $output->item_id,
  696. 'min_amount' => $output->min_amount,
  697. 'max_amount' => $output->max_amount,
  698. ];
  699. }
  700. }
  701. // 如果随机值超过了所有概率之和,使用默认产出
  702. if ($defaultOutput) {
  703. return [
  704. 'item_id' => $defaultOutput->item_id,
  705. 'min_amount' => $defaultOutput->min_amount,
  706. 'max_amount' => $defaultOutput->max_amount,
  707. ];
  708. }
  709. // 如果没有默认产出,使用第一个产出
  710. $firstOutput = $outputs->first();
  711. return [
  712. 'item_id' => $firstOutput->item_id,
  713. 'min_amount' => $firstOutput->min_amount,
  714. 'max_amount' => $firstOutput->max_amount,
  715. ];
  716. }
  717. /**
  718. * 根据物品ID获取产出配置信息
  719. *
  720. * @param int $seedId
  721. * @param int $itemId
  722. * @return array
  723. */
  724. private function getOutputInfoByItemId(int $seedId, int $itemId): array
  725. {
  726. // 获取种子的所有产出配置
  727. $outputs = FarmSeedOutput::where('seed_id', $seedId)->get();
  728. // 查找匹配的产出配置
  729. $targetOutput = $outputs->firstWhere('item_id', $itemId);
  730. if ($targetOutput) {
  731. return [
  732. 'item_id' => $targetOutput->item_id,
  733. 'min_amount' => $targetOutput->min_amount,
  734. 'max_amount' => $targetOutput->max_amount,
  735. ];
  736. }
  737. // 如果没有找到匹配的产出配置,使用种子的默认产出
  738. $seed = FarmSeed::find($seedId);
  739. return [
  740. 'item_id' => $itemId, // 使用传入的物品ID
  741. 'min_amount' => $seed->min_output,
  742. 'max_amount' => $seed->max_output,
  743. ];
  744. }
  745. }