param_ownership.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. <?php
  2. /**
  3. * 请求参数资源归属校验配置
  4. *
  5. * 配合 App\Http\Middleware\CheckCompany 使用:
  6. * 校验请求参数中的资源 ID 是否属于当前登录用户所在公司(cpid),防止跨公司访问。
  7. * 同一公司内不同用户默认视为共享,因此这里只校验公司维度(cpid),不校验 user_id。
  8. *
  9. * 说明:在 config 文件中使用 env() 是安全的(config:cache 时会被固化);
  10. * 业务代码中请勿直接使用 env()。
  11. */
  12. // 分镜归属:mp_episode_segments 无 cpid,靠 anime_id 推导;
  13. // 注意 segment_id 是字符串业务键(形如 202601011200001234001),不能按整数解析。
  14. $segmentDefine = [
  15. 'table' => 'mp_episode_segments',
  16. 'key' => 'segment_id',
  17. 'type' => 'string',
  18. 'via' => ['local' => 'anime_id', 'table' => 'mp_animes', 'key' => 'id'],
  19. ];
  20. // 片段归属:片段同样存放在 mp_episode_segments,但以自增主键 id 定位
  21. $actDefine = [
  22. 'table' => 'mp_episode_segments',
  23. 'key' => 'id',
  24. 'via' => ['local' => 'anime_id', 'table' => 'mp_animes', 'key' => 'id'],
  25. ];
  26. return [
  27. // 总开关(对应 .env 中的 CHECK_COMPANY)
  28. 'enabled' => (bool)env('CHECK_COMPANY', false),
  29. // 跳过校验的角色:平台运营角色本身需要跨公司操作
  30. 'skip_roles' => ['superadmin'],
  31. /*
  32. | 跳过校验的公司ID(内部公司白名单)
  33. | 用于灰度上线或线上异常时先放过内部公司,避免误拦影响自己人。
  34. | 多个用逗号分隔,例如 .env 中 CHECK_COMPANY_SKIP_CPIDS=1,2;
  35. | 留空表示不跳过任何公司(全部校验)。
  36. */
  37. 'skip_cpids' => array_values(array_filter(array_map('intval', explode(',', (string)env('CHECK_COMPANY_SKIP_CPIDS', '1'))))),
  38. // 资源归属列(公司维度)
  39. 'owner_column' => 'cpid',
  40. /*
  41. |--------------------------------------------------------------------------
  42. | 全局参数映射
  43. |--------------------------------------------------------------------------
  44. | 格式:参数名 => ['table' => 表名, 'key' => 主键列(可选,默认 id)]
  45. | 仅当该参数名在全项目范围内语义唯一时才放在这里;
  46. | 参数名有歧义(例如 id、task_id)时请放到下方 routes 中按路由指定。
  47. */
  48. 'params' => [
  49. // 原有校验项,保持行为不变
  50. 'script_id' => ['table' => 'mp_scripts'],
  51. 'anime_id' => ['table' => 'mp_animes'],
  52. 'episode_id' => ['table' => 'mp_anime_episodes'],
  53. // 画布
  54. 'canvas_id' => ['table' => 'mp_canvases'],
  55. // 资产库(个人库 user_id=自身,公共库 user_id=0,归属列均为 cpid)
  56. 'product_id' => ['table' => 'mp_products'],
  57. 'parent_id' => ['table' => 'mp_products'],
  58. 'target_parent_id' => ['table' => 'mp_products'],
  59. 'source_product_id' => ['table' => 'mp_products'],
  60. 'source_anime_id' => ['table' => 'mp_animes'],
  61. 'source_episode_id' => ['table' => 'mp_anime_episodes'],
  62. // 提示词模板
  63. 'template_id' => ['table' => 'mp_prompt_templates'],
  64. ],
  65. /*
  66. |--------------------------------------------------------------------------
  67. | 批量参数
  68. |--------------------------------------------------------------------------
  69. | 值可能是数组、逗号分隔字符串或 JSON 数组,会逐项校验。
  70. */
  71. 'array_params' => [
  72. 'script_ids' => ['table' => 'mp_scripts'],
  73. ],
  74. /*
  75. |--------------------------------------------------------------------------
  76. | 链式校验参数(目标表没有 cpid 列,需要沿父级推导归属)
  77. |--------------------------------------------------------------------------
  78. | 格式:参数名 => [
  79. | 'table' => 目标表,
  80. | 'key' => 目标表主键列(默认 id),
  81. | 'type' => 主键类型 int|string(默认 int),
  82. | 'via' => ['local' => 目标表中的外键列, 'table' => 父表, 'key' => 父表主键列],
  83. | ]
  84. |
  85. | 校验方式:按主键取出记录 → 用记录中的外键查父表 → 校验父表记录的 cpid。
  86. | 记录不存在时按无权限处理(fail-closed)。
  87. */
  88. 'linked_params' => [
  89. // 剧本分集组:mp_script_episode_group.script_id -> mp_scripts
  90. 'group_id' => [
  91. 'table' => 'mp_script_episode_group',
  92. 'via' => ['local' => 'script_id', 'table' => 'mp_scripts', 'key' => 'id'],
  93. ],
  94. // 剧本对话记录:mp_script_records.script_id -> mp_scripts
  95. 'rid' => [
  96. 'table' => 'mp_script_records',
  97. 'via' => ['local' => 'script_id', 'table' => 'mp_scripts', 'key' => 'id'],
  98. ],
  99. // 分镜 / 相邻分镜:mp_episode_segments.anime_id -> mp_animes
  100. 'segment_id' => $segmentDefine,
  101. 'prev_segment_id' => $segmentDefine,
  102. 'target_segment_id' => $segmentDefine,
  103. // 片段 / 相邻片段:mp_episode_segments.anime_id -> mp_animes
  104. 'act_id' => $actDefine,
  105. 'prev_act_id' => $actDefine,
  106. 'target_act_id' => $actDefine,
  107. // 画布节点:mp_canvas_nodes.canvas_id -> mp_canvases
  108. 'node_id' => [
  109. 'table' => 'mp_canvas_nodes',
  110. 'via' => ['local' => 'canvas_id', 'table' => 'mp_canvases', 'key' => 'id'],
  111. ],
  112. ],
  113. // 链式校验的批量参数(值可能是数组 / 逗号分隔 / JSON 数组)
  114. 'linked_array_params' => [
  115. // 节点关联:mp_canvas_nodes.canvas_id -> mp_canvases
  116. 'related_ids' => [
  117. 'table' => 'mp_canvas_nodes',
  118. 'via' => ['local' => 'canvas_id', 'table' => 'mp_canvases', 'key' => 'id'],
  119. ],
  120. ],
  121. /*
  122. |--------------------------------------------------------------------------
  123. | 按路由覆盖(参数名有歧义时使用)
  124. |--------------------------------------------------------------------------
  125. | 键为 $request->path() 的返回值(不含前导斜杠),例如 api/anime/deleteProduct
  126. */
  127. 'routes' => [
  128. 'api/anime/deleteProduct' => ['id' => ['table' => 'mp_products']],
  129. 'api/anime/editProduct' => ['id' => ['table' => 'mp_products']],
  130. 'api/anime/getFolderPath' => ['id' => ['table' => 'mp_products']],
  131. 'api/anime/renameFolder' => ['id' => ['table' => 'mp_products']],
  132. 'api/anime/moveRoleOrFolder' => ['id' => ['table' => 'mp_products']],
  133. 'api/anime/generateThreeView' => ['id' => ['table' => 'mp_products']],
  134. 'api/anime/globalProducts' => ['id' => ['table' => 'mp_products']],
  135. 'api/anime/taskCenter/detail' => ['task_id' => ['table' => 'mp_task_center']],
  136. 'api/anime/taskCenter/list' => ['task_id' => ['table' => 'mp_task_center']],
  137. ],
  138. ];