@stackonward/cli 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (638) hide show
  1. package/dist/index.js +4588 -205
  2. package/dist/index.js.map +1 -1
  3. package/dist/resources/apple/.claude/docs/architecture-template.md +54 -0
  4. package/dist/resources/apple/.claude/docs/architecture.md +52 -0
  5. package/dist/resources/apple/.claude/docs/index.md +13 -0
  6. package/dist/resources/apple/.claude/docs/security-lifecycle.md +57 -0
  7. package/dist/resources/apple/.claude/docs/stack.md +93 -0
  8. package/dist/resources/apple/.claude/docs/verification.md +63 -0
  9. package/dist/resources/apple/AGENTS.md +397 -0
  10. package/dist/resources/apple/CLAUDE.md +397 -0
  11. package/dist/resources/apple/Gemfile +2 -0
  12. package/dist/resources/apple/Gemfile.lock +365 -0
  13. package/dist/resources/apple/_gitignore +14 -0
  14. package/dist/resources/apple/fastlane/Fastfile +123 -0
  15. package/dist/resources/components/nuxt/site-source/app/app.vue +42 -0
  16. package/dist/resources/components/nuxt/site-source/app/assets/css/site-shell.css +1035 -0
  17. package/dist/resources/components/nuxt/site-source/app/assets/icons/brand/.gitkeep +0 -0
  18. package/dist/resources/components/nuxt/site-source/app/assets/icons/common/.gitkeep +0 -0
  19. package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationHeader.vue +35 -0
  20. package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationShell.vue +56 -0
  21. package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationSidebar.vue +40 -0
  22. package/dist/resources/components/nuxt/site-source/app/components/site/BackToTop.vue +42 -0
  23. package/dist/resources/components/nuxt/site-source/app/components/site/ContentArticlePage.vue +40 -0
  24. package/dist/resources/components/nuxt/site-source/app/components/site/ContentListPage.vue +30 -0
  25. package/dist/resources/components/nuxt/site-source/app/components/site/CookieConsent.vue +18 -0
  26. package/dist/resources/components/nuxt/site-source/app/components/site/MarketingFooter.vue +52 -0
  27. package/dist/resources/components/nuxt/site-source/app/components/site/MarketingHeader.vue +106 -0
  28. package/dist/resources/components/nuxt/site-source/app/components/site/MarketingShell.vue +35 -0
  29. package/dist/resources/components/nuxt/site-source/app/components/site/SiteBrand.vue +10 -0
  30. package/dist/resources/components/nuxt/site-source/app/components/site/StaticPage.vue +21 -0
  31. package/dist/resources/components/nuxt/site-source/app/components/site/ToastViewport.vue +25 -0
  32. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseButton.vue +86 -0
  33. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseGradientBorder.vue +59 -0
  34. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseModal.vue +131 -0
  35. package/dist/resources/components/nuxt/site-source/app/components/ui/BasePopover.vue +150 -0
  36. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseText.vue +138 -0
  37. package/dist/resources/components/nuxt/site-source/app/components/ui/CheckoutStatus.vue +112 -0
  38. package/dist/resources/components/nuxt/site-source/app/components/ui/CheckoutSummary.vue +172 -0
  39. package/dist/resources/components/nuxt/site-source/app/components/ui/GoogleSignInButton.vue +113 -0
  40. package/dist/resources/components/nuxt/site-source/app/components/ui/InfiniteScroll.vue +123 -0
  41. package/dist/resources/components/nuxt/site-source/app/components/ui/YoutubeEmbed.vue +126 -0
  42. package/dist/resources/components/nuxt/site-source/app/composables/useAnalytics.ts +24 -0
  43. package/dist/resources/components/nuxt/site-source/app/composables/useCheckout.ts +121 -0
  44. package/dist/resources/components/nuxt/site-source/app/composables/useConsent.ts +31 -0
  45. package/dist/resources/components/nuxt/site-source/app/composables/useGoogleIdentity.ts +163 -0
  46. package/dist/resources/components/nuxt/site-source/app/composables/useNavigation.ts +11 -0
  47. package/dist/resources/components/nuxt/site-source/app/composables/useSeo.ts +33 -0
  48. package/dist/resources/components/nuxt/site-source/app/composables/useSiteShell.ts +58 -0
  49. package/dist/resources/components/nuxt/site-source/app/composables/useToasts.ts +48 -0
  50. package/dist/resources/components/nuxt/site-source/app/layouts/app.vue +5 -0
  51. package/dist/resources/components/nuxt/site-source/app/layouts/default.vue +5 -0
  52. package/dist/resources/components/nuxt/site-source/app/plugins/analytics.client.ts +121 -0
  53. package/dist/resources/components/nuxt/site-source/app/plugins/google-identity.client.ts +24 -0
  54. package/dist/resources/components/nuxt/site-source/app/theme/accordion.ts +9 -0
  55. package/dist/resources/components/nuxt/site-source/app/theme/breadcrumb.ts +8 -0
  56. package/dist/resources/components/nuxt/site-source/app/theme/button.ts +92 -0
  57. package/dist/resources/components/nuxt/site-source/app/theme/carousel.ts +11 -0
  58. package/dist/resources/components/nuxt/site-source/app/theme/checkbox.ts +12 -0
  59. package/dist/resources/components/nuxt/site-source/app/theme/index.ts +30 -0
  60. package/dist/resources/components/nuxt/site-source/app/theme/input.ts +47 -0
  61. package/dist/resources/components/nuxt/site-source/app/theme/modal.ts +9 -0
  62. package/dist/resources/components/nuxt/site-source/app/theme/pagination.ts +6 -0
  63. package/dist/resources/components/nuxt/site-source/app/theme/select.ts +23 -0
  64. package/dist/resources/components/nuxt/site-source/app/theme/stepper.ts +10 -0
  65. package/dist/resources/components/nuxt/site-source/app/theme/toast.ts +8 -0
  66. package/dist/resources/components/nuxt/site-source/app/theme/tooltip.ts +6 -0
  67. package/dist/resources/components/nuxt/site-source/app/types/site-shell.ts +85 -0
  68. package/dist/resources/components/react/ui/Button.tsx +73 -0
  69. package/dist/resources/components/react/ui/Input.tsx +44 -0
  70. package/dist/resources/components/registry.json +355 -0
  71. package/dist/resources/components/vue/feedback/Toast.vue +120 -0
  72. package/dist/resources/components/vue/feedback/useToast.ts +70 -0
  73. package/dist/resources/components/vue/media/FileUpload.vue +229 -0
  74. package/dist/resources/components/vue/media/VideoPlayer.vue +157 -0
  75. package/dist/resources/components/vue/ui/AppImage.vue +147 -0
  76. package/dist/resources/components/vue/ui/BottomSheet.vue +217 -0
  77. package/dist/resources/components/vue/ui/Button.vue +154 -0
  78. package/dist/resources/components/vue/ui/Card.vue +111 -0
  79. package/dist/resources/components/vue/ui/Input.vue +113 -0
  80. package/dist/resources/components/vue/ui/Modal.vue +193 -0
  81. package/dist/resources/components/vue/ui/ScrollArea.vue +163 -0
  82. package/dist/resources/components/vue/ui/Tooltip.vue +115 -0
  83. package/dist/resources/registry.json +158 -0
  84. package/dist/resources/snippets/node/cache.ts +116 -0
  85. package/dist/resources/snippets/node/date.ts +126 -0
  86. package/dist/resources/snippets/node/logger.ts +104 -0
  87. package/dist/resources/snippets/node/validation.ts +132 -0
  88. package/dist/resources/snippets/react/useDebounce.ts +90 -0
  89. package/dist/resources/snippets/registry.json +65 -0
  90. package/dist/resources/snippets/vue/useAsync.ts +96 -0
  91. package/dist/resources/snippets/vue/usePagination.ts +102 -0
  92. package/dist/resources/templates/base/.claude/docs/architecture-template.md +459 -0
  93. package/dist/resources/templates/base/.claude/docs/architecture.md +137 -0
  94. package/dist/resources/templates/base/.claude/docs/index.md +42 -0
  95. package/dist/resources/templates/base/.claude/docs/security-lifecycle.md +129 -0
  96. package/dist/resources/templates/base/.claude/docs/verification.md +121 -0
  97. package/dist/resources/templates/base/AGENTS.md +415 -0
  98. package/dist/resources/templates/base/CLAUDE.md +415 -0
  99. package/dist/resources/templates/go-backend/.claude/docs/api-design.md +486 -0
  100. package/dist/resources/templates/go-backend/.claude/docs/architecture-template.md +449 -0
  101. package/dist/resources/templates/go-backend/.claude/docs/check-list.md +155 -0
  102. package/dist/resources/templates/go-backend/.claude/docs/database.md +369 -0
  103. package/dist/resources/templates/go-backend/.claude/docs/e2e-standards.md +161 -0
  104. package/dist/resources/templates/go-backend/.claude/docs/error-handling.md +558 -0
  105. package/dist/resources/templates/go-backend/.claude/docs/event-runtime-boundary.md +124 -0
  106. package/dist/resources/templates/go-backend/.claude/docs/identifier-contract.md +195 -0
  107. package/dist/resources/templates/go-backend/.claude/docs/index.md +22 -0
  108. package/dist/resources/templates/go-backend/.claude/docs/logging.md +314 -0
  109. package/dist/resources/templates/go-backend/.claude/docs/mediagen-provider-onboarding-sop.md +917 -0
  110. package/dist/resources/templates/go-backend/.claude/docs/mediagen-provider-quick-reference.md +195 -0
  111. package/dist/resources/templates/go-backend/.claude/docs/module-standards.md +309 -0
  112. package/dist/resources/templates/go-backend/.claude/docs/openapi-contract.md +141 -0
  113. package/dist/resources/templates/go-backend/.claude/docs/payment-gateway-onboarding.md +283 -0
  114. package/dist/resources/templates/go-backend/.claude/docs/platform-boundary.md +191 -0
  115. package/dist/resources/templates/go-backend/.claude/docs/quality-gates.md +112 -0
  116. package/dist/resources/templates/go-backend/.dockerignore +6 -0
  117. package/dist/resources/templates/go-backend/.env.example +13 -0
  118. package/dist/resources/templates/go-backend/.gitattributes +2 -0
  119. package/dist/resources/templates/go-backend/AGENTS.md +1139 -0
  120. package/dist/resources/templates/go-backend/CLAUDE.md +1139 -0
  121. package/dist/resources/templates/go-backend/Dockerfile +14 -0
  122. package/dist/resources/templates/go-backend/Makefile +71 -0
  123. package/dist/resources/templates/go-backend/README.md +66 -0
  124. package/dist/resources/templates/go-backend/_gitignore +10 -0
  125. package/dist/resources/templates/go-backend/api/openapi/README.md +29 -0
  126. package/dist/resources/templates/go-backend/api/openapi/dist/artifact-manifest.json +19 -0
  127. package/dist/resources/templates/go-backend/api/openapi/dist/conformance-matrix.json +276 -0
  128. package/dist/resources/templates/go-backend/api/openapi/document.go +30 -0
  129. package/dist/resources/templates/go-backend/api/openapi/package.json +28 -0
  130. package/dist/resources/templates/go-backend/api/openapi/pnpm-lock.yaml +3052 -0
  131. package/dist/resources/templates/go-backend/api/openapi/redocly-source.yaml +18 -0
  132. package/dist/resources/templates/go-backend/api/openapi/redocly.yaml +8 -0
  133. package/dist/resources/templates/go-backend/api/openapi/sdk-generators.json +16 -0
  134. package/dist/resources/templates/go-backend/api/openapi/service-registry.json +39 -0
  135. package/dist/resources/templates/go-backend/api/openapi/services/product-api/conformance/runtime.arazzo.yaml +65 -0
  136. package/dist/resources/templates/go-backend/api/openapi/services/product-api/dist/internal.openapi.json +327 -0
  137. package/dist/resources/templates/go-backend/api/openapi/services/product-api/dist/runtime-contract.json +56 -0
  138. package/dist/resources/templates/go-backend/api/openapi/services/product-api/openapi.yaml +222 -0
  139. package/dist/resources/templates/go-backend/api/openapi/services/worker/conformance/runtime.arazzo.yaml +65 -0
  140. package/dist/resources/templates/go-backend/api/openapi/services/worker/dist/internal.openapi.json +327 -0
  141. package/dist/resources/templates/go-backend/api/openapi/services/worker/dist/runtime-contract.json +56 -0
  142. package/dist/resources/templates/go-backend/api/openapi/services/worker/openapi.yaml +222 -0
  143. package/dist/resources/templates/go-backend/api/openapi/tooling/breaking.mjs +416 -0
  144. package/dist/resources/templates/go-backend/api/openapi/tooling/build.mjs +103 -0
  145. package/dist/resources/templates/go-backend/api/openapi/tooling/conformance_coverage.mjs +111 -0
  146. package/dist/resources/templates/go-backend/api/openapi/tooling/contract_compiler.mjs +62 -0
  147. package/dist/resources/templates/go-backend/api/openapi/tooling/contract_policy.mjs +208 -0
  148. package/dist/resources/templates/go-backend/api/openapi/tooling/delegated_contract.mjs +302 -0
  149. package/dist/resources/templates/go-backend/api/openapi/tooling/deterministic_archive.mjs +69 -0
  150. package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_example_privacy.mjs +93 -0
  151. package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_projection.mjs +498 -0
  152. package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_quality.mjs +346 -0
  153. package/dist/resources/templates/go-backend/api/openapi/tooling/git_artifact_history.mjs +188 -0
  154. package/dist/resources/templates/go-backend/api/openapi/tooling/manifest.mjs +170 -0
  155. package/dist/resources/templates/go-backend/api/openapi/tooling/overlay.mjs +98 -0
  156. package/dist/resources/templates/go-backend/api/openapi/tooling/project.mjs +549 -0
  157. package/dist/resources/templates/go-backend/api/openapi/tooling/route_contract.mjs +156 -0
  158. package/dist/resources/templates/go-backend/api/openapi/tooling/runtime_conformance.mjs +193 -0
  159. package/dist/resources/templates/go-backend/api/openapi/tooling/scalar_asset_localization.mjs +164 -0
  160. package/dist/resources/templates/go-backend/api/openapi/tooling/sdk_generator.mjs +229 -0
  161. package/dist/resources/templates/go-backend/api/openapi/tooling/semantic_version.mjs +64 -0
  162. package/dist/resources/templates/go-backend/api/openapi/tooling/service_contract_inputs.mjs +16 -0
  163. package/dist/resources/templates/go-backend/api/openapi/tooling/test/breaking.test.mjs +365 -0
  164. package/dist/resources/templates/go-backend/api/openapi/tooling/test/build.test.mjs +42 -0
  165. package/dist/resources/templates/go-backend/api/openapi/tooling/test/conformance_coverage.test.mjs +88 -0
  166. package/dist/resources/templates/go-backend/api/openapi/tooling/test/contract_policy.test.mjs +257 -0
  167. package/dist/resources/templates/go-backend/api/openapi/tooling/test/delegated_contract.test.mjs +205 -0
  168. package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_example_privacy.test.mjs +51 -0
  169. package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_projection.test.mjs +368 -0
  170. package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_quality.test.mjs +151 -0
  171. package/dist/resources/templates/go-backend/api/openapi/tooling/test/git_artifact_history.test.mjs +60 -0
  172. package/dist/resources/templates/go-backend/api/openapi/tooling/test/head_policy.test.mjs +8 -0
  173. package/dist/resources/templates/go-backend/api/openapi/tooling/test/overlay.test.mjs +44 -0
  174. package/dist/resources/templates/go-backend/api/openapi/tooling/test/projection.test.mjs +270 -0
  175. package/dist/resources/templates/go-backend/api/openapi/tooling/test/redocly_documentation_rules.test.mjs +146 -0
  176. package/dist/resources/templates/go-backend/api/openapi/tooling/test/release_manifest.test.mjs +76 -0
  177. package/dist/resources/templates/go-backend/api/openapi/tooling/test/route_contract.test.mjs +119 -0
  178. package/dist/resources/templates/go-backend/api/openapi/tooling/test/runtime_conformance.test.mjs +75 -0
  179. package/dist/resources/templates/go-backend/api/openapi/tooling/test/scalar_asset_localization.test.mjs +50 -0
  180. package/dist/resources/templates/go-backend/api/openapi/tooling/test/sdk_generator.test.mjs +31 -0
  181. package/dist/resources/templates/go-backend/api/openapi/tooling/test/semantic_version.test.mjs +60 -0
  182. package/dist/resources/templates/go-backend/api/openapi/tooling/test/service_contract_inputs.test.mjs +27 -0
  183. package/dist/resources/templates/go-backend/cmd/internal/runtime/resources.go +125 -0
  184. package/dist/resources/templates/go-backend/cmd/internal/runtime/runtime.go +102 -0
  185. package/dist/resources/templates/go-backend/cmd/internal/runtime/runtime_test.go +70 -0
  186. package/dist/resources/templates/go-backend/cmd/product-api/lifecycle_test.go +58 -0
  187. package/dist/resources/templates/go-backend/cmd/product-api/main.go +32 -0
  188. package/dist/resources/templates/go-backend/cmd/product-api/wire.go +13 -0
  189. package/dist/resources/templates/go-backend/cmd/product-api/wire_gen.go +94 -0
  190. package/dist/resources/templates/go-backend/cmd/worker/main.go +47 -0
  191. package/dist/resources/templates/go-backend/cmd/worker/wire.go +14 -0
  192. package/dist/resources/templates/go-backend/cmd/worker/wire_gen.go +111 -0
  193. package/dist/resources/templates/go-backend/configs/config.yaml +37 -0
  194. package/dist/resources/templates/go-backend/deployments/compose.yaml +71 -0
  195. package/dist/resources/templates/go-backend/docs/openapi-provenance.json +489 -0
  196. package/dist/resources/templates/go-backend/docs/runtime-provenance.json +175 -0
  197. package/dist/resources/templates/go-backend/docs/source-provenance.json +3568 -0
  198. package/dist/resources/templates/go-backend/docs/standards-provenance.json +1624 -0
  199. package/dist/resources/templates/go-backend/go.mod +256 -0
  200. package/dist/resources/templates/go-backend/go.sum +805 -0
  201. package/dist/resources/templates/go-backend/internal/common/canonicaljson/canonical.go +370 -0
  202. package/dist/resources/templates/go-backend/internal/common/canonicaljson/canonical_test.go +223 -0
  203. package/dist/resources/templates/go-backend/internal/common/checkoutbff/contracts.go +125 -0
  204. package/dist/resources/templates/go-backend/internal/common/checkoutbff/platform_client.go +212 -0
  205. package/dist/resources/templates/go-backend/internal/common/checkoutbff/platform_client_test.go +319 -0
  206. package/dist/resources/templates/go-backend/internal/common/contentaccess/contentaccess.go +50 -0
  207. package/dist/resources/templates/go-backend/internal/common/contracts/contact.go +85 -0
  208. package/dist/resources/templates/go-backend/internal/common/ctxutil/identity.go +20 -0
  209. package/dist/resources/templates/go-backend/internal/common/ctxutil/product.go +53 -0
  210. package/dist/resources/templates/go-backend/internal/common/enumdomain/payment.go +12 -0
  211. package/dist/resources/templates/go-backend/internal/common/enums/dto_admin.go +9 -0
  212. package/dist/resources/templates/go-backend/internal/common/enums/enum.go +1247 -0
  213. package/dist/resources/templates/go-backend/internal/common/enums/handler_admin.go +66 -0
  214. package/dist/resources/templates/go-backend/internal/common/enums/module.go +46 -0
  215. package/dist/resources/templates/go-backend/internal/common/enums/provider.go +10 -0
  216. package/dist/resources/templates/go-backend/internal/common/enums/register.go +11 -0
  217. package/dist/resources/templates/go-backend/internal/common/enums/register_audit.go +274 -0
  218. package/dist/resources/templates/go-backend/internal/common/enums/register_command_replay.go +12 -0
  219. package/dist/resources/templates/go-backend/internal/common/enums/register_config.go +27 -0
  220. package/dist/resources/templates/go-backend/internal/common/enums/register_locale.go +37 -0
  221. package/dist/resources/templates/go-backend/internal/common/enums/register_ops.go +14 -0
  222. package/dist/resources/templates/go-backend/internal/common/enums/register_outbox.go +15 -0
  223. package/dist/resources/templates/go-backend/internal/common/enums/register_rule_expression.go +63 -0
  224. package/dist/resources/templates/go-backend/internal/common/enums/registry.go +77 -0
  225. package/dist/resources/templates/go-backend/internal/common/httporigin/origin.go +62 -0
  226. package/dist/resources/templates/go-backend/internal/common/httporigin/origin_test.go +38 -0
  227. package/dist/resources/templates/go-backend/internal/common/iamcredential/plaintext.go +35 -0
  228. package/dist/resources/templates/go-backend/internal/common/iamcredential/plaintext_test.go +29 -0
  229. package/dist/resources/templates/go-backend/internal/common/iamscope/scope.go +212 -0
  230. package/dist/resources/templates/go-backend/internal/common/iamscope/scope_binding.go +26 -0
  231. package/dist/resources/templates/go-backend/internal/common/iamscope/scope_filter.go +173 -0
  232. package/dist/resources/templates/go-backend/internal/common/iamscope/scope_filter_test.go +279 -0
  233. package/dist/resources/templates/go-backend/internal/common/internalapi/authorization_details.go +10 -0
  234. package/dist/resources/templates/go-backend/internal/common/internalapi/route_permission_policies.go +660 -0
  235. package/dist/resources/templates/go-backend/internal/common/internalapi/route_permission_policies_test.go +331 -0
  236. package/dist/resources/templates/go-backend/internal/common/internalservice/audience_token_source.go +166 -0
  237. package/dist/resources/templates/go-backend/internal/common/internalservice/bootstrap_token_exchanger.go +183 -0
  238. package/dist/resources/templates/go-backend/internal/common/internalservice/bound_service_token_signer.go +52 -0
  239. package/dist/resources/templates/go-backend/internal/common/internalservice/delegated_access_token_exchanger.go +189 -0
  240. package/dist/resources/templates/go-backend/internal/common/internalservice/delegated_access_token_exchanger_test.go +159 -0
  241. package/dist/resources/templates/go-backend/internal/common/internalservice/product_scoped_service_token_signer.go +92 -0
  242. package/dist/resources/templates/go-backend/internal/common/internalservice/provider.go +25 -0
  243. package/dist/resources/templates/go-backend/internal/common/internalservice/request_signer.go +86 -0
  244. package/dist/resources/templates/go-backend/internal/common/internalservice/resolver.go +69 -0
  245. package/dist/resources/templates/go-backend/internal/common/internalservice/resolver_test.go +42 -0
  246. package/dist/resources/templates/go-backend/internal/common/internalservice/route_scope.go +95 -0
  247. package/dist/resources/templates/go-backend/internal/common/internalservice/route_scope_test.go +78 -0
  248. package/dist/resources/templates/go-backend/internal/common/internalservice/service_identity_test.go +387 -0
  249. package/dist/resources/templates/go-backend/internal/common/jsonschema/compiler.go +100 -0
  250. package/dist/resources/templates/go-backend/internal/common/jsonschema/compiler_test.go +41 -0
  251. package/dist/resources/templates/go-backend/internal/common/jwksapi/handler.go +83 -0
  252. package/dist/resources/templates/go-backend/internal/common/jwksapi/handler_test.go +104 -0
  253. package/dist/resources/templates/go-backend/internal/common/locale/locale.go +145 -0
  254. package/dist/resources/templates/go-backend/internal/common/locale/locale_test.go +75 -0
  255. package/dist/resources/templates/go-backend/internal/common/mediaref/mediaref.go +54 -0
  256. package/dist/resources/templates/go-backend/internal/common/mediaref/mediaref_test.go +77 -0
  257. package/dist/resources/templates/go-backend/internal/common/middleware/apikey_auth.go +320 -0
  258. package/dist/resources/templates/go-backend/internal/common/middleware/apikey_throttle.go +158 -0
  259. package/dist/resources/templates/go-backend/internal/common/middleware/auth.go +40 -0
  260. package/dist/resources/templates/go-backend/internal/common/middleware/auth_test.go +50 -0
  261. package/dist/resources/templates/go-backend/internal/common/middleware/client_context.go +86 -0
  262. package/dist/resources/templates/go-backend/internal/common/middleware/cors.go +68 -0
  263. package/dist/resources/templates/go-backend/internal/common/middleware/event_context.go +18 -0
  264. package/dist/resources/templates/go-backend/internal/common/middleware/gateway_identity.go +279 -0
  265. package/dist/resources/templates/go-backend/internal/common/middleware/helpers.go +159 -0
  266. package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_route_permission.go +171 -0
  267. package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_token_auth.go +151 -0
  268. package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_token_auth_test.go +99 -0
  269. package/dist/resources/templates/go-backend/internal/common/middleware/iam_service_identity.go +138 -0
  270. package/dist/resources/templates/go-backend/internal/common/middleware/iam_service_token_auth.go +74 -0
  271. package/dist/resources/templates/go-backend/internal/common/middleware/idempotent.go +273 -0
  272. package/dist/resources/templates/go-backend/internal/common/middleware/idempotent_test.go +83 -0
  273. package/dist/resources/templates/go-backend/internal/common/middleware/internal_route_permission.go +45 -0
  274. package/dist/resources/templates/go-backend/internal/common/middleware/internal_route_permission_test.go +186 -0
  275. package/dist/resources/templates/go-backend/internal/common/middleware/logging.go +191 -0
  276. package/dist/resources/templates/go-backend/internal/common/middleware/metrics.go +45 -0
  277. package/dist/resources/templates/go-backend/internal/common/middleware/middleware_test.go +1516 -0
  278. package/dist/resources/templates/go-backend/internal/common/middleware/principal_context.go +160 -0
  279. package/dist/resources/templates/go-backend/internal/common/middleware/principal_context_test.go +355 -0
  280. package/dist/resources/templates/go-backend/internal/common/middleware/product_context.go +100 -0
  281. package/dist/resources/templates/go-backend/internal/common/middleware/recovery.go +40 -0
  282. package/dist/resources/templates/go-backend/internal/common/middleware/referral_attribution.go +178 -0
  283. package/dist/resources/templates/go-backend/internal/common/middleware/requestid.go +48 -0
  284. package/dist/resources/templates/go-backend/internal/common/middleware/resource_permission.go +790 -0
  285. package/dist/resources/templates/go-backend/internal/common/middleware/resource_permission_test.go +440 -0
  286. package/dist/resources/templates/go-backend/internal/common/middleware/service_credential_auth.go +67 -0
  287. package/dist/resources/templates/go-backend/internal/common/middleware/tenant.go +148 -0
  288. package/dist/resources/templates/go-backend/internal/common/middleware/throttle.go +310 -0
  289. package/dist/resources/templates/go-backend/internal/common/middleware/trusted_client_ip.go +23 -0
  290. package/dist/resources/templates/go-backend/internal/common/middleware/trusted_client_ip_test.go +60 -0
  291. package/dist/resources/templates/go-backend/internal/common/money/currency.go +25 -0
  292. package/dist/resources/templates/go-backend/internal/common/money/errors.go +23 -0
  293. package/dist/resources/templates/go-backend/internal/common/money/format.go +123 -0
  294. package/dist/resources/templates/go-backend/internal/common/money/format_test.go +204 -0
  295. package/dist/resources/templates/go-backend/internal/common/money/gateway.go +144 -0
  296. package/dist/resources/templates/go-backend/internal/common/money/gateway_test.go +165 -0
  297. package/dist/resources/templates/go-backend/internal/common/money/rounding.go +56 -0
  298. package/dist/resources/templates/go-backend/internal/common/money/rounding_test.go +51 -0
  299. package/dist/resources/templates/go-backend/internal/common/principal/context.go +56 -0
  300. package/dist/resources/templates/go-backend/internal/common/principal/principal.go +49 -0
  301. package/dist/resources/templates/go-backend/internal/common/productcode/code.go +24 -0
  302. package/dist/resources/templates/go-backend/internal/common/productcode/code_test.go +35 -0
  303. package/dist/resources/templates/go-backend/internal/common/providerconfig/schema.go +577 -0
  304. package/dist/resources/templates/go-backend/internal/common/providerconfig/schema_test.go +47 -0
  305. package/dist/resources/templates/go-backend/internal/common/requestprivacy/request.go +246 -0
  306. package/dist/resources/templates/go-backend/internal/common/requestprivacy/request_test.go +170 -0
  307. package/dist/resources/templates/go-backend/internal/common/response/pagination.go +60 -0
  308. package/dist/resources/templates/go-backend/internal/common/response/response.go +110 -0
  309. package/dist/resources/templates/go-backend/internal/common/response/response_test.go +463 -0
  310. package/dist/resources/templates/go-backend/internal/common/ruleexpression/ast.go +141 -0
  311. package/dist/resources/templates/go-backend/internal/common/ruleexpression/ast_test.go +81 -0
  312. package/dist/resources/templates/go-backend/internal/common/ruleexpression/canonical.go +184 -0
  313. package/dist/resources/templates/go-backend/internal/common/ruleexpression/canonical_test.go +319 -0
  314. package/dist/resources/templates/go-backend/internal/common/ruleexpression/decoder.go +555 -0
  315. package/dist/resources/templates/go-backend/internal/common/ruleexpression/decoder_test.go +260 -0
  316. package/dist/resources/templates/go-backend/internal/common/ruleexpression/errors.go +55 -0
  317. package/dist/resources/templates/go-backend/internal/common/ruleexpression/evaluator.go +96 -0
  318. package/dist/resources/templates/go-backend/internal/common/ruleexpression/evaluator_test.go +220 -0
  319. package/dist/resources/templates/go-backend/internal/common/ruleexpression/fuzz_test.go +116 -0
  320. package/dist/resources/templates/go-backend/internal/common/ruleexpression/limits.go +331 -0
  321. package/dist/resources/templates/go-backend/internal/common/ruleexpression/schema.go +412 -0
  322. package/dist/resources/templates/go-backend/internal/common/ruleexpression/schema_test.go +164 -0
  323. package/dist/resources/templates/go-backend/internal/common/runtimeinput/runtime_input.go +627 -0
  324. package/dist/resources/templates/go-backend/internal/common/runtimeinput/runtime_input_test.go +245 -0
  325. package/dist/resources/templates/go-backend/internal/common/semver/errors.go +6 -0
  326. package/dist/resources/templates/go-backend/internal/common/semver/semver.go +125 -0
  327. package/dist/resources/templates/go-backend/internal/common/semver/semver_test.go +205 -0
  328. package/dist/resources/templates/go-backend/internal/common/serviceidentity/audience.go +48 -0
  329. package/dist/resources/templates/go-backend/internal/common/serviceidentity/audience_test.go +35 -0
  330. package/dist/resources/templates/go-backend/internal/common/serviceidentity/resource_identifier.go +33 -0
  331. package/dist/resources/templates/go-backend/internal/common/serviceidentity/resource_identifier_test.go +32 -0
  332. package/dist/resources/templates/go-backend/internal/common/tradeno/generator.go +99 -0
  333. package/dist/resources/templates/go-backend/internal/common/tradeno/generator_test.go +396 -0
  334. package/dist/resources/templates/go-backend/internal/common/tradeno/prefix.go +247 -0
  335. package/dist/resources/templates/go-backend/internal/common/types/asset_url.go +11 -0
  336. package/dist/resources/templates/go-backend/internal/common/types/billing.go +89 -0
  337. package/dist/resources/templates/go-backend/internal/common/types/billing_test.go +77 -0
  338. package/dist/resources/templates/go-backend/internal/common/types/country.go +74 -0
  339. package/dist/resources/templates/go-backend/internal/common/types/country_test.go +28 -0
  340. package/dist/resources/templates/go-backend/internal/common/types/currency.go +60 -0
  341. package/dist/resources/templates/go-backend/internal/common/types/identifiers.go +41 -0
  342. package/dist/resources/templates/go-backend/internal/common/types/operator.go +28 -0
  343. package/dist/resources/templates/go-backend/internal/common/types/subscription_entitlement.go +24 -0
  344. package/dist/resources/templates/go-backend/internal/common/types/subscription_entitlement_test.go +35 -0
  345. package/dist/resources/templates/go-backend/internal/common/types/tier.go +72 -0
  346. package/dist/resources/templates/go-backend/internal/common/types/types.go +411 -0
  347. package/dist/resources/templates/go-backend/internal/common/types/types_test.go +295 -0
  348. package/dist/resources/templates/go-backend/internal/common/utils/constants.go +100 -0
  349. package/dist/resources/templates/go-backend/internal/common/utils/crypto.go +158 -0
  350. package/dist/resources/templates/go-backend/internal/common/utils/crypto_test.go +345 -0
  351. package/dist/resources/templates/go-backend/internal/common/utils/duration.go +25 -0
  352. package/dist/resources/templates/go-backend/internal/common/utils/helpers.go +18 -0
  353. package/dist/resources/templates/go-backend/internal/common/utils/json.go +62 -0
  354. package/dist/resources/templates/go-backend/internal/common/utils/json_test.go +76 -0
  355. package/dist/resources/templates/go-backend/internal/common/utils/pagination.go +51 -0
  356. package/dist/resources/templates/go-backend/internal/common/utils/pagination_test.go +45 -0
  357. package/dist/resources/templates/go-backend/internal/common/utils/pointer.go +107 -0
  358. package/dist/resources/templates/go-backend/internal/common/utils/pointer_test.go +15 -0
  359. package/dist/resources/templates/go-backend/internal/common/utils/slice.go +24 -0
  360. package/dist/resources/templates/go-backend/internal/common/utils/snowflake.go +110 -0
  361. package/dist/resources/templates/go-backend/internal/common/utils/snowflake_test.go +316 -0
  362. package/dist/resources/templates/go-backend/internal/common/utils/string.go +16 -0
  363. package/dist/resources/templates/go-backend/internal/common/utils/timefmt.go +42 -0
  364. package/dist/resources/templates/go-backend/internal/common/validator/validator.go +136 -0
  365. package/dist/resources/templates/go-backend/internal/common/validator/validator_test.go +240 -0
  366. package/dist/resources/templates/go-backend/internal/common/youtubeapi/client.go +224 -0
  367. package/dist/resources/templates/go-backend/internal/core/apicontract/document.go +269 -0
  368. package/dist/resources/templates/go-backend/internal/core/apicontract/document_test.go +104 -0
  369. package/dist/resources/templates/go-backend/internal/core/apicontract/route_validator.go +78 -0
  370. package/dist/resources/templates/go-backend/internal/core/apicontract/route_validator_test.go +108 -0
  371. package/dist/resources/templates/go-backend/internal/core/apidocs/assets/scalar-api-reference.js +2385 -0
  372. package/dist/resources/templates/go-backend/internal/core/apidocs/assets/scalar-api-reference.metadata.json +10 -0
  373. package/dist/resources/templates/go-backend/internal/core/apidocs/route.go +107 -0
  374. package/dist/resources/templates/go-backend/internal/core/apidocs/route_test.go +200 -0
  375. package/dist/resources/templates/go-backend/internal/core/apidocs/scalar.go +117 -0
  376. package/dist/resources/templates/go-backend/internal/core/apidocs/scalar_test.go +99 -0
  377. package/dist/resources/templates/go-backend/internal/core/audit/access_log_policy.go +91 -0
  378. package/dist/resources/templates/go-backend/internal/core/audit/access_log_policy_test.go +51 -0
  379. package/dist/resources/templates/go-backend/internal/core/audit/actor_resolver.go +36 -0
  380. package/dist/resources/templates/go-backend/internal/core/audit/constants.go +30 -0
  381. package/dist/resources/templates/go-backend/internal/core/audit/context.go +119 -0
  382. package/dist/resources/templates/go-backend/internal/core/audit/context_test.go +310 -0
  383. package/dist/resources/templates/go-backend/internal/core/audit/dto.go +468 -0
  384. package/dist/resources/templates/go-backend/internal/core/audit/dto_admin.go +172 -0
  385. package/dist/resources/templates/go-backend/internal/core/audit/enum.go +460 -0
  386. package/dist/resources/templates/go-backend/internal/core/audit/enum_test.go +314 -0
  387. package/dist/resources/templates/go-backend/internal/core/audit/errors.go +9 -0
  388. package/dist/resources/templates/go-backend/internal/core/audit/handler.go +568 -0
  389. package/dist/resources/templates/go-backend/internal/core/audit/listener.go +319 -0
  390. package/dist/resources/templates/go-backend/internal/core/audit/listener_test.go +98 -0
  391. package/dist/resources/templates/go-backend/internal/core/audit/middleware.go +669 -0
  392. package/dist/resources/templates/go-backend/internal/core/audit/middleware_test.go +268 -0
  393. package/dist/resources/templates/go-backend/internal/core/audit/model.go +306 -0
  394. package/dist/resources/templates/go-backend/internal/core/audit/partition_job.go +316 -0
  395. package/dist/resources/templates/go-backend/internal/core/audit/ports.go +9 -0
  396. package/dist/resources/templates/go-backend/internal/core/audit/provider.go +30 -0
  397. package/dist/resources/templates/go-backend/internal/core/audit/repository.go +1007 -0
  398. package/dist/resources/templates/go-backend/internal/core/audit/repository_test.go +934 -0
  399. package/dist/resources/templates/go-backend/internal/core/audit/router.go +85 -0
  400. package/dist/resources/templates/go-backend/internal/core/audit/service.go +770 -0
  401. package/dist/resources/templates/go-backend/internal/core/audit/service_test.go +331 -0
  402. package/dist/resources/templates/go-backend/internal/core/audit/vo_admin.go +475 -0
  403. package/dist/resources/templates/go-backend/internal/core/cache/config.go +30 -0
  404. package/dist/resources/templates/go-backend/internal/core/cache/key.go +41 -0
  405. package/dist/resources/templates/go-backend/internal/core/cache/key_test.go +62 -0
  406. package/dist/resources/templates/go-backend/internal/core/cache/l1.go +153 -0
  407. package/dist/resources/templates/go-backend/internal/core/cache/l1_test.go +212 -0
  408. package/dist/resources/templates/go-backend/internal/core/cache/manager.go +278 -0
  409. package/dist/resources/templates/go-backend/internal/core/cache/manager_provider.go +24 -0
  410. package/dist/resources/templates/go-backend/internal/core/cache/metrics.go +126 -0
  411. package/dist/resources/templates/go-backend/internal/core/cache/namespace.go +46 -0
  412. package/dist/resources/templates/go-backend/internal/core/cache/namespace_test.go +90 -0
  413. package/dist/resources/templates/go-backend/internal/core/cache/options.go +39 -0
  414. package/dist/resources/templates/go-backend/internal/core/cache/options_test.go +96 -0
  415. package/dist/resources/templates/go-backend/internal/core/cache/redis.go +311 -0
  416. package/dist/resources/templates/go-backend/internal/core/cache/redis_test.go +51 -0
  417. package/dist/resources/templates/go-backend/internal/core/cache/serializer.go +31 -0
  418. package/dist/resources/templates/go-backend/internal/core/cache/serializer_test.go +139 -0
  419. package/dist/resources/templates/go-backend/internal/core/cache/singleflight.go +53 -0
  420. package/dist/resources/templates/go-backend/internal/core/cache/singleflight_test.go +211 -0
  421. package/dist/resources/templates/go-backend/internal/core/cache/warming.go +86 -0
  422. package/dist/resources/templates/go-backend/internal/core/clock/clock.go +66 -0
  423. package/dist/resources/templates/go-backend/internal/core/clock/clock_test.go +26 -0
  424. package/dist/resources/templates/go-backend/internal/core/clock/runtime_clock.go +283 -0
  425. package/dist/resources/templates/go-backend/internal/core/clock/runtime_clock_test.go +98 -0
  426. package/dist/resources/templates/go-backend/internal/core/commandreplay/errors.go +6 -0
  427. package/dist/resources/templates/go-backend/internal/core/commandreplay/model.go +90 -0
  428. package/dist/resources/templates/go-backend/internal/core/commandreplay/model_test.go +79 -0
  429. package/dist/resources/templates/go-backend/internal/core/commandreplay/namespace.go +7 -0
  430. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence.go +269 -0
  431. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence_test.go +185 -0
  432. package/dist/resources/templates/go-backend/internal/core/commandreplay/provider.go +9 -0
  433. package/dist/resources/templates/go-backend/internal/core/commandreplay/repository.go +22 -0
  434. package/dist/resources/templates/go-backend/internal/core/commandreplay/service.go +853 -0
  435. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_persistence_test.go +432 -0
  436. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_test.go +891 -0
  437. package/dist/resources/templates/go-backend/internal/core/commandreplay/types.go +121 -0
  438. package/dist/resources/templates/go-backend/internal/core/config/config.go +2069 -0
  439. package/dist/resources/templates/go-backend/internal/core/config/config_test.go +1122 -0
  440. package/dist/resources/templates/go-backend/internal/core/config/contract/contract_test.go +388 -0
  441. package/dist/resources/templates/go-backend/internal/core/config/contract/docs.go +198 -0
  442. package/dist/resources/templates/go-backend/internal/core/config/contract/verify.go +424 -0
  443. package/dist/resources/templates/go-backend/internal/core/config/enum.go +46 -0
  444. package/dist/resources/templates/go-backend/internal/core/config/metadata.go +717 -0
  445. package/dist/resources/templates/go-backend/internal/core/config/metadata_test.go +228 -0
  446. package/dist/resources/templates/go-backend/internal/core/config/product_admin_config_test.go +210 -0
  447. package/dist/resources/templates/go-backend/internal/core/crypto/aes_gcm.go +111 -0
  448. package/dist/resources/templates/go-backend/internal/core/crypto/aes_gcm_test.go +364 -0
  449. package/dist/resources/templates/go-backend/internal/core/crypto/encryptor.go +12 -0
  450. package/dist/resources/templates/go-backend/internal/core/crypto/envelope_encryptor.go +134 -0
  451. package/dist/resources/templates/go-backend/internal/core/crypto/envelope_encryptor_test.go +118 -0
  452. package/dist/resources/templates/go-backend/internal/core/crypto/pem.go +112 -0
  453. package/dist/resources/templates/go-backend/internal/core/crypto/pem_test.go +44 -0
  454. package/dist/resources/templates/go-backend/internal/core/crypto/provider.go +130 -0
  455. package/dist/resources/templates/go-backend/internal/core/crypto/provider_test.go +92 -0
  456. package/dist/resources/templates/go-backend/internal/core/database/base_model.go +24 -0
  457. package/dist/resources/templates/go-backend/internal/core/database/clickhouse.go +121 -0
  458. package/dist/resources/templates/go-backend/internal/core/database/database.go +112 -0
  459. package/dist/resources/templates/go-backend/internal/core/database/database_logger_test.go +95 -0
  460. package/dist/resources/templates/go-backend/internal/core/database/errors.go +25 -0
  461. package/dist/resources/templates/go-backend/internal/core/database/metrics_plugin.go +105 -0
  462. package/dist/resources/templates/go-backend/internal/core/database/pool_collector.go +71 -0
  463. package/dist/resources/templates/go-backend/internal/core/database/read_replica.go +520 -0
  464. package/dist/resources/templates/go-backend/internal/core/database/transaction.go +170 -0
  465. package/dist/resources/templates/go-backend/internal/core/database/transaction_test.go +282 -0
  466. package/dist/resources/templates/go-backend/internal/core/distlock/lock.go +353 -0
  467. package/dist/resources/templates/go-backend/internal/core/distlock/lock_test.go +106 -0
  468. package/dist/resources/templates/go-backend/internal/core/errors/handler.go +189 -0
  469. package/dist/resources/templates/go-backend/internal/core/errors/handler_test.go +515 -0
  470. package/dist/resources/templates/go-backend/internal/core/eventbus/eventbus.go +667 -0
  471. package/dist/resources/templates/go-backend/internal/core/eventbus/eventbus_test.go +792 -0
  472. package/dist/resources/templates/go-backend/internal/core/eventstream/noop.go +22 -0
  473. package/dist/resources/templates/go-backend/internal/core/eventstream/provider.go +20 -0
  474. package/dist/resources/templates/go-backend/internal/core/eventstream/publisher.go +38 -0
  475. package/dist/resources/templates/go-backend/internal/core/eventstream/redis.go +109 -0
  476. package/dist/resources/templates/go-backend/internal/core/eventstream/routing.go +104 -0
  477. package/dist/resources/templates/go-backend/internal/core/eventstream/routing_test.go +71 -0
  478. package/dist/resources/templates/go-backend/internal/core/geoip/model.go +19 -0
  479. package/dist/resources/templates/go-backend/internal/core/geoip/service.go +88 -0
  480. package/dist/resources/templates/go-backend/internal/core/health/health.go +333 -0
  481. package/dist/resources/templates/go-backend/internal/core/health/health_test.go +27 -0
  482. package/dist/resources/templates/go-backend/internal/core/httpclient/client.go +772 -0
  483. package/dist/resources/templates/go-backend/internal/core/httpclient/client_test.go +287 -0
  484. package/dist/resources/templates/go-backend/internal/core/httpclient/download.go +578 -0
  485. package/dist/resources/templates/go-backend/internal/core/httpclient/download_test.go +374 -0
  486. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/jwk.go +38 -0
  487. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/keyring.go +144 -0
  488. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/keyring_test.go +128 -0
  489. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/registry.go +60 -0
  490. package/dist/resources/templates/go-backend/internal/core/logger/logger.go +53 -0
  491. package/dist/resources/templates/go-backend/internal/core/logger/logger_test.go +194 -0
  492. package/dist/resources/templates/go-backend/internal/core/observability/metrics.go +358 -0
  493. package/dist/resources/templates/go-backend/internal/core/observability/sentry.go +445 -0
  494. package/dist/resources/templates/go-backend/internal/core/observability/sentry_privacy_test.go +175 -0
  495. package/dist/resources/templates/go-backend/internal/core/observability/tracing.go +187 -0
  496. package/dist/resources/templates/go-backend/internal/core/outbox/model.go +314 -0
  497. package/dist/resources/templates/go-backend/internal/core/outbox/model_test.go +77 -0
  498. package/dist/resources/templates/go-backend/internal/core/outbox/persistence.go +51 -0
  499. package/dist/resources/templates/go-backend/internal/core/outbox/provider.go +23 -0
  500. package/dist/resources/templates/go-backend/internal/core/outbox/relay.go +1138 -0
  501. package/dist/resources/templates/go-backend/internal/core/outbox/relay_postgres_test.go +257 -0
  502. package/dist/resources/templates/go-backend/internal/core/outbox/relay_test.go +690 -0
  503. package/dist/resources/templates/go-backend/internal/core/outbox/rescheduler.go +164 -0
  504. package/dist/resources/templates/go-backend/internal/core/outbox/rescheduler_test.go +209 -0
  505. package/dist/resources/templates/go-backend/internal/core/outbox/writer.go +249 -0
  506. package/dist/resources/templates/go-backend/internal/core/outbox/writer_postgres_test.go +170 -0
  507. package/dist/resources/templates/go-backend/internal/core/outbox/writer_test.go +123 -0
  508. package/dist/resources/templates/go-backend/internal/core/provider.go +198 -0
  509. package/dist/resources/templates/go-backend/internal/core/queue/metrics_middleware.go +38 -0
  510. package/dist/resources/templates/go-backend/internal/core/queue/metrics_middleware_test.go +12 -0
  511. package/dist/resources/templates/go-backend/internal/core/queue/queue.go +288 -0
  512. package/dist/resources/templates/go-backend/internal/core/queue/queue_test.go +159 -0
  513. package/dist/resources/templates/go-backend/internal/core/resilience/bulkhead.go +514 -0
  514. package/dist/resources/templates/go-backend/internal/core/resilience/bulkhead_test.go +984 -0
  515. package/dist/resources/templates/go-backend/internal/core/resilience/circuitbreaker.go +358 -0
  516. package/dist/resources/templates/go-backend/internal/core/resilience/circuitbreaker_test.go +788 -0
  517. package/dist/resources/templates/go-backend/internal/core/resilience/retry.go +318 -0
  518. package/dist/resources/templates/go-backend/internal/core/resilience/retry_test.go +735 -0
  519. package/dist/resources/templates/go-backend/internal/core/schedule/recorder.go +305 -0
  520. package/dist/resources/templates/go-backend/internal/core/schedule/run_context.go +124 -0
  521. package/dist/resources/templates/go-backend/internal/core/schedule/run_context_test.go +85 -0
  522. package/dist/resources/templates/go-backend/internal/core/schedule/scheduler.go +1589 -0
  523. package/dist/resources/templates/go-backend/internal/core/schedule/scheduler_control_test.go +808 -0
  524. package/dist/resources/templates/go-backend/internal/core/storage/oss_provider.go +398 -0
  525. package/dist/resources/templates/go-backend/internal/core/storage/oss_provider_test.go +89 -0
  526. package/dist/resources/templates/go-backend/internal/core/storage/path.go +110 -0
  527. package/dist/resources/templates/go-backend/internal/core/storage/path_test.go +301 -0
  528. package/dist/resources/templates/go-backend/internal/core/storage/s3_provider.go +489 -0
  529. package/dist/resources/templates/go-backend/internal/core/storage/s3_provider_test.go +60 -0
  530. package/dist/resources/templates/go-backend/internal/core/storage/storage.go +268 -0
  531. package/dist/resources/templates/go-backend/internal/core/storage/validation.go +225 -0
  532. package/dist/resources/templates/go-backend/internal/core/storage/validation_test.go +495 -0
  533. package/dist/resources/templates/go-backend/internal/core/temporal/logger.go +38 -0
  534. package/dist/resources/templates/go-backend/internal/core/temporal/provider.go +126 -0
  535. package/dist/resources/templates/go-backend/internal/core/temporal/provider_test.go +67 -0
  536. package/dist/resources/templates/go-backend/internal/core/websocket/client.go +282 -0
  537. package/dist/resources/templates/go-backend/internal/core/websocket/client_test.go +81 -0
  538. package/dist/resources/templates/go-backend/internal/core/websocket/config.go +58 -0
  539. package/dist/resources/templates/go-backend/internal/core/websocket/handler.go +53 -0
  540. package/dist/resources/templates/go-backend/internal/core/websocket/hub.go +269 -0
  541. package/dist/resources/templates/go-backend/internal/core/websocket/message.go +273 -0
  542. package/dist/resources/templates/go-backend/internal/core/websocket/notifier.go +92 -0
  543. package/dist/resources/templates/go-backend/internal/core/websocket/provider.go +33 -0
  544. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/action.go +77 -0
  545. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/action_test.go +238 -0
  546. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/condition.go +107 -0
  547. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/decision.go +256 -0
  548. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/enum.go +643 -0
  549. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/errors.go +102 -0
  550. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/principal_record.go +21 -0
  551. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/product_context.go +486 -0
  552. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/resource.go +215 -0
  553. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/resource_test.go +242 -0
  554. package/dist/resources/templates/go-backend/internal/products/README.md +5 -0
  555. package/dist/resources/templates/go-backend/internal/sharedevents/README.md +5 -0
  556. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.down.sql +13 -0
  557. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.up.sql +886 -0
  558. package/dist/resources/templates/go-backend/pkg/apierror/error.go +327 -0
  559. package/dist/resources/templates/go-backend/pkg/apierror/error_code.go +65 -0
  560. package/dist/resources/templates/go-backend/pkg/apierror/error_test.go +41 -0
  561. package/dist/resources/templates/go-backend/pkg/apierror/error_type.go +14 -0
  562. package/dist/resources/templates/go-backend/pkg/apierror/http_response.go +216 -0
  563. package/dist/resources/templates/go-backend/pkg/apierror/http_response_test.go +216 -0
  564. package/dist/resources/templates/go-backend/scripts/check-openapi-clean-deletion.sh +54 -0
  565. package/dist/resources/templates/go-backend/scripts/test-migrations.sh +32 -0
  566. package/dist/resources/templates/go-backend/tests/infrastructure.sql +69 -0
  567. package/dist/resources/templates/mcp-server/.claude/docs/stack.md +338 -0
  568. package/dist/resources/templates/mcp-server/.editorconfig +13 -0
  569. package/dist/resources/templates/mcp-server/.env.example +8 -0
  570. package/dist/resources/templates/mcp-server/.github/ISSUE_TEMPLATE/bug_report.yml +104 -0
  571. package/dist/resources/templates/mcp-server/.github/ISSUE_TEMPLATE/feature_request.yml +54 -0
  572. package/dist/resources/templates/mcp-server/.github/PULL_REQUEST_TEMPLATE.md +36 -0
  573. package/dist/resources/templates/mcp-server/.github/workflows/ci.yml +78 -0
  574. package/dist/resources/templates/mcp-server/.husky/commit-msg +1 -0
  575. package/dist/resources/templates/mcp-server/.husky/pre-commit +1 -0
  576. package/dist/resources/templates/mcp-server/.lintstagedrc.json +4 -0
  577. package/dist/resources/templates/mcp-server/.nvmrc +1 -0
  578. package/dist/resources/templates/mcp-server/.prettierrc +8 -0
  579. package/dist/resources/templates/mcp-server/CONTRIBUTING.md +184 -0
  580. package/dist/resources/templates/mcp-server/Dockerfile +23 -0
  581. package/dist/resources/templates/mcp-server/LICENSE +21 -0
  582. package/dist/resources/templates/mcp-server/README.md +123 -0
  583. package/dist/resources/templates/mcp-server/_gitignore +26 -0
  584. package/dist/resources/templates/mcp-server/commitlint.config.js +24 -0
  585. package/dist/resources/templates/mcp-server/eslint.config.js +30 -0
  586. package/dist/resources/templates/mcp-server/package.json +72 -0
  587. package/dist/resources/templates/mcp-server/src/index.ts +27 -0
  588. package/dist/resources/templates/mcp-server/src/resources.ts +33 -0
  589. package/dist/resources/templates/mcp-server/src/services/.gitkeep +0 -0
  590. package/dist/resources/templates/mcp-server/src/tools.ts +43 -0
  591. package/dist/resources/templates/mcp-server/src/types/index.ts +19 -0
  592. package/dist/resources/templates/mcp-server/src/utils/index.ts +31 -0
  593. package/dist/resources/templates/mcp-server/tsconfig.json +22 -0
  594. package/dist/resources/templates/mcp-server/tsup.config.ts +14 -0
  595. package/dist/resources/templates/mcp-server/vitest.config.ts +15 -0
  596. package/dist/resources/templates/nuxt/.claude/docs/stack.md +666 -0
  597. package/dist/resources/templates/nuxt/.editorconfig +15 -0
  598. package/dist/resources/templates/nuxt/.env.example +22 -0
  599. package/dist/resources/templates/nuxt/.github/workflows/ci.yml +34 -0
  600. package/dist/resources/templates/nuxt/.husky/commit-msg +1 -0
  601. package/dist/resources/templates/nuxt/.husky/pre-commit +1 -0
  602. package/dist/resources/templates/nuxt/.nvmrc +1 -0
  603. package/dist/resources/templates/nuxt/.prettierignore +9 -0
  604. package/dist/resources/templates/nuxt/.prettierrc +1 -0
  605. package/dist/resources/templates/nuxt/README.md +23 -0
  606. package/dist/resources/templates/nuxt/_gitignore +7 -0
  607. package/dist/resources/templates/nuxt/app/app.config.ts +105 -0
  608. package/dist/resources/templates/nuxt/app/i18n/locales/en.json +5 -0
  609. package/dist/resources/templates/nuxt/app/pages/app/index.vue +224 -0
  610. package/dist/resources/templates/nuxt/app/pages/authors/[slug].vue +37 -0
  611. package/dist/resources/templates/nuxt/app/pages/blog/[slug].vue +55 -0
  612. package/dist/resources/templates/nuxt/app/pages/blog/category/[category].vue +38 -0
  613. package/dist/resources/templates/nuxt/app/pages/blog/index.vue +46 -0
  614. package/dist/resources/templates/nuxt/app/pages/index.vue +626 -0
  615. package/dist/resources/templates/nuxt/app/pages/legal/privacy.vue +21 -0
  616. package/dist/resources/templates/nuxt/app/pages/legal/terms.vue +21 -0
  617. package/dist/resources/templates/nuxt/app/pages/ui/index.vue +397 -0
  618. package/dist/resources/templates/nuxt/commitlint.config.mjs +1 -0
  619. package/dist/resources/templates/nuxt/eslint.config.mjs +4 -0
  620. package/dist/resources/templates/nuxt/lint-staged.config.mjs +8 -0
  621. package/dist/resources/templates/nuxt/nuxt.config.ts +71 -0
  622. package/dist/resources/templates/nuxt/package.json +67 -0
  623. package/dist/resources/templates/nuxt/public/apple-touch-icon.png +0 -0
  624. package/dist/resources/templates/nuxt/public/favicon.ico +0 -0
  625. package/dist/resources/templates/nuxt/public/favicon.svg +5 -0
  626. package/dist/resources/templates/nuxt/public/icon-16x16.png +0 -0
  627. package/dist/resources/templates/nuxt/public/icon-192x192.png +0 -0
  628. package/dist/resources/templates/nuxt/public/icon-32x32.png +0 -0
  629. package/dist/resources/templates/nuxt/public/icon-512x512.png +0 -0
  630. package/dist/resources/templates/nuxt/public/logo.svg +6 -0
  631. package/dist/resources/templates/nuxt/public/media/hero-product-gallery.png +0 -0
  632. package/dist/resources/templates/nuxt/server/api/content/sitemap-urls.get.ts +12 -0
  633. package/dist/resources/templates/nuxt/server/api/preview.get.ts +9 -0
  634. package/dist/resources/templates/nuxt/stylelint.config.mjs +3 -0
  635. package/dist/resources/templates/nuxt/test/e2e/rendering.test.ts +13 -0
  636. package/dist/resources/templates/nuxt/tsconfig.json +3 -0
  637. package/dist/resources/templates/nuxt/vitest.config.ts +8 -0
  638. package/package.json +16 -12
