PetAutoSkillLogic.php 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. <?php
  2. namespace App\Module\Pet\Logic;
  3. use App\Module\Farm\Dtos\LandInfoDto;
  4. use App\Module\Pet\Models\PetActiveSkill;
  5. use App\Module\Farm\Services\CropService;
  6. use App\Module\Farm\Services\LandService;
  7. use App\Module\GameItems\Services\ItemService;
  8. use Illuminate\Support\Facades\Log;
  9. use UCore\Dto\Res;
  10. use UCore\Helper\Logger;
  11. /**
  12. * 宠物自动技能处理逻辑
  13. *
  14. * 处理宠物激活技能的自动执行逻辑
  15. */
  16. class PetAutoSkillLogic
  17. {
  18. /**
  19. * 处理自动收菜技能
  20. *
  21. * @param PetActiveSkill $activeSkill 激活的技能
  22. * @return void
  23. * @throws \Exception
  24. */
  25. public function processAutoHarvest(PetActiveSkill $activeSkill): void
  26. {
  27. // 检查事务是否已开启
  28. \UCore\Db\Helper::check_tr();
  29. $pet = $activeSkill->pet;
  30. $userId = $pet->user_id;
  31. Log::info('开始处理自动收菜技能', [
  32. 'active_skill_id' => $activeSkill->id,
  33. 'pet_id' => $pet->id,
  34. 'user_id' => $userId
  35. ]);
  36. // 获取用户所有可收获的土地
  37. $harvestableLands = LandService::getHarvestableLands($userId);
  38. $harvestCount = 0;
  39. $harvestResults = [];
  40. if (!$harvestableLands->isEmpty()) {
  41. foreach ($harvestableLands as $land) {
  42. // 调用收获服务
  43. $result = CropService::harvestCrop($userId, $land->id);
  44. if ($result instanceof Res && !$result->error) {
  45. $harvestCount++;
  46. $harvestResults[] = [
  47. 'land_id' => $land->id,
  48. 'success' => true,
  49. 'auto_cleared' => false
  50. ];
  51. Log::info('自动收菜成功', [
  52. 'user_id' => $userId,
  53. 'pet_id' => $pet->id,
  54. 'land_id' => $land->id
  55. ]);
  56. } else {
  57. Log::warning('自动收菜失败', [
  58. 'user_id' => $userId,
  59. 'pet_id' => $pet->id,
  60. 'land_id' => $land->id,
  61. 'error' => $result->error ?? '未知错误'
  62. ]);
  63. }
  64. }
  65. }
  66. // 记录统计信息
  67. $this->recordSkillStatistics($activeSkill, 'auto_harvest', [
  68. 'harvest_count' => $harvestCount,
  69. 'total_lands_checked' => $harvestableLands->count(),
  70. 'harvest_results' => $harvestResults
  71. ]);
  72. Log::info('自动收菜技能处理完成', [
  73. 'active_skill_id' => $activeSkill->id,
  74. 'pet_id' => $pet->id,
  75. 'user_id' => $userId,
  76. 'harvest_count' => $harvestCount,
  77. 'total_lands' => $harvestableLands->count()
  78. ]);
  79. }
  80. /**
  81. * 处理自动播种技能
  82. *
  83. * @param PetActiveSkill $activeSkill 激活的技能
  84. * @return void
  85. * @throws \Exception
  86. */
  87. public function processAutoPlant(PetActiveSkill $activeSkill): void
  88. {
  89. // 检查事务是否已开启
  90. \UCore\Db\Helper::check_tr();
  91. $pet = $activeSkill->pet;
  92. $userId = $pet->user_id;
  93. Log::info('开始处理自动播种技能', [
  94. 'active_skill_id' => $activeSkill->id,
  95. 'pet_id' => $pet->id,
  96. 'user_id' => $userId
  97. ]);
  98. // 获取用户所有空闲的土地
  99. $idleLands = LandService::getIdleLands($userId);
  100. if ($idleLands->isEmpty()) {
  101. Log::info('没有空闲的土地', [
  102. 'user_id' => $userId,
  103. 'pet_id' => $pet->id
  104. ]);
  105. return;
  106. }
  107. // 获取优先使用的种子列表
  108. $preferredSeeds = $activeSkill->getConfigValue('preferred_seeds', []);
  109. // 获取用户拥有的种子物品
  110. $availableSeeds = $this->getAvailableSeeds($userId, $preferredSeeds);
  111. if (empty($availableSeeds)) {
  112. Log::info('没有可用的种子', [
  113. 'user_id' => $userId,
  114. 'pet_id' => $pet->id
  115. ]);
  116. return;
  117. }
  118. $plantCount = 0;
  119. $plantResults = [];
  120. foreach ($idleLands as $land) {
  121. if (empty($availableSeeds)) {
  122. break; // 种子用完了
  123. }
  124. // 选择种子(优先使用配置的种子)
  125. $seedItemId = array_shift($availableSeeds);
  126. // 先消耗种子物品
  127. ItemService::consumeItem($userId, $seedItemId, null, 1, [
  128. 'source' => 'pet_auto_plant'
  129. ]);
  130. // 调用种植服务
  131. $result = CropService::plantCrop($userId, $land->id, $seedItemId);
  132. if ($result) {
  133. $plantCount++;
  134. $plantResults[] = [
  135. 'land_id' => $land->id,
  136. 'seed_item_id' => $seedItemId,
  137. 'result' => $result
  138. ];
  139. Log::info('自动播种成功', [
  140. 'user_id' => $userId,
  141. 'pet_id' => $pet->id,
  142. 'land_id' => $land->id,
  143. 'seed_item_id' => $seedItemId
  144. ]);
  145. } else {
  146. Log::warning('自动播种失败', [
  147. 'user_id' => $userId,
  148. 'pet_id' => $pet->id,
  149. 'land_id' => $land->id,
  150. 'seed_item_id' => $seedItemId,
  151. 'error' => '种植服务返回失败'
  152. ]);
  153. }
  154. }
  155. // 记录统计信息
  156. $this->recordSkillStatistics($activeSkill, 'auto_plant', [
  157. 'plant_count' => $plantCount,
  158. 'total_lands_checked' => $idleLands->count(),
  159. 'plant_results' => $plantResults
  160. ]);
  161. Log::info('自动播种技能处理完成', [
  162. 'active_skill_id' => $activeSkill->id,
  163. 'pet_id' => $pet->id,
  164. 'user_id' => $userId,
  165. 'plant_count' => $plantCount,
  166. 'total_lands' => $idleLands->count()
  167. ]);
  168. }
  169. /**
  170. * 获取用户可用的种子
  171. *
  172. * @param int $userId 用户ID
  173. * @param array $preferredSeeds 优先种子列表
  174. * @return array 可用种子ID列表
  175. */
  176. protected function getAvailableSeeds(int $userId, array $preferredSeeds = []): array
  177. {
  178. // 调用物品服务获取用户拥有的种子
  179. $allSeeds = ItemService::getUserSeedItems($userId);
  180. $availableSeeds = [];
  181. // 优先添加配置的种子
  182. foreach ($preferredSeeds as $seedId) {
  183. if (isset($allSeeds[$seedId]) && $allSeeds[$seedId] > 0) {
  184. for ($i = 0; $i < $allSeeds[$seedId]; $i++) {
  185. $availableSeeds[] = $seedId;
  186. }
  187. }
  188. }
  189. // 添加其他种子
  190. foreach ($allSeeds as $seedId => $quantity) {
  191. if (!in_array($seedId, $preferredSeeds) && $quantity > 0) {
  192. for ($i = 0; $i < $quantity; $i++) {
  193. $availableSeeds[] = $seedId;
  194. }
  195. }
  196. }
  197. return $availableSeeds;
  198. }
  199. /**
  200. * 获取清除灾害的道具
  201. *
  202. * @param int $userId 用户ID
  203. * @param int $disasterType 灾害类型
  204. * @return array|null 道具信息
  205. */
  206. protected function getDisasterClearItem(int $userId, int $disasterType): ?array
  207. {
  208. // 根据灾害类型获取对应的清除道具ID
  209. $clearItemMap = [
  210. \App\Module\Farm\Enums\DISASTER_TYPE::DROUGHT->value => 24, // 干旱清除道具ID(洒水壶)
  211. \App\Module\Farm\Enums\DISASTER_TYPE::PEST->value => 23, // 虫害清除道具ID(杀虫剂)
  212. \App\Module\Farm\Enums\DISASTER_TYPE::WEED->value => 22, // 杂草清除道具ID(除草剂)
  213. ];
  214. $itemId = $clearItemMap[$disasterType] ?? null;
  215. if (!$itemId) {
  216. return null;
  217. }
  218. // 检查用户是否拥有该道具
  219. $checkResult = \App\Module\GameItems\Services\ItemService::checkItemQuantity($userId, $itemId, 1);
  220. if ($checkResult->error) {
  221. return null;
  222. }
  223. return [
  224. 'item_id' => $itemId,
  225. 'disaster_type' => $disasterType
  226. ];
  227. }
  228. /**
  229. * 处理自动除草技能
  230. *
  231. * @param PetActiveSkill $activeSkill 激活的技能
  232. * @return void
  233. * @throws \Exception
  234. */
  235. public function processAutoWeeding(PetActiveSkill $activeSkill): void
  236. {
  237. // 检查事务是否已开启
  238. \UCore\Db\Helper::check_tr();
  239. // 记录开始时间用于性能监控
  240. $startTime = microtime(true);
  241. $pet = $activeSkill->pet;
  242. $userId = $pet->user_id;
  243. Log::info('开始处理自动除草技能', [
  244. 'active_skill_id' => $activeSkill->id,
  245. 'pet_id' => $pet->id,
  246. 'user_id' => $userId
  247. ]);
  248. // 获取用户所有有作物的土地
  249. $landsWithCrops = LandService::getLandsWithCrops($userId);
  250. if ($landsWithCrops->isEmpty()) {
  251. Log::info('没有种植作物的土地', [
  252. 'user_id' => $userId,
  253. 'pet_id' => $pet->id
  254. ]);
  255. return;
  256. }
  257. $weedingCount = 0;
  258. $disasterType = \App\Module\Farm\Enums\DISASTER_TYPE::WEED->value;
  259. foreach ($landsWithCrops as $land) {
  260. $landResult = $this->processLandDisasters($userId, $pet, $land, $disasterType, 'weed');
  261. $weedingCount += $landResult['cleared_count'];
  262. // 如果遇到道具不足,记录并可能提前结束
  263. if ($landResult['stopped_reason'] === 'insufficient_items') {
  264. Log::warning('自动除草因道具不足停止', [
  265. 'user_id' => $userId,
  266. 'pet_id' => $pet->id,
  267. 'land_id' => $land->id,
  268. 'cleared_count' => $landResult['cleared_count']
  269. ]);
  270. // 可以选择继续处理其他土地或者停止
  271. // 这里选择继续处理其他土地
  272. }
  273. }
  274. // 记录统计信息
  275. $this->recordSkillStatistics($activeSkill, 'auto_weeding', [
  276. 'weeding_count' => $weedingCount,
  277. 'total_lands_checked' => $landsWithCrops->count(),
  278. 'processing_time_ms' => (microtime(true) - $startTime) * 1000
  279. ]);
  280. Log::info('自动除草技能处理完成', [
  281. 'active_skill_id' => $activeSkill->id,
  282. 'pet_id' => $pet->id,
  283. 'user_id' => $userId,
  284. 'weeding_count' => $weedingCount,
  285. 'total_lands' => $landsWithCrops->count(),
  286. 'processing_time_ms' => round((microtime(true) - $startTime) * 1000, 2)
  287. ]);
  288. }
  289. /**
  290. * 处理自动浇水技能
  291. *
  292. * @param PetActiveSkill $activeSkill 激活的技能
  293. * @return array 返回处理结果统计
  294. */
  295. public function processAutoWatering(PetActiveSkill $activeSkill): array
  296. {
  297. // 检查事务是否已开启
  298. \UCore\Db\Helper::check_tr();
  299. // 记录开始时间用于性能监控
  300. $startTime = microtime(true);
  301. try {
  302. $pet = $activeSkill->pet;
  303. $userId = $pet->user_id;
  304. Log::info('开始处理自动浇水技能', [
  305. 'active_skill_id' => $activeSkill->id,
  306. 'pet_id' => $pet->id,
  307. 'user_id' => $userId
  308. ]);
  309. // 获取用户所有有作物的土地
  310. $landsWithCrops = LandService::getLandsWithCrops($userId);
  311. if ($landsWithCrops->isEmpty()) {
  312. Log::info('没有种植作物的土地', [
  313. 'user_id' => $userId,
  314. 'pet_id' => $pet->id
  315. ]);
  316. return [
  317. 'watering_count' => 0,
  318. 'total_lands_checked' => 0,
  319. 'reason' => '没有种植作物的土地'
  320. ];
  321. }
  322. $wateringCount = 0;
  323. $disasterType = \App\Module\Farm\Enums\DISASTER_TYPE::DROUGHT->value;
  324. foreach ($landsWithCrops as $land) {
  325. $landResult = $this->processLandDisasters($userId, $pet, $land, $disasterType, 'watering');
  326. $wateringCount += $landResult['cleared_count'];
  327. // 如果遇到道具不足,记录并可能提前结束
  328. if ($landResult['stopped_reason'] === 'insufficient_items') {
  329. Log::warning('自动浇水因道具不足停止', [
  330. 'user_id' => $userId,
  331. 'pet_id' => $pet->id,
  332. 'land_id' => $land->id,
  333. 'cleared_count' => $landResult['cleared_count']
  334. ]);
  335. }
  336. }
  337. // 记录统计信息
  338. $statistics = [
  339. 'watering_count' => $wateringCount,
  340. 'total_lands_checked' => $landsWithCrops->count(),
  341. 'processing_time_ms' => (microtime(true) - $startTime) * 1000
  342. ];
  343. $this->recordSkillStatistics($activeSkill, 'auto_watering', $statistics);
  344. Log::info('自动浇水技能处理完成', [
  345. 'active_skill_id' => $activeSkill->id,
  346. 'pet_id' => $pet->id,
  347. 'user_id' => $userId,
  348. 'watering_count' => $wateringCount,
  349. 'total_lands' => $landsWithCrops->count(),
  350. 'processing_time_ms' => round((microtime(true) - $startTime) * 1000, 2)
  351. ]);
  352. return $statistics;
  353. } catch (\Exception $e) {
  354. Log::error('处理自动浇水技能失败', [
  355. 'active_skill_id' => $activeSkill->id,
  356. 'error' => $e->getMessage(),
  357. 'trace' => $e->getTraceAsString()
  358. ]);
  359. return [
  360. 'watering_count' => 0,
  361. 'total_lands_checked' => 0,
  362. 'error' => $e->getMessage(),
  363. 'reason' => '处理自动浇水技能失败'
  364. ];
  365. }
  366. }
  367. /**
  368. * 处理自动杀虫技能
  369. *
  370. * @param PetActiveSkill $activeSkill 激活的技能
  371. * @return void
  372. */
  373. public function processAutoPestControl(PetActiveSkill $activeSkill): void
  374. {
  375. // 记录开始时间用于性能监控
  376. $startTime = microtime(true);
  377. try {
  378. $pet = $activeSkill->pet;
  379. $userId = $pet->user_id;
  380. Log::info('开始处理自动杀虫技能', [
  381. 'active_skill_id' => $activeSkill->id,
  382. 'pet_id' => $pet->id,
  383. 'user_id' => $userId
  384. ]);
  385. // 获取用户所有有作物的土地
  386. $landsWithCrops = LandService::getLandsWithCrops($userId);
  387. if ($landsWithCrops->isEmpty()) {
  388. Log::info('没有种植作物的土地', [
  389. 'user_id' => $userId,
  390. 'pet_id' => $pet->id
  391. ]);
  392. return;
  393. }
  394. Log::info('开始处理自动杀虫技能', [
  395. 'land——number' => $landsWithCrops->count()
  396. ]);
  397. $pestControlCount = 0;
  398. $disasterType = \App\Module\Farm\Enums\DISASTER_TYPE::PEST->value;
  399. foreach ($landsWithCrops as $land) {
  400. $landResult = $this->processLandDisasters($userId, $pet, $land, $disasterType, 'pest_control');
  401. $pestControlCount += $landResult['cleared_count'];
  402. // 如果遇到道具不足,记录并可能提前结束
  403. if ($landResult['stopped_reason'] === 'insufficient_items') {
  404. Log::warning('自动杀虫因道具不足停止', [
  405. 'user_id' => $userId,
  406. 'pet_id' => $pet->id,
  407. 'land_id' => $land->id,
  408. 'cleared_count' => $landResult['cleared_count']
  409. ]);
  410. }
  411. }
  412. // 记录统计信息
  413. $this->recordSkillStatistics($activeSkill, 'auto_pest_control', [
  414. 'pest_control_count' => $pestControlCount,
  415. 'total_lands_checked' => $landsWithCrops->count(),
  416. 'processing_time_ms' => (microtime(true) - $startTime) * 1000
  417. ]);
  418. Log::info('自动杀虫技能处理完成', [
  419. 'active_skill_id' => $activeSkill->id,
  420. 'pet_id' => $pet->id,
  421. 'user_id' => $userId,
  422. 'pest_control_count' => $pestControlCount,
  423. 'total_lands' => $landsWithCrops->count(),
  424. 'processing_time_ms' => round((microtime(true) - $startTime) * 1000, 2)
  425. ]);
  426. } catch (\Exception $e) {
  427. Log::error('处理自动杀虫技能失败', [
  428. 'active_skill_id' => $activeSkill->id,
  429. 'error' => $e->getMessage(),
  430. 'trace' => $e->getTraceAsString()
  431. ]);
  432. }
  433. }
  434. /**
  435. * 处理单块土地上的特定类型灾害
  436. *
  437. * @param int $userId 用户ID
  438. * @param mixed $pet 宠物对象
  439. * @param LandInfoDto $land 土地对象
  440. * @param int $disasterType 灾害类型
  441. * @param string $disasterName 灾害名称(用于日志)
  442. * @return array 处理结果
  443. */
  444. protected function processLandDisasters(int $userId, $pet, LandInfoDto $land, int $disasterType, string $disasterName): array
  445. {
  446. $clearedCount = 0;
  447. $maxAttempts = 10; // 防止无限循环的最大尝试次数
  448. $attempts = 0;
  449. $stoppedReason = 'completed'; // completed, insufficient_items, max_attempts, error
  450. while ($attempts < $maxAttempts) {
  451. $attempts++;
  452. // 检查土地是否还有该类型的灾害
  453. if (!$this->checkSpecificDisaster($land, $disasterType)) {
  454. // 没有该类型灾害了,正常完成
  455. $stoppedReason = 'completed';
  456. break;
  457. }
  458. // 尝试清除灾害
  459. try {
  460. $cleared = $this->autoClearSpecificDisaster($userId, $land, $disasterType);
  461. if ($cleared) {
  462. $clearedCount++;
  463. // 重新获取土地信息以确保数据最新
  464. $land = $this->refreshLandInfo($land);
  465. } else {
  466. // 清除失败,通常是道具不足
  467. $stoppedReason = 'insufficient_items';
  468. break;
  469. }
  470. } catch (\Exception $e) {
  471. Log::error("自动{$disasterName}处理异常", [
  472. 'user_id' => $userId,
  473. 'pet_id' => $pet->id,
  474. 'land_id' => $land->id,
  475. 'disaster_type' => $disasterType,
  476. 'attempts' => $attempts,
  477. 'cleared_count' => $clearedCount,
  478. 'error' => $e->getMessage()
  479. ]);
  480. $stoppedReason = 'error';
  481. break;
  482. }
  483. }
  484. // 如果达到最大尝试次数
  485. if ($attempts >= $maxAttempts) {
  486. $stoppedReason = 'max_attempts';
  487. Log::warning("自动{$disasterName}达到最大尝试次数", [
  488. 'user_id' => $userId,
  489. 'pet_id' => $pet->id,
  490. 'land_id' => $land->id,
  491. 'max_attempts' => $maxAttempts,
  492. 'cleared_count' => $clearedCount
  493. ]);
  494. }
  495. // 记录土地处理结果
  496. if ($clearedCount > 0) {
  497. Log::info("土地{$disasterName}处理完成", [
  498. 'user_id' => $userId,
  499. 'pet_id' => $pet->id,
  500. 'land_id' => $land->id,
  501. 'cleared_count' => $clearedCount,
  502. 'attempts' => $attempts,
  503. 'stopped_reason' => $stoppedReason
  504. ]);
  505. }
  506. return [
  507. 'cleared_count' => $clearedCount,
  508. 'attempts' => $attempts,
  509. 'stopped_reason' => $stoppedReason
  510. ];
  511. }
  512. /**
  513. * 刷新土地信息
  514. *
  515. * @param LandInfoDto $land 土地对象
  516. * @return LandInfoDto 刷新后的土地对象
  517. */
  518. protected function refreshLandInfo(LandInfoDto $land): LandInfoDto
  519. {
  520. try {
  521. // 重新从服务层获取土地信息
  522. $refreshedLands = LandService::getLandsWithCrops($land->userId);
  523. foreach ($refreshedLands as $refreshedLand) {
  524. if ($refreshedLand->id === $land->id) {
  525. return $refreshedLand;
  526. }
  527. }
  528. // 如果没找到,返回原对象
  529. return $land;
  530. } catch (\Exception $e) {
  531. Log::warning('刷新土地信息失败', [
  532. 'land_id' => $land->id,
  533. 'error' => $e->getMessage()
  534. ]);
  535. return $land;
  536. }
  537. }
  538. /**
  539. * 检查特定类型的灾害
  540. *
  541. * @param mixed $land 土地对象
  542. * @param int $disasterType 灾害类型
  543. * @return bool
  544. */
  545. protected function checkSpecificDisaster(LandInfoDto $land, int $disasterType): bool
  546. {
  547. // 获取土地上的作物
  548. $crop = $land->crop;
  549. if (!$crop) {
  550. return false;
  551. }
  552. // 检查作物是否有指定类型的活跃灾害
  553. // 注意:根据2025年07月06日的修复,作物灾害与土地状态无关
  554. // 作物可以有灾害但土地状态仍然是PLANTING,而不是DISASTER
  555. $disasters = $crop->disasters ?? [];
  556. foreach ($disasters as $disaster) {
  557. if (($disaster['status'] ?? '') === 'active' && ($disaster['type'] ?? 0) == $disasterType) {
  558. return true;
  559. }
  560. }
  561. return false;
  562. }
  563. /**
  564. * 自动清除特定类型的灾害
  565. *
  566. * @param int $userId 用户ID
  567. * @param mixed $land 土地对象
  568. * @param int $disasterType 灾害类型
  569. * @return bool
  570. */
  571. protected function autoClearSpecificDisaster(int $userId, $land, int $disasterType): bool
  572. {
  573. // 检查事务是否已开启
  574. \UCore\Db\Helper::check_tr();
  575. // 获取对应的清除道具
  576. $clearItem = $this->getDisasterClearItem($userId, $disasterType);
  577. if (!$clearItem) {
  578. Log::debug('没有找到清除道具', [
  579. 'user_id' => $userId,
  580. 'land_id' => $land->id,
  581. 'disaster_type' => $disasterType
  582. ]);
  583. return false;
  584. }
  585. // 先消耗道具
  586. \App\Module\GameItems\Services\ItemService::consumeItem(
  587. $userId,
  588. $clearItem['item_id'],
  589. null,
  590. 1,
  591. ['source' => 'pet_auto_specific_disaster_clear']
  592. );
  593. // 调用农场服务清除灾害
  594. $result = \App\Module\Farm\Services\CropService::clearDisaster($userId, $land->id, $disasterType);
  595. if ($result) {
  596. Log::info('宠物自动清除特定灾害成功', [
  597. 'user_id' => $userId,
  598. 'land_id' => $land->id,
  599. 'disaster_type' => $disasterType,
  600. 'item_id' => $clearItem['item_id']
  601. ]);
  602. return true;
  603. } else {
  604. Log::warning('宠物自动清除特定灾害失败', [
  605. 'user_id' => $userId,
  606. 'land_id' => $land->id,
  607. 'disaster_type' => $disasterType,
  608. 'error' => '清除灾害服务返回失败'
  609. ]);
  610. return false;
  611. }
  612. }
  613. /**
  614. * 记录技能统计信息
  615. *
  616. * @param PetActiveSkill $activeSkill 激活的技能
  617. * @param string $actionType 操作类型
  618. * @param array $statistics 统计数据
  619. * @return void
  620. */
  621. public function recordSkillStatistics(PetActiveSkill $activeSkill, string $actionType, array $statistics): void
  622. {
  623. $config = $activeSkill->config;
  624. // 确保config是数组类型
  625. if (!is_array($config)) {
  626. // 如果是字符串,尝试解析JSON
  627. if (is_string($config)) {
  628. $config = json_decode($config, true);
  629. if (json_last_error() !== JSON_ERROR_NONE) {
  630. $config = [];
  631. }
  632. } else {
  633. $config = [];
  634. }
  635. }
  636. if (!isset($config['statistics'])) {
  637. $config['statistics'] = [];
  638. }
  639. $config['statistics'][] = [
  640. 'action_type' => $actionType,
  641. 'timestamp' => now()->toDateTimeString(),
  642. 'data' => $statistics
  643. ];
  644. // 只保留最近10条统计记录
  645. if (count($config['statistics']) > 10) {
  646. $config['statistics'] = array_slice($config['statistics'], -10);
  647. }
  648. $activeSkill->config = $config;
  649. $activeSkill->save();
  650. }
  651. /**
  652. * 清理所有枯萎的作物(不使用道具)
  653. *
  654. * 直接查找枯萎状态的作物,而不是通过土地状态
  655. *
  656. * @param int $userId 用户ID
  657. * @return int 清理的数量
  658. * @throws \Exception
  659. */
  660. public function clearAllWitheredCrops(int $userId): int
  661. {
  662. // 检查事务是否已开启
  663. \UCore\Db\Helper::check_tr();
  664. // 直接查找所有枯萎状态的作物
  665. $witheredCrops = \App\Module\Farm\Models\FarmCrop::whereHas('land', function($query) use ($userId) {
  666. $query->where('user_id', $userId);
  667. })
  668. ->where('growth_stage', \App\Module\Farm\Enums\GROWTH_STAGE::WITHERED->value)
  669. ->get();
  670. $clearedCount = 0;
  671. foreach ($witheredCrops as $crop) {
  672. $clearResult = $this->autoClearWitheredCrop($userId, $crop->land_id);
  673. if ($clearResult) {
  674. $clearedCount++;
  675. Log::info('自动清理枯萎作物成功', [
  676. 'user_id' => $userId,
  677. 'land_id' => $crop->land_id,
  678. 'crop_id' => $crop->id
  679. ]);
  680. } else {
  681. Log::warning('自动清理枯萎作物失败', [
  682. 'user_id' => $userId,
  683. 'land_id' => $crop->land_id,
  684. 'crop_id' => $crop->id,
  685. 'error' => '清理操作返回失败'
  686. ]);
  687. }
  688. }
  689. if ($clearedCount > 0) {
  690. Log::info('批量清理枯萎作物完成', [
  691. 'user_id' => $userId,
  692. 'cleared_count' => $clearedCount,
  693. 'total_withered_crops' => $witheredCrops->count()
  694. ]);
  695. }
  696. return $clearedCount;
  697. }
  698. /**
  699. * 自动铲除枯萎的作物
  700. *
  701. * @param int $userId 用户ID
  702. * @param int $landId 土地ID
  703. * @return bool 是否成功铲除
  704. */
  705. protected function autoClearWitheredCrop(int $userId, int $landId): bool
  706. {
  707. // 获取土地信息
  708. $land = \App\Module\Farm\Models\FarmLand::where('id', $landId)
  709. ->where('user_id', $userId)
  710. ->first();
  711. if (!$land) {
  712. Logger::warning('自动清理枯萎作物失败', [
  713. 'user_id' => $userId,
  714. 'land_id' => $landId,
  715. 'error' => '土地不存在'
  716. ]);
  717. return false;
  718. }
  719. // 获取土地上的作物
  720. $crop = \App\Module\Farm\Models\FarmCrop::where('land_id', $landId)->first();
  721. if (!$crop) {
  722. // 如果没有作物但土地状态是枯萎,修正土地状态为空闲
  723. $land->status = \App\Module\Farm\Enums\LAND_STATUS::IDLE->value;
  724. $land->save();
  725. return true;
  726. }
  727. // 检查作物是否为枯萎状态
  728. $cropStageValue = is_object($crop->growth_stage) ? $crop->growth_stage->value : $crop->growth_stage;
  729. if ($cropStageValue !== \App\Module\Farm\Enums\GROWTH_STAGE::WITHERED->value) {
  730. Logger::warning('自动清理枯萎作物失败', [
  731. 'user_id' => $userId,
  732. 'land_id' => $landId,
  733. 'error' => '作物不是枯萎状态'
  734. ]);
  735. return false;
  736. }
  737. // 调用农场服务铲除作物(宠物自动铲除,工具ID为0)
  738. $result = \App\Module\Farm\Services\CropService::removeCrop($userId, $landId, 0);
  739. if ($result['success']) {
  740. Log::info('宠物自动铲除枯萎作物成功', [
  741. 'user_id' => $userId,
  742. 'land_id' => $landId,
  743. 'crop_id' => $crop->id
  744. ]);
  745. return true;
  746. }
  747. else {
  748. Logger::warning('自动清理枯萎作物失败', [
  749. 'user_id' => $userId,
  750. 'land_id' => $landId,
  751. 'error' => '铲除作物失败'
  752. ]);
  753. }
  754. return false;
  755. }
  756. /**
  757. * 处理自动施肥技能
  758. *
  759. * @param PetActiveSkill $activeSkill 激活的技能
  760. * @return void
  761. */
  762. public function processAutoFertilizing(PetActiveSkill $activeSkill): void
  763. {
  764. try {
  765. $pet = $activeSkill->pet;
  766. $userId = $pet->user_id;
  767. Log::info('开始处理自动施肥技能', [
  768. 'active_skill_id' => $activeSkill->id,
  769. 'pet_id' => $pet->id,
  770. 'user_id' => $userId
  771. ]);
  772. // 获取用户所有有作物的土地
  773. $landsWithCrops = LandService::getLandsWithCrops($userId);
  774. if ($landsWithCrops->isEmpty()) {
  775. Log::info('没有种植作物的土地', [
  776. 'user_id' => $userId,
  777. 'pet_id' => $pet->id
  778. ]);
  779. return;
  780. }
  781. $fertilizingCount = 0;
  782. $fertilizingResults = [];
  783. foreach ($landsWithCrops as $land) {
  784. try {
  785. // 检查土地是否可以施肥
  786. $canFertilize = $this->checkCanFertilize($land);
  787. if ($canFertilize) {
  788. // 自动施肥
  789. $fertilized = $this->autoFertilizeCrop($userId, $land);
  790. if ($fertilized) {
  791. $fertilizingCount++;
  792. $fertilizingResults[] = [
  793. 'land_id' => $land->id,
  794. 'success' => true
  795. ];
  796. Log::info('自动施肥成功', [
  797. 'user_id' => $userId,
  798. 'pet_id' => $pet->id,
  799. 'land_id' => $land->id
  800. ]);
  801. }
  802. }
  803. } catch (\Exception $e) {
  804. Log::warning('自动施肥处理失败', [
  805. 'user_id' => $userId,
  806. 'pet_id' => $pet->id,
  807. 'land_id' => $land->id,
  808. 'error' => $e->getMessage()
  809. ]);
  810. }
  811. }
  812. // 记录技能统计信息
  813. $this->recordSkillStatistics($activeSkill, 'auto_fertilizing', [
  814. 'fertilizing_count' => $fertilizingCount,
  815. 'total_lands_checked' => $landsWithCrops->count(),
  816. 'fertilizing_results' => $fertilizingResults
  817. ]);
  818. Log::info('自动施肥技能处理完成', [
  819. 'active_skill_id' => $activeSkill->id,
  820. 'pet_id' => $pet->id,
  821. 'user_id' => $userId,
  822. 'fertilizing_count' => $fertilizingCount,
  823. 'total_lands_checked' => $landsWithCrops->count()
  824. ]);
  825. } catch (\Exception $e) {
  826. Log::error('自动施肥技能处理异常', [
  827. 'active_skill_id' => $activeSkill->id,
  828. 'error' => $e->getMessage(),
  829. 'trace' => $e->getTraceAsString()
  830. ]);
  831. }
  832. }
  833. /**
  834. * 检查作物是否可以施肥
  835. *
  836. * @param mixed $land 土地对象
  837. * @return bool
  838. */
  839. protected function checkCanFertilize($land): bool
  840. {
  841. // 获取土地上的作物
  842. $crop = \App\Module\Farm\Models\FarmCrop::where('land_id', $land->id)->first();
  843. if (!$crop) {
  844. return false;
  845. }
  846. // 检查作物是否已经施肥
  847. if ($crop->fertilized) {
  848. return false;
  849. }
  850. // 检查作物生长阶段是否允许施肥(种子期、发芽期、生长期可以施肥)
  851. $growthStage = is_object($crop->growth_stage) ? $crop->growth_stage->value : $crop->growth_stage;
  852. $allowedStages = [
  853. \App\Module\Farm\Enums\GROWTH_STAGE::SEED->value,
  854. \App\Module\Farm\Enums\GROWTH_STAGE::SPROUT->value,
  855. \App\Module\Farm\Enums\GROWTH_STAGE::GROWTH->value
  856. ];
  857. return in_array($growthStage, $allowedStages);
  858. }
  859. /**
  860. * 自动为作物施肥
  861. *
  862. * @param int $userId 用户ID
  863. * @param mixed $land 土地对象
  864. * @return bool
  865. */
  866. protected function autoFertilizeCrop(int $userId, $land): bool
  867. {
  868. // 检查事务是否已开启
  869. \UCore\Db\Helper::check_tr();
  870. // 获取用户的肥料道具
  871. $fertilizerItem = $this->getFertilizerItem($userId);
  872. if (!$fertilizerItem) {
  873. Log::debug('没有找到肥料道具', [
  874. 'user_id' => $userId,
  875. 'land_id' => $land->id
  876. ]);
  877. return false;
  878. }
  879. // 获取作物信息
  880. $crop = \App\Module\Farm\Models\FarmCrop::where('land_id', $land->id)->first();
  881. if (!$crop) {
  882. return false;
  883. }
  884. // 先消耗肥料道具
  885. ItemService::consumeItem(
  886. $userId,
  887. $fertilizerItem['item_id'],
  888. null,
  889. 1,
  890. ['source' => 'pet_auto_fertilizing', 'land_id' => $land->id]
  891. );
  892. // 使用肥料
  893. $result = CropService::useFertilizer($crop->id, $fertilizerItem['crop_growth_time']);
  894. if ($result instanceof Res && !$result->error) {
  895. return true;
  896. } else {
  897. Log::warning('自动施肥失败', [
  898. 'user_id' => $userId,
  899. 'land_id' => $land->id,
  900. 'error' => $result->error ?? '使用肥料失败'
  901. ]);
  902. return false;
  903. }
  904. }
  905. /**
  906. * 获取用户的肥料道具
  907. *
  908. * @param int $userId 用户ID
  909. * @return array|null
  910. */
  911. protected function getFertilizerItem(int $userId): ?array
  912. {
  913. // 获取用户所有物品
  914. $userItems = ItemService::getUserItems($userId);
  915. foreach ($userItems as $userItem) {
  916. // 检查物品是否为肥料类型
  917. $cropGrowthTime = ItemService::getItemNumericAttribute($userItem->itemId, 'crop_growth_time', 0);
  918. if ($cropGrowthTime > 0 && $userItem->quantity > 0) {
  919. return [
  920. 'item_id' => $userItem->itemId,
  921. 'crop_growth_time' => $cropGrowthTime
  922. ];
  923. }
  924. }
  925. return null;
  926. }
  927. /**
  928. * 收获后自动铲除枯萎的作物
  929. *
  930. * 收获后作物会自动变成枯萎状态,此方法专门处理收获后的枯萎作物铲除
  931. * 直接检查作物状态,而不是土地状态
  932. *
  933. * @param int $userId 用户ID
  934. * @param int $landId 土地ID
  935. * @return array 返回操作结果 ['success' => bool, 'reason' => string, 'crop_id' => int|null]
  936. */
  937. protected function autoClearWitheredCropAfterHarvest(int $userId, int $landId): array
  938. {
  939. try {
  940. // 获取土地信息
  941. $land = \App\Module\Farm\Models\FarmLand::where('id', $landId)
  942. ->where('user_id', $userId)
  943. ->first();
  944. if (!$land) {
  945. return ['success' => false, 'reason' => '土地不存在', 'crop_id' => null];
  946. }
  947. // 获取土地上的作物(直接检查作物状态,不依赖土地状态)
  948. $crop = \App\Module\Farm\Models\FarmCrop::where('land_id', $landId)->first();
  949. if (!$crop) {
  950. return ['success' => false, 'reason' => '土地上没有作物', 'crop_id' => null];
  951. }
  952. // 检查作物是否为枯萎状态
  953. $cropStageValue = is_object($crop->growth_stage) ? $crop->growth_stage->value : $crop->growth_stage;
  954. if ($cropStageValue !== \App\Module\Farm\Enums\GROWTH_STAGE::WITHERED->value) {
  955. return ['success' => false, 'reason' => '作物不是枯萎状态,当前状态: ' . $cropStageValue, 'crop_id' => $crop->id];
  956. }
  957. // 调用农场服务铲除作物(宠物自动铲除,工具ID为0)
  958. $result = \App\Module\Farm\Services\CropService::removeCrop($userId, $landId, 0);
  959. if ($result['success']) {
  960. return [
  961. 'success' => true,
  962. 'reason' => '宠物自动铲除枯萎作物成功',
  963. 'crop_id' => $crop->id
  964. ];
  965. } else {
  966. return [
  967. 'success' => false,
  968. 'reason' => '铲除作物失败',
  969. 'crop_id' => $crop->id
  970. ];
  971. }
  972. } catch (\Exception $e) {
  973. Log::error('宠物自动铲除收获后枯萎作物失败', [
  974. 'user_id' => $userId,
  975. 'land_id' => $landId,
  976. 'error' => $e->getMessage(),
  977. 'trace' => $e->getTraceAsString()
  978. ]);
  979. return [
  980. 'success' => false,
  981. 'reason' => '铲除作物异常: ' . $e->getMessage(),
  982. 'crop_id' => null
  983. ];
  984. }
  985. }
  986. }