涂鸦智能 APP 开发方案(B2C 场景)
一、项目背景与目标
现状问题
| 问题 |
说明 |
| OAuth 授权页无法访问 |
openauth.tuyacn.com DNS 解析失败 |
| H5 模式限制 |
无法使用涂鸦原生 App SDK(需原生环境) |
| 简单模式不适用 |
项目级 token,所有用户共享,不适合 B2C |
目标
- 用户在 APP 内用手机号+验证码登录/注册涂鸦账号
- 登录后获取用户级 token,操作自己的设备
- 支持 WiFi / 蓝牙 / Matter 配网添加设备
- 兼容小米、华为、OPPO、vivo 等主流安卓手机
二、技术方案选型
方案对比
| 方案 |
技术栈 |
优点 |
缺点 |
适合度 |
| A: uni-app + 原生插件 |
Vue3 + Tuya 插件 |
复用现有代码、跨平台 |
需购买插件(¥499)、依赖第三方 |
⭐⭐⭐⭐⭐ |
| B: 原生 Android |
Kotlin/Java + Tuya SDK |
功能最完整、性能最好 |
需重写 UI、开发周期长 |
⭐⭐⭐ |
| C: 微信小程序 |
微信小程序 + Tuya 小程序SDK |
无需下载安装、微信生态 |
仅限微信内使用 |
⭐⭐⭐ |
推荐方案:A — uni-app + 涂鸦原生插件
┌─────────────────────────────────────────────────────────┐
│ 你的 APP 架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌────────────────────────────┐ │
│ │ uni-app │ │ XM-Tuya 原生插件 │ │
│ │ (Vue3) │◄──►│ (封装涂鸦 App SDK) │ │
│ │ │ │ │ │
│ │ • 用户体系 │ │ • 手机号登录/注册 │ │
│ │ • 设备列表 │ │ • WiFi/蓝牙配网 │ │
│ │ • 规则引擎 │ │ • 设备控制 │ │
│ │ • 告警中心 │ │ • 家庭管理 │ │
│ └──────────────┘ └────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ 后端服务 (Spring Boot) │ │
│ │ iot-device:9081 | iot-data:9082 │ │
│ └────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ 涂鸦 IoT 云平台 │ │
│ │ openapi.tuyacn.com │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
三、涂鸦插件能力清单
已有 DCloud 插件:XM-Tuya (ID: 9081)
| 模块 |
功能 |
API 数量 |
| 用户管理 |
手机号/邮箱登录注册、验证码、修改资料 |
18 个 |
| 家庭管理 |
创建/查询家庭、房间管理、成员管理 |
19 个 |
| 设备配网 |
WiFi 快连/AP模式、BLE蓝牙配网、双模配网 |
12 个 |
| 设备控制 |
DP 点读取/下发、设备重命名/移除/重置 |
8 个 |
| 固件升级 |
WiFi/BLE OTA 升级检查与执行 |
5 个 |
| 通用接口 |
带鉴权/无鉴权通用 API 调用 |
2 个 |
支持的登录方式(解决你的核心问题!)
// 方式1: 手机号 + 验证码登录 ⭐推荐
const tuyaUser = uni.requireNativePlugin('XM-TuyaUserModule')
tuyaUser.loginWithPhone({
countryCode: '86', // 中国大陆
phone: '13800138000',
code: '123456' // 短信验证码
}, result => {
console.log('涂鸦登录成功:', result)
})
// 方式2: 手机号 + 密码登录
tuyaUser.loginWithPhonePassword({
countryCode: '86',
phone: '13800138000',
password: 'your_password'
}, callback)
// 方式3: 邮箱登录
tuyaUser.loginWithEmail({ ... })
tuyaUser.loginWithEmailCode({ ... })
四、开发步骤详解
Phase 1: 准备工作(预计 0.5 天)
1.1 涂鸦 IoT 平台配置
- 登录 涂鸦 IoT 平台
- 进入 云开发 → App SDK
- 创建应用:
- 应用类型:Android/iOS
- 包名:
com.iot.saas.app(自定义)
- SHA1/SHA256:打包后从 keystore 获取
- 下载 安全图片 (
t_s.bmp)
- 记录
AppKey 和 AppSecret
1.2 购买/试用 DCloud 插件
1.3 uni-app 项目配置
// manifest.json → App原生插件配置
{
"app-plus": {
"nativePlugins": {
"XM-Tuya": {
"__plugin__": true,
"__plugin_name__": "涂鸦智能生活双端插件"
}
}
}
}
1.4 放置安全图片
uniapp/
└── nativeplugins/
└── XM-Tuya/
└── android/
└── assets/
└── t_s.bmp ← 从涂鸦平台下载的安全图片
Phase 2: 涂鸦集成开发(预计 3-4 天)
2.1 封装涂鸦服务层
// src/services/tuya.ts - 涂鸦 SDK 服务封装
declare const requireNativePlugin: any
class TuyaService {
private userModule: any
private homeModule: any
private deviceModule: any
private configModule: any
constructor() {
this.userModule = requireNativePlugin('XM-TuyaUserModule')
this.homeModule = requireNativePlugin('XM-TuyaHomeModule')
this.deviceModule = requireNativePlugin('XM-TuyaDeviceModule')
this.configModule = requireNativePlugin('XM-TuyaConfigModule')
// 初始化涂鸦 SDK
// #ifdef APP-PLUS
if (this.userModule) {
this.userModule.initWithAppKey({
appKey: 'YOUR_TUYA_APP_KEY',
schemaName: 'tuyasmart' // 自定义 scheme
})
}
// #endif
}
// ========== 用户认证 ==========
/** 发送验证码 */
sendVerifyCode(phone: string, countryCode = '86'): Promise<void> {
return new Promise((resolve, reject) => {
this.userModule.sendVerifyCodeWithUserName({
countryCode,
userName: phone,
region: '86',
codeType: 1 // 1=登录验证码, 2=注册验证码
}, (res: any) => {
res.success ? resolve() : reject(new Error(res.error))
})
})
}
/** 手机号+验证码登录 */
loginWithPhoneCode(phone: string, code: string): Promise<any> {
return new Promise((resolve, reject) => {
this.userModule.loginWithPhone({
countryCode: '86',
phone,
code
}, (res: any) => {
res.success ? resolve(res.data) : reject(new Error(res.error))
})
})
}
/** 注册新账号 */
register(phone: string, code: string, password: string): Promise<void> { ... }
/** 登出 */
logout(): void {
this.userModule.logout()
}
// ========== 家庭管理 ==========
/** 获取家庭列表 */
getHomeList(): Promise<any[]> { ... }
/** 获取家庭下的设备 */
getDeviceList(homeId: number): Promise<any[]> { ... }
// ========== 设备配网 ==========
/** 获取 WiFi 配网 Token */
getWifiToken(homeId: number): Promise<string> { ... }
/** 开始配网 */
startActivator(token: string, ssid: string, password: string, type: string): void { ... }
/** 停止配网 */
stopActivator(): void { ... }
// ========== 设备控制 ==========
/** 发送 DP 指令 */
publishDps(deviceId: string, dps: Record<string, any>): Promise<void> { ... }
/** 获取设备状态 */
getDp(deviceId: string): Promise<Record<string, any>> { ... }
}
export default new TuyaService()
2.2 改造绑定页面
<!-- pages/profile/tuya-bind.vue -->
<template>
<view class="bind-page">
<view class="header">
<text class="title">绑定涂鸦账号</text>
<text class="desc">使用涂鸦账号登录后即可管理您的智能设备</text>
</view>
<!-- 手机号输入 -->
<view class="form-item">
<input v-model="phone" placeholder="请输入手机号"
type="number" maxlength="11" />
</view>
<!-- 验证码 -->
<view class="form-item">
<input v-model="code" placeholder="请输入验证码" type="number" maxlength="6" />
<button class="code-btn" :disabled="countdown > 0" @click="sendCode">
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
</button>
</view>
<!-- 绑定按钮 -->
<button class="submit-btn" :loading="binding" @click="handleBind">
{{ tuyaBound ? '已绑定' : '立即绑定' }}
</button>
<!-- 已有账号?显示解绑 -->
<button v-if="tuyaBound" class="unbind-btn" @click="handleUnbind">
解绑涂鸦账号
</button>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import tuyaService from '@/services/tuya'
const phone = ref('')
const code = ref('')
const binding = ref(false)
const countdown = ref(0)
const tuyaBound = ref(false)
async function sendCode() {
if (!phone.value || phone.value.length !== 11) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
try {
await tuyaService.sendVerifyCode(phone.value)
countdown.value = 60
const timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) clearInterval(timer)
}, 1000)
uni.showToast({ title: '验证码已发送', icon: 'success' })
} catch (e: any) {
uni.showToast({ title: e.message || '发送失败', icon: 'none' })
}
}
async function handleBind() {
if (!phone.value || !code.value) {
uni.showToast({ title: '请填写完整', icon: 'none' })
return
}
try {
binding.value = true
const result = await tuyaService.loginWithPhoneCode(phone.value, code.value)
// 登录成功,保存状态
uni.setStorageSync('tuya_user_info', JSON.stringify(result))
uni.setStorageSync('tuya_bound', true)
uni.showToast({ title: '绑定成功', icon: 'success' })
setTimeout(() => uni.navigateBack(), 1500)
} catch (e: any) {
uni.showToast({ title: e.message || '绑定失败', icon: 'none' })
} finally {
binding.value = false
}
}
</script>
2.3 改造个人中心入口
<!-- profile.vue - bindTuya 方法改造 -->
async function bindTuya() {
if (tuyaBound.value) {
// 解绑逻辑...
} else {
// 跳转到涂鸦绑定页面
// #ifdef APP-PLUS
uni.navigateTo({ url: '/pages/profile/tuya-bind' })
// #endif
// #ifndef APP-PLUS
uni.showToast({ title: '请在APP中使用此功能', icon: 'none' })
// #endif
}
}
Phase 3: 设备同步功能(预计 2-3 天)
3.1 涂鸦设备同步到本地平台
// services/deviceSync.ts
export async function syncTuyaDevices(homeId: number) {
// 1. 从涂鸦 SDK 获取设备列表
const tuyaDevices = await tuyaService.getDeviceList(homeId)
// 2. 同步到我们后端
const syncedDevices = []
for (const dev of tuyaDevices) {
try {
const result = await post('/api/v1/tuya/devices/sync', {
tuyaDeviceId: dev.devId,
name: dev.name,
model: dev.productName,
icon: dev.iconUrl,
online: dev.isOnline,
capabilities: dev.dpCodes || {}
})
syncedDevices.push(result)
} catch (e) {
console.warn(`设备 ${dev.devId} 同步失败`, e)
}
}
return syncedDevices
}
Phase 4: 打包发布(预计 1 天)
4.1 打包流程
HBuilderX 操作步骤:
1. 运行 → 运行到手机或模拟器 → 制作自定义调试基座
2. 测试通过后:
发行 → 原生App-云打包
选择:
- Android
- 使用公共测试证书 (正式版用自己的证书)
- 勾选 XM-Tuya 插件
4.2 Android 兼容性保证
| 品牌 |
兼容性说明 |
特殊处理 |
| 小米 |
✅ 完全兼容 |
MIUI 需授权「位置权限」才能扫描 WiFi |
| 华为 |
✅ 完全兼容 |
HMS 可能需要额外配置 |
| OPPO/vivo |
✅ 完全兼容 |
ColorOS/OriginOS 无特殊要求 |
| 三星 |
✅ 兼容 |
OneUI 标准安卓行为 |
关键权限配置(manifest.json):
<!-- 必要权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<!-- 蓝牙配网需要 -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- Android 12+ 新增 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
五、文件结构变更
uniapp/src/
├── services/
│ ├── tuya.ts ← 新建:涂鸦 SDK 封装服务
│ └── deviceSync.ts ← 新建:设备同步服务
├── pages/
│ ├── profile/
│ │ ├── profile.vue ← 修改:绑定入口改为跳转
│ │ ├── tuya-bind.vue ← 新建:涂鸦账号绑定页
│ │ └── tuya-callback.vue ← 删除或保留备用
│ └── device/
│ ├── add.vue ← 修改:增加涂鸦配网方式
│ └── list.vue ← 修改:支持涂鸦设备展示
├── nativeplugins/ ← 新建目录
│ └── XM-Tuya/
│ └── android/assets/t_s.bmp
└── manifest.json ← 修改:添加原生插件配置
六、开发时间线
| 阶段 |
内容 |
时间 |
| Phase 1 |
平台配置、购买插件、基础搭建 |
0.5 天 |
| Phase 2 |
涂鸦 SDK 集成、登录/绑定功能 |
2-3 天 |
| Phase 3 |
设备同步、配网功能完善 |
2 天 |
| Phase 4 |
测试、适配、打包发布 |
1-2 天 |
| 合计 |
|
约 6-7 天 |
七、费用预算
| 项目 |
费用 |
备注 |
| DCloud 涂鸦插件 |
¥499 |
一次性买断 |
| 涂鸦开发者账号 |
免费 |
个人/企业均可 |
| Android 签名证书 |
免费 |
或 ¥99/年用自有签名 |
| 应用商店上架 |
免费 |
华为/小米/OPPO等免费 |
八、风险与对策
| 风险 |
影响 |
对策 |
| 插件版本滞后 |
SDK 为 4.X,非最新 |
先试用评估,满足需求即可 |
| 云打包排队 |
发布等待时间长 |
使用自定义基座本地调试 |
| 权限被拒 |
Android 10+ 后台限制 |
引导用户手动授权 |
| 涂鸦服务变动 |
API 变更导致不兼容 |
关注官方更新通知 |
九、下一步行动
- ✅ 确认方案 — 你确认是否采用此方案
- 🔧 我帮你实施 — 开始编码改造
- 📦 准备材料 — 你去涂鸦 IoT 平台:
- 创建 App SDK 应用
- 下载安全图片
- 记录 AppKey/AppSecret
- 💰 购买插件 — 在 HBuilderX 中购买/试用 XM-Tuya 插件