MigrateChestToGroupSystemCommand.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <?php
  2. namespace App\Module\GameItems\Console\Commands;
  3. use App\Module\Game\Models\GameConsumeGroup;
  4. use App\Module\Game\Models\GameConsumeItem;
  5. use App\Module\Game\Models\GameRewardGroup;
  6. use App\Module\Game\Models\GameRewardItem;
  7. use App\Module\GameItems\Enums\ITEM_TYPE;
  8. use App\Module\GameItems\Models\Item;
  9. use App\Module\GameItems\Models\ItemChestContent;
  10. use App\Module\GameItems\Models\ItemChestOpenCost;
  11. use App\Module\GameItems\Models\ItemChestConfig;
  12. use Illuminate\Console\Command;
  13. use Illuminate\Support\Facades\DB;
  14. use Illuminate\Support\Facades\Log;
  15. /**
  16. * 宝箱系统迁移命令
  17. *
  18. * 将现有的宝箱内容配置和开启消耗配置迁移到新的消耗组/奖励组系统
  19. */
  20. class MigrateChestToGroupSystemCommand extends Command
  21. {
  22. /**
  23. * 命令签名
  24. *
  25. * @var string
  26. */
  27. protected $signature = 'gameitems:migrate-chest-to-groups {--dry-run : 仅预览迁移结果,不实际执行}';
  28. /**
  29. * 命令描述
  30. *
  31. * @var string
  32. */
  33. protected $description = '将现有宝箱配置迁移到消耗组/奖励组系统';
  34. /**
  35. * 执行命令
  36. *
  37. * @return int
  38. */
  39. public function handle()
  40. {
  41. $isDryRun = $this->option('dry-run');
  42. $this->info('开始宝箱系统迁移...');
  43. $this->info($isDryRun ? '预览模式(不会实际修改数据)' : '实际执行模式');
  44. // 获取所有宝箱类型物品
  45. $chests = Item::where('type', ITEM_TYPE::CHEST)->get();
  46. if ($chests->isEmpty()) {
  47. $this->info('没有找到宝箱类型物品,迁移完成。');
  48. return 0;
  49. }
  50. $this->info("找到 {$chests->count()} 个宝箱物品");
  51. $migratedCount = 0;
  52. $skippedCount = 0;
  53. foreach ($chests as $chest) {
  54. $this->info("处理宝箱: {$chest->name} (ID: {$chest->id})");
  55. try {
  56. if ($this->migrateChest($chest, $isDryRun)) {
  57. $migratedCount++;
  58. } else {
  59. $skippedCount++;
  60. }
  61. } catch (\Exception $e) {
  62. $this->error("迁移宝箱 {$chest->id} 失败: " . $e->getMessage());
  63. Log::error('宝箱迁移失败', [
  64. 'chest_id' => $chest->id,
  65. 'error' => $e->getMessage(),
  66. 'trace' => $e->getTraceAsString()
  67. ]);
  68. }
  69. }
  70. $this->info("迁移完成!");
  71. $this->info("成功迁移: {$migratedCount} 个宝箱");
  72. $this->info("跳过: {$skippedCount} 个宝箱");
  73. return 0;
  74. }
  75. /**
  76. * 迁移单个宝箱
  77. *
  78. * @param Item $chest
  79. * @param bool $isDryRun
  80. * @return bool
  81. */
  82. private function migrateChest(Item $chest, bool $isDryRun): bool
  83. {
  84. // 检查是否已经配置了新系统
  85. $existingConfig = ItemChestConfig::where('item_id', $chest->id)->first();
  86. if ($existingConfig) {
  87. $this->warn(" 宝箱 {$chest->id} 已配置新系统,跳过");
  88. return false;
  89. }
  90. if ($isDryRun) {
  91. $this->previewChestMigration($chest);
  92. return true;
  93. }
  94. DB::beginTransaction();
  95. try {
  96. // 迁移消耗配置
  97. $consumeGroupId = $this->migrateChestCosts($chest);
  98. // 迁移奖励配置
  99. $rewardGroupId = $this->migrateChestContents($chest);
  100. // 创建新配置
  101. ItemChestConfig::create([
  102. 'item_id' => $chest->id,
  103. 'consume_group_id' => $consumeGroupId,
  104. 'reward_group_id' => $rewardGroupId,
  105. 'condition_group_id' => null, // 暂不支持条件组迁移
  106. 'is_active' => true,
  107. ]);
  108. DB::commit();
  109. $this->info(" ✓ 宝箱 {$chest->id} 迁移成功");
  110. return true;
  111. } catch (\Exception $e) {
  112. DB::rollBack();
  113. throw $e;
  114. }
  115. }
  116. /**
  117. * 预览宝箱迁移
  118. *
  119. * @param Item $chest
  120. */
  121. private function previewChestMigration(Item $chest)
  122. {
  123. $this->info(" 预览宝箱 {$chest->id} 迁移:");
  124. // 预览消耗配置
  125. $costs = ItemChestOpenCost::where('chest_id', $chest->id)->where('is_active', true)->get();
  126. if ($costs->isNotEmpty()) {
  127. $this->info(" 将创建消耗组: {$chest->name}_消耗组");
  128. foreach ($costs as $cost) {
  129. $this->info(" - 消耗类型: {$cost->cost_type}, ID: {$cost->cost_id}, 数量: {$cost->cost_quantity}");
  130. }
  131. } else {
  132. $this->info(" 无消耗配置,不创建消耗组");
  133. }
  134. // 预览奖励配置
  135. $contents = ItemChestContent::where('chest_id', $chest->id)->get();
  136. if ($contents->isNotEmpty()) {
  137. $this->info(" 将创建奖励组: {$chest->name}_奖励组");
  138. foreach ($contents as $content) {
  139. $type = $content->item_id ? "物品ID: {$content->item_id}" : "物品组ID: {$content->group_id}";
  140. $this->info(" - {$type}, 数量: {$content->min_quantity}-{$content->max_quantity}, 权重: {$content->weight}");
  141. }
  142. } else {
  143. $this->warn(" 无奖励配置,将创建空奖励组");
  144. }
  145. }
  146. /**
  147. * 迁移宝箱消耗配置
  148. *
  149. * @param Item $chest
  150. * @return int|null
  151. */
  152. private function migrateChestCosts(Item $chest): ?int
  153. {
  154. $costs = ItemChestOpenCost::where('chest_id', $chest->id)->where('is_active', true)->get();
  155. if ($costs->isEmpty()) {
  156. return null;
  157. }
  158. // 创建消耗组
  159. $consumeGroup = GameConsumeGroup::create([
  160. 'name' => $chest->name . '_消耗组',
  161. 'code' => 'chest_' . $chest->id . '_consume_' . time(),
  162. 'description' => "宝箱 {$chest->name} 的开启消耗配置(从旧系统迁移)",
  163. ]);
  164. // 创建消耗项
  165. foreach ($costs as $cost) {
  166. GameConsumeItem::create([
  167. 'group_id' => $consumeGroup->id,
  168. 'consume_type' => $cost->cost_type,
  169. 'target_id' => $cost->cost_id,
  170. 'quantity' => $cost->cost_quantity,
  171. ]);
  172. }
  173. return $consumeGroup->id;
  174. }
  175. /**
  176. * 迁移宝箱内容配置
  177. *
  178. * @param Item $chest
  179. * @return int
  180. */
  181. private function migrateChestContents(Item $chest): int
  182. {
  183. $contents = ItemChestContent::where('chest_id', $chest->id)->get();
  184. // 创建奖励组
  185. $rewardGroup = GameRewardGroup::create([
  186. 'name' => $chest->name . '_奖励组',
  187. 'code' => 'chest_' . $chest->id . '_reward_' . time(),
  188. 'description' => "宝箱 {$chest->name} 的奖励配置(从旧系统迁移)",
  189. 'is_random' => true,
  190. 'random_count' => ($chest->numeric_attributes && $chest->numeric_attributes->max_drop_count > 0) ? $chest->numeric_attributes->max_drop_count : 1,
  191. 'reward_mode' => 1, // 权重选择模式
  192. ]);
  193. // 创建奖励项
  194. foreach ($contents as $content) {
  195. if ($content->item_id) {
  196. // 直接物品奖励
  197. GameRewardItem::create([
  198. 'group_id' => $rewardGroup->id,
  199. 'reward_type' => 1, // 物品类型
  200. 'target_id' => $content->item_id,
  201. 'quantity' => $content->max_quantity, // 使用最大数量
  202. 'weight' => $content->weight,
  203. 'is_guaranteed' => false,
  204. ]);
  205. } elseif ($content->group_id) {
  206. // 物品组奖励 - 需要展开为具体物品
  207. $this->expandGroupToRewardItems($rewardGroup->id, $content);
  208. }
  209. }
  210. return $rewardGroup->id;
  211. }
  212. /**
  213. * 展开物品组为具体奖励项
  214. *
  215. * @param int $rewardGroupId
  216. * @param ItemChestContent $content
  217. */
  218. private function expandGroupToRewardItems(int $rewardGroupId, ItemChestContent $content)
  219. {
  220. $groupItems = \App\Module\GameItems\Models\ItemGroupItem::where('group_id', $content->group_id)->get();
  221. foreach ($groupItems as $groupItem) {
  222. GameRewardItem::create([
  223. 'group_id' => $rewardGroupId,
  224. 'reward_type' => 1, // 物品类型
  225. 'target_id' => $groupItem->item_id,
  226. 'quantity' => $content->max_quantity,
  227. 'weight' => $content->weight * $groupItem->weight, // 组合权重
  228. 'is_guaranteed' => false,
  229. ]);
  230. }
  231. }
  232. }