From 031678c03f8b003641a120caa51fc8e726208f79 Mon Sep 17 00:00:00 2001 From: Guoguo Date: Sat, 2 May 2026 08:48:26 -0700 Subject: [PATCH] feat: complete feature gaps across all modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server: - Add settings-cache with 60s TTL for feature toggle checks - Enforce maintenance_mode on login, enable_binding on device bind Admin console: - Remove dead "发送通知" button from user detail - Firmware check calls real API and compares versions Miniprogram: - Wear-check: dynamic battery/connected from BLE state - Login: hide non-functional phone auth button - Treating: show actual selected regions instead of hardcoded "全脸" - Index: display subscription status with tap to manage - Auto-scan: show "待检测" instead of "--" for PD data - Agreements: tap shows "内容建设中" modal --- admin-console/src/views/DeviceDetailView.vue | 19 ++++++++++++++- admin-console/src/views/UserDetailView.vue | 1 - miniprogram/pages/auto-scan/auto-scan.js | 2 +- miniprogram/pages/auto-scan/auto-scan.wxml | 1 + miniprogram/pages/index/index.wxml | 9 +++++++ miniprogram/pages/index/index.wxss | 24 +++++++++++++++++++ miniprogram/pages/login/login.js | 4 ++++ miniprogram/pages/login/login.wxml | 4 ++-- .../pages/subscribe-plans/subscribe-plans.js | 4 ++++ .../subscribe-plans/subscribe-plans.wxml | 2 +- miniprogram/pages/treating/treating.js | 17 +++++++++++-- miniprogram/pages/treating/treating.wxml | 2 +- miniprogram/pages/wear-check/wear-check.js | 12 +++++++++- miniprogram/pages/wear-check/wear-check.wxml | 5 ++-- server/src/lib/settings-cache.js | 14 +++++++++++ server/src/routes/auth.js | 4 ++++ server/src/routes/device.js | 4 ++++ 17 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 server/src/lib/settings-cache.js diff --git a/admin-console/src/views/DeviceDetailView.vue b/admin-console/src/views/DeviceDetailView.vue index 9ed563b..71734af 100644 --- a/admin-console/src/views/DeviceDetailView.vue +++ b/admin-console/src/views/DeviceDetailView.vue @@ -162,8 +162,25 @@ export default { onViewLogs() { this.$emit('navigate', 'log', { device_id: this.deviceId }) }, - onCheckUpdate() { + async onCheckUpdate() { uni.showToast({ title: '正在检查更新...', icon: 'none' }) + try { + const data = await get('/api/v1/admin/firmware') + const records = data.records || [] + const latest = records.filter(r => r.status === 1).sort((a, b) => b.firmware_id - a.firmware_id)[0] + if (!latest) { + uni.showToast({ title: '暂无固件信息', icon: 'none' }) + return + } + const current = this.device && this.device.firmware_version + if (current && current === latest.version) { + uni.showToast({ title: '当前已是最新版本', icon: 'success' }) + } else { + uni.showToast({ title: '发现新版本 v' + latest.version, icon: 'none', duration: 3000 }) + } + } catch (e) { + uni.showToast({ title: '检查更新失败', icon: 'none' }) + } }, formatDate: formatDateUtil } diff --git a/admin-console/src/views/UserDetailView.vue b/admin-console/src/views/UserDetailView.vue index 0f06ff5..af26e0f 100644 --- a/admin-console/src/views/UserDetailView.vue +++ b/admin-console/src/views/UserDetailView.vue @@ -5,7 +5,6 @@ 👥 用户详情 - {{ user.nickname || user.user_id }} - 请将面罩对准面部 + 连接设备后可获取皮肤数据 🔧 调试工具 diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index eed3fea..a2229f3 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -23,6 +23,15 @@ + + + 💳 + 智能模式 · 剩余{{subRemaining}}天 + 未订阅智能模式 + + + + diff --git a/miniprogram/pages/index/index.wxss b/miniprogram/pages/index/index.wxss index 6af5421..1e8b2a9 100644 --- a/miniprogram/pages/index/index.wxss +++ b/miniprogram/pages/index/index.wxss @@ -18,3 +18,27 @@ .btn-secondary::after { border: none; } + +.sub-info { + display: flex; + justify-content: space-between; + align-items: center; + background: #fff; + border-radius: 16rpx; + padding: 24rpx; + margin-top: 20rpx; +} +.sub-info-left { + display: flex; + align-items: center; + gap: 12rpx; + font-size: 28rpx; + color: #333; +} +.sub-info-icon { + font-size: 32rpx; +} +.sub-info-arrow { + color: #999; + font-size: 32rpx; +} diff --git a/miniprogram/pages/login/login.js b/miniprogram/pages/login/login.js index 7e6e36c..c1c078b 100644 --- a/miniprogram/pages/login/login.js +++ b/miniprogram/pages/login/login.js @@ -65,6 +65,10 @@ Page({ }) }, + onAgreement: function () { + wx.showModal({ title: '提示', content: '用户协议和隐私政策内容建设中', showCancel: false }) + }, + onGetPhoneNumber: function (e) { var self = this if (!e.detail || !e.detail.code) { diff --git a/miniprogram/pages/login/login.wxml b/miniprogram/pages/login/login.wxml index e501ede..ac7757d 100644 --- a/miniprogram/pages/login/login.wxml +++ b/miniprogram/pages/login/login.wxml @@ -18,10 +18,10 @@ - - 登录即表示同意《用户协议》和《隐私政策》 + 登录即表示同意《用户协议》和《隐私政策》 diff --git a/miniprogram/pages/subscribe-plans/subscribe-plans.js b/miniprogram/pages/subscribe-plans/subscribe-plans.js index e626ad2..2accec5 100644 --- a/miniprogram/pages/subscribe-plans/subscribe-plans.js +++ b/miniprogram/pages/subscribe-plans/subscribe-plans.js @@ -91,6 +91,10 @@ Page({ }).catch(function () {}) }, + onAgreement: function () { + wx.showModal({ title: '提示', content: '订阅协议内容建设中', showCancel: false }) + }, + onSelect: function (e) { this.setData({ selected: e.currentTarget.dataset.plan }) }, diff --git a/miniprogram/pages/subscribe-plans/subscribe-plans.wxml b/miniprogram/pages/subscribe-plans/subscribe-plans.wxml index 7af3250..eb9628d 100644 --- a/miniprogram/pages/subscribe-plans/subscribe-plans.wxml +++ b/miniprogram/pages/subscribe-plans/subscribe-plans.wxml @@ -40,6 +40,6 @@ - 支付即表示同意《订阅协议》 + 支付即表示同意《订阅协议》 diff --git a/miniprogram/pages/treating/treating.js b/miniprogram/pages/treating/treating.js index 5cb304c..732d344 100644 --- a/miniprogram/pages/treating/treating.js +++ b/miniprogram/pages/treating/treating.js @@ -18,19 +18,32 @@ Page({ completed: false, startedAt: 0, devMode: false, + regionText: '全脸护理', + }, + + _getRegionText: function (regionMask) { + var REGION_MAP = {1:'左脸', 2:'右脸', 4:'额头', 8:'下巴', 16:'鼻', 32:'左眼', 64:'右眼'} + if (regionMask === 0x7F) return '全脸护理' + var names = [] + for (var bit in REGION_MAP) { + if (regionMask & parseInt(bit)) names.push(REGION_MAP[bit]) + } + return names.length > 0 ? names.join('+') + '护理' : '全脸护理' }, onLoad: function (options) { var app = getApp() this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false }) this._deviceId = ble.getDeviceId() || (app.globalData.currentDevice && app.globalData.currentDevice.device_id) || '' + var regions = parseInt(options.regions) || 0x7F this.setData({ - regions: parseInt(options.regions) || 0x7F, + regions: regions, wavelength: parseInt(options.wavelength) || 2, duration: parseInt(options.duration) || 600000, mode: parseInt(options.mode) || 0, remainingMs: parseInt(options.duration) || 600000, - startedAt: Date.now() + startedAt: Date.now(), + regionText: this._getRegionText(regions) }) this._onStatus = this.onStatus.bind(this) diff --git a/miniprogram/pages/treating/treating.wxml b/miniprogram/pages/treating/treating.wxml index ff7e986..90ba7d3 100644 --- a/miniprogram/pages/treating/treating.wxml +++ b/miniprogram/pages/treating/treating.wxml @@ -16,7 +16,7 @@ - {{mode === 1 ? '✨ 智能模式' : '🔄 普通模式'}} · 全脸护理 + {{mode === 1 ? '✨ 智能模式' : '🔄 普通模式'}} · {{regionText}} diff --git a/miniprogram/pages/wear-check/wear-check.js b/miniprogram/pages/wear-check/wear-check.js index ed45a40..3671e23 100644 --- a/miniprogram/pages/wear-check/wear-check.js +++ b/miniprogram/pages/wear-check/wear-check.js @@ -7,7 +7,9 @@ Page({ checking: false, result: null, error: '', - devMode: false + devMode: false, + battery: 0, + connected: false }, onBack: function () { @@ -21,6 +23,11 @@ Page({ onLoad: function () { var app = getApp() this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false }) + var device = app.globalData.currentDevice || {} + this.setData({ + battery: device.battery || 0, + connected: ble.isConnected() + }) }, onShow: function () { @@ -34,6 +41,9 @@ Page({ if (this._onStatus) ble.off('status', this._onStatus) this._onStatus = function (status) { self.setData({ checking: false }) + if (status.battery !== undefined) { + self.setData({ battery: status.battery, connected: true }) + } if (status.bind_status === 1) { self.setData({ result: 'ok' }) } else { diff --git a/miniprogram/pages/wear-check/wear-check.wxml b/miniprogram/pages/wear-check/wear-check.wxml index 90d8b7e..84a7b71 100644 --- a/miniprogram/pages/wear-check/wear-check.wxml +++ b/miniprogram/pages/wear-check/wear-check.wxml @@ -11,9 +11,10 @@ 光子美容仪 - 已连接 + 已连接 + 未连接 - 电量 85% + 电量 {{battery}}% diff --git a/server/src/lib/settings-cache.js b/server/src/lib/settings-cache.js new file mode 100644 index 0000000..5c92242 --- /dev/null +++ b/server/src/lib/settings-cache.js @@ -0,0 +1,14 @@ +const settingsDao = require('../dao/settings.dao') + +let cache = null +let cacheTime = 0 +const TTL = 60000 // 1 minute + +async function getSettings() { + if (cache && Date.now() - cacheTime < TTL) return cache + cache = await settingsDao.getAll() + cacheTime = Date.now() + return cache +} + +module.exports = { getSettings } diff --git a/server/src/routes/auth.js b/server/src/routes/auth.js index 48bca70..c8492d9 100644 --- a/server/src/routes/auth.js +++ b/server/src/routes/auth.js @@ -3,6 +3,7 @@ const router = require('express').Router() const { ok, fail } = require('../lib/response') const { signUser, readBearer } = require('../lib/auth') const { code2Session } = require('../lib/wechat') +const { getSettings } = require('../lib/settings-cache') const config = require('../config') const userDao = require('../dao/user.dao') const logDao = require('../dao/log.dao') @@ -10,6 +11,9 @@ const logDao = require('../dao/log.dao') const wrap = fn => (req, res, next) => fn(req, res, next).catch(next) router.post('/auth/login', wrap(async (req, res) => { + const settings = await getSettings() + if (settings.maintenance_mode === true) return res.json(fail(2001, '系统维护中,请稍后再试')) + const session = await code2Session(req.body.code || '') let user = await userDao.findByOpenid(session.openid) if (!user) { diff --git a/server/src/routes/device.js b/server/src/routes/device.js index 6d7d22c..ded3973 100644 --- a/server/src/routes/device.js +++ b/server/src/routes/device.js @@ -2,6 +2,7 @@ const router = require('express').Router() const { ok, fail } = require('../lib/response') const { requireUser } = require('../middleware/auth') const { randomHex } = require('../lib/auth') +const { getSettings } = require('../lib/settings-cache') const bindingDao = require('../dao/binding.dao') const deviceDao = require('../dao/device.dao') const commandDao = require('../dao/command.dao') @@ -12,6 +13,9 @@ const logDao = require('../dao/log.dao') const wrap = fn => (req, res, next) => fn(req, res, next).catch(next) router.post('/device/bind', requireUser, wrap(async (req, res) => { + const settings = await getSettings() + if (settings.enable_binding === false) return res.json(fail(2001, '设备绑定已关闭')) + const deviceId = String(req.body.device_id || '').trim() if (!deviceId) return res.json(fail(2001, 'device_id required'))