@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.
- package/dist/index.js +4588 -205
- package/dist/index.js.map +1 -1
- package/dist/resources/apple/.claude/docs/architecture-template.md +54 -0
- package/dist/resources/apple/.claude/docs/architecture.md +52 -0
- package/dist/resources/apple/.claude/docs/index.md +13 -0
- package/dist/resources/apple/.claude/docs/security-lifecycle.md +57 -0
- package/dist/resources/apple/.claude/docs/stack.md +93 -0
- package/dist/resources/apple/.claude/docs/verification.md +63 -0
- package/dist/resources/apple/AGENTS.md +397 -0
- package/dist/resources/apple/CLAUDE.md +397 -0
- package/dist/resources/apple/Gemfile +2 -0
- package/dist/resources/apple/Gemfile.lock +365 -0
- package/dist/resources/apple/_gitignore +14 -0
- package/dist/resources/apple/fastlane/Fastfile +123 -0
- package/dist/resources/components/nuxt/site-source/app/app.vue +42 -0
- package/dist/resources/components/nuxt/site-source/app/assets/css/site-shell.css +1035 -0
- package/dist/resources/components/nuxt/site-source/app/assets/icons/brand/.gitkeep +0 -0
- package/dist/resources/components/nuxt/site-source/app/assets/icons/common/.gitkeep +0 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationHeader.vue +35 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationShell.vue +56 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationSidebar.vue +40 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/BackToTop.vue +42 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/ContentArticlePage.vue +40 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/ContentListPage.vue +30 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/CookieConsent.vue +18 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/MarketingFooter.vue +52 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/MarketingHeader.vue +106 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/MarketingShell.vue +35 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/SiteBrand.vue +10 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/StaticPage.vue +21 -0
- package/dist/resources/components/nuxt/site-source/app/components/site/ToastViewport.vue +25 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/BaseButton.vue +86 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/BaseGradientBorder.vue +59 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/BaseModal.vue +131 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/BasePopover.vue +150 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/BaseText.vue +138 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/CheckoutStatus.vue +112 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/CheckoutSummary.vue +172 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/GoogleSignInButton.vue +113 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/InfiniteScroll.vue +123 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/YoutubeEmbed.vue +126 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useAnalytics.ts +24 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useCheckout.ts +121 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useConsent.ts +31 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useGoogleIdentity.ts +163 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useNavigation.ts +11 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useSeo.ts +33 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useSiteShell.ts +58 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useToasts.ts +48 -0
- package/dist/resources/components/nuxt/site-source/app/layouts/app.vue +5 -0
- package/dist/resources/components/nuxt/site-source/app/layouts/default.vue +5 -0
- package/dist/resources/components/nuxt/site-source/app/plugins/analytics.client.ts +121 -0
- package/dist/resources/components/nuxt/site-source/app/plugins/google-identity.client.ts +24 -0
- package/dist/resources/components/nuxt/site-source/app/theme/accordion.ts +9 -0
- package/dist/resources/components/nuxt/site-source/app/theme/breadcrumb.ts +8 -0
- package/dist/resources/components/nuxt/site-source/app/theme/button.ts +92 -0
- package/dist/resources/components/nuxt/site-source/app/theme/carousel.ts +11 -0
- package/dist/resources/components/nuxt/site-source/app/theme/checkbox.ts +12 -0
- package/dist/resources/components/nuxt/site-source/app/theme/index.ts +30 -0
- package/dist/resources/components/nuxt/site-source/app/theme/input.ts +47 -0
- package/dist/resources/components/nuxt/site-source/app/theme/modal.ts +9 -0
- package/dist/resources/components/nuxt/site-source/app/theme/pagination.ts +6 -0
- package/dist/resources/components/nuxt/site-source/app/theme/select.ts +23 -0
- package/dist/resources/components/nuxt/site-source/app/theme/stepper.ts +10 -0
- package/dist/resources/components/nuxt/site-source/app/theme/toast.ts +8 -0
- package/dist/resources/components/nuxt/site-source/app/theme/tooltip.ts +6 -0
- package/dist/resources/components/nuxt/site-source/app/types/site-shell.ts +85 -0
- package/dist/resources/components/react/ui/Button.tsx +73 -0
- package/dist/resources/components/react/ui/Input.tsx +44 -0
- package/dist/resources/components/registry.json +355 -0
- package/dist/resources/components/vue/feedback/Toast.vue +120 -0
- package/dist/resources/components/vue/feedback/useToast.ts +70 -0
- package/dist/resources/components/vue/media/FileUpload.vue +229 -0
- package/dist/resources/components/vue/media/VideoPlayer.vue +157 -0
- package/dist/resources/components/vue/ui/AppImage.vue +147 -0
- package/dist/resources/components/vue/ui/BottomSheet.vue +217 -0
- package/dist/resources/components/vue/ui/Button.vue +154 -0
- package/dist/resources/components/vue/ui/Card.vue +111 -0
- package/dist/resources/components/vue/ui/Input.vue +113 -0
- package/dist/resources/components/vue/ui/Modal.vue +193 -0
- package/dist/resources/components/vue/ui/ScrollArea.vue +163 -0
- package/dist/resources/components/vue/ui/Tooltip.vue +115 -0
- package/dist/resources/registry.json +158 -0
- package/dist/resources/snippets/node/cache.ts +116 -0
- package/dist/resources/snippets/node/date.ts +126 -0
- package/dist/resources/snippets/node/logger.ts +104 -0
- package/dist/resources/snippets/node/validation.ts +132 -0
- package/dist/resources/snippets/react/useDebounce.ts +90 -0
- package/dist/resources/snippets/registry.json +65 -0
- package/dist/resources/snippets/vue/useAsync.ts +96 -0
- package/dist/resources/snippets/vue/usePagination.ts +102 -0
- package/dist/resources/templates/base/.claude/docs/architecture-template.md +459 -0
- package/dist/resources/templates/base/.claude/docs/architecture.md +137 -0
- package/dist/resources/templates/base/.claude/docs/index.md +42 -0
- package/dist/resources/templates/base/.claude/docs/security-lifecycle.md +129 -0
- package/dist/resources/templates/base/.claude/docs/verification.md +121 -0
- package/dist/resources/templates/base/AGENTS.md +415 -0
- package/dist/resources/templates/base/CLAUDE.md +415 -0
- package/dist/resources/templates/go-backend/.claude/docs/api-design.md +486 -0
- package/dist/resources/templates/go-backend/.claude/docs/architecture-template.md +449 -0
- package/dist/resources/templates/go-backend/.claude/docs/check-list.md +155 -0
- package/dist/resources/templates/go-backend/.claude/docs/database.md +369 -0
- package/dist/resources/templates/go-backend/.claude/docs/e2e-standards.md +161 -0
- package/dist/resources/templates/go-backend/.claude/docs/error-handling.md +558 -0
- package/dist/resources/templates/go-backend/.claude/docs/event-runtime-boundary.md +124 -0
- package/dist/resources/templates/go-backend/.claude/docs/identifier-contract.md +195 -0
- package/dist/resources/templates/go-backend/.claude/docs/index.md +22 -0
- package/dist/resources/templates/go-backend/.claude/docs/logging.md +314 -0
- package/dist/resources/templates/go-backend/.claude/docs/mediagen-provider-onboarding-sop.md +917 -0
- package/dist/resources/templates/go-backend/.claude/docs/mediagen-provider-quick-reference.md +195 -0
- package/dist/resources/templates/go-backend/.claude/docs/module-standards.md +309 -0
- package/dist/resources/templates/go-backend/.claude/docs/openapi-contract.md +141 -0
- package/dist/resources/templates/go-backend/.claude/docs/payment-gateway-onboarding.md +283 -0
- package/dist/resources/templates/go-backend/.claude/docs/platform-boundary.md +191 -0
- package/dist/resources/templates/go-backend/.claude/docs/quality-gates.md +112 -0
- package/dist/resources/templates/go-backend/.dockerignore +6 -0
- package/dist/resources/templates/go-backend/.env.example +13 -0
- package/dist/resources/templates/go-backend/.gitattributes +2 -0
- package/dist/resources/templates/go-backend/AGENTS.md +1139 -0
- package/dist/resources/templates/go-backend/CLAUDE.md +1139 -0
- package/dist/resources/templates/go-backend/Dockerfile +14 -0
- package/dist/resources/templates/go-backend/Makefile +71 -0
- package/dist/resources/templates/go-backend/README.md +66 -0
- package/dist/resources/templates/go-backend/_gitignore +10 -0
- package/dist/resources/templates/go-backend/api/openapi/README.md +29 -0
- package/dist/resources/templates/go-backend/api/openapi/dist/artifact-manifest.json +19 -0
- package/dist/resources/templates/go-backend/api/openapi/dist/conformance-matrix.json +276 -0
- package/dist/resources/templates/go-backend/api/openapi/document.go +30 -0
- package/dist/resources/templates/go-backend/api/openapi/package.json +28 -0
- package/dist/resources/templates/go-backend/api/openapi/pnpm-lock.yaml +3052 -0
- package/dist/resources/templates/go-backend/api/openapi/redocly-source.yaml +18 -0
- package/dist/resources/templates/go-backend/api/openapi/redocly.yaml +8 -0
- package/dist/resources/templates/go-backend/api/openapi/sdk-generators.json +16 -0
- package/dist/resources/templates/go-backend/api/openapi/service-registry.json +39 -0
- package/dist/resources/templates/go-backend/api/openapi/services/product-api/conformance/runtime.arazzo.yaml +65 -0
- package/dist/resources/templates/go-backend/api/openapi/services/product-api/dist/internal.openapi.json +327 -0
- package/dist/resources/templates/go-backend/api/openapi/services/product-api/dist/runtime-contract.json +56 -0
- package/dist/resources/templates/go-backend/api/openapi/services/product-api/openapi.yaml +222 -0
- package/dist/resources/templates/go-backend/api/openapi/services/worker/conformance/runtime.arazzo.yaml +65 -0
- package/dist/resources/templates/go-backend/api/openapi/services/worker/dist/internal.openapi.json +327 -0
- package/dist/resources/templates/go-backend/api/openapi/services/worker/dist/runtime-contract.json +56 -0
- package/dist/resources/templates/go-backend/api/openapi/services/worker/openapi.yaml +222 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/breaking.mjs +416 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/build.mjs +103 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/conformance_coverage.mjs +111 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/contract_compiler.mjs +62 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/contract_policy.mjs +208 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/delegated_contract.mjs +302 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/deterministic_archive.mjs +69 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_example_privacy.mjs +93 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_projection.mjs +498 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_quality.mjs +346 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/git_artifact_history.mjs +188 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/manifest.mjs +170 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/overlay.mjs +98 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/project.mjs +549 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/route_contract.mjs +156 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/runtime_conformance.mjs +193 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/scalar_asset_localization.mjs +164 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/sdk_generator.mjs +229 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/semantic_version.mjs +64 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/service_contract_inputs.mjs +16 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/breaking.test.mjs +365 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/build.test.mjs +42 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/conformance_coverage.test.mjs +88 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/contract_policy.test.mjs +257 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/delegated_contract.test.mjs +205 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_example_privacy.test.mjs +51 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_projection.test.mjs +368 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_quality.test.mjs +151 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/git_artifact_history.test.mjs +60 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/head_policy.test.mjs +8 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/overlay.test.mjs +44 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/projection.test.mjs +270 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/redocly_documentation_rules.test.mjs +146 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/release_manifest.test.mjs +76 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/route_contract.test.mjs +119 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/runtime_conformance.test.mjs +75 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/scalar_asset_localization.test.mjs +50 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/sdk_generator.test.mjs +31 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/semantic_version.test.mjs +60 -0
- package/dist/resources/templates/go-backend/api/openapi/tooling/test/service_contract_inputs.test.mjs +27 -0
- package/dist/resources/templates/go-backend/cmd/internal/runtime/resources.go +125 -0
- package/dist/resources/templates/go-backend/cmd/internal/runtime/runtime.go +102 -0
- package/dist/resources/templates/go-backend/cmd/internal/runtime/runtime_test.go +70 -0
- package/dist/resources/templates/go-backend/cmd/product-api/lifecycle_test.go +58 -0
- package/dist/resources/templates/go-backend/cmd/product-api/main.go +32 -0
- package/dist/resources/templates/go-backend/cmd/product-api/wire.go +13 -0
- package/dist/resources/templates/go-backend/cmd/product-api/wire_gen.go +94 -0
- package/dist/resources/templates/go-backend/cmd/worker/main.go +47 -0
- package/dist/resources/templates/go-backend/cmd/worker/wire.go +14 -0
- package/dist/resources/templates/go-backend/cmd/worker/wire_gen.go +111 -0
- package/dist/resources/templates/go-backend/configs/config.yaml +37 -0
- package/dist/resources/templates/go-backend/deployments/compose.yaml +71 -0
- package/dist/resources/templates/go-backend/docs/openapi-provenance.json +489 -0
- package/dist/resources/templates/go-backend/docs/runtime-provenance.json +175 -0
- package/dist/resources/templates/go-backend/docs/source-provenance.json +3568 -0
- package/dist/resources/templates/go-backend/docs/standards-provenance.json +1624 -0
- package/dist/resources/templates/go-backend/go.mod +256 -0
- package/dist/resources/templates/go-backend/go.sum +805 -0
- package/dist/resources/templates/go-backend/internal/common/canonicaljson/canonical.go +370 -0
- package/dist/resources/templates/go-backend/internal/common/canonicaljson/canonical_test.go +223 -0
- package/dist/resources/templates/go-backend/internal/common/checkoutbff/contracts.go +125 -0
- package/dist/resources/templates/go-backend/internal/common/checkoutbff/platform_client.go +212 -0
- package/dist/resources/templates/go-backend/internal/common/checkoutbff/platform_client_test.go +319 -0
- package/dist/resources/templates/go-backend/internal/common/contentaccess/contentaccess.go +50 -0
- package/dist/resources/templates/go-backend/internal/common/contracts/contact.go +85 -0
- package/dist/resources/templates/go-backend/internal/common/ctxutil/identity.go +20 -0
- package/dist/resources/templates/go-backend/internal/common/ctxutil/product.go +53 -0
- package/dist/resources/templates/go-backend/internal/common/enumdomain/payment.go +12 -0
- package/dist/resources/templates/go-backend/internal/common/enums/dto_admin.go +9 -0
- package/dist/resources/templates/go-backend/internal/common/enums/enum.go +1247 -0
- package/dist/resources/templates/go-backend/internal/common/enums/handler_admin.go +66 -0
- package/dist/resources/templates/go-backend/internal/common/enums/module.go +46 -0
- package/dist/resources/templates/go-backend/internal/common/enums/provider.go +10 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register.go +11 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register_audit.go +274 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register_command_replay.go +12 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register_config.go +27 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register_locale.go +37 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register_ops.go +14 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register_outbox.go +15 -0
- package/dist/resources/templates/go-backend/internal/common/enums/register_rule_expression.go +63 -0
- package/dist/resources/templates/go-backend/internal/common/enums/registry.go +77 -0
- package/dist/resources/templates/go-backend/internal/common/httporigin/origin.go +62 -0
- package/dist/resources/templates/go-backend/internal/common/httporigin/origin_test.go +38 -0
- package/dist/resources/templates/go-backend/internal/common/iamcredential/plaintext.go +35 -0
- package/dist/resources/templates/go-backend/internal/common/iamcredential/plaintext_test.go +29 -0
- package/dist/resources/templates/go-backend/internal/common/iamscope/scope.go +212 -0
- package/dist/resources/templates/go-backend/internal/common/iamscope/scope_binding.go +26 -0
- package/dist/resources/templates/go-backend/internal/common/iamscope/scope_filter.go +173 -0
- package/dist/resources/templates/go-backend/internal/common/iamscope/scope_filter_test.go +279 -0
- package/dist/resources/templates/go-backend/internal/common/internalapi/authorization_details.go +10 -0
- package/dist/resources/templates/go-backend/internal/common/internalapi/route_permission_policies.go +660 -0
- package/dist/resources/templates/go-backend/internal/common/internalapi/route_permission_policies_test.go +331 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/audience_token_source.go +166 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/bootstrap_token_exchanger.go +183 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/bound_service_token_signer.go +52 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/delegated_access_token_exchanger.go +189 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/delegated_access_token_exchanger_test.go +159 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/product_scoped_service_token_signer.go +92 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/provider.go +25 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/request_signer.go +86 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/resolver.go +69 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/resolver_test.go +42 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/route_scope.go +95 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/route_scope_test.go +78 -0
- package/dist/resources/templates/go-backend/internal/common/internalservice/service_identity_test.go +387 -0
- package/dist/resources/templates/go-backend/internal/common/jsonschema/compiler.go +100 -0
- package/dist/resources/templates/go-backend/internal/common/jsonschema/compiler_test.go +41 -0
- package/dist/resources/templates/go-backend/internal/common/jwksapi/handler.go +83 -0
- package/dist/resources/templates/go-backend/internal/common/jwksapi/handler_test.go +104 -0
- package/dist/resources/templates/go-backend/internal/common/locale/locale.go +145 -0
- package/dist/resources/templates/go-backend/internal/common/locale/locale_test.go +75 -0
- package/dist/resources/templates/go-backend/internal/common/mediaref/mediaref.go +54 -0
- package/dist/resources/templates/go-backend/internal/common/mediaref/mediaref_test.go +77 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/apikey_auth.go +320 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/apikey_throttle.go +158 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/auth.go +40 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/auth_test.go +50 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/client_context.go +86 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/cors.go +68 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/event_context.go +18 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/gateway_identity.go +279 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/helpers.go +159 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_route_permission.go +171 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_token_auth.go +151 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_token_auth_test.go +99 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/iam_service_identity.go +138 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/iam_service_token_auth.go +74 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/idempotent.go +273 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/idempotent_test.go +83 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/internal_route_permission.go +45 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/internal_route_permission_test.go +186 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/logging.go +191 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/metrics.go +45 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/middleware_test.go +1516 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/principal_context.go +160 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/principal_context_test.go +355 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/product_context.go +100 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/recovery.go +40 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/referral_attribution.go +178 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/requestid.go +48 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/resource_permission.go +790 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/resource_permission_test.go +440 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/service_credential_auth.go +67 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/tenant.go +148 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/throttle.go +310 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/trusted_client_ip.go +23 -0
- package/dist/resources/templates/go-backend/internal/common/middleware/trusted_client_ip_test.go +60 -0
- package/dist/resources/templates/go-backend/internal/common/money/currency.go +25 -0
- package/dist/resources/templates/go-backend/internal/common/money/errors.go +23 -0
- package/dist/resources/templates/go-backend/internal/common/money/format.go +123 -0
- package/dist/resources/templates/go-backend/internal/common/money/format_test.go +204 -0
- package/dist/resources/templates/go-backend/internal/common/money/gateway.go +144 -0
- package/dist/resources/templates/go-backend/internal/common/money/gateway_test.go +165 -0
- package/dist/resources/templates/go-backend/internal/common/money/rounding.go +56 -0
- package/dist/resources/templates/go-backend/internal/common/money/rounding_test.go +51 -0
- package/dist/resources/templates/go-backend/internal/common/principal/context.go +56 -0
- package/dist/resources/templates/go-backend/internal/common/principal/principal.go +49 -0
- package/dist/resources/templates/go-backend/internal/common/productcode/code.go +24 -0
- package/dist/resources/templates/go-backend/internal/common/productcode/code_test.go +35 -0
- package/dist/resources/templates/go-backend/internal/common/providerconfig/schema.go +577 -0
- package/dist/resources/templates/go-backend/internal/common/providerconfig/schema_test.go +47 -0
- package/dist/resources/templates/go-backend/internal/common/requestprivacy/request.go +246 -0
- package/dist/resources/templates/go-backend/internal/common/requestprivacy/request_test.go +170 -0
- package/dist/resources/templates/go-backend/internal/common/response/pagination.go +60 -0
- package/dist/resources/templates/go-backend/internal/common/response/response.go +110 -0
- package/dist/resources/templates/go-backend/internal/common/response/response_test.go +463 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/ast.go +141 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/ast_test.go +81 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/canonical.go +184 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/canonical_test.go +319 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/decoder.go +555 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/decoder_test.go +260 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/errors.go +55 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/evaluator.go +96 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/evaluator_test.go +220 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/fuzz_test.go +116 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/limits.go +331 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/schema.go +412 -0
- package/dist/resources/templates/go-backend/internal/common/ruleexpression/schema_test.go +164 -0
- package/dist/resources/templates/go-backend/internal/common/runtimeinput/runtime_input.go +627 -0
- package/dist/resources/templates/go-backend/internal/common/runtimeinput/runtime_input_test.go +245 -0
- package/dist/resources/templates/go-backend/internal/common/semver/errors.go +6 -0
- package/dist/resources/templates/go-backend/internal/common/semver/semver.go +125 -0
- package/dist/resources/templates/go-backend/internal/common/semver/semver_test.go +205 -0
- package/dist/resources/templates/go-backend/internal/common/serviceidentity/audience.go +48 -0
- package/dist/resources/templates/go-backend/internal/common/serviceidentity/audience_test.go +35 -0
- package/dist/resources/templates/go-backend/internal/common/serviceidentity/resource_identifier.go +33 -0
- package/dist/resources/templates/go-backend/internal/common/serviceidentity/resource_identifier_test.go +32 -0
- package/dist/resources/templates/go-backend/internal/common/tradeno/generator.go +99 -0
- package/dist/resources/templates/go-backend/internal/common/tradeno/generator_test.go +396 -0
- package/dist/resources/templates/go-backend/internal/common/tradeno/prefix.go +247 -0
- package/dist/resources/templates/go-backend/internal/common/types/asset_url.go +11 -0
- package/dist/resources/templates/go-backend/internal/common/types/billing.go +89 -0
- package/dist/resources/templates/go-backend/internal/common/types/billing_test.go +77 -0
- package/dist/resources/templates/go-backend/internal/common/types/country.go +74 -0
- package/dist/resources/templates/go-backend/internal/common/types/country_test.go +28 -0
- package/dist/resources/templates/go-backend/internal/common/types/currency.go +60 -0
- package/dist/resources/templates/go-backend/internal/common/types/identifiers.go +41 -0
- package/dist/resources/templates/go-backend/internal/common/types/operator.go +28 -0
- package/dist/resources/templates/go-backend/internal/common/types/subscription_entitlement.go +24 -0
- package/dist/resources/templates/go-backend/internal/common/types/subscription_entitlement_test.go +35 -0
- package/dist/resources/templates/go-backend/internal/common/types/tier.go +72 -0
- package/dist/resources/templates/go-backend/internal/common/types/types.go +411 -0
- package/dist/resources/templates/go-backend/internal/common/types/types_test.go +295 -0
- package/dist/resources/templates/go-backend/internal/common/utils/constants.go +100 -0
- package/dist/resources/templates/go-backend/internal/common/utils/crypto.go +158 -0
- package/dist/resources/templates/go-backend/internal/common/utils/crypto_test.go +345 -0
- package/dist/resources/templates/go-backend/internal/common/utils/duration.go +25 -0
- package/dist/resources/templates/go-backend/internal/common/utils/helpers.go +18 -0
- package/dist/resources/templates/go-backend/internal/common/utils/json.go +62 -0
- package/dist/resources/templates/go-backend/internal/common/utils/json_test.go +76 -0
- package/dist/resources/templates/go-backend/internal/common/utils/pagination.go +51 -0
- package/dist/resources/templates/go-backend/internal/common/utils/pagination_test.go +45 -0
- package/dist/resources/templates/go-backend/internal/common/utils/pointer.go +107 -0
- package/dist/resources/templates/go-backend/internal/common/utils/pointer_test.go +15 -0
- package/dist/resources/templates/go-backend/internal/common/utils/slice.go +24 -0
- package/dist/resources/templates/go-backend/internal/common/utils/snowflake.go +110 -0
- package/dist/resources/templates/go-backend/internal/common/utils/snowflake_test.go +316 -0
- package/dist/resources/templates/go-backend/internal/common/utils/string.go +16 -0
- package/dist/resources/templates/go-backend/internal/common/utils/timefmt.go +42 -0
- package/dist/resources/templates/go-backend/internal/common/validator/validator.go +136 -0
- package/dist/resources/templates/go-backend/internal/common/validator/validator_test.go +240 -0
- package/dist/resources/templates/go-backend/internal/common/youtubeapi/client.go +224 -0
- package/dist/resources/templates/go-backend/internal/core/apicontract/document.go +269 -0
- package/dist/resources/templates/go-backend/internal/core/apicontract/document_test.go +104 -0
- package/dist/resources/templates/go-backend/internal/core/apicontract/route_validator.go +78 -0
- package/dist/resources/templates/go-backend/internal/core/apicontract/route_validator_test.go +108 -0
- package/dist/resources/templates/go-backend/internal/core/apidocs/assets/scalar-api-reference.js +2385 -0
- package/dist/resources/templates/go-backend/internal/core/apidocs/assets/scalar-api-reference.metadata.json +10 -0
- package/dist/resources/templates/go-backend/internal/core/apidocs/route.go +107 -0
- package/dist/resources/templates/go-backend/internal/core/apidocs/route_test.go +200 -0
- package/dist/resources/templates/go-backend/internal/core/apidocs/scalar.go +117 -0
- package/dist/resources/templates/go-backend/internal/core/apidocs/scalar_test.go +99 -0
- package/dist/resources/templates/go-backend/internal/core/audit/access_log_policy.go +91 -0
- package/dist/resources/templates/go-backend/internal/core/audit/access_log_policy_test.go +51 -0
- package/dist/resources/templates/go-backend/internal/core/audit/actor_resolver.go +36 -0
- package/dist/resources/templates/go-backend/internal/core/audit/constants.go +30 -0
- package/dist/resources/templates/go-backend/internal/core/audit/context.go +119 -0
- package/dist/resources/templates/go-backend/internal/core/audit/context_test.go +310 -0
- package/dist/resources/templates/go-backend/internal/core/audit/dto.go +468 -0
- package/dist/resources/templates/go-backend/internal/core/audit/dto_admin.go +172 -0
- package/dist/resources/templates/go-backend/internal/core/audit/enum.go +460 -0
- package/dist/resources/templates/go-backend/internal/core/audit/enum_test.go +314 -0
- package/dist/resources/templates/go-backend/internal/core/audit/errors.go +9 -0
- package/dist/resources/templates/go-backend/internal/core/audit/handler.go +568 -0
- package/dist/resources/templates/go-backend/internal/core/audit/listener.go +319 -0
- package/dist/resources/templates/go-backend/internal/core/audit/listener_test.go +98 -0
- package/dist/resources/templates/go-backend/internal/core/audit/middleware.go +669 -0
- package/dist/resources/templates/go-backend/internal/core/audit/middleware_test.go +268 -0
- package/dist/resources/templates/go-backend/internal/core/audit/model.go +306 -0
- package/dist/resources/templates/go-backend/internal/core/audit/partition_job.go +316 -0
- package/dist/resources/templates/go-backend/internal/core/audit/ports.go +9 -0
- package/dist/resources/templates/go-backend/internal/core/audit/provider.go +30 -0
- package/dist/resources/templates/go-backend/internal/core/audit/repository.go +1007 -0
- package/dist/resources/templates/go-backend/internal/core/audit/repository_test.go +934 -0
- package/dist/resources/templates/go-backend/internal/core/audit/router.go +85 -0
- package/dist/resources/templates/go-backend/internal/core/audit/service.go +770 -0
- package/dist/resources/templates/go-backend/internal/core/audit/service_test.go +331 -0
- package/dist/resources/templates/go-backend/internal/core/audit/vo_admin.go +475 -0
- package/dist/resources/templates/go-backend/internal/core/cache/config.go +30 -0
- package/dist/resources/templates/go-backend/internal/core/cache/key.go +41 -0
- package/dist/resources/templates/go-backend/internal/core/cache/key_test.go +62 -0
- package/dist/resources/templates/go-backend/internal/core/cache/l1.go +153 -0
- package/dist/resources/templates/go-backend/internal/core/cache/l1_test.go +212 -0
- package/dist/resources/templates/go-backend/internal/core/cache/manager.go +278 -0
- package/dist/resources/templates/go-backend/internal/core/cache/manager_provider.go +24 -0
- package/dist/resources/templates/go-backend/internal/core/cache/metrics.go +126 -0
- package/dist/resources/templates/go-backend/internal/core/cache/namespace.go +46 -0
- package/dist/resources/templates/go-backend/internal/core/cache/namespace_test.go +90 -0
- package/dist/resources/templates/go-backend/internal/core/cache/options.go +39 -0
- package/dist/resources/templates/go-backend/internal/core/cache/options_test.go +96 -0
- package/dist/resources/templates/go-backend/internal/core/cache/redis.go +311 -0
- package/dist/resources/templates/go-backend/internal/core/cache/redis_test.go +51 -0
- package/dist/resources/templates/go-backend/internal/core/cache/serializer.go +31 -0
- package/dist/resources/templates/go-backend/internal/core/cache/serializer_test.go +139 -0
- package/dist/resources/templates/go-backend/internal/core/cache/singleflight.go +53 -0
- package/dist/resources/templates/go-backend/internal/core/cache/singleflight_test.go +211 -0
- package/dist/resources/templates/go-backend/internal/core/cache/warming.go +86 -0
- package/dist/resources/templates/go-backend/internal/core/clock/clock.go +66 -0
- package/dist/resources/templates/go-backend/internal/core/clock/clock_test.go +26 -0
- package/dist/resources/templates/go-backend/internal/core/clock/runtime_clock.go +283 -0
- package/dist/resources/templates/go-backend/internal/core/clock/runtime_clock_test.go +98 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/errors.go +6 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/model.go +90 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/model_test.go +79 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/namespace.go +7 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence.go +269 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence_test.go +185 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/provider.go +9 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/repository.go +22 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/service.go +853 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/service_persistence_test.go +432 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/service_test.go +891 -0
- package/dist/resources/templates/go-backend/internal/core/commandreplay/types.go +121 -0
- package/dist/resources/templates/go-backend/internal/core/config/config.go +2069 -0
- package/dist/resources/templates/go-backend/internal/core/config/config_test.go +1122 -0
- package/dist/resources/templates/go-backend/internal/core/config/contract/contract_test.go +388 -0
- package/dist/resources/templates/go-backend/internal/core/config/contract/docs.go +198 -0
- package/dist/resources/templates/go-backend/internal/core/config/contract/verify.go +424 -0
- package/dist/resources/templates/go-backend/internal/core/config/enum.go +46 -0
- package/dist/resources/templates/go-backend/internal/core/config/metadata.go +717 -0
- package/dist/resources/templates/go-backend/internal/core/config/metadata_test.go +228 -0
- package/dist/resources/templates/go-backend/internal/core/config/product_admin_config_test.go +210 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/aes_gcm.go +111 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/aes_gcm_test.go +364 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/encryptor.go +12 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/envelope_encryptor.go +134 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/envelope_encryptor_test.go +118 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/pem.go +112 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/pem_test.go +44 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/provider.go +130 -0
- package/dist/resources/templates/go-backend/internal/core/crypto/provider_test.go +92 -0
- package/dist/resources/templates/go-backend/internal/core/database/base_model.go +24 -0
- package/dist/resources/templates/go-backend/internal/core/database/clickhouse.go +121 -0
- package/dist/resources/templates/go-backend/internal/core/database/database.go +112 -0
- package/dist/resources/templates/go-backend/internal/core/database/database_logger_test.go +95 -0
- package/dist/resources/templates/go-backend/internal/core/database/errors.go +25 -0
- package/dist/resources/templates/go-backend/internal/core/database/metrics_plugin.go +105 -0
- package/dist/resources/templates/go-backend/internal/core/database/pool_collector.go +71 -0
- package/dist/resources/templates/go-backend/internal/core/database/read_replica.go +520 -0
- package/dist/resources/templates/go-backend/internal/core/database/transaction.go +170 -0
- package/dist/resources/templates/go-backend/internal/core/database/transaction_test.go +282 -0
- package/dist/resources/templates/go-backend/internal/core/distlock/lock.go +353 -0
- package/dist/resources/templates/go-backend/internal/core/distlock/lock_test.go +106 -0
- package/dist/resources/templates/go-backend/internal/core/errors/handler.go +189 -0
- package/dist/resources/templates/go-backend/internal/core/errors/handler_test.go +515 -0
- package/dist/resources/templates/go-backend/internal/core/eventbus/eventbus.go +667 -0
- package/dist/resources/templates/go-backend/internal/core/eventbus/eventbus_test.go +792 -0
- package/dist/resources/templates/go-backend/internal/core/eventstream/noop.go +22 -0
- package/dist/resources/templates/go-backend/internal/core/eventstream/provider.go +20 -0
- package/dist/resources/templates/go-backend/internal/core/eventstream/publisher.go +38 -0
- package/dist/resources/templates/go-backend/internal/core/eventstream/redis.go +109 -0
- package/dist/resources/templates/go-backend/internal/core/eventstream/routing.go +104 -0
- package/dist/resources/templates/go-backend/internal/core/eventstream/routing_test.go +71 -0
- package/dist/resources/templates/go-backend/internal/core/geoip/model.go +19 -0
- package/dist/resources/templates/go-backend/internal/core/geoip/service.go +88 -0
- package/dist/resources/templates/go-backend/internal/core/health/health.go +333 -0
- package/dist/resources/templates/go-backend/internal/core/health/health_test.go +27 -0
- package/dist/resources/templates/go-backend/internal/core/httpclient/client.go +772 -0
- package/dist/resources/templates/go-backend/internal/core/httpclient/client_test.go +287 -0
- package/dist/resources/templates/go-backend/internal/core/httpclient/download.go +578 -0
- package/dist/resources/templates/go-backend/internal/core/httpclient/download_test.go +374 -0
- package/dist/resources/templates/go-backend/internal/core/jwtkeyring/jwk.go +38 -0
- package/dist/resources/templates/go-backend/internal/core/jwtkeyring/keyring.go +144 -0
- package/dist/resources/templates/go-backend/internal/core/jwtkeyring/keyring_test.go +128 -0
- package/dist/resources/templates/go-backend/internal/core/jwtkeyring/registry.go +60 -0
- package/dist/resources/templates/go-backend/internal/core/logger/logger.go +53 -0
- package/dist/resources/templates/go-backend/internal/core/logger/logger_test.go +194 -0
- package/dist/resources/templates/go-backend/internal/core/observability/metrics.go +358 -0
- package/dist/resources/templates/go-backend/internal/core/observability/sentry.go +445 -0
- package/dist/resources/templates/go-backend/internal/core/observability/sentry_privacy_test.go +175 -0
- package/dist/resources/templates/go-backend/internal/core/observability/tracing.go +187 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/model.go +314 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/model_test.go +77 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/persistence.go +51 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/provider.go +23 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/relay.go +1138 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/relay_postgres_test.go +257 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/relay_test.go +690 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/rescheduler.go +164 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/rescheduler_test.go +209 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/writer.go +249 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/writer_postgres_test.go +170 -0
- package/dist/resources/templates/go-backend/internal/core/outbox/writer_test.go +123 -0
- package/dist/resources/templates/go-backend/internal/core/provider.go +198 -0
- package/dist/resources/templates/go-backend/internal/core/queue/metrics_middleware.go +38 -0
- package/dist/resources/templates/go-backend/internal/core/queue/metrics_middleware_test.go +12 -0
- package/dist/resources/templates/go-backend/internal/core/queue/queue.go +288 -0
- package/dist/resources/templates/go-backend/internal/core/queue/queue_test.go +159 -0
- package/dist/resources/templates/go-backend/internal/core/resilience/bulkhead.go +514 -0
- package/dist/resources/templates/go-backend/internal/core/resilience/bulkhead_test.go +984 -0
- package/dist/resources/templates/go-backend/internal/core/resilience/circuitbreaker.go +358 -0
- package/dist/resources/templates/go-backend/internal/core/resilience/circuitbreaker_test.go +788 -0
- package/dist/resources/templates/go-backend/internal/core/resilience/retry.go +318 -0
- package/dist/resources/templates/go-backend/internal/core/resilience/retry_test.go +735 -0
- package/dist/resources/templates/go-backend/internal/core/schedule/recorder.go +305 -0
- package/dist/resources/templates/go-backend/internal/core/schedule/run_context.go +124 -0
- package/dist/resources/templates/go-backend/internal/core/schedule/run_context_test.go +85 -0
- package/dist/resources/templates/go-backend/internal/core/schedule/scheduler.go +1589 -0
- package/dist/resources/templates/go-backend/internal/core/schedule/scheduler_control_test.go +808 -0
- package/dist/resources/templates/go-backend/internal/core/storage/oss_provider.go +398 -0
- package/dist/resources/templates/go-backend/internal/core/storage/oss_provider_test.go +89 -0
- package/dist/resources/templates/go-backend/internal/core/storage/path.go +110 -0
- package/dist/resources/templates/go-backend/internal/core/storage/path_test.go +301 -0
- package/dist/resources/templates/go-backend/internal/core/storage/s3_provider.go +489 -0
- package/dist/resources/templates/go-backend/internal/core/storage/s3_provider_test.go +60 -0
- package/dist/resources/templates/go-backend/internal/core/storage/storage.go +268 -0
- package/dist/resources/templates/go-backend/internal/core/storage/validation.go +225 -0
- package/dist/resources/templates/go-backend/internal/core/storage/validation_test.go +495 -0
- package/dist/resources/templates/go-backend/internal/core/temporal/logger.go +38 -0
- package/dist/resources/templates/go-backend/internal/core/temporal/provider.go +126 -0
- package/dist/resources/templates/go-backend/internal/core/temporal/provider_test.go +67 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/client.go +282 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/client_test.go +81 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/config.go +58 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/handler.go +53 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/hub.go +269 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/message.go +273 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/notifier.go +92 -0
- package/dist/resources/templates/go-backend/internal/core/websocket/provider.go +33 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/action.go +77 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/action_test.go +238 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/condition.go +107 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/decision.go +256 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/enum.go +643 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/errors.go +102 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/principal_record.go +21 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/product_context.go +486 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/resource.go +215 -0
- package/dist/resources/templates/go-backend/internal/integrations/onex/iam/resource_test.go +242 -0
- package/dist/resources/templates/go-backend/internal/products/README.md +5 -0
- package/dist/resources/templates/go-backend/internal/sharedevents/README.md +5 -0
- package/dist/resources/templates/go-backend/migrations/000001_infrastructure.down.sql +13 -0
- package/dist/resources/templates/go-backend/migrations/000001_infrastructure.up.sql +886 -0
- package/dist/resources/templates/go-backend/pkg/apierror/error.go +327 -0
- package/dist/resources/templates/go-backend/pkg/apierror/error_code.go +65 -0
- package/dist/resources/templates/go-backend/pkg/apierror/error_test.go +41 -0
- package/dist/resources/templates/go-backend/pkg/apierror/error_type.go +14 -0
- package/dist/resources/templates/go-backend/pkg/apierror/http_response.go +216 -0
- package/dist/resources/templates/go-backend/pkg/apierror/http_response_test.go +216 -0
- package/dist/resources/templates/go-backend/scripts/check-openapi-clean-deletion.sh +54 -0
- package/dist/resources/templates/go-backend/scripts/test-migrations.sh +32 -0
- package/dist/resources/templates/go-backend/tests/infrastructure.sql +69 -0
- package/dist/resources/templates/mcp-server/.claude/docs/stack.md +338 -0
- package/dist/resources/templates/mcp-server/.editorconfig +13 -0
- package/dist/resources/templates/mcp-server/.env.example +8 -0
- package/dist/resources/templates/mcp-server/.github/ISSUE_TEMPLATE/bug_report.yml +104 -0
- package/dist/resources/templates/mcp-server/.github/ISSUE_TEMPLATE/feature_request.yml +54 -0
- package/dist/resources/templates/mcp-server/.github/PULL_REQUEST_TEMPLATE.md +36 -0
- package/dist/resources/templates/mcp-server/.github/workflows/ci.yml +78 -0
- package/dist/resources/templates/mcp-server/.husky/commit-msg +1 -0
- package/dist/resources/templates/mcp-server/.husky/pre-commit +1 -0
- package/dist/resources/templates/mcp-server/.lintstagedrc.json +4 -0
- package/dist/resources/templates/mcp-server/.nvmrc +1 -0
- package/dist/resources/templates/mcp-server/.prettierrc +8 -0
- package/dist/resources/templates/mcp-server/CONTRIBUTING.md +184 -0
- package/dist/resources/templates/mcp-server/Dockerfile +23 -0
- package/dist/resources/templates/mcp-server/LICENSE +21 -0
- package/dist/resources/templates/mcp-server/README.md +123 -0
- package/dist/resources/templates/mcp-server/_gitignore +26 -0
- package/dist/resources/templates/mcp-server/commitlint.config.js +24 -0
- package/dist/resources/templates/mcp-server/eslint.config.js +30 -0
- package/dist/resources/templates/mcp-server/package.json +72 -0
- package/dist/resources/templates/mcp-server/src/index.ts +27 -0
- package/dist/resources/templates/mcp-server/src/resources.ts +33 -0
- package/dist/resources/templates/mcp-server/src/services/.gitkeep +0 -0
- package/dist/resources/templates/mcp-server/src/tools.ts +43 -0
- package/dist/resources/templates/mcp-server/src/types/index.ts +19 -0
- package/dist/resources/templates/mcp-server/src/utils/index.ts +31 -0
- package/dist/resources/templates/mcp-server/tsconfig.json +22 -0
- package/dist/resources/templates/mcp-server/tsup.config.ts +14 -0
- package/dist/resources/templates/mcp-server/vitest.config.ts +15 -0
- package/dist/resources/templates/nuxt/.claude/docs/stack.md +666 -0
- package/dist/resources/templates/nuxt/.editorconfig +15 -0
- package/dist/resources/templates/nuxt/.env.example +22 -0
- package/dist/resources/templates/nuxt/.github/workflows/ci.yml +34 -0
- package/dist/resources/templates/nuxt/.husky/commit-msg +1 -0
- package/dist/resources/templates/nuxt/.husky/pre-commit +1 -0
- package/dist/resources/templates/nuxt/.nvmrc +1 -0
- package/dist/resources/templates/nuxt/.prettierignore +9 -0
- package/dist/resources/templates/nuxt/.prettierrc +1 -0
- package/dist/resources/templates/nuxt/README.md +23 -0
- package/dist/resources/templates/nuxt/_gitignore +7 -0
- package/dist/resources/templates/nuxt/app/app.config.ts +105 -0
- package/dist/resources/templates/nuxt/app/i18n/locales/en.json +5 -0
- package/dist/resources/templates/nuxt/app/pages/app/index.vue +224 -0
- package/dist/resources/templates/nuxt/app/pages/authors/[slug].vue +37 -0
- package/dist/resources/templates/nuxt/app/pages/blog/[slug].vue +55 -0
- package/dist/resources/templates/nuxt/app/pages/blog/category/[category].vue +38 -0
- package/dist/resources/templates/nuxt/app/pages/blog/index.vue +46 -0
- package/dist/resources/templates/nuxt/app/pages/index.vue +626 -0
- package/dist/resources/templates/nuxt/app/pages/legal/privacy.vue +21 -0
- package/dist/resources/templates/nuxt/app/pages/legal/terms.vue +21 -0
- package/dist/resources/templates/nuxt/app/pages/ui/index.vue +397 -0
- package/dist/resources/templates/nuxt/commitlint.config.mjs +1 -0
- package/dist/resources/templates/nuxt/eslint.config.mjs +4 -0
- package/dist/resources/templates/nuxt/lint-staged.config.mjs +8 -0
- package/dist/resources/templates/nuxt/nuxt.config.ts +71 -0
- package/dist/resources/templates/nuxt/package.json +67 -0
- package/dist/resources/templates/nuxt/public/apple-touch-icon.png +0 -0
- package/dist/resources/templates/nuxt/public/favicon.ico +0 -0
- package/dist/resources/templates/nuxt/public/favicon.svg +5 -0
- package/dist/resources/templates/nuxt/public/icon-16x16.png +0 -0
- package/dist/resources/templates/nuxt/public/icon-192x192.png +0 -0
- package/dist/resources/templates/nuxt/public/icon-32x32.png +0 -0
- package/dist/resources/templates/nuxt/public/icon-512x512.png +0 -0
- package/dist/resources/templates/nuxt/public/logo.svg +6 -0
- package/dist/resources/templates/nuxt/public/media/hero-product-gallery.png +0 -0
- package/dist/resources/templates/nuxt/server/api/content/sitemap-urls.get.ts +12 -0
- package/dist/resources/templates/nuxt/server/api/preview.get.ts +9 -0
- package/dist/resources/templates/nuxt/stylelint.config.mjs +3 -0
- package/dist/resources/templates/nuxt/test/e2e/rendering.test.ts +13 -0
- package/dist/resources/templates/nuxt/tsconfig.json +3 -0
- package/dist/resources/templates/nuxt/vitest.config.ts +8 -0
- package/package.json +16 -12
|
@@ -0,0 +1,1139 @@
|
|
|
1
|
+
# {{projectName}} Backend
|
|
2
|
+
|
|
3
|
+
> **适用范围**:本文件完整继承 OneX Go 工程标准。产品后端的运行入口为 `cmd/product-api` 和 `cmd/worker`;平台 Identity、IAM、交易、Viewer、产品注册表及其服务拓扑属于上游 OneX。文中的平台模块、表、路由和运维命令按该所有者解释,不表示本仓库重新实现平台业务。通用编码、安全、事务、审计、事件、契约、测试规则均适用于本仓库。具体运行命令以本仓库 `Makefile` 为准,平台专属质量门禁在 OneX 执行。
|
|
4
|
+
> **规则优先级**:用户明确范围与语义影响分级优先;审计路径遵循本文 Audit 的事件信封或请求上下文二选一,不产生重复记录。
|
|
5
|
+
> **来源**:`docs/standards-provenance.json` 记录原文版本、完整性和逐项适配。
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
> **Stack**: Go 1.24 | Gin | GORM | PostgreSQL 16 | Redis 7.0 | Asynq
|
|
9
|
+
> **Architecture**: Full-stack Monorepo (backend/ + frontend/ + admin/) — 无自建网关,使用 APISIX
|
|
10
|
+
> **Products**: Multi-product platform architecture (unified platform API + external product BFFs routed by APISIX)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Product Backend Commands
|
|
15
|
+
|
|
16
|
+
Run in the generated backend directory:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
make build
|
|
20
|
+
make test
|
|
21
|
+
make test-cov
|
|
22
|
+
make test-race
|
|
23
|
+
make lint
|
|
24
|
+
make wire
|
|
25
|
+
make run-api
|
|
26
|
+
make run-worker
|
|
27
|
+
make migrate-up
|
|
28
|
+
make migrate-down
|
|
29
|
+
make migrate-create name=create_resource
|
|
30
|
+
make migrate-version
|
|
31
|
+
make docker-dev
|
|
32
|
+
make docker-down
|
|
33
|
+
make openapi-install
|
|
34
|
+
make openapi-generate
|
|
35
|
+
make openapi-check
|
|
36
|
+
make openapi-test
|
|
37
|
+
make openapi-sdk
|
|
38
|
+
make openapi-breaking
|
|
39
|
+
make openapi-conformance
|
|
40
|
+
make openapi-clean-deletion
|
|
41
|
+
make test-migrations
|
|
42
|
+
make check
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Upstream Platform Commands
|
|
46
|
+
|
|
47
|
+
> 所有 `make` 命令在 `backend/` 目录下执行。
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Development (run from backend/)
|
|
51
|
+
make run-platform # Platform API (:3002)
|
|
52
|
+
make run-worker # Worker (daemon)
|
|
53
|
+
make docker-dev # Docker 全栈开发环境(Air 热重载,不需要手动 rebuild)
|
|
54
|
+
make docker-down # 停止 Docker 开发环境
|
|
55
|
+
|
|
56
|
+
# Build (run from backend/)
|
|
57
|
+
make build # 构建全部 cmd/
|
|
58
|
+
make build-platform # 构建 Platform API
|
|
59
|
+
make build-worker # 构建 Worker
|
|
60
|
+
|
|
61
|
+
# Database (run from backend/)
|
|
62
|
+
make migrate-up # 执行 golang-migrate 迁移
|
|
63
|
+
make migrate-down # 回滚上次迁移
|
|
64
|
+
make migrate-create name=xxx # 创建新迁移文件
|
|
65
|
+
|
|
66
|
+
# Quality (run from backend/)
|
|
67
|
+
make lint # golangci-lint run
|
|
68
|
+
make test # go test ./...
|
|
69
|
+
make test-cov # 覆盖率
|
|
70
|
+
make test-e2e # E2E 测试
|
|
71
|
+
|
|
72
|
+
# Code Generation (run from backend/)
|
|
73
|
+
make generate # 生成 Wire 与 OpenAPI 制品
|
|
74
|
+
make openapi-generate # 生成 OpenAPI 投影与运行时制品
|
|
75
|
+
make openapi-check # 校验契约、确定性与 tracked 制品
|
|
76
|
+
make openapi-breaking # 校验受保护基线与外部承诺兼容性
|
|
77
|
+
make wire # 生成 Wire 依赖注入代码
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Upstream Platform Project Structure
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
backend/ # Go 后端服务
|
|
86
|
+
cmd/
|
|
87
|
+
platform-api/ # 统一平台服务 (:3002) - Identity + Commerce + Credit + Quota
|
|
88
|
+
admin-api/ # Admin BFF (:3003) - 管理后台路由
|
|
89
|
+
worker/ # 后台任务 (Asynq consumer)
|
|
90
|
+
internal/
|
|
91
|
+
core/ # 基础设施
|
|
92
|
+
config/ database/ cache/ logger/ errors/ # 基础: 配置/DB/缓存/日志/错误
|
|
93
|
+
eventbus/ queue/ httpclient/ # 通信: 事件总线/队列/HTTP
|
|
94
|
+
health/ schedule/ observability/ # 运维: 健康检查/定时/可观测性
|
|
95
|
+
resilience/ distlock/ # 弹性: 熔断重试/分布式锁
|
|
96
|
+
common/ # 通用工具
|
|
97
|
+
middleware/ response/ validator/ types/ bffadapter/
|
|
98
|
+
platform/ # 平台领域
|
|
99
|
+
identity/ # 认证 (JWT + OAuth2)
|
|
100
|
+
user/ # 用户管理
|
|
101
|
+
notification/ # 通知系统
|
|
102
|
+
tenant/ # 租户管理
|
|
103
|
+
sharedevents/ # 跨模块事件契约
|
|
104
|
+
pkg/
|
|
105
|
+
apierror/ # 可导出的 API 错误定义
|
|
106
|
+
migrations/ # SQL 迁移文件 (golang-migrate)
|
|
107
|
+
configs/ # 配置文件 (YAML)
|
|
108
|
+
deployments/ # Docker Compose / K8s
|
|
109
|
+
frontend/ # 前端应用 (planned)
|
|
110
|
+
admin/ # 管理后台 (planned)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
产品业务代码、产品 BFF 和产品专属数据模型位于独立产品仓库,不属于 OneX `backend/internal/`。
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Import Path
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
module: {{module}}
|
|
121
|
+
|
|
122
|
+
{{module}}/internal/core/config
|
|
123
|
+
{{module}}/internal/core/database
|
|
124
|
+
github.com/onex-platform/backend/internal/platform/identity
|
|
125
|
+
{{module}}/pkg/apierror
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Key Patterns
|
|
131
|
+
|
|
132
|
+
- **APISIX Gateway**: 外部网关处理路由/限流/认证插件,Go 服务不自建网关
|
|
133
|
+
- **EventBus**: 进程内事件总线,跨模块通信解耦业务逻辑
|
|
134
|
+
- **Google Wire**: 编译时依赖注入,显式依赖图
|
|
135
|
+
- **Zap**: 结构化日志,高性能
|
|
136
|
+
- **GORM**: ORM,Entity 与 Response VO 严格分离
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Upstream Platform Environment Variables
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD
|
|
144
|
+
REDIS_HOST, REDIS_PORT, REDIS_PASSWORD
|
|
145
|
+
IDENTITY_ACCESS_TOKEN_ISSUER, IDENTITY_ACCESS_TOKEN_SIGNING_KEY_ID
|
|
146
|
+
IDENTITY_ACCESS_TOKEN_PRIVATE_KEY_PATH, IDENTITY_ACCESS_TOKEN_PUBLIC_KEY_DIRECTORY
|
|
147
|
+
IDENTITY_OAUTH_STATE_SIGNING_SECRET, IDENTITY_PHONE_HASH_KEY
|
|
148
|
+
OPENAI_API_KEY, OPENAI_BASE_URL
|
|
149
|
+
ANTHROPIC_API_KEY
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Code Rules Reference
|
|
155
|
+
|
|
156
|
+
规范模板在 `backend/.claude/docs/` 目录中。
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
# AI Execution Standard & Coding Rules
|
|
162
|
+
|
|
163
|
+
> Part 1: AI Execution Flow (Phase 1–5) — mandatory workflow for every task
|
|
164
|
+
> Part 2: Coding Rules — project standards and forbidden patterns
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Phase 1: Understand
|
|
169
|
+
|
|
170
|
+
Before touching any code, fully understand the task and its context.
|
|
171
|
+
|
|
172
|
+
### 1.1 Read project documentation
|
|
173
|
+
|
|
174
|
+
1. Read this file (execution flow + coding rules)
|
|
175
|
+
2. Read `backend/.claude/docs/` specifications relevant to the task (api-design, module-standards, database, error-handling, etc.)
|
|
176
|
+
3. Read existing architecture docs in `docs/platform/` for affected modules
|
|
177
|
+
4. Read the CLAUDE.md of each affected cmd/ app if applicable
|
|
178
|
+
|
|
179
|
+
**Read the specs BEFORE designing. Do not rely on memory or assumptions about project conventions.**
|
|
180
|
+
|
|
181
|
+
### 1.2 Scope the impact
|
|
182
|
+
|
|
183
|
+
For each task, answer:
|
|
184
|
+
|
|
185
|
+
- Which modules are affected?
|
|
186
|
+
- Which database tables are affected?
|
|
187
|
+
- Which API endpoints are affected?
|
|
188
|
+
- Does data cross a module boundary?
|
|
189
|
+
- Does this affect Wire dependency graph? Which cmd/ apps need regeneration?
|
|
190
|
+
|
|
191
|
+
### 1.3 Read all affected source files
|
|
192
|
+
|
|
193
|
+
Read every file that will be modified. Understand:
|
|
194
|
+
|
|
195
|
+
- Current function signatures and parameter types
|
|
196
|
+
- Current data structures and their behavior
|
|
197
|
+
- Current data flow: request → handler → service → repository → response
|
|
198
|
+
- Existing patterns and conventions in the file
|
|
199
|
+
|
|
200
|
+
**DO NOT modify any file you have not read in this session. DO NOT assume what code looks like from memory.**
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Phase 2: Design
|
|
205
|
+
|
|
206
|
+
Design the solution BEFORE writing any code. This is the most important phase.
|
|
207
|
+
|
|
208
|
+
### 2.1 Research industry standards (MANDATORY)
|
|
209
|
+
|
|
210
|
+
**Before proposing ANY design, MUST first research industry best practices. This is NOT optional, NOT skippable.**
|
|
211
|
+
|
|
212
|
+
For every design decision, search and answer: "How does {industry leader} solve this exact problem?"
|
|
213
|
+
|
|
214
|
+
- Domain modeling → How do Stripe/Shopify/AWS model this entity?
|
|
215
|
+
- API design → How do Stripe API, GitHub API, Slack API handle this?
|
|
216
|
+
- Architecture pattern → How do large-scale Go projects structure this?
|
|
217
|
+
- State management → How do mature platforms handle this lifecycle?
|
|
218
|
+
|
|
219
|
+
**Research output MUST appear as a concrete table in the design document:**
|
|
220
|
+
|
|
221
|
+
```markdown
|
|
222
|
+
| Decision Point | Industry Pattern | Used By | Our Choice | Rationale |
|
|
223
|
+
|---------------|-----------------|---------|------------|-----------|
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Missing this table = design is incomplete = FORBIDDEN to implement.
|
|
227
|
+
|
|
228
|
+
**Refactoring / rewriting rule**: Do NOT propose removing, replacing, or rewriting existing architecture until you have analyzed what the current design was trying to do and how industry leaders solve the same problem. "This code looks wrong" is not sufficient justification — you must prove the alternative is the industry-accepted approach.
|
|
229
|
+
|
|
230
|
+
### 2.2 Define the target state
|
|
231
|
+
|
|
232
|
+
For each area the task touches, write out the exact desired end state. The design must include concrete code architecture, not just a list of changes.
|
|
233
|
+
|
|
234
|
+
Required for any design that touches code structure:
|
|
235
|
+
|
|
236
|
+
- **Entity / Model**: struct fields, behavior methods (signature + purpose), state machine transitions
|
|
237
|
+
- **Interface**: method signatures, which package defines it, which package implements it
|
|
238
|
+
- **API contract**: endpoint, method, request DTO fields, response VO fields, status codes
|
|
239
|
+
- **Database**: column name, type, nullable, default, comment — for every new/changed column
|
|
240
|
+
- **Data flow**: for every new field, trace: Source → DTO → Service → Repository → Entity → VO → Response
|
|
241
|
+
- **Directory structure**: which files go where, why this tier (Full DDD / Simplified / Flat)
|
|
242
|
+
|
|
243
|
+
**Be concrete, not vague.** "Add a status field" is insufficient. "Add `status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '订单状态'`, with transition map in `enum.go`, behavior methods `IsCancellable()` and `TransitionTo()` on model" is correct.
|
|
244
|
+
|
|
245
|
+
**Design the code architecture in the plan, not during implementation.** Implementation should be filling in a decided design, not making architecture decisions on the fly.
|
|
246
|
+
|
|
247
|
+
### 2.3 Validate the design
|
|
248
|
+
|
|
249
|
+
**Patch vs Design check**: Before proposing any fix, ask: "Am I fixing this in the ONE place that owns this responsibility, or am I adding a workaround somewhere else?" If the answer is workaround, find the right place first.
|
|
250
|
+
|
|
251
|
+
Check every design decision against the FORBIDDEN Patterns table and coding rules in this file. If any check fails, revise the design before proceeding.
|
|
252
|
+
|
|
253
|
+
For refactoring or major changes, validate each decision:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
Decision: {what you propose}
|
|
257
|
+
Principle: {which rule says the current state is wrong}
|
|
258
|
+
Evidence: {measurable proof — line count, grep count, constructor count}
|
|
259
|
+
Risk: {what could go wrong}
|
|
260
|
+
Mitigation: {how to handle the risk}
|
|
261
|
+
Verdict: PROCEED / REJECT / REVISE
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 2.4 Determine design document level
|
|
265
|
+
|
|
266
|
+
Classify the task and produce the corresponding design document. See `.claude/docs/architecture-template.md` for full skeleton.
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
New module or major architecture change?
|
|
270
|
+
→ Level A: full architecture document (all mandatory sections)
|
|
271
|
+
|
|
272
|
+
Touch 3+ files, or change schema/API?
|
|
273
|
+
→ Level B: focused design document
|
|
274
|
+
|
|
275
|
+
Otherwise?
|
|
276
|
+
→ Level C: fix design
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
| Level | What to produce | Destination |
|
|
280
|
+
|-------|----------------|-------------|
|
|
281
|
+
| **A** | Complete architecture document following `.claude/docs/architecture-template.md` | Plan file → archive to `docs/platform/` |
|
|
282
|
+
| **B** | Impact scope, industry reference, schema/model/API changes, data flow, implementation waves | Plan file |
|
|
283
|
+
| **C** | Root cause analysis, fix design, affected files, verification plan | Plan file |
|
|
284
|
+
|
|
285
|
+
**Every section must be concrete.** Column/Type/Nullable/Default/Comment for every DB field. Method signature, parameters, return type, business rule for every method.
|
|
286
|
+
|
|
287
|
+
### 2.5 Decision Autonomy
|
|
288
|
+
|
|
289
|
+
**Inherited from `CLAUDE.md` Phase 2.2.** AI MUST make standard technical decisions autonomously when industry consensus exists. NEVER ask the user to choose between options that have a clear industry-standard answer.
|
|
290
|
+
|
|
291
|
+
Go-specific autonomous decisions (NEVER ask):
|
|
292
|
+
|
|
293
|
+
- Repository pattern vs direct DB → Repository (Go DDD standard)
|
|
294
|
+
- Table-driven tests vs individual → Table-driven (Go standard)
|
|
295
|
+
- `context.Context` as first param → Yes (Go standard)
|
|
296
|
+
- Error wrapping style → `fmt.Errorf("operation: %w", err)` (Go 1.13+ standard)
|
|
297
|
+
- Struct validation → `binding` tags on DTO, behavior methods on model (project convention)
|
|
298
|
+
- Module tier selection → Apply the tier decision flow in this file, do NOT ask user
|
|
299
|
+
|
|
300
|
+
### 2.6 Self-Review Gate (MANDATORY)
|
|
301
|
+
|
|
302
|
+
**Inherited from `CLAUDE.md` Phase 2.6.** After completing ANY design or implementation, MUST self-review BEFORE presenting to user.
|
|
303
|
+
|
|
304
|
+
Go-specific self-review additions:
|
|
305
|
+
|
|
306
|
+
1. Does every new/changed struct field trace through the full data flow? (DTO → Service → Entity → VO)
|
|
307
|
+
2. Does Wire dependency graph remain acyclic?
|
|
308
|
+
3. Are all new exported types documented with GoDoc (Chinese)?
|
|
309
|
+
4. Does every handler call only Service methods (A1/A2 compliance)?
|
|
310
|
+
5. Does every write Service method carry DomainEvent audit fields or call `audit.Enrich()` when no event is published (AU1 compliance)?
|
|
311
|
+
|
|
312
|
+
### 2.7 Present design for approval
|
|
313
|
+
|
|
314
|
+
**Approval gate applies ONLY to Level A designs (new modules, major architecture changes).**
|
|
315
|
+
|
|
316
|
+
Level B/C: present design summary, proceed to implementation immediately.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Phase 3: Plan
|
|
321
|
+
|
|
322
|
+
After design passes self-review (and user approval for Level A), create the implementation plan.
|
|
323
|
+
|
|
324
|
+
### 3.1 List all file modifications
|
|
325
|
+
|
|
326
|
+
For each file: exact path, what function/section to change, what the change is.
|
|
327
|
+
|
|
328
|
+
### 3.2 Map dependencies
|
|
329
|
+
|
|
330
|
+
Determine which changes depend on which others. A change depends on another if it imports, references, or extends something defined in the other.
|
|
331
|
+
|
|
332
|
+
### 3.3 Group into dependency waves
|
|
333
|
+
|
|
334
|
+
Group changes so that each wave only depends on previous waves being complete. Within each wave, independent changes can be executed in parallel.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Phase 4: Implement
|
|
339
|
+
|
|
340
|
+
### 4.1 Execute wave by wave
|
|
341
|
+
|
|
342
|
+
For each wave:
|
|
343
|
+
|
|
344
|
+
1. Implement all changes in the wave (parallel if independent)
|
|
345
|
+
2. After the wave completes, verify types are consistent with previous waves
|
|
346
|
+
3. Proceed to next wave only when current wave is clean
|
|
347
|
+
|
|
348
|
+
### 4.2 Read before write (always)
|
|
349
|
+
|
|
350
|
+
Before modifying each file:
|
|
351
|
+
|
|
352
|
+
1. Read its current content
|
|
353
|
+
2. Confirm your change is compatible with what is actually there
|
|
354
|
+
3. Then make the modification
|
|
355
|
+
|
|
356
|
+
### 4.3 Follow existing patterns
|
|
357
|
+
|
|
358
|
+
When adding code to an existing file, match:
|
|
359
|
+
|
|
360
|
+
- Naming conventions already used in that file
|
|
361
|
+
- Code style and formatting patterns
|
|
362
|
+
- Comment language and level of detail
|
|
363
|
+
- Import organization
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Phase 5: Verify
|
|
368
|
+
|
|
369
|
+
### 5.1 Build
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
go build ./...
|
|
373
|
+
go vet ./...
|
|
374
|
+
make wire # regenerate all affected cmd/
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
All MUST pass. If any fails, fix and rebuild.
|
|
378
|
+
|
|
379
|
+
### 5.2 Design conformance check
|
|
380
|
+
|
|
381
|
+
After build passes, verify implementation matches the approved design. Every item in the design document must be implemented. No silent omissions.
|
|
382
|
+
|
|
383
|
+
### 5.3 Quantitative verification
|
|
384
|
+
|
|
385
|
+
Grep for expected outcomes with concrete numbers. "It compiles" is necessary but not sufficient. Count the things that should exist.
|
|
386
|
+
|
|
387
|
+
### 5.4 Rule conformance check
|
|
388
|
+
|
|
389
|
+
Re-validate against the FORBIDDEN Patterns table below with the actual implemented code. If any rule is violated, fix before marking complete.
|
|
390
|
+
|
|
391
|
+
### 5.5 Automated review loop (Level A/B)
|
|
392
|
+
|
|
393
|
+
**Inherited from `CLAUDE.md` Phase 5.5.** For Level A/B deliveries, dispatch review agents and iterate until zero findings BEFORE presenting to user.
|
|
394
|
+
|
|
395
|
+
Go-specific review dimensions:
|
|
396
|
+
|
|
397
|
+
- FORBIDDEN pattern scan (S/A/T/C/H/D/AU/E rules)
|
|
398
|
+
- Wire dependency graph validity
|
|
399
|
+
- Handler → Service → Repository layering compliance
|
|
400
|
+
- Audit enrichment coverage (every write op)
|
|
401
|
+
- Industry standard alignment (compare against cited references)
|
|
402
|
+
|
|
403
|
+
**The user should receive a clean, reviewed deliverable. If the user has to point out "this doesn't follow the Stripe pattern you cited", that is a Phase 5.5 failure.**
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
# Part 2: Coding Rules
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Code Separation (CRITICAL)
|
|
413
|
+
|
|
414
|
+
BEFORE writing ANY code, check: does it contain constants, types, enums, request/response structs, or error definitions?
|
|
415
|
+
If YES, place them in the correct file. NEVER inline complex type definitions in handler/service files.
|
|
416
|
+
|
|
417
|
+
| Content | NEVER In | ALWAYS In |
|
|
418
|
+
|---------|----------|-----------|
|
|
419
|
+
| constant / magic number (shared) | service, handler | `{package}/constants.go` 或模块级 `const` 块 |
|
|
420
|
+
| constant (single consumer) | — | co-locate in consumer file as unexported `const` |
|
|
421
|
+
| interface (contract) | infrastructure, handler | 消费方所在 package(Go 惯例:接口由使用者定义) |
|
|
422
|
+
| enum (iota / string const) | service, handler | `{package}/enum.go` |
|
|
423
|
+
| type alias | service, handler | `{package}/types.go` |
|
|
424
|
+
| Request struct (user) | handler | `{package}/dto.go` |
|
|
425
|
+
| Request struct (admin) | handler | `{package}/dto_admin.go` |
|
|
426
|
+
| Response VO (user) | anywhere mixed | `{package}/vo.go` (struct + `ToXxxResponse()`) |
|
|
427
|
+
| Response VO (admin) | anywhere mixed | `{package}/vo_admin.go` (struct + `ToAdminXxxResponse()`) |
|
|
428
|
+
| Error sentinel/type | service | `{package}/errors.go` 或 `pkg/apierror/` |
|
|
429
|
+
| Event payload | service | `internal/sharedevents/events.go` |
|
|
430
|
+
| Pagination params | DTO (inline) | Embed `types.PageParams`, `types.AdminPageParams`, or explicitly justified `types.BulkPageParams` from `common/types` |
|
|
431
|
+
|
|
432
|
+
Go 惯例:相关类型可以按领域分组在同一文件中。例如 `domain/model.go` 可包含同一聚合根的多个 struct。
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## File Naming
|
|
437
|
+
|
|
438
|
+
MUST use snake_case (Go 标准)。Go 中不使用类型后缀命名文件,而是按职责命名。
|
|
439
|
+
|
|
440
|
+
| 职责 | File Pattern | Type Pattern |
|
|
441
|
+
|------|-------------|--------------|
|
|
442
|
+
| HTTP Handler (user) | `handler.go` | `XxxHandler` struct |
|
|
443
|
+
| HTTP Handler (admin) | `handler_admin.go` | `XxxAdminHandler` struct |
|
|
444
|
+
| Service | `service.go` 或 `{name}_service.go` | `XxxService` struct |
|
|
445
|
+
| Repository 接口 | `repository.go` (in domain/) | `XxxRepository` interface |
|
|
446
|
+
| Repository 实现 | `persistence.go` (in infrastructure/) | `xxxRepository` struct (unexported) |
|
|
447
|
+
| Request DTO (user) | `dto.go` | `CreateXxxRequest`, `UpdateXxxRequest` |
|
|
448
|
+
| Request DTO (admin) | `dto_admin.go` | `AdminCreateXxxRequest`, `AdminListXxxRequest` |
|
|
449
|
+
| Response VO (user) | `vo.go` | `XxxResponse` struct + `ToXxxResponse()` |
|
|
450
|
+
| Response VO (admin) | `vo_admin.go` | `AdminXxxResponse` struct + `ToAdminXxxResponse()` |
|
|
451
|
+
| Domain Model | `model.go` | `Xxx` struct (PascalCase, NO suffix) |
|
|
452
|
+
| Enum | `enum.go` | `XxxStatus` type + `const` iota 或 string |
|
|
453
|
+
| Constants | `constants.go` | `const` 块 |
|
|
454
|
+
| Error | `errors.go` | `ErrXxxNotFound` sentinel 或 `XxxError` type |
|
|
455
|
+
| Event | `events.go` (in sharedevents/) | `XxxEvent` struct |
|
|
456
|
+
| Listener | `listener.go` | `XxxListener` struct |
|
|
457
|
+
| Middleware | `{name}.go` (in middleware/) | `XxxMiddleware()` func |
|
|
458
|
+
| Wire Provider | `wire.go` | `InitializeXxx()` injector |
|
|
459
|
+
| Wire Provider Set | `provider.go` | `ProviderSet = wire.NewSet(...)` |
|
|
460
|
+
| Test | `{name}_test.go` | `TestXxx`, `BenchmarkXxx` |
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## FORBIDDEN Patterns (CRITICAL)
|
|
465
|
+
|
|
466
|
+
Violation of ANY rule blocks commit. No exceptions.
|
|
467
|
+
|
|
468
|
+
| ID | Cat | Pattern | Fix |
|
|
469
|
+
|----|-----|---------|-----|
|
|
470
|
+
| S1 | Security | Hardcoded secrets in source | Move to config/env, access via `Config` struct |
|
|
471
|
+
| S2 | Security | Dynamic code execution (`reflect` for dispatch) | Use static mapping (map / switch) |
|
|
472
|
+
| S3 | Security | Sensitive data in logs | Log IDs only, NEVER passwords/tokens/PII |
|
|
473
|
+
| S4 | Security | `os.Getenv()` in business code | Use `Config` struct, load once at startup |
|
|
474
|
+
| A1 | Arch | Business logic in handler | Move ALL logic to Service |
|
|
475
|
+
| A2 | Arch | Repository/DB ops in handler | Call Service methods only |
|
|
476
|
+
| A3 | Arch | Direct cross-package type import bypassing interface | Define interface in consumer package |
|
|
477
|
+
| A4 | Arch | Circular package import | Extract shared types to third package |
|
|
478
|
+
| A5 | Arch | Flat directory (all files in `cmd/*/`) | Organize into `internal/` subpackages |
|
|
479
|
+
| A6 | Arch | Silent init failure (swallowed error) | MUST return error, caller decides |
|
|
480
|
+
| A7 | Arch | `init()` with side effects | Explicit initialization in `main()` or Wire |
|
|
481
|
+
| A8 | Arch | Direct `http.ResponseWriter` manipulation in handler | Use Gin context helpers (`c.JSON()`, `c.Status()`) |
|
|
482
|
+
| A11 | Arch | Gateway plugin callback (`forward-auth` / internal auth bridge / similar) directly targeting a single backend node (`http://service:port/...`) | Route through APISIX internal route or shared upstream policy. Gateway-side callbacks MUST NOT bypass upstream health checks, retries, and load-balancing |
|
|
483
|
+
| A12 | Arch | Production public API exposed behind APISIX with single-node upstream or without APISIX health checks | Use multi-node upstreams with active + passive health checks, explicit timeout/retry policy, and keepalive pool. Single-node upstream is allowed only in local dev hot-reload compose |
|
|
484
|
+
| T1 | Type | `interface{}` / `any` without comment | Add concrete type or justification comment |
|
|
485
|
+
| T2 | Type | Type definitions in service file | Move to `model.go`, `dto.go`, `vo.go`, `enum.go` |
|
|
486
|
+
| T3 | Type | Numeric enum without string method | Use `String()` method 或 string const |
|
|
487
|
+
| T4 | Type | Manual singleton (`sync.Once` + global) | Use Wire DI, single instance by construction |
|
|
488
|
+
| T5 | Type | Exported unexported-type method | Keep type/method export level consistent |
|
|
489
|
+
| C1 | Contract | `return entity` from service to handler | Use `return ToXxxResponse(entity)` |
|
|
490
|
+
| C2 | Contract | `{ "code": 200, "data": ... }` wrapper | Return data directly, use HTTP status codes |
|
|
491
|
+
| C3 | Contract | Response struct as request input | Separate Request (dto.go) and Response (vo.go) |
|
|
492
|
+
| C4 | Contract | Chinese in error messages | English ONLY for all error messages |
|
|
493
|
+
| C5 | Contract | Hardcoded numeric error codes | Use string `ErrorCode` const |
|
|
494
|
+
| H1 | Hygiene | `fmt.Println` / `log.Println` | Use `zap.Logger` |
|
|
495
|
+
| H2 | Hygiene | Non-snake_case filename | Rename to `snake_case.go` |
|
|
496
|
+
| H3 | Hygiene | Panic in handler/service | Return error, middleware handles recovery |
|
|
497
|
+
| H4 | Hygiene | Commented-out code | Delete; git has history |
|
|
498
|
+
| H5 | Hygiene | Unused/speculative code (YAGNI) | Delete; write only what is needed NOW |
|
|
499
|
+
| H6 | Hygiene | Exported type from `internal/` that should be unexported | Unexport; only export what other internal packages need |
|
|
500
|
+
| H7 | Hygiene | Naked return in named return function > 5 lines | Use explicit return |
|
|
501
|
+
| H8 | Hygiene | Underscore import `_` without comment | Add `// for side-effect: xxx` comment |
|
|
502
|
+
| A9 | Arch | ANY re-export in `provider.go` (`var NewFoo = sub.NewFoo` or `type X = sub.X`) | `provider.go` ONLY contains `wire.NewSet(...)` and Wire helper functions. ZERO re-exports of any kind. Callers MUST import the actual sub-package directly |
|
|
503
|
+
| A10 | Arch | Handler/Service/Model in module root directory | Business code MUST go into domain/ application/ infrastructure/ subdirectories |
|
|
504
|
+
| D1 | Deferred | `// TODO:` / `// 待后续` / `// 暂跳过` / `// 暂设` / `// 后续实现` in committed code | Either implement completely OR return explicit error. NEVER defer implementation with comments |
|
|
505
|
+
| D2 | Deferred | Fallback branch for "backward compatibility" / "兼容历史数据" | Single code path only. If format changed, migrate data. NEVER add fallback parsing |
|
|
506
|
+
| D3 | Deferred | `// 迁移完成后应移除` / "should be removed after migration" | Remove NOW. Code that "should be removed later" will never be removed |
|
|
507
|
+
| D4 | Deferred | Try-decrypt, if fail try-plaintext pattern | Decrypt or fail. NEVER silently fall back to unencrypted path |
|
|
508
|
+
| D5 | Deferred | Silent skip of unimplemented feature (`// 暂跳过`, empty handler, log-and-return-nil) | Return typed error (`apierror.BadRequest` / `fmt.Errorf`) for unsupported operations. Caller decides |
|
|
509
|
+
| D6 | Deferred | Noop/stub implementation returning `nil, nil` or hardcoded values for production interfaces | Implement fully OR define proper error: `fmt.Errorf("{operation} is not implemented")` |
|
|
510
|
+
| D7 | Deferred | `placeholder` / `stub` / `dummy` / `mock` in non-test production code | Only in `_test.go` files. Production code must be complete |
|
|
511
|
+
| AU1 | Audit | Service write operation missing audit evidence | Set DomainEvent `AuditChanges`, or call `audit.Enrich(ctx, changes, metadata)` after successful write when no event is published; never both |
|
|
512
|
+
| AU2 | Audit | `audit.Enrich()` called on error path (before write succeeds) | Call ONLY after operation **succeeds**, NEVER before error return |
|
|
513
|
+
| AU3 | Audit | `audit.Enrich()` called in Handler | Audit enrichment is business logic, MUST be called in Service layer |
|
|
514
|
+
| E1 | Enforce | Discovered violation deferred with "先不折腾" / "后续再改" / "合理轻量 pattern" | Fix NOW. Rules have zero exceptions. No "thin CRUD exemption" |
|
|
515
|
+
| E2 | Enforce | Code delivered with known violations and a plan to "fix later" | ALL violations MUST be fixed BEFORE delivery. No partial compliance |
|
|
516
|
+
| E3 | Enforce | Fix applied as patch/workaround without proper design thinking | Every fix must be architecturally sound. No band-aids |
|
|
517
|
+
| E4 | Enforce | Discovered bug/violation excluded with "不在本次范围" / "是新功能" | If the scan found it, fix it. Scope is defined by what's broken, not by what you planned to do |
|
|
518
|
+
| E5 | Enforce | Runtime conversion/adapter to "兼容已存数据" when no production data exists | Change the source format directly. Compatibility layers are only for live production data |
|
|
519
|
+
| IS1 | Identity | Backend handler reads business decision state from `c.GetHeader("X-User-Content-*")` / `c.GetHeader("X-User-Tier-*")` / similar plaintext business header injected by gateway | 通过 `principal.MustGet(c)` / `principal.Get(c)` 读取 backend 自己 resolve 的状态。Identity header(X-User-Public-ID/X-Identity-ID/X-Session-ID)仍由 middleware 解析进 ctx,不直接被业务读 |
|
|
520
|
+
| IS2 | Identity | 同一权益 / 决策 concept 暴露 2+ resolve 路径(service / header / wrapper / sidecar)让 handler 自由选择 | One owner per concept:内容访问唯一调 `accessPolicySvc.ResolveContentAccess`;entitlement limit 唯一调 `EntitlementService.GetLimit`。新增功能必须复用现有 service,不得新建并行 reader |
|
|
521
|
+
| IS3 | Reliability | EventBus listener 关键 side-effect(cache invalidate / projection update / outbox dispatch)失败仅 `Warn` 不 `return err` | `return fmt.Errorf("...: %w", err)` — 让 outbox 自动 retry 直至最终一致。模板:`handleSubscriptionEvent` (`listener.go:85-108`) |
|
|
522
|
+
| IS4 | Cache | `cache.Delete(...)` followed by `cache.Set(...)` (race-prone two-step invalidation) | 单次 `cache.Set(...)` 覆盖即可;写入失败 fail-fast。无 Delete 步骤 |
|
|
523
|
+
| IS5 | Gateway | APISIX / 网关注入"非 identity"业务决策 header 给 backend 信任(违反 Stripe / GitHub / AWS 共识) | 网关只注入 identity(X-User-Public-ID/X-Identity-ID/X-Session-ID),并 strip 客户端伪造的同名 header;业务决策(content access / permissions / tier)由 backend middleware 自己 resolve |
|
|
524
|
+
| DB1 | Database | `FOREIGN KEY` constraint in migration | FORBIDDEN. Application layer ensures integrity. FK causes cascade locks, DDL blocking, sharding incompatibility |
|
|
525
|
+
| DB2 | Database | `SELECT *` in queries | ALWAYS specify columns: `Select("id", "name", "email")` |
|
|
526
|
+
| DB3 | Database | Unbounded list query without pagination | ALWAYS paginate. NEVER return all rows |
|
|
527
|
+
| DB4 | Database | Production/DMS SQL uses unverified dialect-sensitive constructs such as `BEGIN`/`COMMIT`, `CROSS JOIN LATERAL (VALUES ...) AS alias(...)`, positional anonymous row output, or multi-statement repair scripts | Use a single PostgreSQL statement compatible with the production SQL runner; validate syntax locally against PostgreSQL before handing it to operators |
|
|
528
|
+
| R1 | Reuse | `gin.H{}` for API list/paginated response | Use `response.NewPaginated()` or `response.NewList()`. Custom structures use VO struct, NEVER build JSON with `gin.H` |
|
|
529
|
+
| R2 | Reuse | `string` type for sort order field | Use `types.SortOrder` from `common/types`. DTO field type + service comparisons MUST use `types.SortOrderASC` / `types.SortOrderDESC` |
|
|
530
|
+
| R3 | Reuse | Inline pagination normalization `if page < 1 { page = 1 }` duplicated | Extract shared helper or use `types.PaginationQuery.Normalize()`. Same logic MUST NOT appear 2+ times in one module |
|
|
531
|
+
| R4 | Reuse | Manual error construction `c.JSON(400, gin.H{"error": ...})` | Use `response.Error(c, apierror.BadRequest(...))` and other `pkg/apierror` constructors |
|
|
532
|
+
| R5 | Reuse | Custom pagination metadata struct (e.g. `PaginationInfo`) | Use `response.PaginationMeta` from `common/response/pagination.go`. NEVER redefine pagination structs |
|
|
533
|
+
| R6 | Reuse | Handler manually parsing `c.Query()` when DTO already defined | Use `c.ShouldBindQuery(&req)` / `c.ShouldBindUri(&req)` to bind existing DTO struct |
|
|
534
|
+
| R7 | Reuse | Handler returning raw `[]T` slice for list endpoint | ALL list endpoints MUST return `response.NewPaginated()` or `response.NewList()`. Service signature: `(items, total, error)`. DTO MUST embed `types.PageParams`, `types.AdminPageParams`, or explicitly justified `types.BulkPageParams` |
|
|
535
|
+
| R8 | Reuse | Repeating in-memory "keyword filter + page slice" helpers inside a module | Reuse `internal/common/utils.FilterAndPaginate()`; the module provides only the matcher. `common/response/pagination.go` owns HTTP response shape only and MUST NOT contain query/filter logic |
|
|
536
|
+
| L1 | Lint | `ALL_CAPS` constant name (e.g. `QUOTA_INSUFFICIENT`) | Use Go CamelCase with `ErrCode` prefix: `ErrCodeQuotaInsufficient`. String VALUE stays ALL_CAPS |
|
|
537
|
+
| L2 | Lint | `err == gorm.ErrRecordNotFound` direct comparison | Use `errors.Is(err, gorm.ErrRecordNotFound)` — supports wrapped errors |
|
|
538
|
+
| L3 | Lint | `for _, item := range largeStructSlice` (copies struct each iteration) | Use `for i := range slice` + `slice[i]` or `item := &slice[i]` |
|
|
539
|
+
| L4 | Lint | `sb.WriteString(fmt.Sprintf(...))` | Use `fmt.Fprintf(sb, ...)` directly |
|
|
540
|
+
| L5 | Lint | Variable name shadows Go builtin (`copy`, `new`, `len`, `max`, `min`) | Rename: `copy` → `taskCopy`, `new` → `current`, `max` → `maxLimit` |
|
|
541
|
+
| L6 | Lint | `http.NewRequest(...)` without context | Use `http.NewRequestWithContext(ctx, ...)` |
|
|
542
|
+
| L7 | Lint | `math/rand` for non-security purposes without `//nolint:gosec` | Add `//nolint:gosec // {reason}` comment explaining why crypto/rand is not needed |
|
|
543
|
+
| I1 | Infra | Inline `Page int` + `PageSize int` in DTO | Embed `types.PageParams` (user), `types.AdminPageParams` (admin max=200), or `types.BulkPageParams` (explicit high-volume read max=1000). NEVER define pagination fields manually |
|
|
544
|
+
| I2 | Infra | Inline `(page-1)*pageSize` offset calculation | Use `.Offset()` from embedded PageParams/AdminPageParams/BulkPageParams/PaginationQuery |
|
|
545
|
+
| I3 | Infra | `gin.H{}` in handler response body | Use VO struct (vo.go) or anonymous struct. gin.H FORBIDDEN in handlers (R1 also covers list responses) |
|
|
546
|
+
| I4 | Infra | `response.ValidationError(c, fmt.Errorf(...))` for missing required params | Use `response.Error(c, apierror.BadRequest(apierror.CodeValidationFailed, "..."))` or add `middleware.RequireProductCode()` at route level |
|
|
547
|
+
| I5 | Infra | `c.Get("key")` to read context values in handler | Use middleware helpers: `middleware.GetUserID(c)`, `middleware.GetProductCode(c)`, etc. |
|
|
548
|
+
| I6 | Infra | DTO-local custom `Normalize()`/`PageOrDefault()` for pagination | DELETE custom methods. Delegate to the shared `PageParams.Normalize()`, `AdminPageParams.Normalize()`, or `BulkPageParams.Normalize()` implementation |
|
|
549
|
+
| I7 | Infra | External list/search HTTP DTO exposes a generic `limit` parameter | Standard pagination MUST use `types.PageParams` / `types.AdminPageParams` / explicitly justified `types.BulkPageParams`; external contracts allow only `page` + `page_size`. `limit` is allowed only as an explicit domain config field (for example `default_limit`, `max_items`), never as a list-query pagination parameter |
|
|
550
|
+
| TX1 | Transaction | `r.db.WithContext(ctx)` in Repository/Persistence | Use `database.GetDB(ctx, r.db)` — ensures transaction participation |
|
|
551
|
+
| TX2 | Transaction | `middleware.WithTransaction` / `middleware.DBOrTx` | Use `database.TransactionManager.WithTransaction` — single mechanism |
|
|
552
|
+
| TX3 | Transaction | Multiple transaction context keys | Single `database.ctxTxKey{}` — one mechanism, one key |
|
|
553
|
+
| N1 | Enum | 管理端展示或选择使用的枚举值、标签、颜色在 registry 外重复维护 | 在 `internal/common/enums/register_xxx.go` 引用 owner 常量注册,并测试实际消费的值集合。私有实现类型和机器协议值不要求注册为管理端选项 |
|
|
554
|
+
| N2 | Reference | `ReferenceID` / `reference_id` 字段用 `fmt.Sprintf` / 字符串拼接 ad-hoc 构造 | 在 owner domain 的 `reference.go` 定义 helper `XxxReferenceID(xxxID int64) string`,调用方 MUST 通过 helper 生成。See `backend/.claude/docs/identifier-contract.md` §3.2 |
|
|
555
|
+
| N3 | Reference | `ReferenceType` 字段使用字符串字面量(e.g. `"order"` / `"subscription"`) | 在 domain 的 `reference.go` 定义 `ReferenceTypeXxx` 常量,所有写入路径 MUST 引用常量 |
|
|
556
|
+
| N4 | Validator | 写入 `p_credit_transactions` / `p_member_point_ledger` / `p_quota_transactions` 时绕过模块的 `ValidateTransactionFields` 直接调用 `db.Create` / `db.Save` | MUST 通过 repo 的 `Create` / `BatchCreate` 方法,repo 内部强制调用 `credit.ValidateTransactionFields`(或对应模块 validator)。禁止 Service/Handler 直接操作 `*gorm.DB` 写入凭证类表 |
|
|
557
|
+
| LC1 | Locale | Domain model, DTO write path, persisted JSON map, or admin API accepts/stores region locale tags such as `zh-CN` / `en-US` as business locale keys | Internal business locale values MUST use OneX short codes validated by `internal/common/locale.IsShortCode`. External headers/imports may call `NormalizeCode` only at boundary adapters before entering domain/service writes |
|
|
558
|
+
| LC2 | Locale | Duplicated supported-locale lists, locale enum metadata, or fallback locale arrays outside `internal/common/locale` / enum registry helpers | Use `commonlocale.SupportedCodes()`, `commonlocale.DefaultCode`, `commonlocale.AdminDefaultCode`, and shared enum helpers. Migrations may list historical external aliases only to repair old data |
|
|
559
|
+
| LC3 | Locale | Localized text/channel JSON validation implemented with ad-hoc key checks or `map[string]string` loops that do not call the common locale validator | Route all localized-map write validation through `commonlocale.IsShortCode` or a module helper that delegates to it. Invalid locale keys fail fast with validation errors |
|
|
560
|
+
|
|
561
|
+
`make lint-enums` / `backend/scripts/check-forbidden-patterns.sh` 运行管理端枚举合同测试及标识符、凭证写入静态检查。详细标识符契约见 `backend/.claude/docs/identifier-contract.md`。
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Delivery Discipline (CRITICAL)
|
|
566
|
+
|
|
567
|
+
Every delivery MUST achieve **zero violations** against this spec. There are NO exceptions, NO exemptions, NO deferrals.
|
|
568
|
+
|
|
569
|
+
| Principle | Detail |
|
|
570
|
+
|-----------|--------|
|
|
571
|
+
| **发现即修正** | Found a violation → fix it NOW in the same delivery. Never defer to "next time" |
|
|
572
|
+
| **无豁免条款** | No pattern is "too simple" or "too thin" to follow the rules. CRUD handler still needs Service layer if rules say so |
|
|
573
|
+
| **设计先行** | Every fix must be properly designed, not patched. If a bug fix requires architectural change, do the architectural change |
|
|
574
|
+
| **不存在后续** | "后续如果需要可以提取" = 现在就提取。"后续处理" = 现在处理 |
|
|
575
|
+
| **交付 = 零违规** | Delivery with known violations is a failed delivery, regardless of how "minor" the violation seems |
|
|
576
|
+
|
|
577
|
+
### Pre-Delivery Checklist
|
|
578
|
+
|
|
579
|
+
BEFORE declaring any task complete, run this mental checklist:
|
|
580
|
+
|
|
581
|
+
1. All files pass FORBIDDEN patterns table (S/A/T/C/H/D/AU/E rules)
|
|
582
|
+
2. Handler layer contains ZERO business logic (A1), ZERO repo calls (A2), ZERO audit calls (AU3)
|
|
583
|
+
3. All write operations in Service carry audit evidence through DomainEvent fields or `audit.Enrich()` (AU1)
|
|
584
|
+
4. No deferred code markers (D1-D7)
|
|
585
|
+
5. `go build ./...` passes
|
|
586
|
+
6. `wire ./cmd/*/` regenerated for all affected cmd directories
|
|
587
|
+
7. No residual imports from refactored/deleted packages
|
|
588
|
+
8. All list/search contracts pass pagination validation: DTOs expose no generic `limit`, Handlers return no raw slices, application layer has no duplicated `filter + slice` helpers, and the `response` package contains no filtering/query logic
|
|
589
|
+
|
|
590
|
+
Skipping ANY item = failed delivery.
|
|
591
|
+
|
|
592
|
+
### Pagination Validation
|
|
593
|
+
|
|
594
|
+
Whenever you modify a list/search endpoint, an options dropdown endpoint, or in-memory application-layer pagination, you MUST validate the pagination contract before delivery:
|
|
595
|
+
|
|
596
|
+
1. **Parameter layer**
|
|
597
|
+
- DTOs MUST embed `types.PageParams`, `types.AdminPageParams`, or an explicitly justified `types.BulkPageParams`
|
|
598
|
+
- Generic pagination MUST NOT define `limit`
|
|
599
|
+
2. **Application layer**
|
|
600
|
+
- In-memory filtering + slicing MUST reuse `internal/common/utils.FilterAndPaginate()`
|
|
601
|
+
- The module keeps only the matcher and MUST NOT duplicate a `keyword -> filter -> slice` shell
|
|
602
|
+
3. **Response layer**
|
|
603
|
+
- List endpoints MUST return `response.NewPaginated()` or `response.NewList()`
|
|
604
|
+
- `internal/common/response/pagination.go` owns response shape only and MUST NOT contain filtering/query logic
|
|
605
|
+
|
|
606
|
+
Recommended pre-delivery checks:
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
rg -n 'form:"limit"|json:"limit"' internal
|
|
610
|
+
rg -n 'response\\.OK\\(c,\\s*items\\)|response\\.OK\\(c,\\s*result\\)' internal
|
|
611
|
+
rg -n 'if .*page < 1|\\(page-1\\)\\*pageSize|slice\\(start, start \\+ pageSize\\)' internal
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## Deferred Code Policy (CRITICAL)
|
|
617
|
+
|
|
618
|
+
Every line of committed code MUST be production-ready. There are ZERO exceptions.
|
|
619
|
+
|
|
620
|
+
| Scenario | WRONG | CORRECT |
|
|
621
|
+
|----------|-------|---------|
|
|
622
|
+
| Feature not ready | `// TODO: implement later` | Return `apierror.BadRequest("Feature X is not supported")` |
|
|
623
|
+
| Data format changed | `if newFormat(err) { fallback(oldFormat) }` | Migrate data. Single code path |
|
|
624
|
+
| Encryption bypass | `if decrypt(err) { tryPlaintext() }` | `if decrypt(err) { return err }` |
|
|
625
|
+
| Module not integrated | `// 暂跳过` / empty block | Reject input that requires the module: `return apierror.BadRequest(...)` |
|
|
626
|
+
| Interface not implemented | `return nil, nil` (noop) | `return nil, fmt.Errorf("X is not implemented")` |
|
|
627
|
+
| Partial implementation | Handle case A, `// case B 待后续` | Handle case A, `default: return fmt.Errorf("unsupported: %s", caseType)` |
|
|
628
|
+
|
|
629
|
+
The principle: **fail loudly, never silently skip**. A production system that returns an error is safer than one that silently does nothing.
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
## API Versioning
|
|
634
|
+
|
|
635
|
+
- Full URL pattern: `/api/v{N}/{resource}` (e.g. `/api/v1/users`)
|
|
636
|
+
- Version prefix set in router group: `router.Group("/api/v1")`
|
|
637
|
+
- Handler ONLY registers resource path: `v1.GET("/users", h.List)`, NEVER full path
|
|
638
|
+
- Breaking changes: create v2 route group, keep v1 compatible until clients migrate
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## API Response Rules
|
|
643
|
+
|
|
644
|
+
| Operation | Status | Body |
|
|
645
|
+
|-----------|--------|------|
|
|
646
|
+
| GET single | 200 | `{ "id": ..., ...fields }` |
|
|
647
|
+
| GET list | 200 | `{ "data": [...], "pagination": {...} }` |
|
|
648
|
+
| POST create | **201** | `{ "id": ..., ...fields }` |
|
|
649
|
+
| PUT/PATCH update | 200 | `{ "id": ..., ...fields }` |
|
|
650
|
+
| DELETE | **204** | Empty body |
|
|
651
|
+
| Error | 4xx/5xx | `{ "error": { "type": "...", "code": "...", "message": "..." } }` |
|
|
652
|
+
|
|
653
|
+
- Request input: struct + `binding` / `validate` tag, 定义在 `dto.go`
|
|
654
|
+
- Response output: struct + `ToXxxResponse()` 转换函数, 定义在 `vo.go`
|
|
655
|
+
- NEVER return Model/Entity directly. ALWAYS transform via `ToXxxResponse()`
|
|
656
|
+
- NEVER use wrapper format `{ "code": 0, "data": ..., "message": "ok" }`
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## Error Handling
|
|
661
|
+
|
|
662
|
+
### Flow
|
|
663
|
+
|
|
664
|
+
```
|
|
665
|
+
Request → Middleware(Auth/Logging/Recovery) → Handler → Service → Repository
|
|
666
|
+
│ (return error)
|
|
667
|
+
Response ← Error Middleware ← ─────────────────── error
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
- Handler: receives error from service, calls `response.Error(c, err)`. NEVER constructs error response manually
|
|
671
|
+
- Service: returns typed errors (`apierror.NotFound(...)`, `apierror.Business(...)`)
|
|
672
|
+
- Middleware: `Recovery` catches panic, `Auth` returns 401/403
|
|
673
|
+
- Messages: English ONLY. Format: `"{Resource} with {field} {value} not found"`
|
|
674
|
+
|
|
675
|
+
### Error Decision Table
|
|
676
|
+
|
|
677
|
+
| Scenario | Error Constructor | Status | ErrorCode |
|
|
678
|
+
|----------|------------------|--------|-----------|
|
|
679
|
+
| Resource not found | `apierror.NotFound(...)` | 404 | `resource_not_found` |
|
|
680
|
+
| Duplicate resource | `apierror.Conflict(...)` | 409 | `resource_conflict` |
|
|
681
|
+
| Invalid input | `apierror.BadRequest(...)` / validator | 400 | `invalid_parameters` |
|
|
682
|
+
| No auth / expired token | `apierror.Unauthorized(...)` | 401 | `unauthorized` |
|
|
683
|
+
| No permission | `apierror.Forbidden(...)` | 403 | `forbidden` |
|
|
684
|
+
| Business rule violated | `apierror.Business(code, msg)` | 422 | (domain-specific) |
|
|
685
|
+
| Rate limited | `apierror.TooManyRequests()` | 429 | `rate_limited` |
|
|
686
|
+
| External service failure | `apierror.Internal(...)` | 500 | `internal_error` |
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## Database Rules
|
|
691
|
+
|
|
692
|
+
- Embed `BaseModel` from `internal/core/database`
|
|
693
|
+
- Column name: `snake_case` (GORM 默认)。Struct field: `PascalCase`
|
|
694
|
+
- ALL columns MUST have `comment` tag (Chinese)
|
|
695
|
+
- Boolean columns: `is_xxx` 命名 + `bool` type
|
|
696
|
+
- Status columns: `varchar(20)` + string const
|
|
697
|
+
- Timestamps: `timestamptz(3)` (毫秒精度)
|
|
698
|
+
- PK: `bigserial`. FK: `bigint`
|
|
699
|
+
- Money: `decimal(19,4)`, 代码中用 `string`
|
|
700
|
+
- JSON: `jsonb` type, Go 中用 `datatypes.JSON` 或自定义 struct
|
|
701
|
+
- ALWAYS specify fields in queries: `Select("id", "name", "email")`. NEVER `SELECT *`
|
|
702
|
+
- ALWAYS paginate list queries. NEVER unbounded results
|
|
703
|
+
- Use `Preload` or `Joins` to avoid N+1 queries
|
|
704
|
+
|
|
705
|
+
### Production SQL / DMS Execution Rules
|
|
706
|
+
|
|
707
|
+
- Production repair SQL MUST be a single statement when the execution tool does not support explicit transactions. Use writable CTEs for atomic multi-table updates.
|
|
708
|
+
- Validate every production SQL statement against PostgreSQL before handing it to operators. Do not rely on visual inspection for CTE, `RETURNING`, JSON, or `LATERAL` syntax.
|
|
709
|
+
- Avoid dialect-sensitive or tool-sensitive constructs in operator-facing SQL: explicit `BEGIN`/`COMMIT`, `CROSS JOIN LATERAL (VALUES ...) AS alias(...)`, anonymous composite row output, and client-specific batch separators.
|
|
710
|
+
- `RETURNING` output MUST use named scalar columns or named JSON object keys. Avoid `RETURNING row(...)` or unnamed `VALUES` outputs that produce `f1`, `f2`, `f3` fields.
|
|
711
|
+
- Destructive or state-changing production SQL MUST include an exact target guard (`id`, business reference, current status, version or timestamp where available) and return `target_count` plus changed row summaries.
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
## Transaction Infrastructure
|
|
716
|
+
|
|
717
|
+
- **单一机制**: `database.TransactionManager`(位于 `core/database/transaction.go`)
|
|
718
|
+
- **REQUIRED propagation**: `txManager.WithTransaction(ctx, fn)` — 加入已有事务,或新建事务
|
|
719
|
+
- **REQUIRES_NEW**: `txManager.WithNewTransaction(ctx, fn)` — 始终创建独立新事务(用于审计日志、幂等 key 等)
|
|
720
|
+
- **Repository 获取 DB**: `database.GetDB(ctx, r.db)` — Repository 内获取 DB 的**唯一**方式,从 context 提取事务,或回退到普通 DB
|
|
721
|
+
- **Service 不感知 `*gorm.DB`**: 回调签名为 `func(ctx context.Context) error`,Service 层看不到 GORM 类型
|
|
722
|
+
- **Savepoint**: GORM 对嵌套 `Transaction()` 调用自动使用 savepoint,无需额外封装
|
|
723
|
+
- **Options**: `WithNewTransaction` 接受 `...*sql.TxOptions`(来自 stdlib),用于设置隔离级别和只读模式
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## Logging Rules
|
|
728
|
+
|
|
729
|
+
- Use `*zap.Logger` field in struct,通过构造函数/Wire 注入
|
|
730
|
+
- NEVER `fmt.Println` / `log.Println` / `log.Fatal`
|
|
731
|
+
- Structured fields first: `logger.Info("order created", zap.Int64("userId", uid), zap.Int64("orderId", oid))`
|
|
732
|
+
- Error logging: `logger.Error("operation failed", zap.Error(err), zap.Int64("contextId", id))`
|
|
733
|
+
- NEVER log passwords, tokens, API keys, PII, card numbers
|
|
734
|
+
- Message: short, past tense (`"order created"`, NOT `"creating order..."`)
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
## Audit Architecture (CloudTrail Envelope Pattern)
|
|
739
|
+
|
|
740
|
+
Three-layer audit architecture. Audit change data is carried on **DomainEvent envelope fields** (AWS CloudTrail pattern), NOT inside the business Payload.
|
|
741
|
+
|
|
742
|
+
### Three-Table Architecture
|
|
743
|
+
|
|
744
|
+
| Table | Source | Retention | Purpose |
|
|
745
|
+
|-------|--------|-----------|---------|
|
|
746
|
+
| `sys_access_logs` | L1 middleware captures all HTTP requests | 30 days | Ops monitoring / operation logs |
|
|
747
|
+
| `sys_audit_logs` | L2 event envelope `AuditChanges` field (business change snapshots) | 7 years | Compliance audit |
|
|
748
|
+
| `sys_login_logs` | L2 events (auth events dual-written, with device/geo info) | 1 year | Security analysis |
|
|
749
|
+
|
|
750
|
+
### Three Layers
|
|
751
|
+
|
|
752
|
+
| Layer | Mechanism | Developer Work |
|
|
753
|
+
|-------|-----------|---------------|
|
|
754
|
+
| L1 Middleware | `AuditMiddleware` → `sys_access_logs` | Zero — automatic |
|
|
755
|
+
| L2 Listener | `AuditListener` reads `AuditChanges` / `AuditMeta` from event envelope → `sys_audit_logs` / `sys_login_logs` | Set event envelope fields |
|
|
756
|
+
| L3 Enrichment | `audit.Enrich()` → middleware writes to `sys_audit_logs` | **Only for write ops WITHOUT events** |
|
|
757
|
+
|
|
758
|
+
### Audit Data Rules (CRITICAL)
|
|
759
|
+
|
|
760
|
+
**Write operations WITH event publishing**: Set audit data on DomainEvent envelope fields. **FORBIDDEN to also call `audit.Enrich()`** (otherwise L2 + L3 = duplicate records).
|
|
761
|
+
|
|
762
|
+
```go
|
|
763
|
+
import "{{module}}/internal/core/eventbus"
|
|
764
|
+
|
|
765
|
+
evt := eventbus.NewDomainEventFromContext(ctx, sharedevents.EventOrderPaid, orderID)
|
|
766
|
+
evt.Payload = &sharedevents.OrderPaidEvent{OrderID: order.ID, Amount: amount} // typed struct pointer (EP1)
|
|
767
|
+
evt.AuditChanges = &eventbus.AuditChangesPayload{
|
|
768
|
+
Before: map[string]any{"status": "pending"},
|
|
769
|
+
After: map[string]any{"status": "paid", "paid_at": time.Now()},
|
|
770
|
+
}
|
|
771
|
+
evt.AuditMeta = &eventbus.AuditMetadataPayload{Reason: "payment confirmed"} // optional
|
|
772
|
+
s.eventBus.PublishAsync(ctx, evt)
|
|
773
|
+
// FORBIDDEN: do NOT call audit.Enrich() here
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
**Write operations WITHOUT event publishing** (pure L3 path): Use `audit.Enrich()`
|
|
777
|
+
|
|
778
|
+
```go
|
|
779
|
+
import "{{module}}/internal/core/audit"
|
|
780
|
+
|
|
781
|
+
// Admin ops without events (e.g., AdminResetPassword, UpsertConfig)
|
|
782
|
+
audit.Enrich(ctx, &audit.ChangesPayload{
|
|
783
|
+
After: map[string]any{"password_reset": true},
|
|
784
|
+
}, nil)
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Decision Flow
|
|
788
|
+
|
|
789
|
+
```
|
|
790
|
+
After write operation completes →
|
|
791
|
+
Does this operation publish a domain event?
|
|
792
|
+
YES → Set audit data on event.AuditChanges (L2 envelope path)
|
|
793
|
+
NO → Call audit.Enrich(ctx, changes, metadata) (L3 context path)
|
|
794
|
+
FORBIDDEN: using both simultaneously
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Exempt Scenarios
|
|
798
|
+
|
|
799
|
+
| Scenario | Reason |
|
|
800
|
+
|----------|--------|
|
|
801
|
+
| Read-only operations (GET / List / Query) | L1 already captures the request |
|
|
802
|
+
| Worker / Cron background tasks | Covered by L2 event envelope (no HTTP context) |
|
|
803
|
+
| EventBus Listener internals | L2 already captures the event |
|
|
804
|
+
| Technical operations (token rotation, cache refresh) | Not a business state change |
|
|
805
|
+
|
|
806
|
+
### FORBIDDEN Patterns
|
|
807
|
+
|
|
808
|
+
| Pattern | Fix |
|
|
809
|
+
|---------|-----|
|
|
810
|
+
| `audit.Enrich()` + `PublishAsync()` in same method | Use envelope fields only, remove Enrich |
|
|
811
|
+
| `_audit_changes` key in `event.Payload` | Deprecated — use `event.AuditChanges` envelope field |
|
|
812
|
+
| `map[string]any` as event Payload | Use typed struct from `sharedevents` |
|
|
813
|
+
|
|
814
|
+
---
|
|
815
|
+
|
|
816
|
+
## Comments
|
|
817
|
+
|
|
818
|
+
- Language: Chinese ONLY. NEVER English comments
|
|
819
|
+
- Style: explain WHY, not WHAT. Skip self-explanatory code
|
|
820
|
+
- GoDoc on ALL exported types, functions, methods
|
|
821
|
+
- NEVER: ticket numbers in comments, commented-out code
|
|
822
|
+
- Struct fields: use `//` comment above field (Chinese description)
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
## OpenAPI Contract
|
|
827
|
+
|
|
828
|
+
- `api/openapi/services/<service>/openapi.yaml` 是 HTTP 契约唯一事实源,Go handler 不保存契约注释或文档专用 struct tag。
|
|
829
|
+
- 每个 operation 必须声明稳定的 `operationId`、显式 `security`、`summary`、`tags`、`responses`、owner、audience 与 stability。
|
|
830
|
+
- Canonical API 文档语言为 `zh-CN`;人类可见文案、顶层 Tag Object 和 `x-tagGroups` 必须使用中文语义,机器标识禁止本地化改名。
|
|
831
|
+
- 每个 operation 必须且只能使用一个已声明标签;每个顶层标签必须在 `x-tagGroups` 中出现且只出现一次,并保持业务阅读顺序。
|
|
832
|
+
- 修改路由、请求、响应、认证或状态码时,同一变更内更新 owner path/component、Arazzo workflow 和生成制品。
|
|
833
|
+
- registry 中全部 HTTP 服务启动时必须通过 Gin route 与内嵌 internal bundle 的精确集合校验。
|
|
834
|
+
- partner/public 只能由 internal canonical source 投影生成,禁止直接编辑 `dist/`。
|
|
835
|
+
- 详细规则和模板见 `.claude/docs/openapi-contract.md`。
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
## Import Order
|
|
840
|
+
|
|
841
|
+
Go 标准 goimports 分组:
|
|
842
|
+
|
|
843
|
+
1. Standard library (`fmt`, `context`, `net/http`)
|
|
844
|
+
2. External packages (`github.com/gin-gonic/gin`, `gorm.io/gorm`)
|
|
845
|
+
3. Internal packages (`github.com/onex-platform/backend/internal/...`)
|
|
846
|
+
|
|
847
|
+
- NEVER import `internal/` package's unexported types from outside
|
|
848
|
+
- NEVER circular imports between packages
|
|
849
|
+
- Run `goimports` to auto-format
|
|
850
|
+
|
|
851
|
+
---
|
|
852
|
+
|
|
853
|
+
## Cross-Module Communication
|
|
854
|
+
|
|
855
|
+
| Need | Method |
|
|
856
|
+
|------|--------|
|
|
857
|
+
| Data from another module | 定义 interface in consumer package,implementation 通过 Wire 注入 |
|
|
858
|
+
| Notify without coupling | `EventBus.Publish(ctx, event)` |
|
|
859
|
+
|
|
860
|
+
- Import interface, NEVER concrete implementation across module boundaries
|
|
861
|
+
- Export only what other packages need (最小化导出)
|
|
862
|
+
- Listener 通过 `EventBus.Subscribe()` 注册
|
|
863
|
+
|
|
864
|
+
### Event Payload Standard (CRITICAL — EP1–EP5)
|
|
865
|
+
|
|
866
|
+
All `DomainEvent.Payload` MUST be a **typed struct pointer**. Aligned with CloudEvents, Stripe Go SDK, Watermill CQRS. Violations WILL be rejected in review.
|
|
867
|
+
|
|
868
|
+
| ID | Rule | Correct | Forbidden |
|
|
869
|
+
| --- | --- | --- | --- |
|
|
870
|
+
| EP1 | Publisher sends pointer | `evt.Payload = &sharedevents.XxxEvent{...}` | `evt.Payload = sharedevents.XxxEvent{...}` (value) |
|
|
871
|
+
| EP2 | Registry factory returns pointer | `EventXxx: func() any { return &XxxEvent{} }` | `return XxxEvent{}` |
|
|
872
|
+
| EP3 | Listener asserts pointer only | `switch p := event.Payload.(type) { case *sharedevents.XxxEvent: ...; default: return err }` | Additional `case sharedevents.XxxEvent:` or `case map[string]any:` branches |
|
|
873
|
+
| EP4 | No map[string]any fallback | Registry + Outbox relay both produce pointers; map fallback is dead code (H5 NO PHANTOM CODE) | Retaining legacy `case map[string]any:` for "defensive" reasons |
|
|
874
|
+
| EP5 | Payload immutable after publish | Constructor fills fields; never mutate after `PublishAsync` | Mutating payload inside handler or later code |
|
|
875
|
+
|
|
876
|
+
**Why pointer?**
|
|
877
|
+
- `json.Unmarshal(&x)` requires a pointer → cross-process Outbox relay ALWAYS produces pointers. Pointer publisher + pointer listener = one uniform type assertion path.
|
|
878
|
+
- `interface{}` boxing escapes value types to heap anyway → no heap-cost benefit to values.
|
|
879
|
+
- Watermill CQRS `Handle(ctx, event *MyEvent) error` signature is industry standard; future bus swap is zero-refactor.
|
|
880
|
+
|
|
881
|
+
**Standard publisher pattern**:
|
|
882
|
+
```go
|
|
883
|
+
evt := eventbus.NewDomainEventFromContext(ctx, sharedevents.EventUserCheckin, fmt.Sprintf("%d", record.ID))
|
|
884
|
+
evt.Payload = &sharedevents.UserCheckinEvent{
|
|
885
|
+
UserID: userID,
|
|
886
|
+
ProductCode: productCode,
|
|
887
|
+
CheckinDate: today.Format("2006-01-02"),
|
|
888
|
+
StreakDays: streakDays,
|
|
889
|
+
}
|
|
890
|
+
s.eventBus.PublishAsync(ctx, evt)
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
**Standard listener pattern**:
|
|
894
|
+
```go
|
|
895
|
+
func (l *Listener) handleXxx(ctx context.Context, event eventbus.DomainEvent) error {
|
|
896
|
+
payload, ok := event.Payload.(*sharedevents.XxxEvent)
|
|
897
|
+
if !ok {
|
|
898
|
+
l.logger.Warn("unexpected payload type",
|
|
899
|
+
zap.String("eventName", event.EventName),
|
|
900
|
+
zap.String("payloadType", fmt.Sprintf("%T", event.Payload)),
|
|
901
|
+
)
|
|
902
|
+
return fmt.Errorf("unexpected payload type %T for event %s", event.Payload, event.EventName)
|
|
903
|
+
}
|
|
904
|
+
// ... process payload.UserID / payload.ProductCode / ...
|
|
905
|
+
return nil
|
|
906
|
+
}
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
### Platform vs External Product Boundary (CRITICAL — PB1–PB4)
|
|
910
|
+
|
|
911
|
+
OneX owns reusable platform capabilities under `internal/platform/`. Product-specific workflows, presentation composition, and public BFF contracts belong to external product repositories; `internal/products/` is not an allowed OneX ownership boundary.
|
|
912
|
+
|
|
913
|
+
| ID | Rule | Correct | Forbidden |
|
|
914
|
+
| --- | --- | --- | --- |
|
|
915
|
+
| PB1 | Platform feature → `internal/platform/` | Checkin, points, member tier, order, and payment remain product-neutral engines | Adding an `internal/products/` implementation or product-specific branch inside a platform module |
|
|
916
|
+
| PB2 | `product_code` is a first-class parameter | Every cross-product entity has `product_code VARCHAR(50)` column + index; every DTO / domain struct threads it through | Hardcoding `"product_beta"` in service logic, SQL, or config keys |
|
|
917
|
+
| PB3 | Product-varying platform rules → DB config, not Go code | Rewards per product live in `p_credit_activity_reward_configs`; new product = INSERT row, zero code change | `if productCode == "sample_product" { amount = 100 }` |
|
|
918
|
+
| PB4 | Client must NOT hardcode platform contract values | Reward amounts, tier thresholds, etc. fetched from API (e.g., `/member/checkin/status` returns `next_reward_amount`) | Frontend `const REWARD = 100` that silently breaks when backend config changes per product |
|
|
919
|
+
|
|
920
|
+
Checkin reference implementation (as of this writing):
|
|
921
|
+
- Module location: `internal/platform/member/` ✓ PB1
|
|
922
|
+
- `UserCheckinEvent.ProductCode` field + `UserProductScoped` interface ✓ PB2
|
|
923
|
+
- Reward config in `p_credit_activity_reward_configs (product_code, activity_type, base_amount)` ✓ PB3
|
|
924
|
+
- API `/member/checkin/status` returns the upcoming reward amount so clients do not hardcode product-varying policy ✓ PB4
|
|
925
|
+
|
|
926
|
+
---
|
|
927
|
+
|
|
928
|
+
## Architecture Decisions
|
|
929
|
+
|
|
930
|
+
### Module Complexity Tiers
|
|
931
|
+
|
|
932
|
+
BEFORE creating a new module, determine its complexity tier. Use ONLY the matching structure.
|
|
933
|
+
|
|
934
|
+
| Tier | Criteria | Structure | Modules |
|
|
935
|
+
|------|----------|-----------|---------|
|
|
936
|
+
| Full DDD | State machine / Saga / CQRS / multi-aggregate | `domain/` + `application/` + `infrastructure/` | identity, notification, order, payment |
|
|
937
|
+
| Simplified DDD | Domain logic without state machine / 2-3 related entities | `domain/` + `application/` + `infrastructure/` | user, tenant |
|
|
938
|
+
| Flat | Pure CRUD / simple queries | `model.go` + `service.go` + `handler.go` + `dto.go` + `vo.go` | audit |
|
|
939
|
+
|
|
940
|
+
Decision flow: Need state machine/Saga? → Full DDD. Complex domain logic? → Simplified. Otherwise → Flat.
|
|
941
|
+
|
|
942
|
+
Upgrade path: Flat → Simplified → Full DDD (one-way only, never downgrade).
|
|
943
|
+
|
|
944
|
+
### Directory Subdivision Rules
|
|
945
|
+
|
|
946
|
+
After determining the module complexity tier, decide whether each layer needs sub-packages using the rules below.
|
|
947
|
+
|
|
948
|
+
#### domain/ — Almost Always Flat
|
|
949
|
+
|
|
950
|
+
| Condition | Structure |
|
|
951
|
+
|-----------|-----------|
|
|
952
|
+
| Default | Single package: model.go, enum.go, repository.go, interfaces.go, events.go |
|
|
953
|
+
| Subdivision trigger | 2+ completely independent aggregate roots (no shared entities) AND files > 12 |
|
|
954
|
+
| Subdivision pattern | `domain/{aggregate}/` |
|
|
955
|
+
|
|
956
|
+
Identity has 10 entities but all revolve around the Identity aggregate root → stays flat.
|
|
957
|
+
|
|
958
|
+
#### application/ — Subdivide by Subdomain
|
|
959
|
+
|
|
960
|
+
| Condition | Structure |
|
|
961
|
+
|-----------|-----------|
|
|
962
|
+
| Service files ≤ 6 AND total files ≤ 15 | Flat: `{name}_service.go` + `handler_{name}.go` |
|
|
963
|
+
| Service files > 6 AND 3+ isolated service groups | Subdivide: `application/{subdomain}/` |
|
|
964
|
+
|
|
965
|
+
Subdivision decision flow:
|
|
966
|
+
1. Draw service dependency graph
|
|
967
|
+
2. Identify "isolated groups" (may have intra-group coupling, but no inter-group service dependencies)
|
|
968
|
+
3. Isolated groups ≥ 3 AND service files > 6 → MUST subdivide
|
|
969
|
+
|
|
970
|
+
Standard structure inside each sub-package: service.go, handler.go, dto.go, vo.go (optional).
|
|
971
|
+
Cross-subdomain orchestration (admin handler, listener) stays at application/ root.
|
|
972
|
+
|
|
973
|
+
#### infrastructure/ — Subdivide by Integration Type
|
|
974
|
+
|
|
975
|
+
| Condition | Structure |
|
|
976
|
+
|-----------|-----------|
|
|
977
|
+
| GORM repositories only | Flat: persistence.go |
|
|
978
|
+
| 3+ external integrations | Subdirectories by type: channels/, webhooks/, etc. |
|
|
979
|
+
| persistence.go > 1500 lines | Split into persistence_{aggregate}.go |
|
|
980
|
+
|
|
981
|
+
#### Module Root
|
|
982
|
+
|
|
983
|
+
{module}/ root ONLY contains assembly files:
|
|
984
|
+
- `module.go` — Module facade (manual assembly)
|
|
985
|
+
- `provider.go` — Wire ProviderSet (only `wire.NewSet`, NEVER constructor re-exports)
|
|
986
|
+
- FORBIDDEN: handler, service, model, or any business code
|
|
987
|
+
|
|
988
|
+
### VO Naming (CRITICAL)
|
|
989
|
+
|
|
990
|
+
Two types of "VO" exist in this project. NEVER confuse them.
|
|
991
|
+
|
|
992
|
+
| Concept | File | Directory | Implementation |
|
|
993
|
+
|---------|------|-----------|---------------|
|
|
994
|
+
| API Response View Object | `vo.go` | `application/` 或 package root | struct + `ToXxxResponse()` func |
|
|
995
|
+
| DDD Value Object | `valueobject.go` | `domain/` | struct with `Equals()` method |
|
|
996
|
+
|
|
997
|
+
Value Object decision: Use `valueobject.go` ONLY when the value has composite structure (multiple fields that change together) AND invariant constraints (e.g., Money = amount + currency + non-negative check). Simple IDs → primitive type. Status → string const.
|
|
998
|
+
|
|
999
|
+
### Product vs Tenant
|
|
1000
|
+
|
|
1001
|
+
Each brand is a **product**, NOT a tenant.
|
|
1002
|
+
|
|
1003
|
+
- Use `ProductContext`, NEVER `TenantContext`
|
|
1004
|
+
- OneX 产品数据隔离:`product_code` + IAM + owner-scoped query
|
|
1005
|
+
- 产品业务代码隔离:独立产品仓库与独立 BFF 进程
|
|
1006
|
+
- Tenant isolation:组织/客户级运行时数据范围,不是 Product 的别名
|
|
1007
|
+
|
|
1008
|
+
### Multi-Product Configuration (三层模型)
|
|
1009
|
+
|
|
1010
|
+
Platform uses a **three-tier configuration model** with a central product registry.
|
|
1011
|
+
|
|
1012
|
+
#### Product Registry
|
|
1013
|
+
|
|
1014
|
+
`p_products` is the **single source of truth** for all platform products. Every module that needs per-product configuration references `p_products.code`.
|
|
1015
|
+
|
|
1016
|
+
| Rule | Detail |
|
|
1017
|
+
|------|--------|
|
|
1018
|
+
| **Field name** | `product_code` — ALL modules, NO exceptions (`product_id`, `app_id` FORBIDDEN) |
|
|
1019
|
+
| **Column type** | `VARCHAR(50)` — ALL modules, unified width |
|
|
1020
|
+
| **Value format** | `snake_case` — regex: `^[a-z][a-z0-9_]{1,49}$` |
|
|
1021
|
+
| **Enum source** | `common/types.Product` — single definition, ALL modules reference it |
|
|
1022
|
+
|
|
1023
|
+
#### Three Tiers
|
|
1024
|
+
|
|
1025
|
+
| Tier | Storage | Content | Example |
|
|
1026
|
+
|------|---------|---------|---------|
|
|
1027
|
+
| Tier 1: Infrastructure | YAML + env var | DB/Redis/JWT/API keys | `configs/config.yaml` |
|
|
1028
|
+
| Tier 2: Platform Module | `p_products` + per-module config tables | Commerce/Notification/OAuth/Tenant | `p_config_product_configs` |
|
|
1029
|
+
| Tier 3: Product-Specific | External product repository | Product workflow, presentation, and product-owned data | `<product-repository>/backend/` |
|
|
1030
|
+
|
|
1031
|
+
#### Per-Module Config Tables (Tier 2)
|
|
1032
|
+
|
|
1033
|
+
Each platform module owns its per-product configuration table. Tables are NOT redundant — each covers a distinct domain:
|
|
1034
|
+
|
|
1035
|
+
| Config Table | Module | Responsibility |
|
|
1036
|
+
|-------------|--------|---------------|
|
|
1037
|
+
| `p_products` | product | Product identity (name, logo, status) |
|
|
1038
|
+
| `p_config_product_configs` | commerce/config | Commerce settings (currency, trial, tax) |
|
|
1039
|
+
| `p_config_payment_configs` | commerce/config | Payment gateways (provider, secrets) |
|
|
1040
|
+
| `p_notification_products` | notification | Channel config (sender, branding, quotas) |
|
|
1041
|
+
| `p_identity_oauth_configs` | identity | OAuth providers (client_id, redirect_uri) |
|
|
1042
|
+
| `p_tenant_apps` | tenant | Tenant features (feature flags, rate limits) |
|
|
1043
|
+
|
|
1044
|
+
All tables use `product_code VARCHAR(50)` to reference `p_products.code`.
|
|
1045
|
+
|
|
1046
|
+
See the upstream OneX `backend/.claude/docs/product-registry-design.md` for the platform-owned registry design; its source revision is recorded in `docs/standards-provenance.json`.
|
|
1047
|
+
|
|
1048
|
+
### Service Topology
|
|
1049
|
+
|
|
1050
|
+
Modular Monolith 架构(Stripe/Shopify 标准)。统一平台 API 服务所有产品,管理后台独立进程。
|
|
1051
|
+
|
|
1052
|
+
| App | Port | Purpose |
|
|
1053
|
+
|-----|------|---------|
|
|
1054
|
+
| `cmd/platform-api/` | 3002 | 统一平台服务(Identity + Commerce + Credit + Quota) |
|
|
1055
|
+
| `cmd/admin-api/` | 3003 | 管理后台 BFF(admin dashboard) |
|
|
1056
|
+
| `cmd/worker/` | — | 后台任务(Asynq consumer) |
|
|
1057
|
+
|
|
1058
|
+
**Per-product domain routing**:APISIX 按 Host + Path 将产品公开合同路由到外部 Product BFF,将平台公开/内部合同路由到对应 OneX 服务。产品前端只配置一个公开产品 origin,不直接选择基础设施服务。
|
|
1059
|
+
|
|
1060
|
+
**路由选择性注册**:所有模块实现 nil-safe `RegisterRoutes(public, authenticated, admin *gin.RouterGroup)`。通过传 nil 跳过不需要的路由组:
|
|
1061
|
+
- admin-api: `RegisterRoutes(nil, nil, admin)` — 仅注册 admin 路由
|
|
1062
|
+
|
|
1063
|
+
**平台适配器**:OneX 进程内跨模块适配器统一在 `internal/common/bffadapter/`;外部 Product BFF 只能通过 OpenAPI HTTP 合同、事件或工作流调用平台能力。
|
|
1064
|
+
|
|
1065
|
+
**Outbox Relay**:platform-api 运行统一 Relay,处理所有域的 outbox 事件。admin-api 不调用 `relay.Start()`,admin 写入的 outbox 记录由 platform-api 的 Relay 投递。
|
|
1066
|
+
|
|
1067
|
+
**Event runtime boundary**:源聚合投影监听器必须留在拥有该状态机的运行时;`worker` 只消费 durable public events 做下游副作用。`user.deleted` 这类部分公开的事件族必须使用精确白名单,禁止 `user.*` 这类宽泛通配。See `backend/.claude/docs/event-runtime-boundary.md`.
|
|
1068
|
+
|
|
1069
|
+
**E2E contract tests**:凡是跨运行时、依赖 relay/worker、会改变最终用户可见真相的工作流,必须同时具备一个全链路 E2E 和一个 `ProvideRelayConfig()` 白名单回归测试。See `backend/.claude/docs/e2e-standards.md`.
|
|
1070
|
+
|
|
1071
|
+
**内部服务间调用**:`/internal/v1/` 路由组,通过 IAM 服务账号凭证认证、已认证主体校验、路由资源策略授权保护。
|
|
1072
|
+
|
|
1073
|
+
**Gateway upstream policy**:所有挂在 APISIX 后的生产 public API upstream 必须使用多节点 + 主被动健康检查 + 显式 timeout/retries/keepalive。任何 gateway plugin 回调(如 `forward-auth`)必须复用 APISIX 内部路由或共享 upstream,禁止直连单个 backend 实例。
|
|
1074
|
+
|
|
1075
|
+
Gateway (APISIX) runs separately, not in Go codebase. Routes: `/api/v1/admin/*` → admin-api (priority 10).
|
|
1076
|
+
|
|
1077
|
+
### Event Contracts
|
|
1078
|
+
|
|
1079
|
+
Cross-module events MUST be defined in `internal/sharedevents/`, NEVER in the publishing module.
|
|
1080
|
+
|
|
1081
|
+
- Event struct: `{Resource}{Action}Event` (e.g., `IdentityCreatedEvent`)
|
|
1082
|
+
- Event name const: `Event{Resource}{Action}` = `"{resource}.{action}"` (e.g., `"identity.created"`)
|
|
1083
|
+
- Import: `import "{{module}}/internal/sharedevents"`
|
|
1084
|
+
|
|
1085
|
+
### Table Naming
|
|
1086
|
+
|
|
1087
|
+
| Scope | Prefix | Example |
|
|
1088
|
+
|-------|--------|---------|
|
|
1089
|
+
| Platform base entity | `p_` | `p_products`, `p_tenants` |
|
|
1090
|
+
| Platform module | `p_{module}_` | `p_user_users`, `p_identity_identities` |
|
|
1091
|
+
| System | `sys_` | `sys_migrations`, `sys_access_logs`, `sys_audit_logs`, `sys_login_logs`, `sys_outbox_events` |
|
|
1092
|
+
|
|
1093
|
+
Product-related columns: ALL tables that reference a product MUST use `product_code VARCHAR(50)` as the column name. NEVER use `product_id`, `app_id`, or other variants.
|
|
1094
|
+
|
|
1095
|
+
Product-specific tables are owned by external product repositories and must not be added to the OneX schema.
|
|
1096
|
+
|
|
1097
|
+
---
|
|
1098
|
+
|
|
1099
|
+
## Dependency Injection (Wire)
|
|
1100
|
+
|
|
1101
|
+
- 每个模块提供 `provider.go`,定义 `wire.NewSet(...)`
|
|
1102
|
+
- 每个 `cmd/` 提供 `wire.go`,定义 injector function
|
|
1103
|
+
- 运行 `make wire` 生成 `wire_gen.go`
|
|
1104
|
+
- NEVER 手动编辑 `wire_gen.go`
|
|
1105
|
+
- NEVER 在 `main.go` 中手动构造依赖
|
|
1106
|
+
|
|
1107
|
+
---
|
|
1108
|
+
|
|
1109
|
+
## Commit Discipline
|
|
1110
|
+
|
|
1111
|
+
- ONE module, ONE feature per commit
|
|
1112
|
+
- Pre-commit: validate against `backend/.claude/docs/` specs
|
|
1113
|
+
- MUST NOT commit with any CRITICAL/MUST violation
|
|
1114
|
+
|
|
1115
|
+
---
|
|
1116
|
+
|
|
1117
|
+
## Scaffold Skills
|
|
1118
|
+
|
|
1119
|
+
创建新模块或新管理页时,优先使用脚手架生成骨架代码,再填充业务逻辑:
|
|
1120
|
+
|
|
1121
|
+
| Skill | Usage | Purpose |
|
|
1122
|
+
|-------|-------|---------|
|
|
1123
|
+
| `/scaffold` | `/scaffold {module} --tier={full-ddd\|simplified\|flat}` | 生成 Go 模块骨架(domain + application + infrastructure) |
|
|
1124
|
+
| `/scaffold-admin` | `/scaffold-admin {module}/{resource}` | 生成 Admin CRUD 页面(VxeGrid + Modal + Form + API) |
|
|
1125
|
+
|
|
1126
|
+
---
|
|
1127
|
+
|
|
1128
|
+
## Reference
|
|
1129
|
+
|
|
1130
|
+
| Doc | Content |
|
|
1131
|
+
|-----|---------|
|
|
1132
|
+
| `.claude/docs/architecture-template.md` | Plan mode mandatory skeleton (Level A/B/C) |
|
|
1133
|
+
| `backend/.claude/docs/api-design.md` | Handler/Service/DTO/VO templates, pagination |
|
|
1134
|
+
| `backend/.claude/docs/module-standards.md` | Tier decision, directory subdivision, VO naming |
|
|
1135
|
+
| `backend/.claude/docs/error-handling.md` | Error types, codes, decision table |
|
|
1136
|
+
| `backend/.claude/docs/database.md` | Model template, column types, migrations |
|
|
1137
|
+
| Upstream OneX `.claude/docs/decisions.md` | Platform Architecture Decision Records; source revision in `docs/standards-provenance.json` |
|
|
1138
|
+
| `internal/platform/trade/order/` | Reference: Full DDD module |
|
|
1139
|
+
| `internal/platform/trade/module.go` | Reference: Module self-registration |
|