admin-service Architecture

duobao backend — operations BFF: admin auth (TOTP + RBAC), prize shipment, withdraw review, audit trail, escalation

admin-service (schema: one_admin, port 6677) — BFF: reads other services via Feign; writes only one_admin tables admin-fe (Vue 3) duobao-admin-fe repo operator browser client ADMIN AUTH /admin/v1/auth/* POST /admin/v1/auth/login (password + TOTP 2FA) POST /admin/v1/auth/logout POST /admin/v1/auth/confirm (step-up TOTP token issue) GET /admin/v1/auth/me guard: AdminAuthFilter (admin JWT — separate secret) v1.0 done OPS ENDPOINTS /admin/v1/* POST /lottery/create POST /lottery/{id}/pause POST /lottery/{id}/force-draw GET /lottery/list POST /withdraw/approve POST /withdraw/reject GET /withdraw/list POST /agent/flag GET /agent/tree?uid= GET /treasury/balance POST /physical/ship POST /physical/refund GET /audit-log POST /kafka/replay guard: @RequirePermission @RequireConfirm (sensitive) RbacInterceptor FILTERS / INTERCEPTORS AdminAuthFilter admin JWT verify + session check ServiceTokenAuthFilter Feign inbound (IAdminService) RbacInterceptor @RequirePermission + @RequireConfirm CONTROLLERS AuthController /admin/v1/auth/* AdminV1Controller /admin/v1/lottery,withdraw,user AdminOpsController /admin/v1/agent,treasury,physical,audit SERVICES (business logic) AdminLogin Svc BCrypt + TOTP AdminToken Svc JWT issue + revoke TotpSvc TOTP 6-digit verify ConfirmToken Svc one-shot step-up AuditSvc @AdminAudit aspect append-only audit_log AdminWithdraw ReviewSvc approve/reject → wallet Feign AdminPhysical PrizeShipSvc ship → prize.physical.shipped AdminPhysical PrizeRefundSvc refund → prize.physical.refunded AdminOpsQuery Svc aggregate from Feign reads FEIGN CALLEE CLIENTS (admin-service calls outbound to query / mutate business services) IGameSvc create/pause IWalletSvc withdraw list IUserSvc user lookup IAgentSvc tree / flag IChainSvc treasury EXPOSES IAdminService to other services (inbound Feign from peers) writeAuditLog · submitForAudit · enqueuePhysicalPrize · reportBtcExposure · escalate (called by: game, wallet, chain, sign, sms, agent-service — via ServiceToken) EVENT PRODUCER AdminEventProducer prize.physical.shipped (AdminPhysicalPrizeShipSvc) + prize.physical.refunded (AdminPhysicalPrizeRefundSvc) EVENT CONSUMERS chain.btc.exposure.alert — writes audit_event, enqueues escalation game.draw.timeout — escalation queue (admin manual intervention: keep / fallback / cancel) wallet.withdraw.failed — escalation for ops review MYBATIS MAPPERS (one_admin schema — reads other schemas via reader accounts, writes own only) AdminUser Mapper AdminRole Mapper AdminPermission Mapper AdminSession Mapper AdminAuditLog Mapper AdminConfirmToken Mapper v1.0 done — 6 mappers + flyway_schema_history (V1 migration, 7 tables) MySQL 8 schema: one_admin (writes) • admin_user • admin_role • admin_permission • admin_role_permission • admin_session • admin_audit_log (append-only) • admin_confirm_token (1-shot) other schemas: reader-only access via Feign — no cross-DB writes v1.0 done RBAC seed: SUPER_ADMIN / OPS RISK / FINANCE in V1 migration Kafka broker kafka:29092 (compose) • produces 2 topics • consumes 3 topics Kafka Events — admin-service PRODUCES prize.physical.shipped partition keyrewardId triggerAdminPhysicalPrizeShipSvc.ship() consumed byuser-service (push shipped + tracking) prize.physical.refunded partition keyrewardId triggerAdminPhysicalPrizeRefundSvc.refund() consumed bywallet(refund) · user(push) CONSUMES (3 topics via @KafkaListener) chain.btc.exposure.alert audit_log + escalate game.draw.timeout escalation queue (ops manual) wallet.withdraw.failed ops review escalation All consumed by admin-service as Kafka consumer group "admin-svc" Idempotency: Redis SETNX eventId + DB audit_log dedup v1.0 — 2 produced, 3 consumed game.round.created also consumed by admin (not listed — read-only view) game.draw.timeout also → replay path (POST /admin/v1/kafka/replay) TOTP + RBAC + step-up confirm token — 3-layer admin auth LEGEND Controllers Services / sibling svc Auth filters DB / Mappers Events / channels External services Helpers / lanes Service boundary Cache / limiter line Kafka publish v1.1 deferred path v1.0 done v1.1 deferred conditional bean

What admin-service owns

  • • Admin authentication: BCrypt password + TOTP 2FA + JWT session + revocable tokens
  • • RBAC: roles (SUPER_ADMIN / OPS / RISK / FINANCE), permission codes, @RequirePermission
  • • Step-up confirm tokens for sensitive operations (@RequireConfirm + one-shot token)
  • • Append-only audit trail via @AdminAudit aspect on all state-changing operations
  • • Prize shipment (produces prize.physical.shipped) + refund (produces prize.physical.refunded)
  • • Withdrawal review queue (approve → calls wallet Feign, reject → same)
  • • Escalation queue: BTC exposure alert, VRF timeout, withdraw failure
  • • Writes ONLY one_admin tables; reads other schemas via Feign only

Kafka events produced + consumed

  • Produces: prize.physical.shipped — after ops records carrier + tracking number
  • Produces: prize.physical.refunded — after ops manually refunds any-state prize
  • Consumes: chain.btc.exposure.alert — audit + escalation
  • Consumes: game.draw.timeout — ops intervention queue (keep / BACKEND fallback / cancel)
  • Consumes: wallet.withdraw.failed — ops review escalation

External dependencies

  • • MySQL one_admin — 7 tables (auth + RBAC + audit + confirm token), V1 migration
  • • Kafka — 2 topics produced, 3 consumed
  • • Feign: IGameService (create/pause/force-draw), IWalletService (withdraw list / approve)
  • • Feign: IUserService (user lookup), IAgentService (tree / flag), IChainService (treasury)
  • • Exposes IAdminService to all other services (audit-log, submit-for-audit, escalate endpoints)