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
这个提交包含在:
@@ -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
|
||||
|
||||
在新工单中引用
屏蔽一个用户