文件

5.3 KiB

Repository Reality

  • WeChat Mini Program (miniprogram/) + Tencent Cloud SCF HTTP backend (server/) + Vue 3 H5 admin console (admin-console/).
  • Backend: Express.js with DAO layer, deployed as Tencent Cloud HTTP Function.
  • Database: TencentDB MySQL, schema in server/sql/schema.sql.
  • Storage: Tencent COS for firmware and admin H5 artifacts.

Architecture

Server (server/src/)

app.js              — Express app, middleware, route mounting, rate limiting
index.js            — SCF entry point (lib/serverless.js adapter)
config.js           — Environment config with production guards
lib/
  db.js             — MySQL2 pool, query/one/transaction helpers, namedPlaceholders
  auth.js           — JWT sign/verify, bcrypt compare, randomHex
  response.js       — ok()/fail() response wrappers
  settings-cache.js — Cached settings with 60s TTL + invalidateCache()
  serverless.js     — SCF event → Express req/res adapter
  utils.js          — toMysqlDate (UTC+8 aware)
middleware/
  auth.js           — requireUser/requireAdmin Express middleware
dao/                — 11 DAO files: admin, binding, command, device, device-event,
                      firmware, log, settings, subscription, treatment, user
routes/             — 7 route files: admin, auth, device, firmware, subscription, treatment, user
scripts/
  local-server.js   — Local dev server (app.listen)
  init-db.js        — Database schema initialization

Route mounting:

  • /api/v1 — auth, user, device, subscription, treatment, firmware
  • /api/v1/admin — admin routes
  • Rate limiting: user login 10/15min, admin login 5/15min

Auth:

  • User: JWT_SECRET, payload.type === 'user', 7d expiry, 1d refresh grace
  • Admin: ADMIN_JWT_SECRET, payload.type === 'admin'
  • bcrypt for passwords, SHA-256 legacy auto-migration

Mini Program (miniprogram/)

pages/              — 15+ pages (index, profile, subscribe-*, treating, etc.)
services/
  ble.js            — Proxy: module.exports = require('./ble/index')
  ble/
    protocol.js     — Constants, frame encode/decode, uint32ToBytes, hexToBytes
    connection.js   — Scan, connect, disconnect, reconnect, event emitter
    commands.js     — writeCommand, startTreatment, stopTreatment, bindDevice
    index.js        — Barrel export (39 exports including REGION_NAMES)
utils/
  api.js            — Named API functions matching server routes
  request.js        — wx.request wrapper with token refresh
  page.js           — getStatusBarHeight, navigateBack, isDevMode
config/
  env.js            — API base URL per environment

BLE frame format: 0xAA 0x55 | length | type | payload | XOR checksum

Device binding: Two-step (bind request → BLE handshake → confirm) or mock-bind (dev only).

Admin Console (admin-console/)

pages/
  login/index.vue   — Standalone login page
  admin/index.vue   — SPA shell: AdminLayout + keep-alive + dynamic <component :is>
views/              — 9 views: Dashboard, DeviceList, DeviceDetail, UserList,
                      UserDetail, Subscription, Record, Log, Settings
components/
  AdminLayout.vue   — Sidebar nav, emits 'navigate' with view names
  DataTable.vue     — Reusable table + pagination
  ConfirmModal.vue  — Reusable modal
utils/
  useList.js        — listMixin for paginated list views
styles/
  common.css        — Shared styles including form classes

SPA routing: Component-based (no URL routing). keep-alive caches views by name property.

Key Design Decisions

  1. Subscription extends, never overwrites — purchase() adds days to existing expire_time via DATE_ADD
  2. Settings cache — 60s TTL in-memory, invalidated on admin save
  3. Feature toggles — maintenance_mode, enable_binding enforced server-side via settings-cache
  4. Timezone — SCF runs UTC, MySQL connection timezone +08:00, toMysqlDate forces UTC+8
  5. Mock endpoints — mock-bind, mock-purchase restricted to non-production (config.nodeEnv !== 'production')
  6. BLE module resolutionservices/ble.js proxy file exists because WeChat require('./ble') doesn't resolve ble/index.js

Security Measures

  • Rate limiting on auth endpoints (express-rate-limit)
  • Production guards: throw if JWT secrets or admin credentials use defaults
  • Parameterized queries (namedPlaceholders) everywhere except one Number()-coerced IN clause
  • JWT type field prevents cross-contamination between user/admin tokens
  • CORS currently * (development phase, to be restricted for production)

Deployment

Backend local dev:

cd server && npm install && npm start

SCF deploy:

  • HTTP function, uses scf_bootstrap (not event handler)
  • Must be in TencentDB VPC/subnet
  • Function name: jw-beauty-api

Admin H5:

cd admin-console && npm install && npm run build:h5 && npm run deploy:cos

Test API base: https://1426323813-ilxkhlxf4p.ap-guangzhou.tencentscf.com

Working Rules

  • Use current code as source of truth, not older planning docs
  • Never commit .env, credentials, or signed URLs
  • WeChat miniprogram require() does not auto-resolve directories — always use explicit proxy files
  • wx.getUserProfile() is deprecated; if reliable profile needed, use chooseAvatar + nickname input
  • Mock/dev endpoints are gated by config.nodeEnv !== 'production'