GenerateChestJsonCommand.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <?php
  2. namespace App\Module\GameItems\Commands;
  3. use App\Module\Game\DCache\ChestJsonConfig;
  4. use App\Module\GameItems\Config\ChestContentWhitelist;
  5. use App\Module\GameItems\Config\ChestCostWhitelist;
  6. use App\Module\GameItems\Config\NumericAttributesWhitelist;
  7. use App\Module\GameItems\Enums\CHEST_COST_TYPE;
  8. use App\Module\GameItems\Enums\ITEM_TYPE;
  9. use Illuminate\Console\Command;
  10. use App\Module\GameItems\Models\Item;
  11. /**
  12. * 生成宝箱JSON数据命令
  13. *
  14. * 该命令用于从数据库中的宝箱相关表生成宝箱JSON数据文件,供客户端使用。
  15. * 生成的JSON文件包含宝箱的基本信息和内容配置,如宝箱ID、可能获得的物品及其概率等。
  16. * 该命令通常在宝箱配置更新后运行,以确保客户端获取最新的宝箱数据。
  17. */
  18. class GenerateChestJsonCommand extends Command
  19. {
  20. /**
  21. * 命令名称和签名
  22. *
  23. * @var string
  24. */
  25. protected $signature = 'gameitems:generate-chest-json';
  26. /**
  27. * 命令描述
  28. *
  29. * @var string
  30. */
  31. protected $description = 'Generate chest.json from Item-chest table';
  32. /**
  33. * 执行命令
  34. */
  35. /**
  36. * 生成宝箱JSON数据
  37. *
  38. * @param bool $saveToFile 是否保存到文件
  39. * @return array|bool 生成的数据或失败标志
  40. */
  41. public static function generateJson()
  42. {
  43. try {
  44. // 查询Item表中的宝箱数据
  45. $chests = Item::query()
  46. ->where('type', ITEM_TYPE::CHEST)
  47. ->with([
  48. 'chest_contents' => function ($query) {
  49. $query->select([
  50. 'id', 'chest_id', 'item_id', 'group_id',
  51. 'min_quantity', 'max_quantity', 'weight'
  52. ])
  53. ->where('weight', '>', 0)
  54. ->with([
  55. 'item' => function ($query) {
  56. $query->select(['id', 'name']);
  57. },
  58. 'group' => function ($query) {
  59. $query->select(['id', 'name'])
  60. ->with(['groupItems' => function ($query) {
  61. $query->with(['item' => function ($query) {
  62. $query->select(['id', 'name']);
  63. }]);
  64. }]);
  65. }
  66. ]);
  67. },
  68. 'chest_costs' => function ($query) {
  69. $query->select([
  70. 'id', 'chest_id', 'cost_type', 'cost_id', 'cost_quantity'
  71. ])
  72. ->where('is_active', true)
  73. ->with([
  74. 'costItem' => function ($query) {
  75. $query->select(['id', 'name']);
  76. }
  77. ]);
  78. }
  79. ])
  80. ->select(['id', 'name','numeric_attributes'])
  81. ->get();
  82. // 处理数据,去除不必要的字段
  83. $processedChests = $chests->map(function ($chest) {
  84. // 检查宝箱是否有内容数据
  85. if ($chest->chest_contents->isEmpty()) {
  86. return null; // 跳过没有内容的宝箱
  87. }
  88. $chestData = [
  89. 'id' => $chest->id,
  90. 'name' => $chest->name,
  91. ];
  92. // 添加宝箱数值属性中的必要字段,使用白名单过滤
  93. if (!empty($chest->numeric_attributes)) {
  94. $numericAttrs = NumericAttributesWhitelist::filter($chest->numeric_attributes);
  95. if (!empty($numericAttrs)) {
  96. foreach ($numericAttrs as $key => $value) {
  97. $chestData[$key] = $value;
  98. }
  99. }
  100. }
  101. // 处理宝箱内容
  102. $contents = $chest->chest_contents->flatMap(function ($content) {
  103. // 根据是物品还是物品组处理
  104. if ($content->item_id && $content->item) {
  105. // 单个物品
  106. $contentData = [
  107. 'id' => $content->id,
  108. 'weight' => $content->weight,
  109. 'min_quantity' => $content->min_quantity,
  110. 'max_quantity' => $content->max_quantity,
  111. 'item_id' => $content->item_id,
  112. 'item_name' => $content->item->name,
  113. 'type' => 'item',
  114. ];
  115. // 使用白名单过滤内容数据
  116. return [ChestContentWhitelist::filter($contentData)];
  117. } elseif ($content->group_id && $content->group && $content->group->groupItems) {
  118. // 物品组 - 将每个物品组中的物品展开为单独的内容项
  119. $groupItems = [];
  120. $totalGroupWeight = $content->group->groupItems->sum('weight');
  121. foreach ($content->group->groupItems as $groupItem) {
  122. if (!$groupItem->item) {
  123. continue;
  124. }
  125. // 计算组内物品的实际权重(组权重 * 物品在组内的权重比例)
  126. $itemWeight = $totalGroupWeight > 0
  127. ? $content->weight * ($groupItem->weight / $totalGroupWeight)
  128. : $content->weight;
  129. $itemContentData = [
  130. 'id' => $content->id . '_' . $groupItem->id, // 创建唯一ID
  131. 'weight' => round($itemWeight, 3),
  132. 'min_quantity' => $content->min_quantity,
  133. 'max_quantity' => $content->max_quantity,
  134. 'item_id' => $groupItem->item->id,
  135. 'item_name' => $groupItem->item->name,
  136. 'type' => 'item',
  137. 'from_group' => true,
  138. 'group_id' => $content->group_id,
  139. 'group_name' => $content->group->name,
  140. ];
  141. // 使用白名单过滤内容数据
  142. $groupItems[] = ChestContentWhitelist::filter($itemContentData);
  143. }
  144. return $groupItems;
  145. }
  146. return [];
  147. })->toArray();
  148. // 检查处理后的内容是否为空
  149. if (empty($contents)) {
  150. return null; // 如果没有有效的内容,跳过这个宝箱
  151. }
  152. $chestData['contents'] = $contents;
  153. // 处理宝箱开启消耗
  154. $chestData['costs'] = $chest->chest_costs->map(function ($cost) {
  155. $costData = [
  156. 'cost_type' => $cost->cost_type,
  157. 'cost_id' => $cost->cost_id,
  158. 'cost_quantity' => $cost->cost_quantity,
  159. ];
  160. // 如果是物品消耗,添加物品名称
  161. if ($cost->cost_type == CHEST_COST_TYPE::ITEM->value && $cost->costItem) {
  162. $costData['item_name'] = $cost->costItem->name;
  163. }
  164. // 使用白名单过滤消耗数据
  165. return ChestCostWhitelist::filter($costData);
  166. })->toArray();
  167. return $chestData;
  168. })
  169. ->filter() // 过滤掉返回值为null的宝箱
  170. ->toArray();
  171. // 准备完整数据,包含生成时间
  172. $data = [
  173. 'generated_ts' => time(),
  174. 'chests' => $processedChests
  175. ];
  176. return $data;
  177. } catch (\Exception $e) {
  178. // 不使用Log,直接输出到控制台
  179. echo 'Generate chest.json failed: ' . $e->getMessage() . "\n";
  180. echo $e->getTraceAsString() . "\n";
  181. return false;
  182. }
  183. }
  184. /**
  185. * 执行命令
  186. *
  187. * @return int
  188. */
  189. public function handle()
  190. {
  191. $this->info('Generating chest JSON data...');
  192. $result = ChestJsonConfig::getData([], true);
  193. if ($result && isset($result['chests'])) {
  194. $chestCount = count($result['chests']);
  195. $contentCount = 0;
  196. $costCount = 0;
  197. foreach ($result['chests'] as $chest) {
  198. $contentCount += count($chest['contents'] ?? []);
  199. $costCount += count($chest['costs'] ?? []);
  200. }
  201. $this->info('Successfully generated chest.json with timestamp');
  202. $this->info("Processed {$chestCount} chests with {$contentCount} content items and {$costCount} cost items");
  203. return 0; // 成功
  204. } else {
  205. $this->error('Failed to generate chest.json');
  206. return 1; // 失败
  207. }
  208. }
  209. }