feat: add user deactivation and vendor BLE protocol

这个提交包含在:
Guoguo
2026-05-11 21:14:41 +08:00
父节点 c95d47e0c1
当前提交 583fcb7e3d
修改 16 个文件,包含 698 行新增52 行删除
+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
}
+31
查看文件
@@ -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) => {