refactor: restructure entire project for human maintainability
Server: - Add Express framework, replace custom router/request parser - Create DAO layer (12 files) centralizing all 73 SQL queries - Rewrite 7 route files as thin Express controllers calling DAOs - Add SCF-to-Express adapter (lib/serverless.js) - Add auth middleware (middleware/auth.js) - Remove dead code from lib/auth.js Admin console: - Extract DataTable component (table + pagination) - Extract ConfirmModal component (modal + form styles) - Create listMixin for paginated list pages - Move form styles to common.css for slot compatibility - Refactor device + subscription pages as examples Miniprogram: - Split 734-line BLE monolith into 4 focused modules (protocol, connection, commands, barrel index) - Create API module (utils/api.js) with named functions - Create page utilities (utils/page.js) - Refactor index + profile pages to use API module
这个提交包含在:
@@ -0,0 +1,341 @@
|
||||
// BLE connection: scan, connect, disconnect, reconnect logic
|
||||
|
||||
var protocol = require('./protocol')
|
||||
var SERVICE = protocol.SERVICE
|
||||
var CHAR = protocol.CHAR
|
||||
|
||||
// Shared state — accessed by commands.js via getters/setters
|
||||
var _deviceId = null
|
||||
var _connected = false
|
||||
var _chars = {}
|
||||
var _autoReconnect = true
|
||||
var _reconnecting = false
|
||||
|
||||
// Event emitter — shared across modules
|
||||
var _listeners = {}
|
||||
|
||||
function on(event, callback) {
|
||||
if (!_listeners[event]) _listeners[event] = []
|
||||
_listeners[event].push(callback)
|
||||
}
|
||||
|
||||
function off(event, callback) {
|
||||
if (!_listeners[event]) return
|
||||
if (callback) {
|
||||
_listeners[event] = _listeners[event].filter(function (cb) { return cb !== callback })
|
||||
} else {
|
||||
_listeners[event] = []
|
||||
}
|
||||
}
|
||||
|
||||
function emit(event, data) {
|
||||
if (!_listeners[event]) return
|
||||
_listeners[event].forEach(function (cb) {
|
||||
try { cb(data) } catch (e) { console.error('ble emit error:', e) }
|
||||
})
|
||||
}
|
||||
|
||||
// --- state accessors (used by commands.js) ---
|
||||
|
||||
function getDeviceId() {
|
||||
return _deviceId
|
||||
}
|
||||
|
||||
function isConnected() {
|
||||
return _connected && _deviceId !== null
|
||||
}
|
||||
|
||||
function getChars() {
|
||||
return _chars
|
||||
}
|
||||
|
||||
// --- notification handling ---
|
||||
|
||||
function handleNotification(frame) {
|
||||
var pendingAcks = require('./commands').getPendingAcks()
|
||||
switch (frame.type) {
|
||||
case protocol.NOTIFY.STATUS_REPORT:
|
||||
emit('status', protocol.parseStatusReport(frame.payload))
|
||||
break
|
||||
case protocol.NOTIFY.ACK:
|
||||
var ack = protocol.parseAck(frame.payload)
|
||||
emit('ack', ack)
|
||||
if (pendingAcks[ack.seq]) {
|
||||
if (ack.error_code === 0) {
|
||||
pendingAcks[ack.seq].resolve(ack)
|
||||
} else {
|
||||
pendingAcks[ack.seq].reject(ack)
|
||||
}
|
||||
delete pendingAcks[ack.seq]
|
||||
}
|
||||
break
|
||||
case protocol.NOTIFY.TREATMENT_COMPLETE:
|
||||
emit('treatment_complete', protocol.parseTreatmentComplete(frame.payload))
|
||||
break
|
||||
case protocol.NOTIFY.EXCEPTION:
|
||||
emit('exception', protocol.parseException(frame.payload))
|
||||
break
|
||||
case protocol.NOTIFY.BIND_SUCCESS:
|
||||
emit('bind_result', { success: frame.payload[0] === 0x00 })
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// --- service/characteristic discovery ---
|
||||
|
||||
function discoverChars(deviceId, serviceId, group) {
|
||||
return new Promise(function (resolve) {
|
||||
wx.getBLEDeviceCharacteristics({
|
||||
deviceId: deviceId,
|
||||
serviceId: serviceId,
|
||||
success: function (res) {
|
||||
var chars = res.characteristics || []
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
var c = chars[i]
|
||||
var uuid = c.uuid.toUpperCase()
|
||||
if (uuid.indexOf(CHAR.DEVICE_INFO) !== -1) _chars.deviceInfo = { uuid: c.uuid, serviceId: serviceId }
|
||||
if (uuid.indexOf(CHAR.COMMAND) !== -1) _chars.command = { uuid: c.uuid, serviceId: serviceId }
|
||||
if (uuid.indexOf(CHAR.STATUS) !== -1) _chars.status = { uuid: c.uuid, serviceId: serviceId }
|
||||
if (uuid.indexOf(CHAR.BOND_INFO) !== -1) _chars.bondInfo = { uuid: c.uuid, serviceId: serviceId }
|
||||
if (uuid.indexOf(CHAR.OTA_CONTROL) !== -1) _chars.otaControl = { uuid: c.uuid, serviceId: serviceId }
|
||||
if (uuid.indexOf(CHAR.OTA_DATA) !== -1) _chars.otaData = { uuid: c.uuid, serviceId: serviceId }
|
||||
if (uuid.indexOf(CHAR.OTA_STATUS) !== -1) _chars.otaStatus = { uuid: c.uuid, serviceId: serviceId }
|
||||
}
|
||||
resolve()
|
||||
},
|
||||
fail: function () { resolve() }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function subscribeToNotifications(deviceId, serviceId) {
|
||||
return new Promise(function (resolve) {
|
||||
if (!_chars.status) { resolve(); return }
|
||||
|
||||
wx.notifyBLECharacteristicValueChange({
|
||||
deviceId: deviceId,
|
||||
serviceId: _chars.status.serviceId,
|
||||
characteristicId: _chars.status.uuid,
|
||||
state: true,
|
||||
success: function () {
|
||||
wx.onBLECharacteristicValueChange(function (res) {
|
||||
var frame = protocol.parseFrame(res.value)
|
||||
if (frame) handleNotification(frame)
|
||||
})
|
||||
resolve()
|
||||
},
|
||||
fail: function () { resolve() }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function discoverServices(deviceId, callbacks) {
|
||||
wx.getBLEDeviceServices({
|
||||
deviceId: deviceId,
|
||||
success: function (res) {
|
||||
var services = res.services
|
||||
var serviceMap = {}
|
||||
for (var i = 0; i < services.length; i++) {
|
||||
var uuid = services[i].uuid.toUpperCase()
|
||||
if (uuid.indexOf(SERVICE.DEVICE_INFO) !== -1) {
|
||||
serviceMap.deviceInfo = services[i].uuid
|
||||
} else if (uuid.indexOf(SERVICE.DATA_COMM) !== -1) {
|
||||
serviceMap.dataComm = services[i].uuid
|
||||
} else if (uuid.indexOf(SERVICE.OTA) !== -1) {
|
||||
serviceMap.ota = services[i].uuid
|
||||
}
|
||||
}
|
||||
|
||||
var tasks = []
|
||||
if (serviceMap.deviceInfo) {
|
||||
tasks.push(discoverChars(deviceId, serviceMap.deviceInfo, 'deviceInfo'))
|
||||
}
|
||||
if (serviceMap.dataComm) {
|
||||
tasks.push(discoverChars(deviceId, serviceMap.dataComm, 'dataComm'))
|
||||
}
|
||||
|
||||
Promise.all(tasks).then(function () {
|
||||
subscribeToNotifications(deviceId, serviceMap.dataComm).then(function () {
|
||||
if (callbacks.onConnected) callbacks.onConnected({ deviceId: deviceId })
|
||||
})
|
||||
})
|
||||
},
|
||||
fail: function () {
|
||||
if (callbacks.onError) callbacks.onError({ msg: '服务发现失败' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --- scan & connect ---
|
||||
|
||||
function startScan(callbacks) {
|
||||
wx.openBluetoothAdapter({
|
||||
success: function () {
|
||||
wx.startBluetoothDevicesDiscovery({
|
||||
allowDuplicatesKey: false,
|
||||
success: function () {
|
||||
wx.offBluetoothDeviceFound()
|
||||
wx.onBluetoothDeviceFound(function (res) {
|
||||
var devices = res.devices || []
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
var d = devices[i]
|
||||
var name = (d.name || '').toUpperCase()
|
||||
var localName = (d.localName || '').toUpperCase()
|
||||
if (name.indexOf('HOX') !== -1 || localName.indexOf('HOX') !== -1 ||
|
||||
name.indexOf('LIGHTMASK') !== -1 || localName.indexOf('LIGHTMASK') !== -1) {
|
||||
wx.stopBluetoothDevicesDiscovery({})
|
||||
if (callbacks.onFound) callbacks.onFound(d)
|
||||
connect(d.deviceId, callbacks)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: function () {
|
||||
if (callbacks.onError) callbacks.onError({ msg: '扫描失败' })
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: function () {
|
||||
if (callbacks.onError) callbacks.onError({ msg: '请开启蓝牙' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function stopScan() {
|
||||
wx.stopBluetoothDevicesDiscovery({})
|
||||
wx.offBluetoothDeviceFound()
|
||||
}
|
||||
|
||||
function connect(deviceId, callbacks) {
|
||||
_deviceId = deviceId
|
||||
_chars = {}
|
||||
|
||||
wx.createBLEConnection({
|
||||
deviceId: deviceId,
|
||||
timeout: 10000,
|
||||
success: function () {
|
||||
_connected = true
|
||||
discoverServices(deviceId, callbacks)
|
||||
},
|
||||
fail: function () {
|
||||
_connected = false
|
||||
if (callbacks.onError) callbacks.onError({ msg: '连接失败' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
_autoReconnect = false
|
||||
_reconnecting = false
|
||||
if (_deviceId) {
|
||||
wx.closeBLEConnection({ deviceId: _deviceId })
|
||||
_deviceId = null
|
||||
}
|
||||
_connected = false
|
||||
_chars = {}
|
||||
var commands = require('./commands')
|
||||
commands.clearPendingAcks()
|
||||
_listeners = {}
|
||||
wx.closeBluetoothAdapter({})
|
||||
}
|
||||
|
||||
// --- reconnect ---
|
||||
|
||||
function attemptReconnect(deviceId, retriesLeft) {
|
||||
wx.createBLEConnection({
|
||||
deviceId: deviceId,
|
||||
timeout: 10000,
|
||||
success: function () {
|
||||
_connected = true
|
||||
_chars = {}
|
||||
wx.getBLEDeviceServices({
|
||||
deviceId: deviceId,
|
||||
success: function (res) {
|
||||
var services = res.services
|
||||
var serviceMap = {}
|
||||
for (var i = 0; i < services.length; i++) {
|
||||
var uuid = services[i].uuid.toUpperCase()
|
||||
if (uuid.indexOf(SERVICE.DEVICE_INFO) !== -1) {
|
||||
serviceMap.deviceInfo = services[i].uuid
|
||||
} else if (uuid.indexOf(SERVICE.DATA_COMM) !== -1) {
|
||||
serviceMap.dataComm = services[i].uuid
|
||||
}
|
||||
}
|
||||
|
||||
var tasks = []
|
||||
if (serviceMap.deviceInfo) {
|
||||
tasks.push(discoverChars(deviceId, serviceMap.deviceInfo, 'deviceInfo'))
|
||||
}
|
||||
if (serviceMap.dataComm) {
|
||||
tasks.push(discoverChars(deviceId, serviceMap.dataComm, 'dataComm'))
|
||||
}
|
||||
|
||||
Promise.all(tasks).then(function () {
|
||||
var serviceId = serviceMap.dataComm || null
|
||||
return subscribeToNotifications(deviceId, serviceId)
|
||||
}).then(function () {
|
||||
_reconnecting = false
|
||||
emit('reconnected', { deviceId: deviceId })
|
||||
})
|
||||
},
|
||||
fail: function () {
|
||||
if (retriesLeft > 1) {
|
||||
setTimeout(function () {
|
||||
attemptReconnect(deviceId, retriesLeft - 1)
|
||||
}, 2000)
|
||||
} else {
|
||||
_reconnecting = false
|
||||
emit('reconnect_failed', { deviceId: deviceId })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: function () {
|
||||
if (retriesLeft > 1) {
|
||||
setTimeout(function () {
|
||||
attemptReconnect(deviceId, retriesLeft - 1)
|
||||
}, 2000)
|
||||
} else {
|
||||
_reconnecting = false
|
||||
emit('reconnect_failed', { deviceId: deviceId })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setAutoReconnect(enabled) {
|
||||
_autoReconnect = enabled
|
||||
}
|
||||
|
||||
// --- BLE connection state change listener ---
|
||||
|
||||
wx.onBLEConnectionStateChange(function (res) {
|
||||
if (!res.connected) {
|
||||
_connected = false
|
||||
emit('disconnected', { deviceId: res.deviceId })
|
||||
if (_autoReconnect && !_reconnecting && _deviceId) {
|
||||
_reconnecting = true
|
||||
setTimeout(function () {
|
||||
attemptReconnect(_deviceId, 3)
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
on: on,
|
||||
off: off,
|
||||
emit: emit,
|
||||
|
||||
getDeviceId: getDeviceId,
|
||||
isConnected: isConnected,
|
||||
getChars: getChars,
|
||||
|
||||
startScan: startScan,
|
||||
stopScan: stopScan,
|
||||
connect: connect,
|
||||
disconnect: disconnect,
|
||||
attemptReconnect: attemptReconnect,
|
||||
setAutoReconnect: setAutoReconnect
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户