package routes import ( "github.com/gofiber/fiber/v2" "github.com/break/junhong_cmp_fiber/internal/handler/admin" "github.com/break/junhong_cmp_fiber/internal/model/dto" auditquery "github.com/break/junhong_cmp_fiber/internal/query/audit" integrationquery "github.com/break/junhong_cmp_fiber/internal/query/integration" "github.com/break/junhong_cmp_fiber/pkg/openapi" ) // registerAuditRoutes 注册平台基础审计调查只读路由。 func registerAuditRoutes(router fiber.Router, handler *admin.AuditHandler, doc *openapi.Generator, basePath string) { agent := router.Group("/agent/resource-activities") Register(agent, doc, basePath+"/agent/resource-activities", "GET", "/:resource_type/:identifier", handler.AgentResourceActivities, RouteSpec{ Summary: "查询代理资源活动", Description: "代理业务页映射:卡详情使用 `resource_type=iot_card`、`identifier=response.data.iccid`;设备详情使用 `device`、`response.data.virtual_no`;分配详情使用 `asset_allocation_record` 和分配单号;换货详情使用 `exchange_order` 和换货单号;店铺详情使用 `shop` 和店铺编号;企业详情使用 `enterprise` 和企业编号。身份与店铺范围只读取认证上下文,前端不得传入或推断。缺少稳定 identifier 时隐藏入口。仅查询 `retention` 标明的在线窗口。", Tags: []string{"资源活动"}, Input: new(dto.SubjectResourceActivityRequest), Output: new(auditquery.SubjectActivityPage), Auth: true, }) enterprise := router.Group("/enterprise/resource-activities") Register(enterprise, doc, basePath+"/enterprise/resource-activities", "GET", "/:resource_type/:identifier", handler.EnterpriseResourceActivities, RouteSpec{ Summary: "查询企业资源活动", Description: "企业仅支持当前有效授权资产:卡列表/详情使用 `resource_type=iot_card`、`identifier=response.data.iccid`;设备列表/详情使用 `resource_type=device`、`identifier=response.data.virtual_no`。企业身份与授权范围只读取认证上下文,前端不得传入或推断。缺少稳定 identifier 时隐藏入口。响应只含主体安全投影,不含平台操作者、风险、内部原因或 before/after。", Tags: []string{"资源活动"}, Input: new(dto.EnterpriseResourceActivityRequest), Output: new(auditquery.SubjectActivityPage), Auth: true, }) audit := router.Group("/audit") groupPath := basePath + "/audit" Register(audit, doc, groupPath, "GET", "/events", handler.ListEvents, RouteSpec{ Summary: "查询全局审计事件", Description: "平台业务页按内部 ID 进入:卡 `resource_type=iot_card&resource_id=response.data.id`,设备 `device/id`,账号 `account/id`,店铺 `shop/id`,企业 `enterprise/id`,订单 `order/id`,退款 `refund/id`,充值 `agent_recharge/id`。筛选 action 必须使用响应 `action_code`,不可用中文名称反推。缺少稳定 ID 时隐藏入口。固定倒序分页,只查 `retention` 在线窗口,不提供导出、修改或删除。", Tags: []string{"审计调查"}, Input: new(dto.AuditEventListRequest), Output: new(auditquery.EventPage), Auth: true, }) Register(audit, doc, groupPath, "GET", "/events/:event_id", handler.GetEvent, RouteSpec{ Summary: "查询审计事件详情", Description: "`event_id` 来自列表的 `event_id` 或 `investigation_refs.event_id`。响应 `investigation_refs` 映射:`actor_ref.kind/id` → 操作者时间线;`resource_refs[].resource_type/resource_id` → 资源时间线;`request_id` → 请求时间线;`correlation_id` → 关联时间线;`integration_refs[].integration_id` → 外部集成详情。引用字段为空时隐藏对应入口,不按名称、时间或摘要猜测。", Tags: []string{"审计调查"}, Input: new(dto.AuditEventIDParams), Output: new(auditquery.EventDetail), Auth: true, }) Register(audit, doc, groupPath, "GET", "/actors/:kind/:id/events", handler.ListActorEvents, RouteSpec{ Summary: "查询操作者行为时间线", Description: "`kind/id` 来自事件 `investigation_refs.actor_ref`;人工账号页也可使用 `kind=account`、`id=response.data.id`。历史名称使用响应 `actor_name` 快照,不以当前账号名称覆盖。action 使用事件 `action_code`,resource_type/resource_id 使用事件资源引用。", Tags: []string{"审计调查"}, Input: new(dto.AuditActorEventsRequest), Output: new(auditquery.EventPage), Auth: true, }) // 资源搜索静态路径必须先于资源动态时间线路径,避免被动态参数吞掉。 Register(audit, doc, groupPath, "GET", "/resources/search", handler.SearchResources, RouteSpec{ Summary: "精确搜索注册资源", Description: "用于平台调查选择器:卡 keyword 使用 ICCID/VirtualNo,设备使用 VirtualNo/IMEI/SN,店铺使用店铺编号,订单使用订单号,退款使用退款单号。选择结果后将 `items[].resource_type/resource_id` 原样传给资源时间线;`historical=true` 表示仅由历史快照命中。仅精确搜索,不做任意 JSON 模糊搜索。", Tags: []string{"审计调查"}, Input: new(dto.AuditResourceSearchRequest), Output: new(auditquery.ResourceSearchPage), Auth: true, }) Register(audit, doc, groupPath, "GET", "/resources/:resource_type/:resource_id/timeline", handler.ResourceTimeline, RouteSpec{ Summary: "查询通用资源时间线", Description: "`resource_type/resource_id` 必须来自平台业务页的内部 `response.data.id`、资源搜索 `items[]` 或 `investigation_refs.resource_refs[]`。设备卡槽可使用资源引用返回的卡槽类型和 ID,不自行拼接。事件在资源作为 `primary/affected/reference` 时均返回;缺少 resource_id 时隐藏入口。", Tags: []string{"审计调查"}, Input: new(dto.AuditResourceTimelineRequest), Output: new(auditquery.EventPage), Auth: true, }) Register(audit, doc, groupPath, "GET", "/requests/:request_id/timeline", handler.RequestTimeline, RouteSpec{ Summary: "查询请求关联时间线", Description: "`request_id` 来自事件 `investigation_refs.request_id`、Integration `request_id/linkage.request_id`,也可从 Access Log 粘贴。响应节点的 `investigation_refs` 可继续跳转事件、资源、操作者或 Integration 详情。只组合在线持久化事实,不扫描 Access Log 或对象存储。", Tags: []string{"审计调查"}, Input: new(dto.AuditRequestTimelineParams), Output: new(auditquery.LinkTimeline), Auth: true, }) Register(audit, doc, groupPath, "GET", "/correlations/:correlation_id/timeline", handler.CorrelationTimeline, RouteSpec{ Summary: "查询业务关联时间线", Description: "`correlation_id` 来自事件 `investigation_refs.correlation_id` 或 Integration `correlation_id/linkage.correlation_id`。响应节点的 `investigation_refs` 可继续跳转其他视角。只组合在线持久化事实;相同 correlation 不等于技术重试,重试序列只认 Integration 的 `trigger.series`。", Tags: []string{"审计调查"}, Input: new(dto.AuditCorrelationTimelineParams), Output: new(auditquery.LinkTimeline), Auth: true, }) Register(audit, doc, groupPath, "GET", "/finance/timeline", handler.FinanceTimeline, RouteSpec{ Summary: "查询资金调查时间线", Description: "任一稳定条件即可进入,关联事实由服务端补全:订单页 `order_id=response.data.id`,退款页 `refund_id=response.data.id`,充值页 `recharge_id=response.data.id`,钱包页 `wallet_id=response.data.id`,店铺页 `shop_id=response.data.id`;也支持各业务编号、第三方交易号、actor 或 correlation。金额单位为分,以 `amount_authority.authoritative=true` 指向的业务表字段为权威。", Tags: []string{"审计调查"}, Input: new(dto.AuditFinanceTimelineRequest), Output: new(auditquery.FinanceTimelinePage), Auth: true, }) Register(audit, doc, groupPath, "GET", "/risks/overview", handler.RiskOverview, RouteSpec{ Summary: "查询风险调查总览", Description: "时间范围最长31天,缺省使用当前在线窗口。`signals[].code` 固定为 high_risk、finance、security、failed、denied、partial、unknown;`risks/results/sources[].code` 可原样回填同名筛选参数,`actions[].code` 回填 action。name 字段只用于中文展示。", Tags: []string{"审计调查"}, Input: new(dto.AuditRiskOverviewRequest), Output: new(auditquery.RiskOverview), Auth: true, }) Register(audit, doc, groupPath, "GET", "/risks/events", handler.RiskEvents, RouteSpec{ Summary: "查询风险事件明细", Description: "筛选值来自风险总览:`risks[].code→risk`、`results[].code→result`、`actions[].code→action`、`sources[].code→source`。明细 `investigation_refs` 按事件详情相同规则跳转。缺省只查在线窗口,不提供处置或封禁能力。", Tags: []string{"审计调查"}, Input: new(dto.AuditRiskEventsRequest), Output: new(auditquery.RiskEventPage), Auth: true, }) // Integration 总览静态路径必须先于动态详情路径,避免 overview 被当作 integration_id。 Register(audit, doc, groupPath, "GET", "/integrations/overview", handler.IntegrationOverview, RouteSpec{ Summary: "查询外部集成交互总览", Description: "筛选来自调查输入或其他视角稳定字段。`results[].code→result`,`results[].category→result_category`,`providers[].code→provider`,`directions[].code→direction`;所有 name 仅用于中文展示。趋势严格分为 processing、succeeded、indeterminate、failed、not_sent 五类,bucket 为 hour 或 day。", Tags: []string{"审计调查"}, Input: new(dto.IntegrationOverviewRequest), Output: new(integrationquery.Overview), Auth: true, }) Register(audit, doc, groupPath, "GET", "/integrations", handler.ListIntegrations, RouteSpec{ Summary: "查询外部集成交互列表", Description: "组合筛选来自总览 code、事件调查引用或业务页稳定资源字段;operation 必须使用列表/详情返回的稳定编码。列表 `integration_id` 原样传给详情;`request_id/correlation_id` 可跳转链路时间线;`resource.type/resource.id` 均存在时可跳转资源时间线。固定倒序分页,不提供任意摘要搜索。", Tags: []string{"审计调查"}, Input: new(dto.IntegrationListRequest), Output: new(integrationquery.ListPage), Auth: true, }) Register(audit, doc, groupPath, "GET", "/integrations/:integration_id", handler.GetIntegration, RouteSpec{ Summary: "查询外部集成交互详情", Description: "`integration_id` 来自列表、事件 `investigation_refs.integration_refs[]`,或通知 `GET /notifications/{id}/target`:仅当 `available=true` 且 `target_type=integration_log` 时,将 `target_key` 原样作为 integration_id;否则隐藏入口。`linkage.request_id/correlation_id` 可跳转链路时间线;`fidelity` 为 false 时禁止按时间、资源或摘要猜测缺失关系。只读,不提供恢复、修改、删除或导出。", Tags: []string{"审计调查"}, Input: new(dto.IntegrationIDParams), Output: new(integrationquery.DetailResponse), Auth: true, }) }