feat: add user deactivation and vendor BLE protocol
这个提交包含在:
+40
-2
@@ -1,4 +1,4 @@
|
||||
const { query, one, limitClause } = require('../lib/db')
|
||||
const { query, one, transaction, limitClause } = require('../lib/db')
|
||||
|
||||
/**
|
||||
* Find user by WeChat openid
|
||||
@@ -165,6 +165,42 @@ async function findByIdAdmin(userId) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user status.
|
||||
* @param {number} userId
|
||||
* @param {number} status - 1=active, 2=disabled, 3=cancelled
|
||||
* @returns {Promise<Array>} query result
|
||||
*/
|
||||
async function updateStatus(userId, status) {
|
||||
return query(
|
||||
'UPDATE users SET status = :status WHERE user_id = :user_id',
|
||||
{ user_id: userId, status }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft-cancel a user account and unbind all active devices.
|
||||
* Historical treatment/subscription/log rows are kept.
|
||||
* @param {number} userId
|
||||
* @returns {Promise<{userAffectedRows: number, unboundRows: number}>}
|
||||
*/
|
||||
async function deactivate(userId) {
|
||||
return transaction(async conn => {
|
||||
const [userResult] = await conn.execute(
|
||||
'UPDATE users SET status = 3 WHERE user_id = ? AND status <> 3',
|
||||
[userId]
|
||||
)
|
||||
const [unbindResult] = await conn.execute(
|
||||
'UPDATE bindings SET bind_status = 2, unbind_time = NOW() WHERE user_id = ? AND bind_status = 1',
|
||||
[userId]
|
||||
)
|
||||
return {
|
||||
userAffectedRows: userResult.affectedRows || 0,
|
||||
unboundRows: unbindResult.affectedRows || 0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
findByOpenid,
|
||||
create,
|
||||
@@ -174,5 +210,7 @@ module.exports = {
|
||||
updatePhone,
|
||||
listAdmin,
|
||||
countAdmin,
|
||||
findByIdAdmin
|
||||
findByIdAdmin,
|
||||
updateStatus,
|
||||
deactivate
|
||||
}
|
||||
|
||||
@@ -153,6 +153,37 @@ router.get('/users/:user_id', requireAdmin, wrap(async (req, res) => {
|
||||
res.json(ok(result))
|
||||
}))
|
||||
|
||||
router.post('/users/:user_id/unbind', requireAdmin, wrap(async (req, res) => {
|
||||
const userId = req.params.user_id
|
||||
const targetUser = await userDao.findById(userId)
|
||||
if (!targetUser) return res.json(fail(1004, 'USER_NOT_FOUND'))
|
||||
const deviceId = req.body.device_id ? String(req.body.device_id).trim() : null
|
||||
const result = await bindingDao.unbindByUser(userId, deviceId)
|
||||
await logDao.write({
|
||||
admin_id: req.admin.admin_id,
|
||||
user_id: userId,
|
||||
action: 'admin_user_unbind',
|
||||
detail: '后台解绑用户设备: user=' + userId + ', device=' + (deviceId || 'all'),
|
||||
ip: req.ip
|
||||
})
|
||||
res.json(ok({ message: 'success', affected_rows: result.affectedRows || 0 }))
|
||||
}))
|
||||
|
||||
router.post('/users/:user_id/deactivate', requireAdmin, wrap(async (req, res) => {
|
||||
const userId = req.params.user_id
|
||||
const targetUser = await userDao.findById(userId)
|
||||
if (!targetUser) return res.json(fail(1004, 'USER_NOT_FOUND'))
|
||||
const result = await userDao.deactivate(userId)
|
||||
await logDao.write({
|
||||
admin_id: req.admin.admin_id,
|
||||
user_id: userId,
|
||||
action: 'admin_user_deactivate',
|
||||
detail: '后台注销用户: user=' + userId + ', unbound=' + result.unboundRows,
|
||||
ip: req.ip
|
||||
})
|
||||
res.json(ok({ message: 'success', unbound_rows: result.unboundRows }))
|
||||
}))
|
||||
|
||||
// --- Subscriptions ---
|
||||
|
||||
router.get('/subscriptions', requireAdmin, wrap(async (req, res) => {
|
||||
|
||||
在新工单中引用
屏蔽一个用户