feat: 新增运动节拍训练工具套件与子工具页面
新增运动节拍RepClock全套小程序页面、后端API与数据库服务,新增subhub子工具汇总页面,优化全局主页样式与配置,为web静态资源添加版本缓存参数,补充配套调试脚本
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
const http = require('http')
|
||||
const BASE = { host: 'localhost', port: 5053, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Mp-Timeassistant': 'mp_node_client_enc_test_xxx' } }
|
||||
const post = (path, obj) => new Promise((res, rej) => {
|
||||
const body = Buffer.from(JSON.stringify(obj), 'utf8')
|
||||
const req = http.request({ ...BASE, method: 'POST', path, headers: { ...BASE.headers, 'Content-Length': body.length } }, (r) => {
|
||||
let data = []
|
||||
r.on('data', (c) => data.push(c))
|
||||
r.on('end', () => { try { res({ status: r.statusCode, headers: r.headers, body: JSON.parse(Buffer.concat(data).toString('utf8')) }) } catch (e) { rej(e) } })
|
||||
})
|
||||
req.on('error', rej)
|
||||
req.write(body); req.end()
|
||||
})
|
||||
const get = (path) => new Promise((res, rej) => {
|
||||
http.get({ ...BASE, path }, (r) => {
|
||||
let data = []
|
||||
r.on('data', (c) => data.push(c))
|
||||
r.on('end', () => { try { res({ status: r.statusCode, body: JSON.parse(Buffer.concat(data).toString('utf8')) }) } catch (e) { rej(e) } })
|
||||
}).on('error', rej)
|
||||
})
|
||||
;(async () => {
|
||||
console.log('=== Step1: POST /cards via UTF-8 JSON (like WeChat mini-program) ===')
|
||||
const body = {
|
||||
title: '瑜伽垫上腰部运动',
|
||||
loopMode: 'loop_n_times',
|
||||
loopCount: 2,
|
||||
steps: [
|
||||
{ name: '热身动作', durationSec: 30, restSec: 10, type: 'action' },
|
||||
{ name: '平板支撑', durationSec: 45, restSec: 15, type: 'action' },
|
||||
],
|
||||
}
|
||||
const pr = await post('/api/mp_rep_clock/kits/rep_clock/cards', body)
|
||||
console.log('POST status=', pr.status)
|
||||
const c = pr.body.card
|
||||
console.log('card.id:', c.id)
|
||||
console.log('card.title (JSON.stringify):', JSON.stringify(c.title), ' chars:', Array.from(c.title).map((ch) => ch.charCodeAt(0).toString(16).padStart(4, '0')).join(' '))
|
||||
console.log('card.loopTimes:', c.loopTimes, ' voice:', c.voice, ' ttsReady:', c.ttsReady)
|
||||
console.log('card.steps:')
|
||||
c.steps.forEach((s, i) => console.log(` [${i}] name=${JSON.stringify(s.name)} durationSec=${s.durationSec} restSec=${s.restSec} type=${s.type}`))
|
||||
|
||||
console.log('\n=== Step2: GET /cards ===')
|
||||
const gr = await get('/api/mp_rep_clock/kits/rep_clock/cards')
|
||||
const c0 = gr.body.cards[0]
|
||||
console.log('cards[0].title=', JSON.stringify(c0.title))
|
||||
console.log('cards[0].ttsReady=', c0.ttsReady, ' loopTimes=', c0.loopTimes, ' voice=', c0.voice)
|
||||
c0.steps.forEach((s, i) => console.log(` [${i}] name=${JSON.stringify(s.name)} restSec=${s.restSec}`))
|
||||
|
||||
console.log('\n=== Step3: HEX dump directly from DB to prove storage ===')
|
||||
const Database = require('better-sqlite3')
|
||||
const path = require('path')
|
||||
const db = new Database(path.join(process.cwd(), 'data', 'mp_rep_clock.db'))
|
||||
const row = db.prepare(`SELECT id, hex(title) ht, hex(steps_json) hsj FROM rc_cards WHERE id=? ORDER BY rowid DESC LIMIT 1`).get(c.id)
|
||||
console.log('DB hex(title):', row.ht.substring(0, 200))
|
||||
const titleFromDb = Buffer.from(row.ht, 'hex').toString('utf8')
|
||||
console.log('DB decoded title:', JSON.stringify(titleFromDb))
|
||||
const stepsParsed = JSON.parse(Buffer.from(row.hsj, 'hex').toString('utf8'))
|
||||
stepsParsed.forEach((s, i) => console.log(` DB steps[${i}] name=${JSON.stringify(s.name)} restSec=${s.restSec}`))
|
||||
console.log('\n=== ALL ENCODING CHECKS PASSED if title decoded correctly above ===')
|
||||
})().catch((e) => { console.error('ERR', e); process.exit(1) })
|
||||
Reference in New Issue
Block a user