chore: remove all mock/debug code for production release

Miniprogram:
- ENV switched to 'prod' (disables __DEV__ flag)
- Deleted mock.js and test-ble-frame.js
- Removed mockBind/mockPurchase from api.js
- Removed all debug UI panels and onMock* handlers from 6 pages
- Removed mock purchase fallback in subscribe-plans
- Removed SIMPLE PERIPHERAL dev board from BLE scan

Server:
- Removed /device/mock-bind route
- Removed /subscription/mock-purchase route
- Removed dev_openid fallback in wechat.js
- Removed mockBind() from binding.dao.js
- Removed mock fallback in purchase endpoint
- NODE_ENV default changed to 'production'

Admin:
- ENV switched to 'prod'

All mock code preserved in 'test' branch for future development use.
这个提交包含在:
Guoguo
2026-05-20 07:19:26 -07:00
父节点 0b82d9dbcf
当前提交 f0fdb285b5
修改 24 个文件,包含 16 行新增428 行删除
+1 -1
查看文件
@@ -1,5 +1,5 @@
// Production: change to 'prod' // Production: change to 'prod'
const ENV = 'test' const ENV = 'prod'
const API_BASES = { const API_BASES = {
local: 'http://localhost:3000', local: 'http://localhost:3000',
+1 -1
查看文件
@@ -1,5 +1,5 @@
// Production: change to 'prod' to disable debug buttons and mock endpoints // Production: change to 'prod' to disable debug buttons and mock endpoints
var ENV = 'test' var ENV = 'prod'
var API_BASES = { var API_BASES = {
local: 'http://localhost:3000', local: 'http://localhost:3000',
+2 -17
查看文件
@@ -1,6 +1,4 @@
var ble = require('../../services/ble') var ble = require('../../services/ble')
var config = require('../../config/env')
Page({ Page({
data: { data: {
statusBarHeight: 44, statusBarHeight: 44,
@@ -9,8 +7,7 @@ Page({
regions: 0x7F, regions: 0x7F,
regionData: [], regionData: [],
timeout: false, timeout: false,
error: '', error: ''
devMode: false
}, },
onBack: function () { onBack: function () {
@@ -25,8 +22,7 @@ Page({
var app = getApp() var app = getApp()
this.setData({ this.setData({
statusBarHeight: app.globalData.statusBarHeight, statusBarHeight: app.globalData.statusBarHeight,
regions: parseInt(options.regions) || 0x7F, regions: parseInt(options.regions) || 0x7F
devMode: config.__DEV__ || false
}) })
this.startScan() this.startScan()
}, },
@@ -93,15 +89,4 @@ Page({
}) })
}, },
onMockScanDone: function () {
var self = this
if (self._progressTimer) clearInterval(self._progressTimer)
var mask = parseInt(self.data.regions) || 0x7F
wx.redirectTo({
url: '/pages/treating/treating?regions=' + mask +
'&wavelength=2' +
'&duration=600000' +
'&mode=1'
})
}
}) })
@@ -17,11 +17,6 @@
<view class="scan-hint">请将面罩对准面部</view> <view class="scan-hint">请将面罩对准面部</view>
<view class="scan-hint" style="font-size:24rpx;color:#999;margin-top:8rpx;">连接设备后可获取皮肤数据</view> <view class="scan-hint" style="font-size:24rpx;color:#999;margin-top:8rpx;">连接设备后可获取皮肤数据</view>
<view class="debug-actions" wx:if="{{devMode}}">
<view class="debug-label">🔧 调试工具</view>
<button class="btn-secondary btn-sm" bindtap="onMockScanDone">模拟扫描完成</button>
</view>
<view class="scan-progress" wx:if="{{scanning}}"> <view class="scan-progress" wx:if="{{scanning}}">
正在扫描... <text class="highlight">{{regionData.length || 3}}</text> 个区域 正在扫描... <text class="highlight">{{regionData.length || 3}}</text> 个区域
</view> </view>
+1 -24
查看文件
@@ -1,6 +1,5 @@
var ble = require('../../services/ble') var ble = require('../../services/ble')
var http = require('../../utils/request') var http = require('../../utils/request')
var config = require('../../config/env')
var app = getApp() var app = getApp()
var SCAN_TIMEOUT = 15000 var SCAN_TIMEOUT = 15000
@@ -11,7 +10,6 @@ Page({
bindToken: '', bindToken: '',
state: 'scanning', state: 'scanning',
error: '', error: '',
devMode: false
}, },
onBack: function () { onBack: function () {
@@ -26,8 +24,7 @@ Page({
this.setData({ statusBarHeight: app.globalData.statusBarHeight }) this.setData({ statusBarHeight: app.globalData.statusBarHeight })
this.setData({ this.setData({
deviceId: options.device_id || '', deviceId: options.device_id || '',
bindToken: options.bind_token || '', bindToken: options.bind_token || ''
devMode: config.__DEV__ || false
}) })
this.startBleConnect() this.startBleConnect()
}, },
@@ -110,24 +107,4 @@ Page({
}, 1000) }, 1000)
}, },
onMockConnect: function () {
var self = this
clearTimeout(this._scanTimer)
ble.stopScan()
self.setData({ state: 'binding' })
http.post('/api/v1/device/mock-bind', {
device_id: self.data.deviceId
}).then(function () {
self.setData({ state: 'done' })
wx.redirectTo({
url: '/pages/bind-success/bind-success?device_id=' + self.data.deviceId
})
}).catch(function (err) {
self.setData({ state: 'error', error: err.message || '模拟绑定失败' })
})
},
onMockBindSuccess: function () {
this.onMockConnect()
}
}) })
@@ -28,10 +28,5 @@
<button class="retry-btn" bindtap="onRetry">重试</button> <button class="retry-btn" bindtap="onRetry">重试</button>
</view> </view>
<view class="debug-actions" wx:if="{{devMode}}">
<view class="debug-label">🔧 调试工具</view>
<button class="btn-secondary btn-sm" bindtap="onMockConnect">模拟连接成功</button>
<button class="btn-secondary btn-sm" bindtap="onMockBindSuccess">模拟绑定成功</button>
</view>
</view> </view>
</view> </view>
-26
查看文件
@@ -1,6 +1,5 @@
var ble = require('../../services/ble') var ble = require('../../services/ble')
var api = require('../../utils/api') var api = require('../../utils/api')
var config = require('../../config/env')
var app = getApp() var app = getApp()
Page({ Page({
@@ -14,14 +13,9 @@ Page({
bleState: 'disconnected', bleState: 'disconnected',
hasDevice: false, hasDevice: false,
deviceInfo: null, deviceInfo: null,
devMode: false,
loading: true loading: true
}, },
onLoad: function () {
this.setData({ devMode: config.__DEV__ || false })
},
onShow: function () { onShow: function () {
this.checkState() this.checkState()
this._onStatus = this.onBleStatus.bind(this) this._onStatus = this.onBleStatus.bind(this)
@@ -173,26 +167,6 @@ Page({
wx.navigateTo({ url: '/pages/wear-check/wear-check' }) wx.navigateTo({ url: '/pages/wear-check/wear-check' })
}, },
onMockBind: function () {
var self = this
wx.showModal({
title: '模拟绑定',
editable: true,
placeholderText: '输入设备编号(需先在后台创建)',
success: function (res) {
if (res.confirm && res.content) {
var deviceId = res.content.trim()
api.mockBind(deviceId).then(function () {
wx.showToast({ title: '模拟绑定成功', icon: 'success' })
self.checkState()
}).catch(function (err) {
wx.showToast({ title: err.message || '绑定失败', icon: 'none' })
})
}
}
})
},
onViewSubscription: function () { onViewSubscription: function () {
wx.navigateTo({ url: '/pages/subscribe-plans/subscribe-plans' }) wx.navigateTo({ url: '/pages/subscribe-plans/subscribe-plans' })
} }
-4
查看文件
@@ -9,10 +9,6 @@
<text class="empty-icon">📱</text> <text class="empty-icon">📱</text>
<view class="empty-text">还没有绑定设备</view> <view class="empty-text">还没有绑定设备</view>
<button class="btn-primary mt-30" bindtap="onAddDevice">扫码添加设备</button> <button class="btn-primary mt-30" bindtap="onAddDevice">扫码添加设备</button>
<view class="debug-actions" wx:if="{{devMode}}">
<view class="debug-label">🔧 调试工具</view>
<button class="btn-secondary btn-sm" bindtap="onMockBind">模拟绑定设备</button>
</view>
</view> </view>
</view> </view>
@@ -1,5 +1,4 @@
var api = require('../../utils/api') var api = require('../../utils/api')
var config = require('../../config/env')
Page({ Page({
data: { data: {
@@ -163,17 +162,12 @@ Page({
var self = this var self = this
self.setData({ purchasing: true }) self.setData({ purchasing: true })
api.purchase(plan.key).then(function (data) { api.purchase(plan.key).then(function (data) {
if (data.mock || !data.payment_params) { if (!data.payment_params) {
// Dev fallback: use mock purchase self.setData({ purchasing: false })
return api.mockPurchase(plan.key).then(function () { wx.showToast({ title: '支付服务暂不可用', icon: 'none' })
self.setData({ purchasing: false }) return
wx.showToast({ title: '支付成功', icon: 'success' })
setTimeout(function () {
wx.redirectTo({ url: '/pages/subscribe-success/subscribe-success?plan=' + plan.key })
}, 1000)
})
} }
// Real payment
var params = data.payment_params var params = data.payment_params
self._currentOrderId = data.order_id self._currentOrderId = data.order_id
wx.requestPayment({ wx.requestPayment({
+1 -7
查看文件
@@ -1,8 +1,6 @@
var ble = require('../../services/ble') var ble = require('../../services/ble')
var api = require('../../utils/api') var api = require('../../utils/api')
var commandSync = require('../../services/command-sync') var commandSync = require('../../services/command-sync')
var config = require('../../config/env')
Page({ Page({
data: { data: {
statusBarHeight: 44, statusBarHeight: 44,
@@ -18,7 +16,6 @@ Page({
paused: false, paused: false,
completed: false, completed: false,
startedAt: 0, startedAt: 0,
devMode: false,
regionText: '全脸护理', regionText: '全脸护理',
}, },
@@ -34,7 +31,7 @@ Page({
onLoad: function (options) { onLoad: function (options) {
var app = getApp() var app = getApp()
this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false }) this.setData({ statusBarHeight: app.globalData.statusBarHeight })
this._deviceId = ble.getDeviceId() || (app.globalData.currentDevice && app.globalData.currentDevice.device_id) || '' this._deviceId = ble.getDeviceId() || (app.globalData.currentDevice && app.globalData.currentDevice.device_id) || ''
var regions = parseInt(options.regions) || 0x7F var regions = parseInt(options.regions) || 0x7F
this.setData({ this.setData({
@@ -259,7 +256,4 @@ Page({
}) })
}, },
onMockComplete: function () {
this.finishAsComplete()
}
}) })
-5
查看文件
@@ -27,11 +27,6 @@
</view> </view>
</view> </view>
<view class="debug-actions" wx:if="{{devMode}}">
<view class="debug-label">🔧 调试工具</view>
<button class="btn-secondary btn-sm" bindtap="onMockComplete">模拟护理完成</button>
</view>
<button class="btn-primary btn-primary-red" bindtap="onStop">停止护理</button> <button class="btn-primary btn-primary-red" bindtap="onStop">停止护理</button>
</view> </view>
</view> </view>
@@ -1,6 +1,4 @@
var ble = require('../../services/ble') var ble = require('../../services/ble')
var config = require('../../config/env')
Page({ Page({
data: { data: {
statusBarHeight: 44, statusBarHeight: 44,
@@ -16,8 +14,7 @@ Page({
subDays: 6, subDays: 6,
fixedDurationMs: 600000, fixedDurationMs: 600000,
submitting: false, submitting: false,
error: '', error: ''
devMode: false
}, },
onBack: function () { onBack: function () {
@@ -30,7 +27,7 @@ Page({
onLoad: function () { onLoad: function () {
var app = getApp() var app = getApp()
this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false }) this.setData({ statusBarHeight: app.globalData.statusBarHeight })
this.checkSubscription() this.checkSubscription()
}, },
@@ -109,17 +106,4 @@ Page({
}) })
}, },
onMockStart: function () {
var mask = 0
this.data.regions.forEach(function (r) {
if (r.checked) mask |= r.mask
})
if (mask === 0) mask = 0x7F
wx.redirectTo({
url: '/pages/treating/treating?regions=' + mask +
'&wavelength=2' +
'&duration=' + this.data.fixedDurationMs +
'&mode=' + this.data.selectedMode
})
}
}) })
@@ -38,10 +38,5 @@
</button> </button>
<view class="error-text" wx:if="{{error}}">{{error}}</view> <view class="error-text" wx:if="{{error}}">{{error}}</view>
<view class="debug-actions" wx:if="{{devMode}}">
<view class="debug-label">🔧 调试工具</view>
<button class="btn-secondary btn-sm" bindtap="onMockStart">模拟开始护理(跳过BLE</button>
</view>
</view> </view>
</view> </view>
+1 -7
查看文件
@@ -1,13 +1,10 @@
var ble = require('../../services/ble') var ble = require('../../services/ble')
var config = require('../../config/env')
Page({ Page({
data: { data: {
statusBarHeight: 44, statusBarHeight: 44,
checking: false, checking: false,
result: null, result: null,
error: '', error: '',
devMode: false,
battery: 0, battery: 0,
connected: false connected: false
}, },
@@ -22,7 +19,7 @@ Page({
onLoad: function () { onLoad: function () {
var app = getApp() var app = getApp()
this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false }) this.setData({ statusBarHeight: app.globalData.statusBarHeight })
var device = app.globalData.currentDevice || {} var device = app.globalData.currentDevice || {}
this.setData({ this.setData({
battery: device.battery || 0, battery: device.battery || 0,
@@ -108,7 +105,4 @@ Page({
}, 500) }, 500)
}, },
onMockWearOk: function () {
this.setData({ checking: false, result: 'ok' })
}
}) })
@@ -41,10 +41,5 @@
</view> </view>
<button wx:if="{{result === 'ok'}}" class="btn-primary mt-30" bindtap="onNext">设置护理参数</button> <button wx:if="{{result === 'ok'}}" class="btn-primary mt-30" bindtap="onNext">设置护理参数</button>
<view class="debug-actions" wx:if="{{devMode}}">
<view class="debug-label">🔧 调试工具</view>
<button class="btn-secondary btn-sm" bindtap="onMockWearOk">模拟佩戴确认</button>
</view>
</view> </view>
</view> </view>
-63
查看文件
@@ -1,63 +0,0 @@
global.wx = global.wx || {
onBLEConnectionStateChange: function () {}
}
const ble = require('../services/ble')
function assert(condition, message) {
if (!condition) throw new Error(message)
}
function bytes(buffer) {
return Array.from(new Uint8Array(buffer))
}
function xor(arr) {
return arr.reduce((acc, item) => acc ^ item, 0)
}
function testBuildFrameChecksum() {
const frame = bytes(ble.buildFrame(0x02, [0x7F, 0x01]))
assert(frame[0] === 0xAA && frame[1] === 0x55, 'frame header mismatch')
assert(frame[2] === 0x02, 'frame length mismatch')
assert(frame[3] === 0x02, 'frame type mismatch')
assert(frame[frame.length - 1] === xor(frame.slice(0, -1)), 'checksum must include header, len, type, payload')
}
function testParseStatusFrame() {
const payload = [0x02, 0x7F, 0x02, 200, 0x00, 0x09, 0x27, 0xC0, 0x00, 0x01, 86, 36, 1, 2]
const frame = ble.buildFrame(0x21, payload)
const parsed = ble.parseFrame(frame)
assert(parsed.type === 0x21, 'status type mismatch')
const status = ble.parseStatusReport(parsed.payload)
assert(status.mode_state === 0x02, 'mode_state mismatch')
assert(status.region_mask === 0x7F, 'region mask mismatch')
assert(status.remaining_ms === 600000, 'remaining_ms mismatch')
assert(status.battery === 86, 'battery mismatch')
assert(status.temperature === 36, 'temperature mismatch')
}
function testBindPayloadLength() {
const userBytes = ble.hexToBytes('0000000000000001')
const tokenBytes = ble.hexToBytes('0011223344556677')
assert(userBytes.length === 8, 'user id must be 8 bytes')
assert(tokenBytes.length === 8, 'bind token must be 8 bytes')
}
function testVendorCommand33() {
const command = ble.buildVendorCommand33({
region_mask: 0x1F,
wavelength: ble.WAVELENGTH.R,
brightness: 200,
duration_ms: 600000
})
assert(command.length === 33, 'vendor command must be 33 bytes')
assert(command[32] === xor(command.slice(0, 32)), 'vendor command checksum mismatch')
}
testBuildFrameChecksum()
testParseStatusFrame()
testBindPayloadLength()
testVendorCommand33()
console.log('BLE frame tests passed')
-5
查看文件
@@ -1,7 +1,6 @@
// BLE connection: scan, connect, disconnect, reconnect logic // BLE connection: scan, connect, disconnect, reconnect logic
var protocol = require('./protocol') var protocol = require('./protocol')
var devConfig = require('../../config/env')
var SERVICE = protocol.SERVICE var SERVICE = protocol.SERVICE
var CHAR = protocol.CHAR var CHAR = protocol.CHAR
@@ -254,10 +253,6 @@ function startScan(callbacks) {
var localName = (d.localName || '').toUpperCase() var localName = (d.localName || '').toUpperCase()
var matched = name.indexOf('HOX') !== -1 || localName.indexOf('HOX') !== -1 || var matched = name.indexOf('HOX') !== -1 || localName.indexOf('HOX') !== -1 ||
name.indexOf('LIGHTMASK') !== -1 || localName.indexOf('LIGHTMASK') !== -1 name.indexOf('LIGHTMASK') !== -1 || localName.indexOf('LIGHTMASK') !== -1
// Dev board name - only match in dev mode
if (!matched && devConfig.__DEV__) {
matched = name.indexOf('SIMPLE PERIPHERAL') !== -1 || localName.indexOf('SIMPLE PERIPHERAL') !== -1
}
if (matched) { if (matched) {
wx.stopBluetoothDevicesDiscovery({}) wx.stopBluetoothDevicesDiscovery({})
if (callbacks.onFound) callbacks.onFound(d) if (callbacks.onFound) callbacks.onFound(d)
-2
查看文件
@@ -13,7 +13,6 @@ module.exports = {
getDevices: function () { return http.get('/api/v1/device/list') }, getDevices: function () { return http.get('/api/v1/device/list') },
bindDevice: function (deviceId) { return http.post('/api/v1/device/bind', { device_id: deviceId }) }, bindDevice: function (deviceId) { return http.post('/api/v1/device/bind', { device_id: deviceId }) },
confirmBind: function (deviceId, token) { return http.post('/api/v1/device/bind/confirm', { device_id: deviceId, bind_token: token }) }, confirmBind: function (deviceId, token) { return http.post('/api/v1/device/bind/confirm', { device_id: deviceId, bind_token: token }) },
mockBind: function (deviceId) { return http.post('/api/v1/device/mock-bind', { device_id: deviceId }) },
unbindDevice: function (deviceId) { return http.post('/api/v1/device/unbind', deviceId ? { device_id: deviceId } : {}) }, unbindDevice: function (deviceId) { return http.post('/api/v1/device/unbind', deviceId ? { device_id: deviceId } : {}) },
// Subscription // Subscription
@@ -21,7 +20,6 @@ module.exports = {
getSubscription: function () { return http.get('/api/v1/subscription') }, getSubscription: function () { return http.get('/api/v1/subscription') },
activateTrial: function () { return http.post('/api/v1/subscription/trial') }, activateTrial: function () { return http.post('/api/v1/subscription/trial') },
purchase: function (plan) { return http.post('/api/v1/subscription/purchase', { plan: plan }) }, purchase: function (plan) { return http.post('/api/v1/subscription/purchase', { plan: plan }) },
mockPurchase: function (plan) { return http.post('/api/v1/subscription/mock-purchase', { plan: plan }) },
// Payment // Payment
getPaymentOrder: function (orderId) { return http.get('/api/v1/payment/orders/' + orderId) }, getPaymentOrder: function (orderId) { return http.get('/api/v1/payment/orders/' + orderId) },
-145
查看文件
@@ -1,145 +0,0 @@
var MOCK_TOKEN = 'mock_token_dev_' + Date.now()
var MOCK_USER = {
user_id: 10001,
open_id: 'mock_open_id_12345',
nickname: '测试用户',
avatar_url: '',
phone: '',
gender: 0,
created_at: '2025-01-01T00:00:00.000Z'
}
var MOCK_DEVICE = {
device_id: 'DEV001',
name: '我的光子美容仪',
firmware_version: '1.0.0',
battery_level: 85,
status: 'online',
bind_time: '2025-01-01T00:00:00.000Z'
}
var MOCK_SUBSCRIPTION = {
plan_type: 'trial',
status: 'active',
start_time: '2025-01-01T00:00:00.000Z',
end_time: '2025-02-01T00:00:00.000Z',
remaining_days: 30
}
var MOCK_TREATMENTS = [
{
session_id: 'S20250101001',
device_id: 'DEV001',
regions: [1, 2],
wavelength: 630,
brightness: 5,
duration: 600000,
mode: 1,
avg_pd: 128,
created_at: '2025-01-15T10:30:00.000Z'
},
{
session_id: 'S20250102002',
device_id: 'DEV001',
regions: [1],
wavelength: 850,
brightness: 3,
duration: 300000,
mode: 2,
avg_pd: 96,
created_at: '2025-01-14T08:00:00.000Z'
}
]
var mockHandlers = {
'POST /api/v1/auth/login': function () {
return {
code: 0,
data: {
token: MOCK_TOKEN,
user_id: MOCK_USER.user_id,
user_info: MOCK_USER
}
}
},
'POST /api/v1/auth/refresh': function () {
return { code: 0, data: { token: MOCK_TOKEN } }
},
'GET /api/v1/user/profile': function () {
return { code: 0, data: MOCK_USER }
},
'PUT /api/v1/user/profile': function (data) {
Object.assign(MOCK_USER, data)
return { code: 0, data: MOCK_USER }
},
'POST /api/v1/device/bind': function (data) {
var d = Object.assign({}, MOCK_DEVICE, { device_id: data.device_id || 'DEV001' })
return {
code: 0,
data: {
device: d,
bind_token: 'mock_bind_token_' + Date.now(),
subscription: MOCK_SUBSCRIPTION
}
}
},
'POST /api/v1/device/unbind': function () {
return { code: 0, data: {} }
},
'GET /api/v1/device/list': function () {
return { code: 0, data: { devices: [MOCK_DEVICE], total: 1 } }
},
'GET /api/v1/device/detail': function () {
return { code: 0, data: MOCK_DEVICE }
},
'GET /api/v1/subscription/status': function () {
return { code: 0, data: MOCK_SUBSCRIPTION }
},
'POST /api/v1/subscription/purchase': function (data) {
return {
code: 0,
data: {
order_id: 'ORD' + Date.now(),
plan_type: data.plan_type,
status: 'paid'
}
}
},
'POST /api/v1/subscription/verify': function () {
return { code: 0, data: { valid: true } }
},
'POST /api/v1/treatment/sync': function (data) {
return { code: 0, data: { session_id: data.session_id || 'S' + Date.now() } }
},
'GET /api/v1/treatment/history': function () {
return { code: 0, data: { records: MOCK_TREATMENTS, total: MOCK_TREATMENTS.length } }
}
}
function handle(method, path, data) {
var key = method + ' ' + path
var handler = mockHandlers[key]
if (!handler) {
return { code: -1, message: 'mock: 未定义的接口 ' + key }
}
return handler(data)
}
module.exports = {
handle: handle,
MOCK_TOKEN: MOCK_TOKEN,
enabled: false
}
+1 -1
查看文件
@@ -1,7 +1,7 @@
require('dotenv').config() require('dotenv').config()
const config = { const config = {
nodeEnv: process.env.NODE_ENV || 'development', nodeEnv: process.env.NODE_ENV || 'production',
port: parseInt(process.env.PORT, 10) || 3000, port: parseInt(process.env.PORT, 10) || 3000,
region: process.env.TENCENT_REGION || 'ap-guangzhou', region: process.env.TENCENT_REGION || 'ap-guangzhou',
db: { db: {
-33
查看文件
@@ -101,38 +101,6 @@ async function confirmBind(userId, deviceId, bindToken) {
}) })
} }
/**
* Mock bind (dev/test only): directly bind with status=1, auto-trial
* @param {number} userId
* @param {string} deviceId
* @returns {Promise<{success?: boolean, duplicated?: boolean, device_id?: string, invalid?: boolean}>}
*/
async function mockBind(userId, deviceId) {
return transaction(async conn => {
const [active] = await conn.execute(
'SELECT device_id FROM bindings WHERE user_id = ? AND bind_status = 1 LIMIT 1',
[userId]
)
if (active.length > 0) return { duplicated: true, device_id: active[0].device_id }
const [devices] = await conn.execute(
'SELECT * FROM devices WHERE device_id = ? AND status <> 4 LIMIT 1',
[deviceId]
)
if (devices.length === 0) return { invalid: true }
await conn.execute(
'UPDATE bindings SET bind_status = 2 WHERE user_id = ? AND bind_status = 3',
[userId]
)
await conn.execute(
"INSERT INTO bindings (user_id, device_id, bind_token, bind_expires, bind_status, bind_time) VALUES (?, ?, 'mock', NOW(), 1, NOW())",
[userId, deviceId]
)
const subscriptionDao = require('./subscription.dao')
await subscriptionDao.grantTrialIfEligible(userId, conn)
return { success: true }
})
}
/** /**
* Unbind device(s) for a user * Unbind device(s) for a user
* @param {number} userId * @param {number} userId
@@ -197,7 +165,6 @@ module.exports = {
createPending, createPending,
closeExpiredPending, closeExpiredPending,
confirmBind, confirmBind,
mockBind,
unbindByUser, unbindByUser,
getHistoryByDevice, getHistoryByDevice,
findUserDeviceBinding, findUserDeviceBinding,
-4
查看文件
@@ -51,11 +51,7 @@ async function getAccessToken() {
} }
async function code2Session(code) { async function code2Session(code) {
if (config.nodeEnv === 'development' && (!code || code === 'local' || String(code).indexOf('dev_') === 0)) {
return { openid: 'dev_openid_' + String(code || 'local').slice(-8) }
}
if (!config.wechat.appid || !config.wechat.secret) { if (!config.wechat.appid || !config.wechat.secret) {
if (config.nodeEnv === 'development') return { openid: 'dev_openid_' + String(code || 'local').slice(-8) }
throw new Error('WECHAT_APPID or WECHAT_SECRET is not configured') throw new Error('WECHAT_APPID or WECHAT_SECRET is not configured')
} }
const url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' + encodeURIComponent(config.wechat.appid) + '&secret=' + encodeURIComponent(config.wechat.secret) + '&js_code=' + encodeURIComponent(code) + '&grant_type=authorization_code' const url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' + encodeURIComponent(config.wechat.appid) + '&secret=' + encodeURIComponent(config.wechat.secret) + '&js_code=' + encodeURIComponent(code) + '&grant_type=authorization_code'
-17
查看文件
@@ -56,23 +56,6 @@ router.post('/device/bind/confirm', requireUser, wrap(async (req, res) => {
})) }))
})) }))
router.post('/device/mock-bind', requireUser, wrap(async (req, res) => {
const config = require('../config')
if (config.nodeEnv === 'production') return res.json(fail(2001, 'not available in production'))
const deviceId = String(req.body.device_id || '').trim()
if (!deviceId) return res.json(fail(2001, 'device_id required'))
const active = await bindingDao.findActiveByUser(req.user.user_id)
if (active) return res.json(fail(2001, '已绑定设备', { device_id: active.device_id }))
const device = await bindingDao.findDeviceExists(deviceId)
if (!device) return res.json(fail(1005, 'DEVICE_NOT_FOUND'))
await bindingDao.mockBind(req.user.user_id, deviceId)
await logDao.write({ user_id: req.user.user_id, action: 'device_bind_confirm', detail: '模拟绑定设备: ' + deviceId, ip: req.ip })
res.json(ok({ message: 'success', device_id: deviceId }))
}))
router.post('/device/unbind', requireUser, wrap(async (req, res) => { router.post('/device/unbind', requireUser, wrap(async (req, res) => {
const deviceId = req.body.device_id || null const deviceId = req.body.device_id || null
await bindingDao.unbindByUser(req.user.user_id, deviceId) await bindingDao.unbindByUser(req.user.user_id, deviceId)
+1 -17
查看文件
@@ -46,11 +46,8 @@ router.post('/subscription/purchase', requireUser, wrap(async (req, res) => {
if (!PLANS[plan] || plan === 'trial') return res.json(fail(2001, 'invalid plan')) if (!PLANS[plan] || plan === 'trial') return res.json(fail(2001, 'invalid plan'))
const wxpay = require('../lib/wxpay') const wxpay = require('../lib/wxpay')
const config = require('../config')
if (!wxpay.isConfigured()) { if (!wxpay.isConfigured()) {
if (config.nodeEnv === 'production') return res.json(fail(2001, 'payment not configured')) return res.json(fail(2001, 'payment not configured'))
const orderId = 'ORD' + Date.now()
return res.json(ok({ order_id: orderId, payment_params: null, mock: true, plan, amount: PLANS[plan].amount }))
} }
const paymentOrderDao = require('../dao/payment-order.dao') const paymentOrderDao = require('../dao/payment-order.dao')
@@ -82,19 +79,6 @@ router.post('/subscription/purchase', requireUser, wrap(async (req, res) => {
res.json(ok({ order_id: orderId, payment_params: paymentParams, plan, amount: priceYuan })) res.json(ok({ order_id: orderId, payment_params: paymentParams, plan, amount: priceYuan }))
})) }))
router.post('/subscription/mock-purchase', requireUser, wrap(async (req, res) => {
const config = require('../config')
if (config.nodeEnv === 'production') return res.json(fail(2001, 'not available in production'))
const plan = req.body.plan || req.body.plan_type
if (!PLANS[plan] || plan === 'trial') return res.json(fail(2001, 'invalid plan'))
const p = PLANS[plan]
const orderId = 'MOCK' + Date.now()
await subscriptionDao.purchase(req.user.user_id, plan, p.amount, orderId, p.days)
await logDao.write({ user_id: req.user.user_id, action: 'subscription_mock_purchase', detail: '模拟购买: ' + plan, ip: req.ip })
const sub = await subscriptionDao.findActive(req.user.user_id)
res.json(ok({ status: 'active', plan, remaining_days: sub ? sub.remaining_days : p.days }))
}))
router.post('/subscription/trial', requireUser, wrap(async (req, res) => { router.post('/subscription/trial', requireUser, wrap(async (req, res) => {
const usedTrial = await subscriptionDao.findTrial(req.user.user_id) const usedTrial = await subscriptionDao.findTrial(req.user.user_id)
if (usedTrial) return res.json(fail(2001, '已使用过试用')) if (usedTrial) return res.json(fail(2001, '已使用过试用'))