PointsDisplay.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php
  2. namespace App\Libs;
  3. use App\Models\MpUserPointsDetail;
  4. use Illuminate\Support\Facades\DB;
  5. /**
  6. * 积分明细展示辅助类
  7. *
  8. * 统一定义积分流水(列表/导出)对外展示的模型名与分辨率:
  9. * - 模型名:隐藏供应商字样(百度/快快AI/智帧/字节跳动等),如百度Seedance2.0 → Seedance2.0
  10. * 模型展示名优先取配置表(mp_video_models / mp_image_models / mp_text_models)的 name,
  11. * 配置缺失时回退内置映射,仍未命中则仅剥离供应商前缀原样返回。
  12. * - 分辨率:视频显示 480p/720p/1080p/4k 与 超分720p/超分1080p;图片显示 实际宽*高(如 1600*2848);
  13. * 其他类型(AI对话/积分发放等)返回空串。
  14. */
  15. class PointsDisplay
  16. {
  17. /**
  18. * 需要从展示名中隐藏的供应商字样(仅处理名称开头的前缀)
  19. */
  20. const VENDOR_WORDS = [
  21. '百度',
  22. '快快AI',
  23. '快快',
  24. '智帧平台',
  25. '智帧',
  26. '字节跳动',
  27. ];
  28. /**
  29. * 英文供应商前缀(模型ID开头)
  30. */
  31. const VENDOR_PREFIXES = [
  32. 'baidu-',
  33. 'kuaikuai-',
  34. ];
  35. /**
  36. * 配置表缺失时的内置兜底映射(模型ID => 展示名)
  37. */
  38. const MODEL_NAME_FALLBACK = [
  39. // 视频(Seedance 系列)
  40. 'doubao-seedance-1-5-pro-251215' => 'Seedance1.5pro',
  41. 'doubao-seedance-1-0-pro-250528' => 'Seedance1.0pro',
  42. 'doubao-seedance-1-0-pro-fast-251015' => 'Seedance1.0profast',
  43. 'doubao-seedance-2-0-260128' => 'Seedance2.0',
  44. 'doubao-seedance-2-0-fast-260128' => 'Seedance2.0fast',
  45. 'doubao-seedance-2.0' => 'Seedance2.0',
  46. 'baidu-doubao-seedance-2-0-260128' => 'Seedance2.0',
  47. 'baidu-doubao-seedance-2-0-fast-260128' => 'Seedance2.0fast',
  48. 'baidu-doubao-seedance-2-0-mini-260615' => 'Seedance2.0mini',
  49. 'baidu-doubao-seedance-2-5-260628' => 'Seedance2.5',
  50. 'baidu-dreamina-seedance-2-0-260128' => 'Seedance2.0(海外)',
  51. 'baidu-dreamina-seedance-2-0-fast-260128' => 'Seedance2.0fast(海外)',
  52. 'baidu-dreamina-seedance-2-0-mini-260615' => 'Seedance2.0mini(海外)',
  53. 'baidu-dreamina-seedance-2-5-260628' => 'Seedance2.5(海外)',
  54. 'zhizhen-20' => 'Seedance2.0',
  55. 'zhizhen-20-fast' => 'Seedance2.0fast',
  56. 'zhizhen-20-mini' => 'Seedance2.0mini',
  57. 'seed-2' => 'Seedance2.0',
  58. 'seed-2-fast' => 'Seedance2.0fast',
  59. 'seed-2-mini' => 'Seedance2.0mini',
  60. 'seed-2.5' => 'Seedance2.5',
  61. // 文本
  62. 'baidu-glm-5.2' => 'GLM-5.2',
  63. 'baidu-glm-5.3' => 'GLM-5.3',
  64. ];
  65. /**
  66. * 视频分辨率取值 => 展示文案
  67. */
  68. const VIDEO_RESOLUTION_LABELS = [
  69. '480p' => '480p',
  70. '720p' => '720p',
  71. '1080p' => '1080p',
  72. '4k' => '4k',
  73. '2160p' => '4k',
  74. '4096x2160' => '4k',
  75. 'sr_720p' => '超分720p',
  76. 'sr720p' => '超分720p',
  77. '超分720p' => '超分720p',
  78. 'sr_1080p' => '超分1080p',
  79. 'sr1080p' => '超分1080p',
  80. '超分1080p' => '超分1080p',
  81. ];
  82. /**
  83. * 配置表读取的模型展示名缓存(单次请求内只查一次)
  84. *
  85. * @var array|null
  86. */
  87. private static $modelNameMap = null;
  88. /**
  89. * 获取模型对外展示名(隐藏百度/快快等供应商字样)
  90. *
  91. * @param string $model 模型ID(charge_info.model)
  92. * @return string
  93. */
  94. public static function modelName(string $model = ''): string
  95. {
  96. $model = trim($model);
  97. if ($model === '' || $model === 'unknown') {
  98. return '';
  99. }
  100. $map = self::modelNameMap();
  101. $name = $map[$model] ?? self::MODEL_NAME_FALLBACK[$model] ?? '';
  102. if ($name === '') {
  103. $name = $model;
  104. }
  105. return self::stripVendor($name);
  106. }
  107. /**
  108. * 获取明细的分辨率展示文案
  109. *
  110. * - 视频:480p/720p/1080p/4k、超分720p、超分1080p(来自 charge_info.video_resolution)
  111. * - 图片:实际宽*高,如 1600*2848(来自 charge_info.width / height)
  112. * - 其他类型或无数据:空串
  113. *
  114. * @param string $type 明细类型(video/image/chat/system/company)
  115. * @param array $chargeInfo 计费信息
  116. * @return string
  117. */
  118. public static function resolution(string $type, array $chargeInfo = []): string
  119. {
  120. if ($type === MpUserPointsDetail::TYPE_VIDEO) {
  121. $resolution = strtolower(trim((string)getProp($chargeInfo, 'video_resolution', '')));
  122. if ($resolution === '') {
  123. return '';
  124. }
  125. return self::VIDEO_RESOLUTION_LABELS[$resolution] ?? $resolution;
  126. }
  127. if ($type === MpUserPointsDetail::TYPE_IMAGE) {
  128. $width = (int)getProp($chargeInfo, 'width', 0);
  129. $height = (int)getProp($chargeInfo, 'height', 0);
  130. if ($width <= 0 || $height <= 0) {
  131. return '';
  132. }
  133. return $width . '*' . $height;
  134. }
  135. return '';
  136. }
  137. /**
  138. * 仅保留名称主体:剥离供应商字样前缀(百度Seedance2.0 → Seedance2.0,baidu-xxx → xxx)
  139. *
  140. * @param string $name
  141. * @return string
  142. */
  143. private static function stripVendor(string $name): string
  144. {
  145. $name = trim($name);
  146. // 英文前缀(模型ID形态)
  147. foreach (self::VENDOR_PREFIXES as $prefix) {
  148. if (stripos($name, $prefix) === 0) {
  149. $name = trim(substr($name, strlen($prefix)));
  150. }
  151. }
  152. // 中文前缀(配置表 name 形态),可能连续出现(如“百度智帧xxx”)
  153. $changed = true;
  154. while ($changed) {
  155. $changed = false;
  156. foreach (self::VENDOR_WORDS as $word) {
  157. if (mb_strpos($name, $word) === 0) {
  158. $name = trim(mb_substr($name, mb_strlen($word)));
  159. $changed = true;
  160. break;
  161. }
  162. }
  163. }
  164. return $name;
  165. }
  166. /**
  167. * 模型ID => 配置表展示名(读取失败或未命中时返回空数组,由调用方走兜底映射)
  168. *
  169. * @return array
  170. */
  171. private static function modelNameMap(): array
  172. {
  173. if (self::$modelNameMap !== null) {
  174. return self::$modelNameMap;
  175. }
  176. $map = [];
  177. foreach (['mp_video_models', 'mp_image_models', 'mp_text_models'] as $table) {
  178. try {
  179. $rows = DB::table($table)->select('model', 'name')->get();
  180. } catch (\Throwable $e) {
  181. continue;
  182. }
  183. foreach ($rows as $row) {
  184. $model = trim((string)getProp($row, 'model', ''));
  185. $name = trim((string)getProp($row, 'name', ''));
  186. if ($model !== '' && $name !== '') {
  187. $map[$model] = $name;
  188. }
  189. }
  190. }
  191. self::$modelNameMap = $map;
  192. return self::$modelNameMap;
  193. }
  194. }