纳入范围: - backend/ Spring Boot 多模块后端(iot-common/device/rule/data/alarm/auth) - frontend/admin/ Vue3 + Element Plus 管理后台 - Android/ 涂鸦原生 APP (com.xhai.iot) - backend/sql/ Flyway 风格迁移脚本(V2~V21) - docs/ 对接文档与设计文档 排除范围: - 构建产物: node_modules/ target/ build/ dist/ .gradle/ - 签名密钥: *.jks *.keystore (严禁入库) - 本机配置: Android/local.properties - 运行日志: logs/ *.log - AI 协作本地数据 .codebuddy/ 与第三方 demo tuya-demo/ .gitattributes 统一以 LF 存储, Windows 批处理保留 CRLF
504 lines
16 KiB
Markdown
504 lines
16 KiB
Markdown
# 涂鸦智能 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 个 |
|
||
|
||
### 支持的登录方式(解决你的核心问题!)
|
||
|
||
```javascript
|
||
// 方式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 平台配置
|
||
1. 登录 [涂鸦 IoT 平台](https://iot.tuya.com)
|
||
2. 进入 **云开发 → App SDK**
|
||
3. 创建应用:
|
||
- 应用类型:Android/iOS
|
||
- 包名:`com.iot.saas.app`(自定义)
|
||
- SHA1/SHA256:打包后从 keystore 获取
|
||
4. 下载 **安全图片** (`t_s.bmp`)
|
||
5. 记录 `AppKey` 和 `AppSecret`
|
||
|
||
#### 1.2 购买/试用 DCloud 插件
|
||
- 插件地址:[ext.dcloud.net.cn/plugin?id=9081](https://ext.dcloud.net.cn/plugin?id=9081)
|
||
- 价格:¥499(支持免费试用)
|
||
- 操作:
|
||
1. 在 HBuilderX 中购买/试用该插件
|
||
2. 绑定到当前项目 AppID
|
||
|
||
#### 1.3 uni-app 项目配置
|
||
```json
|
||
// 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 封装涂鸦服务层
|
||
|
||
```typescript
|
||
// 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 改造绑定页面
|
||
|
||
```vue
|
||
<!-- 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 改造个人中心入口
|
||
|
||
```vue
|
||
<!-- 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 涂鸦设备同步到本地平台
|
||
|
||
```typescript
|
||
// 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):
|
||
|
||
```xml
|
||
<!-- 必要权限 -->
|
||
<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 变更导致不兼容 | 关注官方更新通知 |
|
||
|
||
---
|
||
|
||
## 九、下一步行动
|
||
|
||
1. ✅ **确认方案** — 你确认是否采用此方案
|
||
2. 🔧 **我帮你实施** — 开始编码改造
|
||
3. 📦 **准备材料** — 你去涂鸦 IoT 平台:
|
||
- 创建 App SDK 应用
|
||
- 下载安全图片
|
||
- 记录 AppKey/AppSecret
|
||
4. 💰 **购买插件** — 在 HBuilderX 中购买/试用 XM-Tuya 插件
|