@@ -0,0 +1,1007 @@
1
+ package audit
2
+
3
+ import (
4
+ "context"
5
+ "strconv"
6
+ "strings"
7
+ "time"
8
+
9
+ "gorm.io/gorm"
10
+ "gorm.io/gorm/clause"
11
+
12
+ "{{module}}/internal/common/iamscope"
13
+ "{{module}}/internal/core/database"
14
+ )
15
+
16
+ // batchInsertSize 单次批量插入条数,平衡内存与写入效率
17
+ const batchInsertSize = 100
18
+
19
+ var iamPrincipalResourceTypes = []string{
20
+ string(AuditResourceIAMPrincipal),
21
+ string(AuditResourceIAMServiceAccount),
22
+ }
23
+
24
+ func normalizeKeyword(keyword string) (string, string, bool) {
25
+ kw := strings.TrimSpace(keyword)
26
+ if kw == "" {
27
+ return "", "", false
28
+ }
29
+ return kw, "%" + strings.ToLower(kw) + "%", true
30
+ }
31
+
32
+ func keywordTextCondition(column string) string {
33
+ return "LOWER(" + column + ") LIKE ?"
34
+ }
35
+
36
+ type auditScopeBinding struct {
37
+ productColumn string
38
+ tenantColumn string
39
+ selfColumn string
40
+ }
41
+
42
+ func (b auditScopeBinding) Column(dim iamscope.ScopeDimension) (string, bool) {
43
+ switch dim {
44
+ case iamscope.ScopeDimensionProduct:
45
+ return b.productColumn, b.productColumn != ""
46
+ case iamscope.ScopeDimensionTenant:
47
+ return b.tenantColumn, b.tenantColumn != ""
48
+ case iamscope.ScopeDimensionAssignedToSelf, iamscope.ScopeDimensionCreatedBySelf:
49
+ return b.selfColumn, b.selfColumn != ""
50
+ default:
51
+ return "", false
52
+ }
53
+ }
54
+
55
+ var (
56
+ auditLogScopeBinding = auditScopeBinding{
57
+ productColumn: "product_code",
58
+ tenantColumn: "tenant_id",
59
+ selfColumn: "iam_principal_id",
60
+ }
61
+ operationLogScopeBinding = auditScopeBinding{
62
+ productColumn: "product_code",
63
+ tenantColumn: "tenant_id",
64
+ selfColumn: "iam_principal_id",
65
+ }
66
+ accessLogScopeBinding = auditScopeBinding{
67
+ productColumn: "product_code",
68
+ selfColumn: "iam_principal_id",
69
+ }
70
+ loginLogScopeBinding = auditScopeBinding{
71
+ productColumn: "product_code",
72
+ }
73
+ )
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // AccessLogRepository — 访问日志仓储
77
+ // ---------------------------------------------------------------------------
78
+
79
+ // AccessLogRepository 访问日志仓储接口(append-only,严禁 Update/Delete)
80
+ type AccessLogRepository interface {
81
+ // BatchCreate 批量插入访问日志
82
+ BatchCreate(ctx context.Context, logs []*AccessLog) error
83
+ // FindByQuery 分页查询访问日志
84
+ FindByQuery(ctx context.Context, query *AdminListAccessLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*AccessLog, int64, error)
85
+ // FindByID 根据 ID 查询单条访问日志
86
+ FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*AccessLog, error)
87
+ }
88
+
89
+ // accessLogRepository GORM 实现
90
+ type accessLogRepository struct {
91
+ db *gorm.DB
92
+ actorSearch ActorReferenceSearcher
93
+ }
94
+
95
+ // NewAccessLogRepository 构建访问日志仓储实例
96
+ func NewAccessLogRepository(db *gorm.DB, actorSearch ActorReferenceSearcher) AccessLogRepository {
97
+ return &accessLogRepository{db: db, actorSearch: actorSearch}
98
+ }
99
+
100
+ // BatchCreate 批量插入,使用 CreateInBatches 优化大批量写入
101
+ func (r *accessLogRepository) BatchCreate(ctx context.Context, logs []*AccessLog) error {
102
+ return database.GetDB(ctx, r.db).
103
+ Table((AccessLog{}).TableName()).
104
+ CreateInBatches(accessLogCreateMaps(logs), batchInsertSize).Error
105
+ }
106
+
107
+ func accessLogCreateMaps(logs []*AccessLog) []map[string]any {
108
+ // GORM 批量插入的列值 map 需要承载 string、int、bool、指针等不同列类型。
109
+ items := make([]map[string]any, 0, len(logs))
110
+ for _, log := range logs {
111
+ items = append(items, map[string]any{
112
+ "trace_id": log.TraceID,
113
+ "request_id": log.RequestID,
114
+ "actor_type": log.ActorType,
115
+ "actor_id": log.ActorID,
116
+ "auth_subject_type": log.AuthSubjectType,
117
+ "iam_principal_id": log.IAMPrincipalID,
118
+ "credential_public_id": log.CredentialPublicID,
119
+ "token_fingerprint": log.TokenFingerprint,
120
+ "auth_mechanism": log.AuthMechanism,
121
+ "actor_ip": log.ActorIP,
122
+ "actor_user_agent": log.ActorUserAgent,
123
+ "http_method": log.HTTPMethod,
124
+ "http_path": log.HTTPPath,
125
+ "http_status": log.HTTPStatus,
126
+ "product_code": log.ProductCode,
127
+ "resource_type": log.ResourceType,
128
+ "resource_id": log.ResourceID,
129
+ "is_success": log.IsSuccess,
130
+ "error_message": log.ErrorMessage,
131
+ "duration_ms": log.DurationMS,
132
+ })
133
+ }
134
+ return items
135
+ }
136
+
137
+ // FindByQuery 分页查询,明确指定查询字段(禁止全列查询)
138
+ func (r *accessLogRepository) FindByQuery(ctx context.Context, query *AdminListAccessLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*AccessLog, int64, error) {
139
+ db := database.GetDB(ctx, r.db).Model(&AccessLog{}).
140
+ Select(
141
+ "id", "trace_id", "request_id",
142
+ "actor_type", "actor_id", "actor_ip",
143
+ "auth_subject_type", "iam_principal_id", "credential_public_id", "token_fingerprint", "auth_mechanism",
144
+ "http_method", "http_path", "http_status",
145
+ "product_code", "resource_type", "resource_id",
146
+ "is_success", "error_message", "duration_ms",
147
+ "created_at",
148
+ )
149
+ var err error
150
+ db, err = iamscope.ApplyScope(db, scope, accessLogScopeBinding, principalID)
151
+ if err != nil {
152
+ return nil, 0, err
153
+ }
154
+
155
+ // 可选过滤条件
156
+ if query.PrincipalID != nil {
157
+ db = db.Where("iam_principal_id = ?", *query.PrincipalID)
158
+ }
159
+ if query.ActorID != nil {
160
+ db = db.Where("actor_id = ?", *query.ActorID)
161
+ }
162
+ if query.HTTPMethod != nil && *query.HTTPMethod != "" {
163
+ db = db.Where("http_method = ?", *query.HTTPMethod)
164
+ }
165
+ if query.HTTPStatus != nil {
166
+ db = db.Where("http_status = ?", *query.HTTPStatus)
167
+ }
168
+ if query.ProductCode != nil && *query.ProductCode != "" {
169
+ db = db.Where("product_code = ?", *query.ProductCode)
170
+ }
171
+ if query.ResourceType != nil && *query.ResourceType != "" {
172
+ db = db.Where("resource_type = ?", *query.ResourceType)
173
+ }
174
+ if query.ResourceID != nil && *query.ResourceID != "" {
175
+ db = db.Where("resource_id = ?", *query.ResourceID)
176
+ }
177
+ if query.IsSuccess != nil {
178
+ db = db.Where("is_success = ?", *query.IsSuccess)
179
+ }
180
+ if query.DateFrom != nil {
181
+ db = db.Where("created_at >= ?", *query.DateFrom)
182
+ }
183
+ if query.DateTo != nil {
184
+ db = db.Where("created_at <= ?", *query.DateTo)
185
+ }
186
+ if query.PathKeyword != nil {
187
+ if _, like, ok := normalizeKeyword(*query.PathKeyword); ok {
188
+ db = db.Where(keywordTextCondition("http_path"), like)
189
+ }
190
+ }
191
+ if query.RequestID != nil && *query.RequestID != "" {
192
+ db = db.Where("request_id = ?", *query.RequestID)
193
+ }
194
+ if query.TraceID != nil && *query.TraceID != "" {
195
+ db = db.Where("trace_id = ?", *query.TraceID)
196
+ }
197
+ if query.Keyword != nil {
198
+ if kw, like, ok := normalizeKeyword(*query.Keyword); ok {
199
+ matches, err := r.actorSearch.Search(ctx, kw)
200
+ if err != nil {
201
+ return nil, 0, err
202
+ }
203
+ actorSubQuery := matches.ActorIDs
204
+ principalSubQuery := matches.PrincipalIDs
205
+ principalTextSubQuery := make([]string, len(matches.PrincipalIDs))
206
+ for i, id := range matches.PrincipalIDs {
207
+ principalTextSubQuery[i] = strconv.FormatInt(id, 10)
208
+ }
209
+ conditions := r.db.
210
+ Where(keywordTextCondition("http_path"), like).
211
+ Or(keywordTextCondition("resource_type"), like).
212
+ Or(keywordTextCondition("resource_id"), like).
213
+ Or(keywordTextCondition("trace_id"), like).
214
+ Or(keywordTextCondition("request_id"), like).
215
+ Or(keywordTextCondition("actor_ip"), like).
216
+ Or("actor_id IN (?)", actorSubQuery).
217
+ Or("iam_principal_id IN (?)", principalSubQuery).
218
+ Or("resource_type IN ? AND resource_id IN (?)", iamPrincipalResourceTypes, principalTextSubQuery)
219
+ if parsedID, parseErr := strconv.ParseInt(kw, 10, 64); parseErr == nil {
220
+ conditions = conditions.
221
+ Or("actor_id = ?", parsedID).
222
+ Or("iam_principal_id = ?", parsedID).
223
+ Or("resource_type IN ? AND resource_id = ?", iamPrincipalResourceTypes, strconv.FormatInt(parsedID, 10))
224
+ }
225
+ db = db.Where(conditions)
226
+ }
227
+ }
228
+
229
+ var total int64
230
+ if err := db.Count(&total).Error; err != nil {
231
+ return nil, 0, err
232
+ }
233
+
234
+ var logs []*AccessLog
235
+ if err := db.Order("created_at DESC").
236
+ Order("id DESC").
237
+ Offset(query.Offset()).
238
+ Limit(query.PageSize).
239
+ Find(&logs).Error; err != nil {
240
+ return nil, 0, err
241
+ }
242
+ return logs, total, nil
243
+ }
244
+
245
+ // FindByID 查询单条访问日志(包含 actor_user_agent 等完整字段)
246
+ func (r *accessLogRepository) FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*AccessLog, error) {
247
+ db := database.GetDB(ctx, r.db).Model(&AccessLog{}).
248
+ Select(
249
+ "id", "trace_id", "request_id",
250
+ "actor_type", "actor_id", "actor_ip", "actor_user_agent",
251
+ "auth_subject_type", "iam_principal_id", "credential_public_id", "token_fingerprint", "auth_mechanism",
252
+ "http_method", "http_path", "http_status",
253
+ "product_code", "resource_type", "resource_id",
254
+ "is_success", "error_message", "duration_ms",
255
+ "created_at",
256
+ )
257
+ var err error
258
+ db, err = iamscope.ApplyScope(db, scope, accessLogScopeBinding, principalID)
259
+ if err != nil {
260
+ return nil, err
261
+ }
262
+
263
+ var log AccessLog
264
+ if err := db.First(&log, "id = ?", id).Error; err != nil {
265
+ return nil, err
266
+ }
267
+ return &log, nil
268
+ }
269
+
270
+ // ---------------------------------------------------------------------------
271
+ // LoginLogRepository — 登录日志仓储
272
+ // ---------------------------------------------------------------------------
273
+
274
+ // LoginLogRepository 登录日志仓储接口(append-only,严禁 Update/Delete)
275
+ type LoginLogRepository interface {
276
+ // BatchCreate 批量插入登录日志
277
+ BatchCreate(ctx context.Context, logs []*LoginLog) error
278
+ // FindByQuery 分页查询登录日志
279
+ FindByQuery(ctx context.Context, query *AdminListLoginLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*LoginLog, int64, error)
280
+ // FindByID 根据 ID 查询单条登录日志
281
+ FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*LoginLog, error)
282
+ // CountFailedByIP 统计指定 IP 在指定时间之后的失败次数(用于安全策略)
283
+ CountFailedByIP(ctx context.Context, ip string, since time.Time) (int64, error)
284
+ // FindByIdentity 查询指定身份的最近登录记录
285
+ FindByIdentity(ctx context.Context, identityID int64, limit int) ([]*LoginLog, error)
286
+ }
287
+
288
+ // loginLogRepository GORM 实现
289
+ type loginLogRepository struct {
290
+ db *gorm.DB
291
+ actorSearch ActorReferenceSearcher
292
+ }
293
+
294
+ // NewLoginLogRepository 构建登录日志仓储实例
295
+ func NewLoginLogRepository(db *gorm.DB, actorSearch ActorReferenceSearcher) LoginLogRepository {
296
+ return &loginLogRepository{db: db, actorSearch: actorSearch}
297
+ }
298
+
299
+ // BatchCreate 批量插入,使用 CreateInBatches 优化大批量写入
300
+ func (r *loginLogRepository) BatchCreate(ctx context.Context, logs []*LoginLog) error {
301
+ return database.GetDB(ctx, r.db).
302
+ Table((LoginLog{}).TableName()).
303
+ CreateInBatches(loginLogCreateMaps(logs), batchInsertSize).Error
304
+ }
305
+
306
+ func loginLogCreateMaps(logs []*LoginLog) []map[string]any {
307
+ // GORM 批量插入的列值 map 需要承载 string、int、bool、指针等不同列类型。
308
+ items := make([]map[string]any, 0, len(logs))
309
+ for _, log := range logs {
310
+ items = append(items, map[string]any{
311
+ "identity_id": log.IdentityID,
312
+ "actor_type": log.ActorType,
313
+ "actor_ip": log.ActorIP,
314
+ "actor_user_agent": log.ActorUserAgent,
315
+ "login_action": log.LoginAction,
316
+ "login_method": log.LoginMethod,
317
+ "is_success": log.IsSuccess,
318
+ "failure_reason": log.FailureReason,
319
+ "product_code": log.ProductCode,
320
+ "session_id": log.SessionID,
321
+ "request_id": log.RequestID,
322
+ "device_type": log.DeviceType,
323
+ "device_fingerprint": log.DeviceFingerprint,
324
+ "os_name": log.OSName,
325
+ "browser_name": log.BrowserName,
326
+ "geo_country": log.GeoCountry,
327
+ "geo_city": log.GeoCity,
328
+ })
329
+ }
330
+ return items
331
+ }
332
+
333
+ // FindByQuery 分页查询,列表不返回 actor_user_agent(减少传输量)
334
+ func (r *loginLogRepository) FindByQuery(ctx context.Context, query *AdminListLoginLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*LoginLog, int64, error) {
335
+ db := database.GetDB(ctx, r.db).Model(&LoginLog{}).
336
+ Select(
337
+ "id", "identity_id", "actor_type", "actor_ip",
338
+ "login_action", "login_method",
339
+ "is_success", "failure_reason",
340
+ "product_code", "session_id", "request_id",
341
+ "device_type", "os_name", "browser_name",
342
+ "geo_country", "geo_city",
343
+ "created_at",
344
+ )
345
+ var err error
346
+ db, err = iamscope.ApplyScope(db, scope, loginLogScopeBinding, principalID)
347
+ if err != nil {
348
+ return nil, 0, err
349
+ }
350
+
351
+ // 可选过滤条件
352
+ if query.ActorIP != nil && *query.ActorIP != "" {
353
+ db = db.Where("actor_ip = ?", *query.ActorIP)
354
+ }
355
+ if query.LoginAction != nil && *query.LoginAction != "" {
356
+ db = db.Where("login_action = ?", *query.LoginAction)
357
+ }
358
+ if query.LoginMethod != nil && *query.LoginMethod != "" {
359
+ db = db.Where("login_method = ?", *query.LoginMethod)
360
+ }
361
+ if query.IsSuccess != nil {
362
+ db = db.Where("is_success = ?", *query.IsSuccess)
363
+ }
364
+ if query.ProductCode != nil && *query.ProductCode != "" {
365
+ db = db.Where("product_code = ?", *query.ProductCode)
366
+ }
367
+ if query.DateFrom != nil {
368
+ db = db.Where("created_at >= ?", *query.DateFrom)
369
+ }
370
+ if query.DateTo != nil {
371
+ db = db.Where("created_at <= ?", *query.DateTo)
372
+ }
373
+ if query.RequestID != nil && *query.RequestID != "" {
374
+ db = db.Where("request_id = ?", *query.RequestID)
375
+ }
376
+ if query.Keyword != nil {
377
+ if kw, like, ok := normalizeKeyword(*query.Keyword); ok {
378
+ matches, err := r.actorSearch.Search(ctx, kw)
379
+ if err != nil {
380
+ return nil, 0, err
381
+ }
382
+ identitySubQuery := matches.IdentityIDs
383
+ conditions := r.db.
384
+ Where(keywordTextCondition("actor_ip"), like).
385
+ Or(keywordTextCondition("session_id"), like).
386
+ Or(keywordTextCondition("request_id"), like).
387
+ Or(keywordTextCondition("device_type"), like).
388
+ Or(keywordTextCondition("os_name"), like).
389
+ Or(keywordTextCondition("browser_name"), like).
390
+ Or(keywordTextCondition("geo_country"), like).
391
+ Or(keywordTextCondition("geo_city"), like).
392
+ Or("identity_id IN (?)", identitySubQuery)
393
+ db = db.Where(conditions)
394
+ }
395
+ }
396
+
397
+ var total int64
398
+ if err := db.Count(&total).Error; err != nil {
399
+ return nil, 0, err
400
+ }
401
+
402
+ var logs []*LoginLog
403
+ if err := db.Order("created_at DESC").
404
+ Offset(query.Offset()).
405
+ Limit(query.PageSize).
406
+ Find(&logs).Error; err != nil {
407
+ return nil, 0, err
408
+ }
409
+ return logs, total, nil
410
+ }
411
+
412
+ // FindByID 查询单条登录日志(包含设备指纹和 User-Agent)
413
+ func (r *loginLogRepository) FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*LoginLog, error) {
414
+ db := database.GetDB(ctx, r.db).Model(&LoginLog{}).
415
+ Select(
416
+ "id", "identity_id", "actor_type", "actor_ip", "actor_user_agent",
417
+ "login_action", "login_method",
418
+ "is_success", "failure_reason",
419
+ "product_code", "session_id", "request_id",
420
+ "device_type", "device_fingerprint", "os_name", "browser_name",
421
+ "geo_country", "geo_city",
422
+ "created_at",
423
+ )
424
+ var err error
425
+ db, err = iamscope.ApplyScope(db, scope, loginLogScopeBinding, principalID)
426
+ if err != nil {
427
+ return nil, err
428
+ }
429
+
430
+ var log LoginLog
431
+ if err := db.First(&log, "id = ?", id).Error; err != nil {
432
+ return nil, err
433
+ }
434
+ return &log, nil
435
+ }
436
+
437
+ // CountFailedByIP 统计指定 IP 在 since 之后的登录失败次数
438
+ // 用于安全策略判断是否需要触发 IP 封禁或验证码
439
+ func (r *loginLogRepository) CountFailedByIP(ctx context.Context, ip string, since time.Time) (int64, error) {
440
+ var count int64
441
+ err := database.GetDB(ctx, r.db).Model(&LoginLog{}).
442
+ Where("actor_ip = ? AND is_success = false AND created_at >= ?", ip, since).
443
+ Count(&count).Error
444
+ return count, err
445
+ }
446
+
447
+ // FindByIdentity 查询指定身份的最近登录记录(用于用户安全页面展示)
448
+ func (r *loginLogRepository) FindByIdentity(ctx context.Context, identityID int64, limit int) ([]*LoginLog, error) {
449
+ var logs []*LoginLog
450
+ err := database.GetDB(ctx, r.db).Model(&LoginLog{}).
451
+ Select(
452
+ "id", "identity_id", "actor_type", "actor_ip",
453
+ "login_action", "login_method",
454
+ "is_success", "failure_reason",
455
+ "product_code", "session_id", "request_id",
456
+ "device_type", "os_name", "browser_name",
457
+ "geo_country", "geo_city",
458
+ "created_at",
459
+ ).
460
+ Where("identity_id = ?", identityID).
461
+ Order("created_at DESC").
462
+ Limit(limit).
463
+ Find(&logs).Error
464
+ return logs, err
465
+ }
466
+
467
+ // ---------------------------------------------------------------------------
468
+ // AuditRepository — 审计日志仓储
469
+ // ---------------------------------------------------------------------------
470
+
471
+ // AuditRepository 审计日志仓储接口(append-only,严禁 Update/Delete)
472
+ type AuditRepository interface {
473
+ // BatchCreate 批量插入审计记录
474
+ BatchCreate(ctx context.Context, logs []*AuditLog) error
475
+ // ClaimEventAudit 领取领域事件审计处理权;重复事件返回 false
476
+ ClaimEventAudit(ctx context.Context, inbox *AuditEventInbox) (bool, error)
477
+ // FindByQuery 分页查询审计日志
478
+ FindByQuery(ctx context.Context, query *AdminListAuditLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*AuditLog, int64, error)
479
+ // FindByID 根据 ID 查询单条审计日志(含完整字段)
480
+ FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*AuditLog, error)
481
+ // CountByCategory 按分类统计(用于统计接口)
482
+ CountByCategory(ctx context.Context, from, to time.Time, scope iamscope.ScopeConstraint, principalID int64) ([]CategoryCount, error)
483
+ // CountByRiskLevel 按风险级别统计
484
+ CountByRiskLevel(ctx context.Context, from, to time.Time, scope iamscope.ScopeConstraint, principalID int64) ([]RiskLevelCount, error)
485
+ }
486
+
487
+ // CategoryCount 分类统计结果
488
+ type CategoryCount struct {
489
+ // 分类
490
+ Category AuditCategory `json:"category"`
491
+ // 数量
492
+ Count int64 `json:"count"`
493
+ }
494
+
495
+ // RiskLevelCount 风险级别统计结果
496
+ type RiskLevelCount struct {
497
+ // 风险级别
498
+ RiskLevel RiskLevel `json:"risk_level"`
499
+ // 数量
500
+ Count int64 `json:"count"`
501
+ }
502
+
503
+ // auditRepository GORM 实现
504
+ type auditRepository struct {
505
+ db *gorm.DB
506
+ actorSearch ActorReferenceSearcher
507
+ }
508
+
509
+ // NewAuditRepository 构建审计日志仓储实例
510
+ func NewAuditRepository(db *gorm.DB, actorSearch ActorReferenceSearcher) AuditRepository {
511
+ return &auditRepository{db: db, actorSearch: actorSearch}
512
+ }
513
+
514
+ // BatchCreate 批量插入,使用 CreateInBatches 优化大批量写入
515
+ func (r *auditRepository) BatchCreate(ctx context.Context, logs []*AuditLog) error {
516
+ return database.GetDB(ctx, r.db).
517
+ Table((AuditLog{}).TableName()).
518
+ CreateInBatches(auditLogCreateMaps(logs), batchInsertSize).Error
519
+ }
520
+
521
+ // ClaimEventAudit 通过 inbox 主键原子领取领域事件审计处理权。
522
+ func (r *auditRepository) ClaimEventAudit(ctx context.Context, inbox *AuditEventInbox) (bool, error) {
523
+ claim := database.GetDB(ctx, r.db).
524
+ Clauses(clause.OnConflict{DoNothing: true}).
525
+ Create(inbox)
526
+ if claim.Error != nil {
527
+ return false, claim.Error
528
+ }
529
+ return claim.RowsAffected == 1, nil
530
+ }
531
+
532
+ func auditLogCreateMaps(logs []*AuditLog) []map[string]any {
533
+ // GORM 批量插入的列值 map 需要承载 string、int、bool、JSON 等不同列类型。
534
+ items := make([]map[string]any, 0, len(logs))
535
+ for _, log := range logs {
536
+ items = append(items, map[string]any{
537
+ "event_id": log.EventID,
538
+ "occurred_at": log.OccurredAt,
539
+ "trace_id": log.TraceID,
540
+ "request_id": log.RequestID,
541
+ "actor_type": log.ActorType,
542
+ "actor_id": log.ActorID,
543
+ "auth_subject_type": log.AuthSubjectType,
544
+ "iam_principal_id": log.IAMPrincipalID,
545
+ "credential_public_id": log.CredentialPublicID,
546
+ "token_fingerprint": log.TokenFingerprint,
547
+ "auth_mechanism": log.AuthMechanism,
548
+ "actor_ip": log.ActorIP,
549
+ "actor_user_agent": log.ActorUserAgent,
550
+ "action": log.Action,
551
+ "category": log.Category,
552
+ "risk_level": log.RiskLevel,
553
+ "resource_type": log.ResourceType,
554
+ "resource_id": log.ResourceID,
555
+ "product_code": log.ProductCode,
556
+ "tenant_id": log.TenantID,
557
+ "changes": log.Changes,
558
+ "metadata": log.Metadata,
559
+ "is_success": log.IsSuccess,
560
+ "error_message": log.ErrorMessage,
561
+ })
562
+ }
563
+ return items
564
+ }
565
+
566
+ // FindByQuery 分页查询,默认轻量列;详情型列表可显式请求 changes/metadata。
567
+ func (r *auditRepository) FindByQuery(ctx context.Context, query *AdminListAuditLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*AuditLog, int64, error) {
568
+ selectedColumns := []string{
569
+ "id", "event_id", "occurred_at", "trace_id", "request_id",
570
+ "actor_type", "actor_id", "actor_ip",
571
+ "auth_subject_type", "iam_principal_id", "credential_public_id", "token_fingerprint", "auth_mechanism",
572
+ "action", "category", "risk_level",
573
+ "resource_type", "resource_id",
574
+ "product_code", "tenant_id",
575
+ "is_success", "error_message",
576
+ "created_at",
577
+ }
578
+ if query.IncludePayload != nil && *query.IncludePayload {
579
+ selectedColumns = append(selectedColumns, "actor_user_agent", "changes", "metadata")
580
+ }
581
+ db := database.GetDB(ctx, r.db).Model(&AuditLog{}).
582
+ Select(
583
+ selectedColumns,
584
+ )
585
+ var err error
586
+ db, err = iamscope.ApplyScope(db, scope, auditLogScopeBinding, principalID)
587
+ if err != nil {
588
+ return nil, 0, err
589
+ }
590
+
591
+ // 可选过滤条件
592
+ if query.PrincipalID != nil {
593
+ db = db.Where("iam_principal_id = ?", *query.PrincipalID)
594
+ }
595
+ if query.ActorID != nil {
596
+ db = db.Where("actor_id = ?", *query.ActorID)
597
+ }
598
+ if query.ActorType != nil && *query.ActorType != "" {
599
+ db = db.Where("actor_type = ?", *query.ActorType)
600
+ }
601
+ if query.Action != nil && *query.Action != "" {
602
+ db = db.Where("action = ?", *query.Action)
603
+ }
604
+ if query.Category != nil && *query.Category != "" {
605
+ db = db.Where("category = ?", *query.Category)
606
+ }
607
+ if query.RiskLevel != nil && *query.RiskLevel != "" {
608
+ db = db.Where("risk_level = ?", *query.RiskLevel)
609
+ }
610
+ if query.ResourceType != nil && *query.ResourceType != "" &&
611
+ query.ResourceID != nil && *query.ResourceID != "" &&
612
+ query.IncludeRelated != nil && *query.IncludeRelated {
613
+ if *query.ResourceType == string(AuditResourceIAMPrincipal) || *query.ResourceType == string(AuditResourceIAMServiceAccount) {
614
+ db = db.Where(
615
+ strings.Join([]string{
616
+ "(resource_type = ? AND resource_id = ?)",
617
+ "(metadata ->> 'related_resource_type' = ? AND metadata ->> 'related_resource_id' = ?)",
618
+ "(changes #>> '{after,principal_id}' = ? OR changes #>> '{before,principal_id}' = ?)",
619
+ }, " OR "),
620
+ *query.ResourceType,
621
+ *query.ResourceID,
622
+ *query.ResourceType,
623
+ *query.ResourceID,
624
+ *query.ResourceID,
625
+ *query.ResourceID,
626
+ )
627
+ } else {
628
+ db = db.Where(
629
+ strings.Join([]string{
630
+ "(resource_type = ? AND resource_id = ?)",
631
+ "(metadata ->> 'related_resource_type' = ? AND metadata ->> 'related_resource_id' = ?)",
632
+ }, " OR "),
633
+ *query.ResourceType,
634
+ *query.ResourceID,
635
+ *query.ResourceType,
636
+ *query.ResourceID,
637
+ )
638
+ }
639
+ } else {
640
+ if query.ResourceType != nil && *query.ResourceType != "" {
641
+ db = db.Where("resource_type = ?", *query.ResourceType)
642
+ } else if resourceTypes := query.ResourceTypeValues(); len(resourceTypes) > 0 {
643
+ db = db.Where("resource_type IN ?", resourceTypes)
644
+ }
645
+ if query.ResourceID != nil && *query.ResourceID != "" {
646
+ db = db.Where("resource_id = ?", *query.ResourceID)
647
+ }
648
+ }
649
+ if query.ProductCode != nil && *query.ProductCode != "" {
650
+ db = db.Where("product_code = ?", *query.ProductCode)
651
+ }
652
+ if query.TenantID != nil && *query.TenantID != "" {
653
+ db = db.Where("tenant_id = ?", *query.TenantID)
654
+ }
655
+ if query.IsSuccess != nil {
656
+ db = db.Where("is_success = ?", *query.IsSuccess)
657
+ }
658
+ if query.DateFrom != nil {
659
+ db = db.Where("COALESCE(occurred_at, created_at) >= ?", *query.DateFrom)
660
+ }
661
+ if query.DateTo != nil {
662
+ db = db.Where("COALESCE(occurred_at, created_at) <= ?", *query.DateTo)
663
+ }
664
+ if query.TraceID != nil && *query.TraceID != "" {
665
+ db = db.Where("trace_id = ?", *query.TraceID)
666
+ }
667
+ if query.RequestID != nil && *query.RequestID != "" {
668
+ db = db.Where("request_id = ?", *query.RequestID)
669
+ }
670
+ // 审计检索必须覆盖对象、链路和操作者三类证据,支撑控制台单关键词入口。
671
+ if query.Keyword != nil {
672
+ if kw, like, ok := normalizeKeyword(*query.Keyword); ok {
673
+ matches, err := r.actorSearch.Search(ctx, kw)
674
+ if err != nil {
675
+ return nil, 0, err
676
+ }
677
+ actorSubQuery := matches.ActorIDs
678
+ principalSubQuery := matches.PrincipalIDs
679
+ principalTextSubQuery := make([]string, len(matches.PrincipalIDs))
680
+ for i, id := range matches.PrincipalIDs {
681
+ principalTextSubQuery[i] = strconv.FormatInt(id, 10)
682
+ }
683
+ conditions := r.db.
684
+ Where("event_id = ?", kw).
685
+ Or(keywordTextCondition("event_id"), like).
686
+ Or(keywordTextCondition("resource_type"), like).
687
+ Or(keywordTextCondition("resource_id"), like).
688
+ Or(keywordTextCondition("action"), like).
689
+ Or(keywordTextCondition("category"), like).
690
+ Or(keywordTextCondition("trace_id"), like).
691
+ Or(keywordTextCondition("request_id"), like).
692
+ Or(keywordTextCondition("tenant_id"), like).
693
+ Or("actor_id IN (?)", actorSubQuery).
694
+ Or("iam_principal_id IN (?)", principalSubQuery).
695
+ Or("resource_type IN ? AND resource_id IN (?)", iamPrincipalResourceTypes, principalTextSubQuery)
696
+ if parsedID, err := strconv.ParseInt(kw, 10, 64); err == nil {
697
+ conditions = conditions.
698
+ Or("actor_id = ?", parsedID).
699
+ Or("iam_principal_id = ?", parsedID).
700
+ Or("resource_type IN ? AND resource_id = ?", iamPrincipalResourceTypes, strconv.FormatInt(parsedID, 10))
701
+ }
702
+ db = db.Where(conditions)
703
+ }
704
+ }
705
+
706
+ var total int64
707
+ if err := db.Count(&total).Error; err != nil {
708
+ return nil, 0, err
709
+ }
710
+
711
+ var logs []*AuditLog
712
+ if err := db.Order("COALESCE(occurred_at, created_at) DESC").
713
+ Offset(query.Offset()).
714
+ Limit(query.PageSize).
715
+ Find(&logs).Error; err != nil {
716
+ return nil, 0, err
717
+ }
718
+ return logs, total, nil
719
+ }
720
+
721
+ // FindByID 查询单条记录(包含 changes、metadata、actor_user_agent 等完整字段)
722
+ func (r *auditRepository) FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*AuditLog, error) {
723
+ db := database.GetDB(ctx, r.db).Model(&AuditLog{}).
724
+ Select(
725
+ "id", "event_id", "occurred_at", "trace_id", "request_id",
726
+ "actor_type", "actor_id", "actor_ip", "actor_user_agent",
727
+ "auth_subject_type", "iam_principal_id", "credential_public_id", "token_fingerprint", "auth_mechanism",
728
+ "action", "category", "risk_level",
729
+ "resource_type", "resource_id",
730
+ "product_code", "tenant_id",
731
+ "changes", "metadata",
732
+ "is_success", "error_message",
733
+ "created_at",
734
+ )
735
+ var err error
736
+ db, err = iamscope.ApplyScope(db, scope, auditLogScopeBinding, principalID)
737
+ if err != nil {
738
+ return nil, err
739
+ }
740
+
741
+ var log AuditLog
742
+ if err := db.First(&log, "id = ?", id).Error; err != nil {
743
+ return nil, err
744
+ }
745
+ return &log, nil
746
+ }
747
+
748
+ // CountByCategory 按分类统计指定事件时间范围内的审计记录数量
749
+ func (r *auditRepository) CountByCategory(ctx context.Context, from, to time.Time, scope iamscope.ScopeConstraint, principalID int64) ([]CategoryCount, error) {
750
+ var results []CategoryCount
751
+ db := database.GetDB(ctx, r.db).Model(&AuditLog{}).
752
+ Select("category", "COUNT(*) as count").
753
+ Where("COALESCE(occurred_at, created_at) BETWEEN ? AND ?", from, to)
754
+ var err error
755
+ db, err = iamscope.ApplyScope(db, scope, auditLogScopeBinding, principalID)
756
+ if err != nil {
757
+ return nil, err
758
+ }
759
+ err = db.
760
+ Group("category").
761
+ Order("count DESC").
762
+ Find(&results).Error
763
+ return results, err
764
+ }
765
+
766
+ // CountByRiskLevel 按风险级别统计指定事件时间范围内的审计记录数量
767
+ func (r *auditRepository) CountByRiskLevel(ctx context.Context, from, to time.Time, scope iamscope.ScopeConstraint, principalID int64) ([]RiskLevelCount, error) {
768
+ var results []RiskLevelCount
769
+ db := database.GetDB(ctx, r.db).Model(&AuditLog{}).
770
+ Select("risk_level", "COUNT(*) as count").
771
+ Where("COALESCE(occurred_at, created_at) BETWEEN ? AND ?", from, to)
772
+ var err error
773
+ db, err = iamscope.ApplyScope(db, scope, auditLogScopeBinding, principalID)
774
+ if err != nil {
775
+ return nil, err
776
+ }
777
+ err = db.
778
+ Group("risk_level").
779
+ Order("count DESC").
780
+ Find(&results).Error
781
+ return results, err
782
+ }
783
+
784
+ // ---------------------------------------------------------------------------
785
+ // OperationLogRepository — 操作日志仓储
786
+ // ---------------------------------------------------------------------------
787
+
788
+ // OperationLogRepository 操作日志仓储接口(先插入 started,再补齐 completed)
789
+ type OperationLogRepository interface {
790
+ // Create 同步创建操作日志 started 记录
791
+ Create(ctx context.Context, log *OperationLog) error
792
+ // Complete 补齐操作日志完成态字段
793
+ Complete(ctx context.Context, id int64, entry CompleteOperationEntry, completedAt time.Time) error
794
+ // FindByQuery 分页查询操作日志
795
+ FindByQuery(ctx context.Context, query *AdminListOperationLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*OperationLog, int64, error)
796
+ // FindByID 根据 ID 查询单条操作日志
797
+ FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*OperationLog, error)
798
+ }
799
+
800
+ type operationLogRepository struct {
801
+ db *gorm.DB
802
+ actorSearch ActorReferenceSearcher
803
+ }
804
+
805
+ // NewOperationLogRepository 构建操作日志仓储实例
806
+ func NewOperationLogRepository(db *gorm.DB, actorSearch ActorReferenceSearcher) OperationLogRepository {
807
+ return &operationLogRepository{db: db, actorSearch: actorSearch}
808
+ }
809
+
810
+ // Create 同步创建操作日志 started 记录
811
+ func (r *operationLogRepository) Create(ctx context.Context, log *OperationLog) error {
812
+ return database.GetDB(ctx, r.db).
813
+ Select(
814
+ "trace_id", "request_id",
815
+ "actor_type", "actor_id", "actor_ip", "actor_user_agent",
816
+ "auth_subject_type", "iam_principal_id", "credential_public_id", "token_fingerprint", "auth_mechanism",
817
+ "operation_action", "operation_category", "risk_level", "source_module",
818
+ "http_method", "http_path",
819
+ "product_code", "tenant_id", "resource_type", "resource_id",
820
+ "is_success", "request_context", "operation_metadata", "record_status",
821
+ ).
822
+ Create(log).Error
823
+ }
824
+
825
+ // Complete 补齐操作日志完成态字段
826
+ func (r *operationLogRepository) Complete(ctx context.Context, id int64, entry CompleteOperationEntry, completedAt time.Time) error {
827
+ result := database.GetDB(ctx, r.db).
828
+ Table((OperationLog{}).TableName()).
829
+ Where("id = ? AND record_status = ?", id, operationRecordStatusStarted).
830
+ Updates(map[string]any{
831
+ "request_id": entry.RequestID,
832
+ "iam_principal_id": entry.IAMPrincipalID,
833
+ "iam_action": entry.IAMAction,
834
+ "iam_resource": entry.IAMResource,
835
+ "decision_allowed": entry.DecisionAllowed,
836
+ "decision_reason_code": entry.DecisionReasonCode,
837
+ "decision_reason_kind": entry.DecisionReasonKind,
838
+ "http_status": entry.HTTPStatus,
839
+ "duration_ms": entry.DurationMS,
840
+ "is_success": entry.IsSuccess,
841
+ "error_message": normalizeAuditErrorMessage(entry.ErrorMessage),
842
+ "response_context": entry.ResponseContext,
843
+ "policy_violation": entry.PolicyViolation,
844
+ "record_status": operationRecordStatusCompleted,
845
+ "completed_at": completedAt,
846
+ })
847
+ if result.Error != nil {
848
+ return result.Error
849
+ }
850
+ if result.RowsAffected == 0 {
851
+ return gorm.ErrRecordNotFound
852
+ }
853
+ return nil
854
+ }
855
+
856
+ // FindByQuery 分页查询操作日志,列表不返回 JSON 证据载荷。
857
+ func (r *operationLogRepository) FindByQuery(ctx context.Context, query *AdminListOperationLogsRequest, scope iamscope.ScopeConstraint, principalID int64) ([]*OperationLog, int64, error) {
858
+ db := database.GetDB(ctx, r.db).Model(&OperationLog{}).
859
+ Select(
860
+ "id", "trace_id", "request_id",
861
+ "actor_type", "actor_id", "actor_ip",
862
+ "auth_subject_type", "iam_principal_id", "credential_public_id", "token_fingerprint", "auth_mechanism",
863
+ "operation_action", "operation_category", "risk_level", "source_module",
864
+ "iam_action", "iam_resource", "decision_allowed", "decision_reason_code", "decision_reason_kind",
865
+ "http_method", "http_path", "http_status", "duration_ms",
866
+ "product_code", "tenant_id", "resource_type", "resource_id",
867
+ "is_success", "error_message", "record_status", "completed_at", "created_at",
868
+ )
869
+ var err error
870
+ db, err = iamscope.ApplyScope(db, scope, operationLogScopeBinding, principalID)
871
+ if err != nil {
872
+ return nil, 0, err
873
+ }
874
+
875
+ if query.PrincipalID != nil {
876
+ db = db.Where("iam_principal_id = ?", *query.PrincipalID)
877
+ }
878
+ if query.ActorID != nil {
879
+ db = db.Where("actor_id = ?", *query.ActorID)
880
+ }
881
+ if query.OperationAction != nil && *query.OperationAction != "" {
882
+ db = db.Where("operation_action = ?", *query.OperationAction)
883
+ }
884
+ if query.OperationCategory != nil && *query.OperationCategory != "" {
885
+ db = db.Where("operation_category = ?", *query.OperationCategory)
886
+ }
887
+ if query.RiskLevel != nil && *query.RiskLevel != "" {
888
+ db = db.Where("risk_level = ?", *query.RiskLevel)
889
+ }
890
+ if query.IAMAction != nil && *query.IAMAction != "" {
891
+ db = db.Where("iam_action = ?", *query.IAMAction)
892
+ }
893
+ if query.ResourceType != nil && *query.ResourceType != "" {
894
+ db = db.Where("resource_type = ?", *query.ResourceType)
895
+ }
896
+ if query.ResourceID != nil && *query.ResourceID != "" {
897
+ db = db.Where("resource_id = ?", *query.ResourceID)
898
+ }
899
+ if query.HTTPMethod != nil && *query.HTTPMethod != "" {
900
+ db = db.Where("http_method = ?", *query.HTTPMethod)
901
+ }
902
+ if query.HTTPStatus != nil {
903
+ db = db.Where("http_status = ?", *query.HTTPStatus)
904
+ }
905
+ if query.ProductCode != nil && *query.ProductCode != "" {
906
+ db = db.Where("product_code = ?", *query.ProductCode)
907
+ }
908
+ if query.TenantID != nil && *query.TenantID != "" {
909
+ db = db.Where("tenant_id = ?", *query.TenantID)
910
+ }
911
+ if query.IsSuccess != nil {
912
+ db = db.Where("is_success = ?", *query.IsSuccess)
913
+ }
914
+ if query.RecordStatus != nil && *query.RecordStatus != "" {
915
+ db = db.Where("record_status = ?", *query.RecordStatus)
916
+ }
917
+ if query.DateFrom != nil {
918
+ db = db.Where("created_at >= ?", *query.DateFrom)
919
+ }
920
+ if query.DateTo != nil {
921
+ db = db.Where("created_at <= ?", *query.DateTo)
922
+ }
923
+ if query.RequestID != nil && *query.RequestID != "" {
924
+ db = db.Where("request_id = ?", *query.RequestID)
925
+ }
926
+ if query.TraceID != nil && *query.TraceID != "" {
927
+ db = db.Where("trace_id = ?", *query.TraceID)
928
+ }
929
+ if query.Keyword != nil {
930
+ if kw, like, ok := normalizeKeyword(*query.Keyword); ok {
931
+ matches, err := r.actorSearch.Search(ctx, kw)
932
+ if err != nil {
933
+ return nil, 0, err
934
+ }
935
+ actorSubQuery := matches.ActorIDs
936
+ principalSubQuery := matches.PrincipalIDs
937
+ principalTextSubQuery := make([]string, len(matches.PrincipalIDs))
938
+ for i, id := range matches.PrincipalIDs {
939
+ principalTextSubQuery[i] = strconv.FormatInt(id, 10)
940
+ }
941
+ conditions := r.db.
942
+ Where(keywordTextCondition("http_path"), like).
943
+ Or(keywordTextCondition("resource_type"), like).
944
+ Or(keywordTextCondition("resource_id"), like).
945
+ Or(keywordTextCondition("trace_id"), like).
946
+ Or(keywordTextCondition("request_id"), like).
947
+ Or(keywordTextCondition("tenant_id"), like).
948
+ Or(keywordTextCondition("iam_action"), like).
949
+ Or(keywordTextCondition("source_module"), like).
950
+ Or(keywordTextCondition("operation_action"), like).
951
+ Or(keywordTextCondition("operation_category"), like).
952
+ Or(keywordTextCondition("actor_ip"), like).
953
+ Or("actor_id IN (?)", actorSubQuery).
954
+ Or("iam_principal_id IN (?)", principalSubQuery).
955
+ Or("resource_type IN ? AND resource_id IN (?)", iamPrincipalResourceTypes, principalTextSubQuery)
956
+ if parsedID, parseErr := strconv.ParseInt(kw, 10, 64); parseErr == nil {
957
+ conditions = conditions.
958
+ Or("actor_id = ?", parsedID).
959
+ Or("iam_principal_id = ?", parsedID).
960
+ Or("resource_type IN ? AND resource_id = ?", iamPrincipalResourceTypes, strconv.FormatInt(parsedID, 10))
961
+ }
962
+ db = db.Where(conditions)
963
+ }
964
+ }
965
+
966
+ var total int64
967
+ if err := db.Count(&total).Error; err != nil {
968
+ return nil, 0, err
969
+ }
970
+
971
+ var logs []*OperationLog
972
+ if err := db.Order("created_at DESC, id DESC").
973
+ Offset(query.Offset()).
974
+ Limit(query.PageSize).
975
+ Find(&logs).Error; err != nil {
976
+ return nil, 0, err
977
+ }
978
+ return logs, total, nil
979
+ }
980
+
981
+ // FindByID 查询单条操作日志(包含完整 JSON 证据载荷)
982
+ func (r *operationLogRepository) FindByID(ctx context.Context, id int64, scope iamscope.ScopeConstraint, principalID int64) (*OperationLog, error) {
983
+ db := database.GetDB(ctx, r.db).Model(&OperationLog{}).
984
+ Select(
985
+ "id", "trace_id", "request_id",
986
+ "actor_type", "actor_id", "actor_ip", "actor_user_agent",
987
+ "auth_subject_type", "iam_principal_id", "credential_public_id", "token_fingerprint", "auth_mechanism",
988
+ "operation_action", "operation_category", "risk_level", "source_module",
989
+ "iam_action", "iam_resource", "decision_allowed", "decision_reason_code", "decision_reason_kind",
990
+ "http_method", "http_path", "http_status", "duration_ms",
991
+ "product_code", "tenant_id", "resource_type", "resource_id",
992
+ "is_success", "error_message",
993
+ "request_context", "response_context", "operation_metadata", "policy_violation",
994
+ "record_status", "completed_at", "created_at",
995
+ )
996
+ var err error
997
+ db, err = iamscope.ApplyScope(db, scope, operationLogScopeBinding, principalID)
998
+ if err != nil {
999
+ return nil, err
1000
+ }
1001
+
1002
+ var log OperationLog
1003
+ if err := db.First(&log, "id = ?", id).Error; err != nil {
1004
+ return nil, err
1005
+ }
1006
+ return &log, nil
1007
+ }