diff --git a/miniprogram/app.js b/miniprogram/app.js index 6341b2e..f9ed01e 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -9,6 +9,13 @@ App({ }, onLaunch: function () { + if (!wx.cloud) { + console.error('请使用 2.2.3 或以上的基础库以使用云能力') + return + } + wx.cloud.init({ + traceUser: true + }) this.checkLogin() }, @@ -28,30 +35,16 @@ App({ self.globalData.userId = String(profile.user_id) }).catch(function () { wx.removeStorageSync('token') - wx.reLaunch({ url: '/pages/login/login' }) }) }, doLogin: function () { - return new Promise(function (resolve, reject) { - wx.login({ - success: function (res) { - if (!res.code) { - reject({ message: 'wx.login failed' }) - return - } - http.post('/api/v1/auth/login', { - code: res.code - }).then(function (data) { - wx.setStorageSync('token', data.token) - var app = getApp() - app.globalData.userInfo = data.user_info - app.globalData.userId = data.user_id - resolve(data) - }).catch(reject) - }, - fail: reject - }) + return http.post('/api/v1/auth/login', {}).then(function (data) { + wx.setStorageSync('token', data.token) + var app = getApp() + app.globalData.userInfo = data.user_info + app.globalData.userId = data.user_id + return data }) } }) diff --git a/miniprogram/cloud-functions/admin/index.js b/miniprogram/cloud-functions/admin/index.js new file mode 100644 index 0000000..4ada633 --- /dev/null +++ b/miniprogram/cloud-functions/admin/index.js @@ -0,0 +1,23 @@ +const cloud = require('wx-server-sdk') +cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) +const db = cloud.database() + +exports.main = async (event, context) => { + const { action } = event + + if (action === 'dashboard') { + const deviceCount = await db.collection('bindings').where({ status: 'active' }).count() + const userCount = await db.collection('users').count() + const treatmentCount = await db.collection('treatment_records').count() + return { + code: 0, + data: { + device_count: deviceCount.total, + user_count: userCount.total, + treatment_count: treatmentCount.total + } + } + } + + return { code: -1, message: '未知操作' } +} diff --git a/miniprogram/cloud-functions/admin/package.json b/miniprogram/cloud-functions/admin/package.json new file mode 100644 index 0000000..48ccd4e --- /dev/null +++ b/miniprogram/cloud-functions/admin/package.json @@ -0,0 +1,9 @@ +{ + "name": "admin", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.6.3" + } +} diff --git a/miniprogram/cloud-functions/auth/index.js b/miniprogram/cloud-functions/auth/index.js new file mode 100644 index 0000000..2a7c21d --- /dev/null +++ b/miniprogram/cloud-functions/auth/index.js @@ -0,0 +1,51 @@ +const cloud = require('wx-server-sdk') +cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) +const db = cloud.database() + +exports.main = async (event, context) => { + const wxContext = cloud.getWXContext() + const openid = wxContext.OPENID + + if (event.action === 'login') { + let userRecord = await db.collection('users').where({ openid }).get() + + if (userRecord.data.length === 0) { + const newUser = { + openid, + nickname: '', + avatar_url: '', + phone: '', + gender: 0, + created_at: db.serverDate() + } + await db.collection('users').add({ data: newUser }) + userRecord = await db.collection('users').where({ openid }).get() + } + + const user = userRecord.data[0] + return { + code: 0, + data: { + token: openid + '_' + Date.now(), + user_id: user._id, + user_info: { + user_id: user._id, + nickname: user.nickname || '用户' + openid.slice(-4), + avatar_url: user.avatar_url || '', + phone: user.phone || '', + gender: user.gender || 0 + } + } + } + } + + if (event.action === 'refresh') { + const userRecord = await db.collection('users').where({ openid }).get() + if (userRecord.data.length === 0) { + return { code: 1001, message: '用户不存在' } + } + return { code: 0, data: { token: openid + '_' + Date.now() } } + } + + return { code: -1, message: '未知操作' } +} diff --git a/miniprogram/cloud-functions/auth/package.json b/miniprogram/cloud-functions/auth/package.json new file mode 100644 index 0000000..ba88cd1 --- /dev/null +++ b/miniprogram/cloud-functions/auth/package.json @@ -0,0 +1,9 @@ +{ + "name": "auth", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.6.3" + } +} diff --git a/miniprogram/cloud-functions/device/index.js b/miniprogram/cloud-functions/device/index.js new file mode 100644 index 0000000..d20326b --- /dev/null +++ b/miniprogram/cloud-functions/device/index.js @@ -0,0 +1,89 @@ +const cloud = require('wx-server-sdk') +cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) +const db = cloud.database() +const _ = db.command + +exports.main = async (event, context) => { + const wxContext = cloud.getWXContext() + const openid = wxContext.OPENID + const { action } = event + + if (action === 'bind') { + const { device_id } = event.data + if (!device_id) return { code: -1, message: '缺少设备ID' } + + const existBind = await db.collection('bindings').where({ openid, status: 'active' }).count() + if (existBind.total > 0) { + const exist = await db.collection('bindings').where({ openid, status: 'active' }).get() + return { code: 2001, message: '已绑定设备', data: { device_id: exist.data[0].device_id } } + } + + await db.collection('bindings').add({ + data: { + openid, + device_id, + status: 'active', + bind_time: db.serverDate() + } + }) + + const subExist = await db.collection('subscriptions').where({ openid, status: 'active' }).count() + if (subExist.total === 0) { + await db.collection('subscriptions').add({ + data: { + openid, + plan_type: 'trial', + status: 'active', + start_time: db.serverDate(), + end_time: new Date(Date.now() + 7 * 24 * 3600 * 1000), + remaining_days: 7 + } + }) + } + + return { + code: 0, + data: { + device_id, + bind_token: 'bt_' + Date.now(), + subscription: { plan_type: 'trial', remaining_days: 7 } + } + } + } + + if (action === 'unbind') { + await db.collection('bindings').where({ openid, status: 'active' }).update({ data: { status: 'inactive' } }) + return { code: 0, data: {} } + } + + if (action === 'list') { + const bindings = await db.collection('bindings').where({ openid, status: 'active' }).get() + const devices = bindings.data.map(b => ({ + device_id: b.device_id, + name: '我的光面膜', + status: 'online', + bind_time: b.bind_time + })) + return { code: 0, data: { devices, total: devices.length } } + } + + if (action === 'detail') { + const { device_id } = event.data + const bindings = await db.collection('bindings').where({ openid, device_id, status: 'active' }).get() + if (bindings.data.length === 0) return { code: -1, message: '设备未绑定' } + const b = bindings.data[0] + return { + code: 0, + data: { + device_id: b.device_id, + name: '我的光面膜', + firmware_version: '1.0.0', + battery_level: 85, + status: 'online', + bind_time: b.bind_time + } + } + } + + return { code: -1, message: '未知操作' } +} diff --git a/miniprogram/cloud-functions/device/package.json b/miniprogram/cloud-functions/device/package.json new file mode 100644 index 0000000..6c4e457 --- /dev/null +++ b/miniprogram/cloud-functions/device/package.json @@ -0,0 +1,9 @@ +{ + "name": "device", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.6.3" + } +} diff --git a/miniprogram/cloud-functions/subscription/index.js b/miniprogram/cloud-functions/subscription/index.js new file mode 100644 index 0000000..224367d --- /dev/null +++ b/miniprogram/cloud-functions/subscription/index.js @@ -0,0 +1,58 @@ +const cloud = require('wx-server-sdk') +cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) +const db = cloud.database() + +exports.main = async (event, context) => { + const wxContext = cloud.getWXContext() + const openid = wxContext.OPENID + const { action } = event + + if (action === 'status') { + const subs = await db.collection('subscriptions').where({ openid, status: 'active' }).get() + if (subs.data.length === 0) { + return { code: 0, data: { plan_type: 'none', status: 'inactive', remaining_days: 0 } } + } + const sub = subs.data[0] + const now = new Date() + const end = new Date(sub.end_time) + const remain = Math.max(0, Math.ceil((end - now) / (24 * 3600 * 1000))) + return { + code: 0, + data: { + plan_type: sub.plan_type, + status: remain > 0 ? 'active' : 'expired', + remaining_days: remain, + start_time: sub.start_time, + end_time: sub.end_time + } + } + } + + if (action === 'purchase') { + const { plan_type } = event.data + const price = plan_type === 'monthly' ? 99 : 899 + const days = plan_type === 'monthly' ? 30 : 365 + + await db.collection('subscriptions').where({ openid, status: 'active' }).update({ data: { status: 'expired' } }) + + await db.collection('subscriptions').add({ + data: { + openid, + plan_type, + status: 'active', + start_time: db.serverDate(), + end_time: new Date(Date.now() + days * 24 * 3600 * 1000), + remaining_days: days, + price + } + }) + + return { code: 0, data: { order_id: 'ORD' + Date.now(), plan_type, status: 'paid' } } + } + + if (action === 'verify') { + return { code: 0, data: { valid: true } } + } + + return { code: -1, message: '未知操作' } +} diff --git a/miniprogram/cloud-functions/subscription/package.json b/miniprogram/cloud-functions/subscription/package.json new file mode 100644 index 0000000..02fdf1c --- /dev/null +++ b/miniprogram/cloud-functions/subscription/package.json @@ -0,0 +1,9 @@ +{ + "name": "subscription", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.6.3" + } +} diff --git a/miniprogram/cloud-functions/treatment/index.js b/miniprogram/cloud-functions/treatment/index.js new file mode 100644 index 0000000..a716134 --- /dev/null +++ b/miniprogram/cloud-functions/treatment/index.js @@ -0,0 +1,42 @@ +const cloud = require('wx-server-sdk') +cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) +const db = cloud.database() +const _ = db.command + +exports.main = async (event, context) => { + const wxContext = cloud.getWXContext() + const openid = wxContext.OPENID + const { action } = event + + if (action === 'sync') { + const d = event.data + await db.collection('treatment_records').add({ + data: { + openid, + session_id: d.session_id || 'S' + Date.now(), + device_id: d.device_id || '', + regions: d.regions || 0, + total_duration_ms: d.total_duration_ms || 0, + mode: d.mode || 0, + avg_pd: d.avg_pd || 0, + created_at: db.serverDate() + } + }) + return { code: 0, data: { session_id: d.session_id || 'S' + Date.now() } } + } + + if (action === 'history') { + const { page = 1, page_size = 20 } = event.data || {} + const totalRes = await db.collection('treatment_records').where({ openid }).count() + const records = await db.collection('treatment_records') + .where({ openid }) + .orderBy('created_at', 'desc') + .skip((page - 1) * page_size) + .limit(page_size) + .get() + + return { code: 0, data: { records: records.data, total: totalRes.total } } + } + + return { code: -1, message: '未知操作' } +} diff --git a/miniprogram/cloud-functions/treatment/package.json b/miniprogram/cloud-functions/treatment/package.json new file mode 100644 index 0000000..d18288a --- /dev/null +++ b/miniprogram/cloud-functions/treatment/package.json @@ -0,0 +1,9 @@ +{ + "name": "treatment", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.6.3" + } +} diff --git a/miniprogram/cloud-functions/user/index.js b/miniprogram/cloud-functions/user/index.js new file mode 100644 index 0000000..9a6fdff --- /dev/null +++ b/miniprogram/cloud-functions/user/index.js @@ -0,0 +1,39 @@ +const cloud = require('wx-server-sdk') +cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) +const db = cloud.database() + +exports.main = async (event, context) => { + const wxContext = cloud.getWXContext() + const openid = wxContext.OPENID + const { action } = event + + if (action === 'profile') { + const users = await db.collection('users').where({ openid }).get() + if (users.data.length === 0) return { code: 1001, message: '用户不存在' } + const user = users.data[0] + const binds = await db.collection('bindings').where({ openid, status: 'active' }).count() + return { + code: 0, + data: { + user_id: user._id, + nickname: user.nickname || '用户' + openid.slice(-4), + avatar_url: user.avatar_url || '', + phone: user.phone || '', + gender: user.gender || 0, + device_count: binds.total, + created_at: user.created_at + } + } + } + + if (action === 'update') { + const updates = {} + if (event.data.nickname) updates.nickname = event.data.nickname + if (event.data.avatar_url) updates.avatar_url = event.data.avatar_url + if (event.data.gender !== undefined) updates.gender = event.data.gender + await db.collection('users').where({ openid }).update({ data: updates }) + return { code: 0, data: {} } + } + + return { code: -1, message: '未知操作' } +} diff --git a/miniprogram/cloud-functions/user/package.json b/miniprogram/cloud-functions/user/package.json new file mode 100644 index 0000000..9ef5d33 --- /dev/null +++ b/miniprogram/cloud-functions/user/package.json @@ -0,0 +1,9 @@ +{ + "name": "user", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.6.3" + } +} diff --git a/miniprogram/pages/login/login.js b/miniprogram/pages/login/login.js index b25ac88..7612c2a 100644 --- a/miniprogram/pages/login/login.js +++ b/miniprogram/pages/login/login.js @@ -14,7 +14,7 @@ Page({ wx.switchTab({ url: '/pages/index/index' }) }).catch(function (err) { self.setData({ loading: false }) - wx.showToast({ title: '登录失败', icon: 'none' }) + wx.showToast({ title: err.message || '登录失败', icon: 'none' }) }) } }) diff --git a/miniprogram/project.config.json b/miniprogram/project.config.json index 3321221..c34ace3 100644 --- a/miniprogram/project.config.json +++ b/miniprogram/project.config.json @@ -50,6 +50,7 @@ "libVersion": "3.15.2", "appid": "wxc4045074ef298510", "projectname": "hox-beauty", + "cloudfunctionRoot": "cloud-functions/", "condition": {}, "simulatorPluginLibVersion": {}, "editorSetting": {} diff --git a/miniprogram/utils/request.js b/miniprogram/utils/request.js index a4e7920..291177b 100644 --- a/miniprogram/utils/request.js +++ b/miniprogram/utils/request.js @@ -1,13 +1,35 @@ -var API_BASE = 'https://api.lightmask.com' -var USE_MOCK = true +var USE_CLOUD = true + +function callCloud(funcName, action, data) { + return new Promise(function (resolve, reject) { + wx.cloud.callFunction({ + name: funcName, + data: { action: action, data: data } + }).then(function (res) { + if (res.result && res.result.code === 0) { + resolve(res.result.data) + } else if (res.result && (res.result.code === 1001 || res.result.code === 1002)) { + wx.removeStorageSync('token') + wx.reLaunch({ url: '/pages/login/login' }) + reject(res.result) + } else { + reject(res.result || { code: -1, message: '请求失败' }) + } + }).catch(function (err) { + reject({ code: 2002, message: err.errMsg || '网络异常' }) + }) + }) +} var mock = null -if (USE_MOCK) { +if (!USE_CLOUD) { mock = require('./mock') } -function request(options) { - if (USE_MOCK && mock && mock.enabled) { +var API_BASE = 'https://api.lightmask.com' + +function httpRequest(options) { + if (mock && mock.enabled) { return new Promise(function (resolve) { setTimeout(function () { var result = mock.handle(options.method || 'GET', options.path, options.data) @@ -49,6 +71,33 @@ function request(options) { }) } +var FUNC_MAP = { + '/api/v1/auth/login': { fn: 'auth', action: 'login' }, + '/api/v1/auth/refresh': { fn: 'auth', action: 'refresh' }, + '/api/v1/user/profile': { fn: 'user', action: 'profile' }, + '/api/v1/user/update': { fn: 'user', action: 'update' }, + '/api/v1/device/bind': { fn: 'device', action: 'bind' }, + '/api/v1/device/unbind': { fn: 'device', action: 'unbind' }, + '/api/v1/device/list': { fn: 'device', action: 'list' }, + '/api/v1/device/detail': { fn: 'device', action: 'detail' }, + '/api/v1/subscription/status': { fn: 'subscription', action: 'status' }, + '/api/v1/subscription/purchase': { fn: 'subscription', action: 'purchase' }, + '/api/v1/subscription/verify': { fn: 'subscription', action: 'verify' }, + '/api/v1/treatment/sync': { fn: 'treatment', action: 'sync' }, + '/api/v1/treatment/history': { fn: 'treatment', action: 'history' } +} + +function request(options) { + if (USE_CLOUD) { + var mapping = FUNC_MAP[options.path] + if (mapping) { + return callCloud(mapping.fn, mapping.action, options.data) + } + return Promise.reject({ code: -1, message: '未映射的接口' }) + } + return httpRequest(options) +} + function get(path, data) { return request({ path: path, method: 'GET', data: data }) }