碰一碰发视频系统是一套基于 NFC 近场通信的门店营销工具,顾客用支持 NFC 的手机轻触门店的 NFC 立牌 / 桌贴 / 标签,即可一键打开带 POI 定位、文案与热门 BGM 的短视频模板,快速发布到抖音 / 小红书 / 大众点评等平台,实现线下触达→线上裂变→数据复盘的闭环宣传。

核心构成与工作流程
- 硬件:NFC 标签 / 立牌 / 桌贴(被动式,无需供电,成本低、部署快);支持 NFC 的手机。
- 软件:商家管理后台(上传素材、配置模板、设置福利、查看数据);前端跳转链路(NDEF URI/Deep Link,直达小程序 / APP / 网页);AI 剪辑与文案生成(批量产出适配多平台的短视频与文案)。
- 流程:商家上传素材→生成模板并绑定 NFC→门店摆放引导→顾客碰一碰→一键发布→后台统计曝光 / 转化。
为什么适合门店用 NFC 做宣传
- 门槛极低:无需扫码,碰一下就触发,转化率远高于传统扫码,适合快节奏场景与各类客群。
- 裂变高效:顾客自发发布带定位的视频,形成本地流量池与口碑传播,成本可控。
- 多平台适配:一键分发至抖音 / 小红书 / 大众点评等,同时沉淀私域(加微信、连 WiFi、领券)。
- 数据可追溯:后台看触碰量、发布量、播放量、到店转化,便于优化素材与点位。
门店落地步骤(快速上手)
- 选服务商与套餐:确定支持的平台、AI 剪辑能力、后台数据维度,领取 NFC 立牌 / 桌贴并开通后台。
- 制作素材与模板:上传门店环境、菜品 / 产品、服务等视频 / 图片,用 AI 生成多套带定位与团购链接的短视频模板,配好文案与话题标签。
- 部署与引导:将立牌放在收银台、餐桌、门头等高触达点位,贴 "碰一碰发视频领福利" 提示,培训员工引导顾客参与。
- 运营与复盘:设置发布奖励(折扣、赠品、积分),定期更新模板与 BGM,根据后台数据调整点位与素材。
常见问题与注意事项
- 手机兼容性:iOS/Android 主流机型大多支持 NFC;iOS 需开启 NFC,部分场景跳转需通过 Safari;Android 可直接拉起 APP,体验更优。
- 链路稳定性:用 Deep Link/App Linking 确保直达 APP,避免浏览器中转;测试不同机型的触发成功率,优化标签位置与天线设计。
- 合规与隐私:仅收集必要的触碰与发布数据,不采集手机号等敏感信息;文案与素材需符合平台规则,避免违规限流。
- 成本与 ROI:NFC 标签 / 立牌成本低,主要支出为服务商年费 / 流量费;建议先小范围试点,跑通转化再扩大部署。
选型建议(简表)
| 维度 | 推荐选项 | 避坑点 |
|---|---|---|
| 平台覆盖 | 抖音 / 小红书 / 大众点评 / 私域 | 仅支持单一平台,裂变受限 |
| 链路体验 | Deep Link 直达 APP,无浏览器中转 | 跳转链路长,用户流失率高 |
| 内容能力 | AI 一键剪辑、多模板、带 POI / 团购 | 无 AI 能力,需手动剪辑,效率低 |
| 数据后台 | 触碰量、发布量、播放量、转化漏斗 | 仅统计触碰量,无法复盘效果 |
碰一碰发视频系统开发逻辑代码:
碰一碰发视频系统部分代码逻辑展示
碰一碰发视频系统的核心逻辑围绕 设备近距离检测(碰一碰触发)、视频数据传输、接收端处理 三大核心模块展开,以下是分场景的代码逻辑实现(含关键注释),涵盖从触发到视频展示的完整流程。
核心前提说明
- 碰一碰触发方式:优先采用 NFC(近场通信) 或 蓝牙 BLE 近距离感应(手机 / 设备间常用),本文以 NFC 为例(最贴合 "碰一碰" 直觉)。
- 视频传输:小视频(<100MB)直接通过蓝牙 BLE/NFC 传输;大视频通过 "碰一碰获取视频 URL + 局域网 / 5G 下载" 实现(避免传输超时)。
- 适配场景:手机→手机、手机→智能设备(如电视 / 音箱)、设备→设备。
一、整体架构流程图
plaintext
触发端(发送方) 接收端(接收方)
┌───────────────────┐ ┌───────────────────┐
│ 1. 初始化NFC/BLE模块 │ │ 1. 初始化NFC/BLE模块 │
│ 2. 加载待发送视频数据 │ │ 2. 开启感应监听模式 │
│ 3. 碰一碰触发连接 │◄─────►│ 3. 感应到设备并建立连接 │
│ 4. 传输视频(或URL) │ │ 4. 接收视频数据(或URL) │
│ 5. 传输进度反馈 │ │ 5. 解析数据并校验 │
│ 6. 传输完成回调 │ │ 6. 本地存储/直接播放 │
└───────────────────┘ └───────────────────┘
二、关键模块代码实现(以 Android 为例,Java/Kotlin)
模块 1:NFC 触发与连接(核心触发逻辑)
发送方(触发端)代码
kotlin
class NfcSenderActivity : AppCompatActivity() {
private lateinit var nfcAdapter: NfcAdapter
private var videoData: VideoInfo? = null // 待发送视频信息(路径/URL/大小)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nfc_sender)
// 1. 初始化NFC适配器
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (nfcAdapter == null) {
Toast.makeText(this, "设备不支持NFC", Toast.LENGTH_SHORT).show()
finish()
return
}
if (!nfcAdapter.isEnabled) {
// 引导用户开启NFC
startActivity(Intent(Settings.ACTION_NFC_SETTINGS))
}
// 2. 加载待发送视频(示例:从本地选择视频)
videoData = VideoInfo(
path = "/sdcard/Download/test_video.mp4", // 本地视频路径
url = "http://192.168.1.100:8080/video/test.mp4", // 备选:局域网URL
size = File("/sdcard/Download/test_video.mp4").length(), // 视频大小
md5 = getFileMD5("/sdcard/Download/test_video.mp4") // 校验码
)
}
// 3. 碰一碰时触发NFC发送(系统回调)
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
// 检测到接收方设备,开始传输视频数据
sendVideoToReceiver(videoData!!)
}
}
// 4. 发送视频(小视频直接传数据,大视频传URL)
private fun sendVideoToReceiver(video: VideoInfo) {
val ndefMessage = NdefMessage(
arrayOf(
NdefRecord.createMime(
"application/vnd.碰一碰视频",
Json.encodeToString(video).toByteArray() // 视频信息序列化
)
)
)
try {
// 通过NFC发送数据
nfcAdapter.setNdefPushMessage(ndefMessage, this)
// 监听传输进度
nfcAdapter.setOnNdefPushCompleteCallback {
runOnUiThread { Toast.makeText(this, "视频发送触发成功!", Toast.LENGTH_SHORT).show() }
}
} catch (e: Exception) {
Toast.makeText(this, "发送失败:${e.message}", Toast.LENGTH_SHORT).show()
}
}
// 辅助:计算文件MD5(用于校验)
private fun getFileMD5(filePath: String): String {
// 实现MD5计算逻辑(略,核心是校验文件完整性)
}
// 视频信息数据类
data class VideoInfo(
val path: String, // 本地路径(发送方)
val url: String, // 下载URL(接收方用)
val size: Long, // 大小(字节)
val md5: String // 校验码
)
}
接收方(感应端)代码
kotlin
class NfcReceiverActivity : AppCompatActivity() {
private lateinit var nfcAdapter: NfcAdapter
private lateinit var pendingIntent: PendingIntent
private val ndefDetectedFilters = arrayOf(
IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
addDataMimeType("application/vnd.碰一碰视频") // 匹配发送方的MIME类型
}
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nfc_receiver)
// 1. 初始化NFC适配器
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (nfcAdapter == null || !nfcAdapter.isEnabled) {
Toast.makeText(this, "请开启NFC功能", Toast.LENGTH_SHORT).show()
finish()
return
}
// 2. 创建PendingIntent,用于接收NFC触发事件
pendingIntent = PendingIntent.getActivity(
this, 0,
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
}
// 3. 开启NFC监听(Activity可见时)
override fun onResume() {
super.onResume()
nfcAdapter.enableForegroundDispatch(this, pendingIntent, ndefDetectedFilters, null)
}
// 4. 关闭NFC监听(Activity不可见时)
override fun onPause() {
super.onPause()
nfcAdapter.disableForegroundDispatch(this)
}
// 5. 接收NFC数据(碰一碰后触发)
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
val rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
rawMessages?.let { messages ->
val ndefMessage = messages[0] as NdefMessage
val record = ndefMessage.records[0]
val videoJson = String(record.payload)
val videoInfo = Json.decodeFromString<VideoInfo>(videoJson) // 反序列化
// 6. 处理视频(下载/直接播放)
handleReceivedVideo(videoInfo)
}
}
}
// 7. 处理接收的视频信息
private fun handleReceivedVideo(videoInfo: VideoInfo) {
// 校验视频完整性(避免传输错误)
if (videoInfo.size > 1024 * 1024 * 100) { // 大视频(>100MB):通过URL下载
downloadVideoFromUrl(videoInfo.url, videoInfo.md5)
} else {
// 小视频:直接从发送方拉取数据(需配合蓝牙传输,NFC仅传元信息)
pullVideoFromSender(videoInfo)
}
}
// 8. 从URL下载视频(大视频场景)
private fun downloadVideoFromUrl(videoUrl: String, md5: String) {
// 使用Retrofit/OkHttp下载视频
val downloadDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES)!!
val targetFile = File(downloadDir, "received_video_${System.currentTimeMillis()}.mp4")
OkHttpClient().newCall(Request.Builder().url(videoUrl).build()).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
response.body?.byteStream()?.use { inputStream ->
FileOutputStream(targetFile).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
// 校验MD5
if (getFileMD5(targetFile.path) == md5) {
runOnUiThread {
Toast.makeText(this@NfcReceiverActivity, "下载完成,开始播放", Toast.LENGTH_SHORT).show()
playVideo(targetFile.path) // 播放视频
}
} else {
runOnUiThread { Toast.makeText(this@NfcReceiverActivity, "视频校验失败", Toast.LENGTH_SHORT).show() }
}
}
override fun onFailure(call: Call, e: IOException) {
runOnUiThread { Toast.makeText(this@NfcReceiverActivity, "下载失败:${e.message}", Toast.LENGTH_SHORT).show() }
}
})
}
// 9. 直接播放视频(本地文件)
private fun playVideo(videoPath: String) {
val intent = Intent(Intent.ACTION_VIEW)
val uri = FileProvider.getUriForFile(this, "${packageName}.fileprovider", File(videoPath))
intent.setDataAndType(uri, "video/*")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
}
// 复用发送方的VideoInfo数据类(略)
}