文件
jw-beauty/server/src/routes/device.js
T
Guoguo f0fdb285b5 chore: remove all mock/debug code for production release
Miniprogram:
- ENV switched to 'prod' (disables __DEV__ flag)
- Deleted mock.js and test-ble-frame.js
- Removed mockBind/mockPurchase from api.js
- Removed all debug UI panels and onMock* handlers from 6 pages
- Removed mock purchase fallback in subscribe-plans
- Removed SIMPLE PERIPHERAL dev board from BLE scan

Server:
- Removed /device/mock-bind route
- Removed /subscription/mock-purchase route
- Removed dev_openid fallback in wechat.js
- Removed mockBind() from binding.dao.js
- Removed mock fallback in purchase endpoint
- NODE_ENV default changed to 'production'

Admin:
- ENV switched to 'prod'

All mock code preserved in 'test' branch for future development use.
2026-05-20 07:19:26 -07:00

127 行
5.4 KiB
JavaScript

const router = require('express').Router()
const { ok, fail } = require('../lib/response')
const { requireUser } = require('../middleware/auth')
const { randomHex } = require('../lib/auth')
const { getSettings } = require('../lib/settings-cache')
const bindingDao = require('../dao/binding.dao')
const deviceDao = require('../dao/device.dao')
const commandDao = require('../dao/command.dao')
const subscriptionDao = require('../dao/subscription.dao')
const deviceEventDao = require('../dao/device-event.dao')
const logDao = require('../dao/log.dao')
const wrap = fn => (req, res, next) => fn(req, res, next).catch(next)
router.post('/device/bind', requireUser, wrap(async (req, res) => {
const settings = await getSettings()
if (settings.enable_binding === false) return res.json(fail(2001, '设备绑定已关闭'))
const deviceId = String(req.body.device_id || '').trim()
if (!deviceId) return res.json(fail(2001, 'device_id required'))
const active = await bindingDao.findActiveByUser(req.user.user_id)
if (active) return res.json(fail(2001, '已绑定设备', { device_id: active.device_id }))
const device = await bindingDao.findDeviceExists(deviceId)
if (!device) return res.json(fail(1005, 'DEVICE_NOT_FOUND'))
// Cancel any stale pending bindings for this user before creating a new one
await bindingDao.cancelPending(req.user.user_id)
const bindToken = randomHex(8)
await bindingDao.createPending(req.user.user_id, deviceId, bindToken)
await logDao.write({ user_id: req.user.user_id, action: 'device_bind_request', detail: '申请绑定设备: ' + deviceId, ip: req.ip })
const sub = await subscriptionDao.findActive(req.user.user_id)
res.json(ok({
device_id: deviceId,
bind_token: bindToken,
subscription: sub ? { plan: sub.plan, remaining_days: sub.remaining_days } : { plan: 'none', remaining_days: 0 }
}))
}))
router.post('/device/bind/confirm', requireUser, wrap(async (req, res) => {
const deviceId = String(req.body.device_id || '').trim()
const bindToken = String(req.body.bind_token || '').trim()
if (!deviceId || !bindToken) return res.json(fail(2001, 'device_id and bind_token required'))
const confirmed = await bindingDao.confirmBind(req.user.user_id, deviceId, bindToken)
if (!confirmed) return res.json(fail(2001, 'bind_token invalid or expired'))
await logDao.write({ user_id: req.user.user_id, action: 'device_bind_confirm', detail: '确认绑定设备: ' + deviceId, ip: req.ip })
const sub = await subscriptionDao.findActive(req.user.user_id)
res.json(ok({
message: 'success',
subscription: sub ? { plan: sub.plan, remaining_days: sub.remaining_days } : { plan: 'none', remaining_days: 0 }
}))
}))
router.post('/device/unbind', requireUser, wrap(async (req, res) => {
const deviceId = req.body.device_id || null
await bindingDao.unbindByUser(req.user.user_id, deviceId)
await logDao.write({ user_id: req.user.user_id, action: 'device_unbind', detail: '解绑设备: ' + (deviceId || 'current'), ip: req.ip })
res.json(ok({ message: 'success' }))
}))
router.get('/device/list', requireUser, wrap(async (req, res) => {
const devices = await deviceDao.listByUser(req.user.user_id)
res.json(ok({ devices, total: devices.length }))
}))
router.get('/device/command/pending', requireUser, wrap(async (req, res) => {
const deviceId = String(req.query.device_id || '').trim()
if (!deviceId) return res.json(fail(2001, 'device_id required'))
const active = await bindingDao.findActiveByUser(req.user.user_id)
if (!active || active.device_id !== deviceId) return res.json(fail(1006, 'DEVICE_NOT_BOUND'))
const commands = await commandDao.getPending(deviceId)
if (commands.length > 0) {
await commandDao.markPulled(commands.map(c => c.command_id))
}
res.json(ok({
commands: commands.map(c => ({
seq: c.command_id,
opcode: c.opcode,
payload: typeof c.payload_json === 'string' ? JSON.parse(c.payload_json) : c.payload_json || {}
}))
}))
}))
router.post('/device/command/result', requireUser, wrap(async (req, res) => {
const commandId = parseInt(req.body.command_id || req.body.seq, 10)
const success = req.body.success !== false
if (!commandId) return res.json(fail(2001, 'command_id required'))
const cmd = await commandDao.findByIdForUser(commandId, req.user.user_id)
if (!cmd) return res.json(fail(1006, 'device_not_bound'))
await commandDao.finish(commandId, success, req.body)
res.json(ok({ message: 'success' }))
}))
router.post('/device/event', requireUser, wrap(async (req, res) => {
const deviceId = String(req.body.device_id || '').trim()
if (!deviceId) return res.json(fail(2001, 'device_id required'))
const active = await bindingDao.findActiveByUser(req.user.user_id)
if (!active || active.device_id !== deviceId) return res.json(fail(1006, 'device_not_bound'))
await deviceEventDao.create({
device_id: deviceId,
user_id: req.user.user_id,
event_type: req.body.event_type || 'device_error',
error_code: req.body.error_code || null,
temperature: req.body.temperature || null,
payload: req.body
})
await logDao.write({ user_id: req.user.user_id, action: 'device_event', detail: '设备事件: ' + deviceId, ip: req.ip })
res.json(ok({ message: 'ok' }))
}))
router.get('/device/:device_id', requireUser, wrap(async (req, res) => {
const device = await deviceDao.findBoundDevice(req.user.user_id, req.params.device_id)
if (!device) return res.json(fail(1006, 'DEVICE_NOT_BOUND'))
res.json(ok(device))
}))
module.exports = router