活码
受众:开发者 摘要:活码模块基础版能力、模块边界、API、后台入口和后续扩展计划。
spring-open-module-live-code 提供活码 / 动态二维码业务能力。基础版先做“一个长期二维码对应一个可调整目标地址”,并已支持 SVG / PNG 二维码、二维码物料清单、后台预览、批量创建、批量下载、扫码统计、扫码分析摘要、异常访问风险摘要、转化事件摘要、转化事件明细、转化事件 CSV 导出、扫码日趋势、扫码明细、渠道统计、地区统计、Campaign 运营统计、企业治理摘要、规则与安全治理审计及 CSV 导出、业务联动 Contributor、时间 / 渠道 / 场景 / 地区 / 设备 / 语言 / A-B 百分比规则分流、二维码样式、Logo 覆盖和分组标签。
什么时候使用
适合这些场景:
- 物料二维码长期不变,但落地页需要随运营调整。
- 线下海报、门店桌牌、活动展架、售后卡片等二维码入口。
- 企业微信群、客服入口、产品说明书、活动报名页等可替换目标。
- 需要统计扫码次数、最近扫码时间,并按时间、渠道、场景、地区、设备或百分比灰度分流不同目标。
如果只是把长 URL 缩短成短 URL,请使用 短链接。如果二维码承载支付、登录、邀请或营销活动,应由对应业务模块发起,活码只提供入口与解析能力。
模块边界
| 项 | 值 |
|---|---|
| Maven 模块 | spring-open-module-live-code |
| API 前缀 | /api/v1/live-codes/** |
| 配置前缀 | spring.open.live-code.* |
| Java 包名 | com.springopen.module.livecode |
| 数据表 | live_code、live_code_scan_record、live_code_conversion_event |
| 权限码前缀 | live-code:* |
应用默认引入该模块,可通过配置关闭:
spring:
open:
live-code:
enabled: truespring.open.live-code.enabled 已通过模块 ServiceProvider 声明为配置中心元数据。启用配置中心后,后台可以发现并维护活码模块启停配置;未通过配置中心覆盖时继续使用 application.yml / 环境变量中的默认装配值。
活码目标地址的域名安全策略也通过配置中心元数据声明,运行时会优先读取配置中心,未配置时等价于不限制:
| 配置 | 说明 |
|---|---|
spring.open.live-code.security.allowed-target-domains | 活码目标链接允许访问的域名列表,留空表示不限制;支持逗号、分号、换行或 JSON 数组。 |
spring.open.live-code.security.blocked-target-domains | 活码目标链接禁止访问的域名列表,命中后保存和解析都会拒绝;支持逗号、分号、换行或 JSON 数组。 |
匹配规则:禁止列表优先;允许列表为空时默认放行;允许列表不为空时,目标域名必须命中其中一项。example.com 会匹配自身及其子域名,*.example.com 只匹配子域名,.example.com 也只匹配子域名。策略会同时覆盖主 targetUrl、ruleJson.routes[].targetUrl 和 ruleJson.fallbackTargetUrl,避免已发布二维码在运营调整后继续跳转到被禁用域名。
活码公开解析和扫码跳转入口支持 API 配额治理。配额配置通过配置中心运行期生效,默认启用 300 次 / 60 秒的同一客户端 IP + 同一活码编码窗口限制;限流 key 只保存活码编码和客户端 IP 的摘要,不保存明文 IP:
| 配置 | 说明 |
|---|---|
spring.open.live-code.quota.public-resolve.enabled | 是否启用活码公开解析和扫码跳转入口的 API 配额限制。 |
spring.open.live-code.quota.public-resolve.max-attempts | 同一客户端 IP 对同一活码编码在一个窗口期内允许解析的最大次数;小于等于 0 表示不限制。 |
spring.open.live-code.quota.public-resolve.window-seconds | 活码公开解析 API 配额的固定窗口秒数;小于等于 0 表示不限制。 |
spring.open.live-code.quota.public-resolve.store | 活码公开解析 API 配额使用的 RateLimit store,留空时使用 RateLimit starter 默认 store。 |
活码异常访问风险摘要也通过配置中心声明阈值。后台扫码分析摘要会展示风险等级、风险分、爬虫占比、最高频 IP 及其扫码占比,样本数不足时只展示指标、不提升风险等级:
| 配置 | 说明 |
|---|---|
spring.open.live-code.risk.min-scan-sample | 异常访问风险判断的最小扫码样本数,默认 10。 |
spring.open.live-code.risk.medium-bot-scan-rate-percent | 疑似爬虫扫码占比达到该百分比时标记为中风险,默认 20。 |
spring.open.live-code.risk.high-bot-scan-rate-percent | 疑似爬虫扫码占比达到该百分比时标记为高风险,默认 50。 |
spring.open.live-code.risk.medium-top-ip-rate-percent | 单一 IP 扫码占比达到该百分比时标记为中风险,默认 50。 |
spring.open.live-code.risk.high-top-ip-rate-percent | 单一 IP 扫码占比达到该百分比时标记为高风险,默认 70。 |
二维码物料治理
后台活码列表的“预览”操作会同时读取二维码 SVG 和物料清单。物料清单接口为 GET /api/v1/admin/live-codes/live-codes/{id}/qr-code-material,返回扫码入口、物料版本、SVG / PNG 文件名、尺寸、留白、颜色和 Logo 配置状态,方便运营确认线下海报、桌牌或活动物料使用的是当前版本。
物料版本格式为 lc-{id}-{updatedAt},其中时间来自活码最近更新时间;目标地址、规则、样式或 Logo 配置发生变更后,后台可以用该版本识别需要重新导出的物料。
SVG 资源会按 styleJson.logoUrl 渲染中心 Logo;PNG 资源只复用尺寸、留白和颜色,不抓取外部 Logo 图片,避免后端在导出位图时对任意外部地址发起请求。需要带 Logo 的位图物料时,应优先导出 SVG 后通过受控内网转码流程生成。
数据表
活码模块当前维护三张表:
| 表 | 说明 |
|---|---|
live_code | 活码主体,保存活码编码、目标 URL、分组、标签、状态、有效期、访问密码、规则 JSON、样式 JSON、扫码次数和扩展信息 |
live_code_scan_record | 扫码明细,保存活码 ID、编码、标题、命中目标、渠道、场景、地区、设备、IP、Referer、User-Agent 和扫码时间 |
live_code_conversion_event | 转化事件,保存活码 ID、编码、事件名、幂等 key、转化金额、渠道、场景、地区、设备、IP、Referer、User-Agent、扩展元数据和转化时间 |
活码编码 code 全局唯一,只允许字母、数字、下划线和中划线,长度 3 到 64。活码编码一旦创建会长期占用,逻辑删除后也不会重新分配给其他活码,避免线下二维码被误解析到新内容。
机制说明
活码的后台管理、公开解析和浏览器跳转都统一走 LiveCodeService。Controller 只做权限、请求转换和响应包装,不在入口层重复业务判断。
保存活码时,后端会统一规范化 code、targetUrl、有效期和访问密码动作。code 不传时生成 8 位编码;targetUrl 基础版只允许 http / https,且必须通过当前域名安全策略;startsAt 和 endsAt 必须形成合法时间窗口。
更新访问密码时必须通过 accessPasswordAction 显式表达意图:
keep或不传:保留原密码。change:使用本次accessPassword修改密码。clear:清空访问密码。
解析活码时会依次检查编码、启用状态、有效期和访问密码。由于部分数据库默认 collation 大小写不敏感,后端会先查候选记录,再在 Java 内用 String.equals 精确匹配真实 code,避免 AbC123 和 abc123 被误认为同一个公开入口。
扫码次数通过 MyBatis-Plus setIncrBy 在数据库侧原子递增,避免高并发扫码时统计丢失。解析成功后会同时写入 live_code_scan_record,记录渠道、场景、地区、设备、IP、Referer、User-Agent 和本次命中的目标 URL,便于后台分析真实投放效果。后台列表支持按运营分组和标签筛选;Campaign 统计复用 groupName 作为运营活动桶,按分组聚合活码数量、启用数量、扫码次数和最近扫码时间,不额外新增 Campaign 表。ruleJson 支持时间窗口、渠道、场景、地区、设备、语言和 A-B 百分比分流,styleJson 支持二维码前景色、背景色、尺寸、留白和居中 Logo 覆盖。
后台扫码分析抽屉会先读取 GET /api/v1/admin/live-codes/live-codes/{id}/analysis-summary,展示 PV、独立 IP、去重 Referer、疑似爬虫扫码数、转化事件数、去重转化 key 和最近转化时间;最近转化事件通过 GET /api/v1/admin/live-codes/live-codes/conversion-events 下钻查看,也可以通过 GET /api/v1/admin/live-codes/live-codes/conversion-events/export 导出 CSV 留档,渠道、地区、趋势和扫码明细仍走各自接口,便于运营先看摘要,再排查投放来源、转化效果或异常访问。
落地页完成注册、下单、留资等业务动作后,可以调用 POST /api/v1/live-codes/live-codes/{code}/conversions 记录转化事件。conversionKey 非空时作为同一活码、同一事件名下的幂等 key,重复上报会返回既有事件,不重复写入;该接口只记录转化,不递增扫码次数。
企业治理元数据
活码模块不直接接管支付、登录、邀请、Marketing、审批、团队或租户的业务状态。需要把活码纳入企业运营治理时,可以在 extraJson.enterpriseGovernance 写入受控元数据,供后台运营、审计和外部业务模块按约定读取:
{
"enterpriseGovernance": {
"tenantId": "tenant-a",
"teamId": "growth-team",
"ownerUserId": "10001",
"approvalStatus": "pending",
"approvalTicketId": "APP-20260701-001",
"brandMaterialStatus": "pending",
"allowedChannels": ["poster", "wechat"],
"blockedScenes": ["risk"],
"allowedRegions": ["CN-ZJ", "CN-SH"],
"blockedDevices": ["Bot"],
"linkages": {
"payment": {
"enabled": true,
"operationKey": "cashier-poster-a"
},
"login": {
"enabled": true,
"scene": "member-sign-in"
},
"invite": {
"enabled": true,
"inviteCode": "SUMMER"
},
"marketing": {
"enabled": true,
"campaignCode": "summer-2026"
}
}
}
}保存活码时会校验治理元数据结构:approvalStatus 只允许 draft / pending / approved / rejected,brandMaterialStatus 只允许 not_required / pending / approved / rejected,渠道、场景、地区和设备名单只允许非空字符串或非空字符串数组。linkages 内置 payment / login / invite / marketing 四类开关;其他模块或应用装配层可以注册 LiveCodeBusinessLinkageContributor Spring Bean 贡献新的联动 key。未登记的 linkage key 会在创建或更新时返回业务错误,避免运营误把任意 JSON 当作治理配置。
后台活码列表会展示企业治理标签、审批状态、品牌物料状态和业务联动类型;表单提供标准治理 JSON 示例按钮,方便运营按项目统一格式填写。列表操作支持提交治理审批、通过治理审批和驳回治理审批:动作会接入 Approval Workflow live-code.governance.review 校验状态流转和权限,把最新动作、操作人、备注、TraceId 和处理时间写回 extraJson.enterpriseGovernance,便于后台审计和排障。该审批只管理活码入口治理状态,不替代 IAM 团队权限、支付、登录、邀请或 Marketing 等 owning module 的真实业务状态。
后台管理接口同时支持团队范围权限:live-code:live-code:team-view、team-create、team-update、team-delete 和 team-review。拥有全局权限时可管理全部活码;只有团队权限时,接口会按 extraJson.enterpriseGovernance.ownerUserId 和 teamId 收窄到当前用户本人或授权团队范围。团队 ID 由应用装配层或团队模块实现 LiveCodeTeamAccessResolver 提供,活码模块不直接依赖 IAM / Organization;没有 resolver 时,团队权限只能操作 ownerUserId 等于当前用户的活码。团队范围创建会在缺失 ownerUserId 时自动写入当前用户,且不能把活码分配给未授权团队或其他负责人。
治理摘要接口用于看板统计,响应会保留内置四类固定计数字段,并通过 linkageCounts 返回全部已登记联动 key 的动态计数:
GET /api/v1/admin/live-codes/live-codes/governance-summary响应包含已配置治理元数据的活码数量、租户隔离、团队归属、待审批、品牌物料通过、访问治理和支付 / 登录 / 邀请 / Marketing 联动覆盖数。该接口只读取 extraJson 汇总,不新增表,也不替代 owning module 的真实支付、登录、邀请或营销状态机。
业务联动的运行期执行通过事件 contract 完成。其他模块或应用装配层先实现 LiveCodeBusinessLinkageContributor 注册自己的 linkage key,再监听 LiveCodeBusinessLinkageTriggeredEvent:
trigger=resolve:公开解析或/q/{code}跳转成功、扫码明细写入后触发。trigger=conversion:落地页首次写入转化事件后触发;带conversionKey的重复上报只返回既有事件,不重复触发。- 事件携带
liveCodeId、code、targetUrl、resolvedTargetUrl、启用的linkageKeys、对应linkageMetadataJson、渠道、场景、地区、设备、语言、IP、Referer、User-Agent、转化事件名和转化 key。
推荐监听器使用 @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)。Live Code 只保证入口和事件上下文,不直接调用 Payment、IAM、Distribution、Marketing 或其他 owning module,也不写它们的状态机或审计记录。
规则与安全治理审计接口用于发布二维码物料前的运营自检:
GET /api/v1/admin/live-codes/live-codes/rule-audit-report响应聚合当前访问范围内的活码总量、启用数量、规则覆盖、fallback 覆盖、二维码样式 / Logo 覆盖、密码保护、访问治理、审批状态、域名安全配置、公开入口配额和异常访问风险阈值。该接口只读现有活码和配置中心运行值,不写活码状态、不改变公开解析或扫码跳转行为。
需要在发布前留档时,可以导出同一访问范围内的规则与安全治理审计 CSV:
GET /api/v1/admin/live-codes/live-codes/rule-audit-report/exportCSV 固定输出规则覆盖、物料治理、安全域名、公开入口配额和风险阈值字段。Admin 活码页的审计卡片提供下载入口,便于运营在批量导出二维码物料前归档当前治理快照。
公开解析和 /q/{code} 跳转会执行 allowedChannels / blockedChannels / allowedScenes / blockedScenes / allowedRegions / blockedRegions / allowedDevices / blockedDevices:禁止名单优先;允许名单非空时,当前渠道、场景、地区或设备必须命中名单;名单支持大小写无关匹配和 * 通配。未通过治理规则时返回业务错误,不会增加扫码次数,也不会写入扫码明细。渠道为空时按 direct 处理,场景、地区或设备为空且配置了对应允许名单时会被拒绝;设备由扫码 User-Agent 粗分为 Mobile、Tablet、Desktop 或 Bot。
后台管理 API
后台接口需要登录并校验权限:
下表列出全局权限。若只授予对应团队权限,接口仍可调用,但只能访问当前用户本人或 LiveCodeTeamAccessResolver 返回团队范围内的活码。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
GET | /api/v1/admin/live-codes/live-codes | live-code:live-code:view | 分页查询活码 |
GET | /api/v1/admin/live-codes/live-codes/statistics | live-code:live-code:view | 查询活码统计总览 |
GET | /api/v1/admin/live-codes/live-codes/campaign-statistics | live-code:live-code:view | 按运营分组查询 Campaign 活码数量、启用数量、扫码次数和最近扫码时间 |
GET | /api/v1/admin/live-codes/live-codes/governance-summary | live-code:live-code:view | 查询企业治理摘要,聚合租户、团队、审批、访问治理和业务联动覆盖数 |
GET | /api/v1/admin/live-codes/live-codes/rule-audit-report | live-code:live-code:view | 查询规则、物料、安全、公开入口配额和风险阈值的只读审计报告 |
GET | /api/v1/admin/live-codes/live-codes/rule-audit-report/export | live-code:live-code:view | 导出规则、物料、安全、公开入口配额和风险阈值 CSV |
GET | /api/v1/admin/live-codes/live-codes/scan-records | live-code:live-code:view | 分页查询扫码明细,可按活码、编码、渠道、地区和设备筛选 |
GET | /api/v1/admin/live-codes/live-codes/conversion-events | live-code:live-code:view | 分页查询转化事件,可按活码、事件名、转化 key、渠道、场景、地区和设备筛选 |
GET | /api/v1/admin/live-codes/live-codes/conversion-events/export | live-code:live-code:view | 导出转化事件 CSV,可按活码、事件名、转化 key、渠道、场景、地区和设备筛选 |
GET | /api/v1/admin/live-codes/live-codes/scan-records/export | live-code:live-code:view | 导出扫码明细 CSV,可按活码、编码、渠道、地区和设备筛选 |
GET | /api/v1/admin/live-codes/live-codes/{id}/analysis-summary | live-code:live-code:view | 查询指定活码扫码和转化分析摘要,包含 PV、独立 IP、去重来源、疑似爬虫、最近扫码时间、转化事件数、去重转化 key 和最近转化时间 |
GET | /api/v1/admin/live-codes/live-codes/{id}/channel-statistics | live-code:live-code:view | 查询指定活码的渠道扫码次数和最近扫码时间 |
GET | /api/v1/admin/live-codes/live-codes/{id}/region-statistics | live-code:live-code:view | 查询指定活码的地区扫码次数和最近扫码时间 |
GET | /api/v1/admin/live-codes/live-codes/{id}/scan-trend | live-code:live-code:view | 查询指定活码最近一段时间的按天扫码趋势 |
GET | /api/v1/admin/live-codes/live-codes/{id} | live-code:live-code:view | 查看活码详情 |
GET | /api/v1/admin/live-codes/live-codes/{id}/qr-code.svg | live-code:live-code:view | 生成指向 /q/{code} 的 SVG 二维码 |
GET | /api/v1/admin/live-codes/live-codes/{id}/qr-code.png | live-code:live-code:view | 生成指向 /q/{code} 的 PNG 二维码 |
POST | /api/v1/admin/live-codes/live-codes/qr-codes.zip | live-code:live-code:view | 按 ID 批量下载 SVG 二维码 ZIP |
POST | /api/v1/admin/live-codes/live-codes | live-code:live-code:create | 创建活码 |
POST | /api/v1/admin/live-codes/live-codes/batch | live-code:live-code:create | 批量创建活码,任一条失败时整体回滚 |
POST | /api/v1/admin/live-codes/live-codes/{id}/governance/submit | live-code:live-code:update | 提交活码企业治理审批,draft / rejected 可进入 pending |
POST | /api/v1/admin/live-codes/live-codes/{id}/governance/approve | live-code:live-code:review | 通过活码企业治理审批,pending 进入 approved |
POST | /api/v1/admin/live-codes/live-codes/{id}/governance/reject | live-code:live-code:review | 驳回活码企业治理审批,pending 进入 rejected,必须填写备注 |
PUT | /api/v1/admin/live-codes/live-codes/{id} | live-code:live-code:update | 修改活码 |
DELETE | /api/v1/admin/live-codes/live-codes/{id} | live-code:live-code:delete | 删除活码 |
DELETE | /api/v1/admin/live-codes/live-codes | live-code:live-code:delete | 批量删除活码 |
创建请求示例:
{
"code": "support",
"title": "售后服务入口",
"targetUrl": "https://example.com/support",
"description": "线下售后卡片二维码",
"groupName": "线下活动",
"tags": "展会,售后",
"status": 1,
"startsAt": "2026-06-01T00:00:00",
"endsAt": "2026-12-31T23:59:59",
"accessPassword": "123456",
"ruleJson": "{\"fallbackTargetUrl\":\"https://example.com/default\",\"routes\":[{\"targetUrl\":\"https://example.com/mobile-poster\",\"startsAt\":\"2026-06-01T09:00:00\",\"endsAt\":\"2026-06-01T18:00:00\",\"channel\":[\"poster\",\"wechat\"],\"scene\":\"store\",\"device\":\"Mobile\"}]}",
"styleJson": "{\"foregroundColor\":\"#0057ff\",\"backgroundColor\":\"#f8fafc\",\"size\":320,\"margin\":2,\"logoUrl\":\"/assets/logo.png\",\"logoSize\":72,\"logoPadding\":8,\"logoBackgroundColor\":\"#ffffff\"}"
}code 可不传,后端会生成 8 位编码。targetUrl 和规则内所有目标地址只允许 http / https。
批量创建接口接收 items 数组,数组项字段和单条创建一致。后端会逐条复用单条创建校验和规范化逻辑,并在同一个事务里保存;任意一条编码重复、目标地址非法或规则 JSON 不合法时,本次批量创建整体回滚。单次最多 100 条:
{
"items": [
{
"code": "campaign-poster-a",
"title": "活动海报 A",
"targetUrl": "https://example.com/campaign/a",
"groupName": "夏季活动",
"tags": "poster,offline",
"status": 1
},
{
"code": "campaign-poster-b",
"title": "活动海报 B",
"targetUrl": "https://example.com/campaign/b",
"groupName": "夏季活动",
"tags": "poster,offline",
"status": 1
}
]
}分页查询支持 keyword、status、groupName 和 tag。keyword 会匹配编码、标题、目标地址、分组和标签;groupName 精确匹配运营分组;tag 对标签字符串做包含匹配。首段标签以逗号分隔字符串保存,适合后台快速筛选和列表展示。
修改活码时,扫码密码采用显式动作,避免表单未填写时误清空旧密码:
accessPasswordAction | 说明 |
|---|---|
keep 或不传 | 保持原密码 |
change | 使用本次 accessPassword 修改密码 |
clear | 清空访问密码 |
规则分流
ruleJson 用于同一个二维码在不同时间、渠道、场景、地区、设备、语言或百分比灰度下跳转到不同目标。当前生产接入时间窗口、channel、scene、region、device、language 和 trafficPercent 分流,适合活动页、工作时段客服入口、线下海报、门店桌牌、区域活动、移动端专属落地页、多语言落地页和 A-B 测试。
可直接复制这个结构:
{
"fallbackTargetUrl": "https://example.com/default",
"routes": [
{
"targetUrl": "https://example.com/workday",
"startsAt": "2026-06-01T09:00:00",
"endsAt": "2026-06-01T18:00:00"
},
{
"targetUrl": "https://example.com/night",
"startsAt": "2026-06-01T18:00:00",
"endsAt": "2026-06-02T09:00:00"
},
{
"targetUrl": "https://example.com/mobile-poster",
"channel": ["poster", "wechat"],
"scene": "store",
"region": ["CN-ZJ", "CN-SH"],
"device": "Mobile"
},
{
"targetUrl": "https://example.com/zh",
"language": ["zh", "zh-CN"]
},
{
"targetUrl": "https://example.com/variant-a",
"trafficPercent": 30
}
]
}匹配规则:
routes按数组顺序匹配,命中第一条满足全部条件的 route 后返回该targetUrl。startsAt可不填;填写后表示当前时间必须大于等于该时间。endsAt可不填;填写后表示当前时间必须早于该时间。- 同一条规则同时填写
startsAt和endsAt时,endsAt必须晚于startsAt。 channel可填字符串或字符串数组,对应公开解析和/q/{code}的channel查询参数;未传时按direct处理。scene可填字符串或字符串数组,对应公开解析和/q/{code}的scene查询参数。region可填字符串或字符串数组,对应公开解析和/q/{code}的region查询参数,例如CN-ZJ、CN-SH或业务自定义区域编码。device可填字符串或字符串数组,后端根据 User-Agent 粗分为Bot/Tablet/Mobile/Desktop后匹配。language可填字符串或字符串数组,对应公开解析和/q/{code}的Accept-Language首选语言;zh可命中zh-CN、zh-TW等子标签,zh-CN只命中同一具体标签。trafficPercent可填0到100的整数,后端会基于活码编码、渠道、场景、地区、IP、Referer、设备和 User-Agent 生成稳定 bucket;bucket 小于trafficPercent时命中。0永不命中,100总是命中。- 同一条 route 同时填写时间、渠道、场景、地区、设备、语言和
trafficPercent时,必须全部满足才算命中。 - 没有任何规则命中时,优先返回
fallbackTargetUrl;未配置 fallback 时返回活码主targetUrl。
ruleJson 必须是 JSON Object;routes 必须是数组;每条 route 必须有合法的 http / https targetUrl。选择器字段只允许非空字符串或非空字符串数组,数组项也不能是空白字符串,匹配时忽略大小写;语言标签会把下划线归一为短横线后匹配;trafficPercent 只允许 0 到 100 的整数。配置错误会在保存活码时返回业务错误,避免扫码时才暴露异常或因空选择器静默落到 fallback。
如果解析时没有任何 route 命中并实际使用了 fallbackTargetUrl,Live Code 会发布 live-code.route.fallback-used 通知规则事件源。应用层监听器只按 Notification 通知规则的运行时配置发送站内信 / 邮件等通道;默认未配置接收人时静默跳过,不影响扫码解析、扫码次数和扫码明细写入。事件 payload 包含活码 ID、编码、标题、主目标、兜底目标、渠道、场景、地区、设备、语言、IP、Referer、User-Agent 和发生时间,可用于运营检查规则覆盖是否过窄。
后台统计
后台统计接口用于管理端看板和扫码分析入口:
GET /api/v1/admin/live-codes/live-codes/statistics响应包含活码总量、启用 / 禁用数量、未开始数量、已过期数量、密码保护数量、扫码总次数、最新扫码时间,以及按扫码次数排序的 Top 活码列表:
{
"code": "200",
"message": "成功",
"data": {
"total": 12,
"enabled": 10,
"disabled": 2,
"notStarted": 1,
"expired": 3,
"passwordProtected": 4,
"totalScans": 358,
"latestScannedAt": "2026-06-03T17:30:00",
"topLiveCodes": [
{
"id": 1,
"code": "support",
"title": "售后服务入口",
"targetUrl": "https://example.com/support",
"scanCount": 128,
"lastScannedAt": "2026-06-03T17:30:00"
}
]
}
}Top 活码列表只统计未删除记录,按 scanCount 倒序、lastScannedAt 倒序、id 倒序取前 5 条。
后台活码管理页的“分析”入口会同时读取渠道统计、地区统计、扫码趋势和扫码明细:
GET /api/v1/admin/live-codes/live-codes/{id}/channel-statistics
GET /api/v1/admin/live-codes/live-codes/{id}/region-statistics
GET /api/v1/admin/live-codes/live-codes/{id}/scan-trend?days=14
GET /api/v1/admin/live-codes/live-codes/scan-records?liveCodeId=1&page=1&size=10
GET /api/v1/admin/live-codes/live-codes/scan-records/export?liveCodeId=1&channel=poster扫码日趋势默认返回最近 14 天,days 最大 90;响应按日期升序返回连续日期桶,某天没有扫码时 scanCount 为 0,适合后台趋势图直接渲染。地区统计按 region 聚合扫码次数和最近扫码时间,空地区归为 未知。扫码明细返回本次解析的渠道、场景、地区、设备、IP、命中目标和扫码时间。扫码明细导出返回 text/csv;charset=UTF-8 附件,字段包含活码 ID、编码、标题、主目标、本次命中目标、扫码时间、渠道、场景、地区、设备、IP、Referer 和 User-Agent,并复用列表筛选条件。转化事件导出同样返回 text/csv;charset=UTF-8 附件,字段包含活码 ID、编码、标题、事件名、转化 key、转化金额、渠道、场景、地区、设备、IP、Referer、User-Agent 和转化时间,适合投放复盘或人工对账留档。channel 为空时后端归为 direct;设备会按 User-Agent 粗分为 Desktop、Mobile、Tablet、Bot 或 未知。分析摘要中的转化事件数来自公开转化上报表,去重转化 key 只统计非空 conversionKey,最近转化时间用于判断投放落地页是否仍有有效转化。
二维码生成
后台可直接生成可扫码 SVG 二维码:
GET /api/v1/admin/live-codes/live-codes/1/qr-code.svg?baseUrl=https://example.com响应类型为 image/svg+xml,内容是指向浏览器跳转入口的 QR Code。baseUrl 可选:
- 不传
baseUrl:二维码内容为相对入口/q/{code},适合后台内嵌预览。 - 传
https://example.com:二维码内容为https://example.com/q/{code},适合下载后投放到线下物料。 baseUrl可以包含部署上下文路径,例如https://example.com/app会生成https://example.com/app/q/{code}。
baseUrl 只允许 http / https,且不能包含 query 或 fragment。二维码图形由 ZXing Core 生成 SVG,不依赖图片文件落盘。
需要位图素材时可下载单个 PNG 二维码:
GET /api/v1/admin/live-codes/live-codes/1/qr-code.png?baseUrl=https://example.com响应类型为 image/png。PNG 使用同一套二维码内容、尺寸、留白、前景色和背景色配置;如果 styleJson 配置了 logoUrl,PNG 会使用更高纠错级别生成基础位图,但不会由服务端抓取远程 Logo 图片,避免二维码下载链路成为外部图片代理或 SSRF 入口。需要带 Logo 的品牌物料时继续使用 SVG 资源。
批量下载接口会按请求 ID 生成 ZIP 附件,每个活码一个 SVG 文件:
POST /api/v1/admin/live-codes/live-codes/qr-codes.zip
Content-Type: application/json{
"ids": [1, 2, 3],
"baseUrl": "https://example.com"
}后台“活码管理”列表支持单条“预览”操作:页面会用当前登录态拉取 qr-code.svg,在弹窗中展示二维码,并提供扫码入口复制和单张 PNG 下载。ZIP 内文件名使用活码编码,例如 support.svg。baseUrl 规则与单个 SVG / PNG 接口一致;同一请求里的重复 ID 会去重并保留首次出现顺序。后台列表也支持勾选多条记录后直接批量下载 SVG 二维码。
二维码样式可通过后台保存的 styleJson 生效。“活码管理”表单已提供结构化样式控件,可配置前景色、背景色、二维码尺寸、留白、Logo 地址、Logo 尺寸、Logo 留白和 Logo 背板色,并会在保存时同步生成 styleJson;仍可从既有 JSON 读取回表单,便于兼容历史数据。
{
"foregroundColor": "#0057ff",
"backgroundColor": "#f8fafc",
"size": 320,
"margin": 2,
"logoUrl": "/assets/logo.png",
"logoSize": 72,
"logoPadding": 8,
"logoBackgroundColor": "#ffffff"
}foregroundColor 可用别名 darkColor 或 color,backgroundColor 可用别名 lightColor。颜色只支持 #RGB 或 #RRGGBB;size 范围为 128 到 1024,margin 范围为 0 到 8。logoUrl 只允许 http / https 绝对地址或以 / 开头的站内绝对路径,不允许协议相对地址、反斜线或 javascript: 等脚本协议;配置后二维码使用更高纠错级别并在中心渲染 Logo。logoSize 不传时按二维码尺寸自动计算,最大不超过二维码宽度的三分之一;logoPadding 不传时默认为 6,最大 32;logoBackgroundColor 使用同样的十六进制颜色规则。配置不合法时,创建 / 更新活码会返回样式配置错误。
公开解析与跳转
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/live-codes/live-codes/{code} | 返回无密码活码解析结果,适合 App / PC 自行处理跳转 |
POST | /api/v1/live-codes/live-codes/{code}/resolve | 解析带密码活码,请求体提交 accessPassword,避免密码出现在 URL 和访问日志 |
POST | /api/v1/live-codes/live-codes/{code}/conversions | 记录落地页转化事件,支持 conversionKey 幂等上报 |
GET | /q/{code} | 浏览器 302 跳转到目标 URL |
公开解析和跳转入口都支持可选 channel / scene / region 查询参数,并会读取浏览器 Accept-Language 首选语言,用于区分海报、门店、社媒、活动批次、区域活动和多语言落地页等投放来源。例如:
GET /q/support?channel=poster&scene=store-a®ion=CN-ZJ公开解析返回:
{
"code": "200",
"message": "成功",
"data": {
"code": "support",
"title": "售后服务入口",
"targetUrl": "https://example.com/support",
"passwordRequired": false
}
}如果活码未开始、已禁用、已过期或密码错误,接口返回统一 Result 业务错误。带密码的活码应使用 POST /resolve 验证,再由前端自行跳转;/q/{code} 第一阶段只适合无密码活码。
扫码次数由后端原子递增,避免高并发扫码时统计丢失。公开解析返回的是本次规则分流后的 targetUrl,后台保存的主目标地址不会被规则命中结果覆盖;后台扫码明细会保存本次命中的实际目标地址和地区参数。
转化事件请求示例:
POST /api/v1/live-codes/live-codes/support/conversions?channel=poster&scene=store-a®ion=CN-ZJ
Content-Type: application/json{
"eventName": "lead",
"conversionKey": "lead-20260630-0001",
"conversionValue": "99.00",
"metadataJson": "{\"form\":\"contact\"}"
}eventName 不传时默认为 conversion;conversionKey 建议使用订单号、表单提交编号、注册流水号等业务侧稳定标识。转化接口会采集同一请求的渠道、场景、地区、设备、IP、Referer 和 User-Agent,方便后台把扫码来源和转化来源放在同一个活码分析摘要里观察。
权限与菜单
迁移会写入:
| 资源 | code |
|---|---|
| 菜单 | live-code |
| 菜单 | live-code:live-code |
| 查看 | live-code:live-code:view |
| 创建 | live-code:live-code:create |
| 修改 | live-code:live-code:update |
| 删除 | live-code:live-code:delete |
| 查看团队范围 | live-code:live-code:team-view |
| 创建团队范围 | live-code:live-code:team-create |
| 修改团队范围 | live-code:live-code:team-update |
| 删除团队范围 | live-code:live-code:team-delete |
| 审批团队范围 | live-code:live-code:team-review |
默认授予 super-admin 角色。
分阶段规划
| 阶段 | 目标 | 说明 |
|---|---|---|
| Phase 1 基础版 | 活码 CRUD、公开解析、/q/{code} 跳转、状态、有效期、访问密码、扫码次数 | 已按最小闭环实现 |
| Phase 2 国内常见能力 | SVG 二维码生成、后台预览、批量创建、批量下载、时间规则分流、扫码明细、渠道统计、地区统计、二维码样式、Logo 覆盖、分组、状态批量管理、落地页模板 | SVG / PNG 二维码、后台预览、批量创建、批量下载、扫码统计、扫码日趋势、扫码明细、渠道统计、地区统计、时间 / 渠道 / 场景 / 地区 / 设备 / A-B 百分比规则分流、基础二维码样式、Logo 覆盖和分组标签已接入;落地页继续按功能簇推进 |
| Phase 3 国际化运营能力 | 品牌域名、Campaign、设备 / 地区 / 语言规则分流、Webhook | 参考 Bitly / Rebrandly 类链接平台的运营分析和团队协作 |
| Phase 4 企业治理 | 团队权限、审计、风险检测、多租户隔离 | 面向企业生产使用;扫码明细 CSV 导出、转化事件 CSV 导出、公开解析 API 配额、转化事件首段、企业治理元数据、治理摘要和团队范围权限已接入 |
活码模块不直接承载支付、登录、邀请、营销活动业务逻辑;这些业务通过事件、应用装配层或后续业务模块接入。