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
这个提交包含在:
Guoguo
2026-04-29 05:58:20 -07:00
父节点 9f0e629c82
当前提交 bb4b80f867
修改 46 个文件,包含 4306 行新增2020 行删除
+44 -50
查看文件
@@ -1,7 +1,11 @@
const { one, query, transaction } = require('../lib/db')
const router = require('express').Router()
const { ok, fail } = require('../lib/response')
const { requireUser, requireAdmin } = require('../lib/auth')
const { writeLog } = require('../lib/log')
const { requireUser } = require('../middleware/auth')
const { requireAdmin } = require('../middleware/auth')
const subscriptionDao = require('../dao/subscription.dao')
const logDao = require('../dao/log.dao')
const wrap = fn => (req, res, next) => fn(req, res, next).catch(next)
const PLANS = {
trial: { amount: 0, days: 7 },
@@ -9,54 +13,44 @@ const PLANS = {
yearly: { amount: 899, days: 365 }
}
function register(router) {
router.get('/api/v1/subscription', async ctx => {
const user = await requireUser(ctx)
if (!user) return fail(1001, 'invalid_token')
const sub = await one('SELECT *, GREATEST(DATEDIFF(expire_time, NOW()), 0) AS remaining_days FROM subscriptions WHERE user_id = :user_id AND status = 1 ORDER BY expire_time DESC LIMIT 1', { user_id: user.user_id })
if (!sub) return ok({ status: 'inactive', plan: 'none', remaining_days: 0 })
return ok({ status: sub.remaining_days > 0 ? 'active' : 'expired', plan: sub.plan, start_time: sub.start_time, expire_time: sub.expire_time, remaining_days: sub.remaining_days })
})
router.get('/subscription', requireUser, wrap(async (req, res) => {
const sub = await subscriptionDao.findActive(req.user.user_id)
if (!sub) return res.json(ok({ status: 'inactive', plan: 'none', remaining_days: 0 }))
res.json(ok({
status: sub.remaining_days > 0 ? 'active' : 'expired',
plan: sub.plan,
start_time: sub.start_time,
expire_time: sub.expire_time,
remaining_days: sub.remaining_days
}))
}))
router.post('/api/v1/subscription/purchase', async ctx => {
const user = await requireUser(ctx)
if (!user) return fail(1001, 'invalid_token')
const plan = ctx.body.plan || ctx.body.plan_type
if (!PLANS[plan]) return fail(2001, 'invalid plan')
const orderId = 'ORD' + Date.now()
return ok({ order_id: orderId, payment_params: {}, plan, amount: PLANS[plan].amount })
})
router.post('/subscription/purchase', requireUser, wrap(async (req, res) => {
const plan = req.body.plan || req.body.plan_type
if (!PLANS[plan]) return res.json(fail(2001, 'invalid plan'))
const orderId = 'ORD' + Date.now()
res.json(ok({ order_id: orderId, payment_params: {}, plan, amount: PLANS[plan].amount }))
}))
router.post('/api/v1/subscription/trial', async ctx => {
const user = await requireUser(ctx)
if (!user) return fail(1001, 'invalid_token')
const usedTrial = await one('SELECT subscription_id FROM subscriptions WHERE user_id = :user_id AND plan = \'trial\' LIMIT 1', { user_id: user.user_id })
if (usedTrial) return fail(2001, '已使用过试用')
const activeSub = await one('SELECT subscription_id FROM subscriptions WHERE user_id = :user_id AND status = 1 LIMIT 1', { user_id: user.user_id })
if (activeSub) return fail(2001, '已有有效订阅')
const orderId = 'TRIAL' + Date.now()
await query('INSERT INTO subscriptions (user_id, plan, status, amount, order_id, start_time, expire_time) VALUES (:user_id, \'trial\', 1, 0, :order_id, NOW(), DATE_ADD(NOW(), INTERVAL 7 DAY))', { user_id: user.user_id, order_id: orderId })
return ok({ status: 'active', plan: 'trial', remaining_days: 7 })
})
router.post('/subscription/trial', requireUser, wrap(async (req, res) => {
const usedTrial = await subscriptionDao.findTrial(req.user.user_id)
if (usedTrial) return res.json(fail(2001, '已使用过试用'))
const activeSub = await subscriptionDao.findActive(req.user.user_id)
if (activeSub) return res.json(fail(2001, '已有有效订阅'))
await subscriptionDao.createTrial(req.user.user_id)
res.json(ok({ status: 'active', plan: 'trial', remaining_days: 7 }))
}))
// Temporary: admin-only until payment integration
router.post('/api/v1/subscription/verify', async ctx => {
const admin = await requireAdmin(ctx)
if (!admin) return fail(1002, '未授权,请重新登录')
const userId = ctx.body.user_id
if (!userId) return fail(2001, 'user_id required')
const plan = ctx.body.plan || ctx.body.plan_type || 'monthly'
if (!PLANS[plan]) return fail(2001, 'invalid plan')
const p = PLANS[plan]
await transaction(async conn => {
await conn.execute('UPDATE subscriptions SET status = 2 WHERE user_id = ? AND status = 1', [userId])
await conn.execute('INSERT INTO subscriptions (user_id, plan, status, amount, order_id, start_time, expire_time) VALUES (?, ?, 1, ?, ?, NOW(), DATE_ADD(NOW(), INTERVAL ? DAY))', [
userId, plan, p.amount, ctx.body.order_id || 'ORD' + Date.now(), p.days
])
})
await writeLog({ admin_id: admin.admin_id, action: 'subscription_verify', detail: '订阅生效: ' + plan + ' user:' + userId, ip: ctx.ip })
return ok({ status: 'active', plan, remaining_days: p.days })
})
}
// Temporary: admin-only until payment integration
router.post('/subscription/verify', requireAdmin, wrap(async (req, res) => {
const userId = req.body.user_id
if (!userId) return res.json(fail(2001, 'user_id required'))
const plan = req.body.plan || req.body.plan_type || 'monthly'
if (!PLANS[plan]) return res.json(fail(2001, 'invalid plan'))
const p = PLANS[plan]
await subscriptionDao.purchase(userId, plan, p.amount, req.body.order_id || 'ORD' + Date.now(), p.days)
await logDao.write({ admin_id: req.admin.admin_id, action: 'subscription_verify', detail: '订阅生效: ' + plan + ' user:' + userId, ip: req.ip })
res.json(ok({ status: 'active', plan, remaining_days: p.days }))
}))
module.exports = register
module.exports = router