LandLogic.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <?php
  2. namespace App\Module\Farm\Logics;
  3. use App\Module\Farm\Dtos\LandInfoDto;
  4. use App\Module\Farm\Enums\LAND_STATUS;
  5. use App\Module\Farm\Enums\LAND_TYPE;
  6. use App\Module\Farm\Enums\UPGRADE_TYPE;
  7. use App\Module\Farm\Events\LandUpgradedEvent;
  8. use App\Module\Farm\Models\FarmLand;
  9. use App\Module\Farm\Models\FarmLandType;
  10. use App\Module\Farm\Models\FarmLandUpgradeConfig;
  11. use App\Module\Farm\Models\FarmUpgradeLog;
  12. use App\Module\Farm\Models\FarmUser;
  13. use App\Module\Game\Services\ConsumeService;
  14. use Illuminate\Support\Collection;
  15. use Illuminate\Support\Facades\Log;
  16. use UCore\Dto\Res;
  17. use UCore\Exception\LogicException;
  18. use UCore\Db\Helper;
  19. /**
  20. * 土地管理逻辑
  21. */
  22. class LandLogic
  23. {
  24. /**
  25. * 获取用户的所有土地
  26. *
  27. * @param int $userId
  28. * @return Collection|LandInfoDto[]
  29. */
  30. public function getUserLands(int $userId): Collection
  31. {
  32. try {
  33. $lands = FarmLand::where('user_id', $userId)
  34. ->orderBy('position')
  35. ->get();
  36. // dd($lands);
  37. return $lands->map(function ($land) {
  38. return LandInfoDto::fromModel($land);
  39. });
  40. } catch (\Exception $e) {
  41. Log::error('获取用户土地失败', [
  42. 'user_id' => $userId,
  43. 'error' => $e->getMessage(),
  44. 'trace' => $e->getTraceAsString()
  45. ]);
  46. return collect();
  47. }
  48. }
  49. /**
  50. * 获取用户指定位置的土地
  51. *
  52. * @param int $userId
  53. * @param int $position
  54. * @return LandInfoDto|null
  55. */
  56. public function getUserLandByPosition(int $userId, int $position): ?LandInfoDto
  57. {
  58. try {
  59. $land = FarmLand::where('user_id', $userId)
  60. ->where('position', $position)
  61. ->first();
  62. if (!$land) {
  63. return null;
  64. }
  65. return LandInfoDto::fromModel($land);
  66. } catch (\Exception $e) {
  67. Log::error('获取用户指定位置土地失败', [
  68. 'user_id' => $userId,
  69. 'position' => $position,
  70. 'error' => $e->getMessage(),
  71. 'trace' => $e->getTraceAsString()
  72. ]);
  73. return null;
  74. }
  75. }
  76. /**
  77. * 创建土地
  78. *
  79. * @param int $userId
  80. * @param int $position
  81. * @param int $landType
  82. * @return FarmLand|null
  83. */
  84. public function createLand(int $userId, int $position, int $landType): ?FarmLand
  85. {
  86. try {
  87. // 检查位置是否已有土地
  88. $existingLand = FarmLand::where('user_id', $userId)
  89. ->where('position', $position)
  90. ->first();
  91. if ($existingLand) {
  92. return $existingLand;
  93. }
  94. // 创建新土地
  95. $land = new FarmLand();
  96. $land->user_id = $userId;
  97. $land->position = $position;
  98. $land->land_type = $landType;
  99. $land->status = LAND_STATUS::IDLE->value;
  100. $land->save();
  101. Log::info('创建土地成功', [
  102. 'user_id' => $userId,
  103. 'position' => $position,
  104. 'land_type' => $landType,
  105. 'land_id' => $land->id
  106. ]);
  107. return $land;
  108. } catch (\Exception $e) {
  109. Log::error('创建土地失败', [
  110. 'user_id' => $userId,
  111. 'position' => $position,
  112. 'land_type' => $landType,
  113. 'error' => $e->getMessage(),
  114. 'trace' => $e->getTraceAsString()
  115. ]);
  116. return null;
  117. }
  118. }
  119. /**
  120. * 升级土地
  121. * 调用前必须已经完成所有验证(土地归属、状态、路径、材料等)
  122. * 要求调用者已开启事务
  123. *
  124. * @param int $userId
  125. * @param int $landId
  126. * @param int $targetType
  127. * @return bool
  128. */
  129. public function upgradeLand(int $userId, int $landId, int $targetType): bool
  130. {
  131. // 验证事务是否已开启
  132. Helper::check_tr();
  133. // 获取土地信息
  134. /**
  135. * @var FarmLand $land
  136. */
  137. $land = FarmLand::where('id', $landId)
  138. ->where('user_id', $userId)
  139. ->first();
  140. // 防错误机制:基础数据检查
  141. if (!$land) {
  142. throw new LogicException('Logic层防错误:土地不存在,验证层应该已经检查过');
  143. }
  144. // 防错误机制:状态检查
  145. if ($land->status !== LAND_STATUS::IDLE->value) {
  146. throw new LogicException('Logic层防错误:土地状态不允许升级,验证层应该已经检查过');
  147. }
  148. // 防错误机制:类型检查
  149. if ($land->land_type === $targetType) {
  150. throw new LogicException('Logic层防错误:土地已经是目标类型,验证层应该已经检查过');
  151. }
  152. // 获取升级配置(这里不再进行完整验证,只获取配置)
  153. $config = FarmLandUpgradeConfig::where('from_type_id', $land->land_type)
  154. ->where('to_type_id', $targetType)
  155. ->first();
  156. // 防错误机制:配置检查
  157. if (!$config) {
  158. throw new LogicException('Logic层防错误:升级配置不存在,验证层应该已经检查过');
  159. }
  160. // 更新土地类型
  161. $oldType = $land->land_type;
  162. $land->land_type = $targetType;
  163. $land->save();
  164. // 创建升级记录
  165. $upgradeLog = new FarmUpgradeLog();
  166. $upgradeLog->user_id = $userId;
  167. $upgradeLog->upgrade_type = UPGRADE_TYPE::LAND->value;
  168. $upgradeLog->target_id = $landId;
  169. $upgradeLog->old_level = $oldType;
  170. $upgradeLog->new_level = $targetType;
  171. $upgradeLog->materials_consumed = [];
  172. $upgradeLog->upgrade_time = now();
  173. $upgradeLog->created_at = now();
  174. $upgradeLog->save();
  175. // 进行消耗
  176. $ec = ConsumeService::executeConsume($userId, $config->materials, 'land_upgrade', $upgradeLog->id, false);
  177. if ($ec->error) {
  178. throw new LogicException('消耗失败');
  179. }
  180. $upgradeLog->materials_consumed = $ec->data['list'];
  181. $upgradeLog->save();
  182. // 触发土地升级事件
  183. event(new LandUpgradedEvent($userId, $land, $oldType, $targetType, $upgradeLog));
  184. Log::info('土地升级成功', [
  185. 'user_id' => $userId,
  186. 'land_id' => $landId,
  187. 'old_type' => $oldType,
  188. 'new_type' => $targetType,
  189. 'upgrade_log_id' => $upgradeLog->id
  190. ]);
  191. return true;
  192. }
  193. /**
  194. * 检查升级路径
  195. *
  196. * @param $userId
  197. * @param $currentType
  198. * @param $targetType
  199. * @param bool $checkM 是否检查消耗
  200. * @return Res
  201. * @throws \Exception
  202. */
  203. static public function checkUpgradePath(int $userId, int $currentType, int $targetType, bool $checkM = true): Res
  204. {
  205. // 获取升级配置
  206. /**
  207. * @var FarmLandUpgradeConfig $upgradeConfig
  208. */
  209. $upgradeConfig = FarmLandUpgradeConfig::where('from_type_id', $currentType)
  210. ->where('to_type_id', $targetType)
  211. ->first();
  212. if (!$upgradeConfig) {
  213. Log::error('升级路径不存在', [
  214. 'user_id' => $userId,
  215. 'current_type' => $currentType,
  216. 'target_type' => $targetType
  217. ]);
  218. return Res::error('升级路径不存在', [
  219. 'user_id' => $userId,
  220. 'current_type' => $currentType,
  221. 'target_type' => $targetType
  222. ]);
  223. }
  224. // 检查用户房屋等级是否满足要求
  225. $targetLandType = FarmLandType::find($targetType);
  226. $farmUser = FarmUser::where('user_id', $userId)->first();
  227. if (!$farmUser || $farmUser->house_level < $targetLandType->unlock_house_level) {
  228. Log::error('房屋等级不足,无法升级到该土地类型', [
  229. 'user_id' => $userId,
  230. 'current_type' => $currentType,
  231. 'target_type' => $targetType,
  232. 'house_level' => $farmUser->house_level,
  233. 'unlock_house_level' => $targetLandType->unlock_house_level
  234. ]);
  235. return Res::error('房屋等级不足,无法升级到该土地类型', [
  236. 'user_id' => $userId,
  237. 'current_type' => $currentType,
  238. 'target_type' => $targetType,
  239. 'house_level' => $farmUser->house_level,
  240. 'unlock_house_level' => $targetLandType->unlock_house_level
  241. ]);
  242. }
  243. // 检查特殊土地数量限制
  244. if ($targetLandType->is_special) {
  245. $specialLandCount = FarmLand::where('user_id', $userId)
  246. ->whereIn('land_type', [ LAND_TYPE::GOLD->value, LAND_TYPE::BLUE->value, LAND_TYPE::PURPLE->value ])
  247. ->count();
  248. $houseConfig = $farmUser->houseConfig;
  249. if ($specialLandCount >= $houseConfig->special_land_limit) {
  250. Log::error('特殊土地数量已达上限', [
  251. 'user_id' => $userId,
  252. 'current_type' => $currentType,
  253. 'target_type' => $targetType,
  254. 'special_land_count' => $specialLandCount,
  255. 'special_land_limit' => $houseConfig->special_land_limit
  256. ]);
  257. return Res::error('特殊土地数量已达上限', [
  258. 'user_id' => $userId,
  259. 'current_type' => $currentType,
  260. 'target_type' => $targetType,
  261. 'special_land_count' => $specialLandCount,
  262. 'special_land_limit' => $houseConfig->special_land_limit
  263. ]);
  264. }
  265. }
  266. // 检查消耗
  267. if ($checkM) {
  268. $res = ConsumeService::checkConsume($userId, $upgradeConfig->materials);
  269. if ($res->error) {
  270. Log::error('当前路径资源不足', [
  271. 'user_id' => $userId,
  272. 'current_type' => $currentType,
  273. 'target_type' => $targetType,
  274. 'error' => $res->message
  275. ]);
  276. return Res::error('当前路径资源不足', [
  277. 'msg' => $res->message
  278. ]);
  279. }
  280. }
  281. return Res::success('升级路径可用', [
  282. 'config' => $upgradeConfig
  283. ]);
  284. }
  285. /**
  286. * 获取升级所需材料
  287. *
  288. * @param int $currentType 当前土地类型
  289. * @param int $targetType 目标土地类型
  290. * @return array 升级所需材料
  291. */
  292. public function getUpgradeMaterials(int $currentType, int $targetType): array
  293. {
  294. try {
  295. // 从数据库中获取升级配置
  296. $upgradeConfig = FarmLandUpgradeConfig::where('from_type_id', $currentType)
  297. ->where('to_type_id', $targetType)
  298. ->first();
  299. if (!$upgradeConfig) {
  300. return [];
  301. }
  302. // 使用模型的 getUpgradeMaterials 方法获取材料
  303. return $upgradeConfig->getUpgradeMaterials();
  304. } catch (\Exception $e) {
  305. Log::error('获取升级所需材料失败', [
  306. 'current_type' => $currentType,
  307. 'target_type' => $targetType,
  308. 'error' => $e->getMessage(),
  309. 'trace' => $e->getTraceAsString()
  310. ]);
  311. return [];
  312. }
  313. }
  314. /**
  315. * 获取可用的升级路径(不进行消耗检查)
  316. *
  317. * @param int $userId 用户ID
  318. * @param int $landId 土地ID
  319. * @param int|null $toType 目标土地类型ID(可选)
  320. * @return FarmLandUpgradeConfig[]
  321. *
  322. */
  323. public function getAvailableUpgradePaths(int $userId, int $landId, ?int $toType = null): array
  324. {
  325. try {
  326. // 获取土地信息
  327. $land = FarmLand::where('id', $landId)
  328. ->where('user_id', $userId)
  329. ->first();
  330. if (!$land) {
  331. return [];
  332. }
  333. // 获取当前土地类型
  334. $currentType = $land->land_type;
  335. // 获取用户房屋等级
  336. /**
  337. * @var FarmUser $farmUser
  338. */
  339. $farmUser = FarmUser::where('user_id', $userId)->first();
  340. if (!$farmUser) {
  341. return [];
  342. }
  343. $houseLevel = $farmUser->house_level;
  344. // 获取可用的升级路径
  345. $query = FarmLandUpgradeConfig::where('from_type_id', $currentType)
  346. ->with('toType');
  347. // 如果指定了目标类型,则只获取该类型的升级路径
  348. if ($toType !== null) {
  349. $query->where('to_type_id', $toType);
  350. }
  351. $upgradePaths = $query->get();
  352. // 过滤出符合房屋等级要求的升级路径
  353. $availablePaths = $upgradePaths->filter(function ($path) use ($houseLevel) {
  354. return $path->toType->unlock_house_level <= $houseLevel;
  355. });
  356. // 检查特殊土地数量限制
  357. $specialLandCount = FarmLand::where('user_id', $userId)
  358. ->whereIn('land_type', [ LAND_TYPE::GOLD->value, LAND_TYPE::BLUE->value, LAND_TYPE::PURPLE->value ])
  359. ->count();
  360. $houseConfig = $farmUser->houseConfig;
  361. // 如果没有找到对应的房屋配置,使用默认值0
  362. $specialLandLimit = $houseConfig ? $houseConfig->special_land_limit : 0;
  363. // 如果特殊土地已达上限,过滤掉特殊土地升级路径
  364. if ($specialLandCount >= $specialLandLimit) {
  365. $availablePaths = $availablePaths->filter(function ($path) {
  366. return !$path->toType->is_special;
  367. });
  368. }
  369. // 格式化返回结果
  370. return $availablePaths->toArray();
  371. } catch (\Exception $e) {
  372. Log::error('获取可用升级路径失败', [
  373. 'user_id' => $userId,
  374. 'land_id' => $landId,
  375. 'error' => $e->getMessage(),
  376. 'trace' => $e->getTraceAsString()
  377. ]);
  378. return [];
  379. }
  380. }
  381. }