Selaa lähdekoodia

feat(fatloss-miniprogram): add api wrappers and format utils

Sisyphus 3 päivää sitten
vanhempi
sitoutus
e0f8952b5f
2 muutettua tiedostoa jossa 235 lisäystä ja 0 poistoa
  1. 203 0
      xiaofat-miniprogram/utils/api.js
  2. 32 0
      xiaofat-miniprogram/utils/format.js

+ 203 - 0
xiaofat-miniprogram/utils/api.js

@@ -0,0 +1,203 @@
+import config from '../config.js'
+
+const BASE_URL = config.API_BASE_URL
+var _dedupMap = {}
+var DEDUP_WINDOW_MS = 3000
+
+function _makeDedupKey(method, url, body) {
+  var bodyStr = ''
+  try { bodyStr = JSON.stringify(body || {}) } catch (e) { bodyStr = '' }
+  return method + '|' + url + '|' + bodyStr
+}
+
+function _tryDedup(method, fullUrl, body) {
+  var key = _makeDedupKey(method, fullUrl, body)
+  var cached = _dedupMap[key]
+  if (cached && Date.now() - cached.ts < DEDUP_WINDOW_MS) {
+    return cached.promise
+  }
+  return null
+}
+
+function _registerDedup(method, fullUrl, body, promise) {
+  var key = _makeDedupKey(method, fullUrl, body)
+  _dedupMap[key] = { promise: promise, ts: Date.now() }
+  var _cleanup = function() {
+    setTimeout(function() {
+      if (_dedupMap[key] && _dedupMap[key].promise === promise) {
+        delete _dedupMap[key]
+      }
+    }, DEDUP_WINDOW_MS)
+  }
+  promise.then(_cleanup, _cleanup)
+}
+
+function _clearDedup() {
+  _dedupMap = {}
+}
+export { _clearDedup as clearApiDedup }
+
+function request(url, method = 'POST', data = {}, options = {}) {
+  var fullUrl = BASE_URL + url
+  const token = uni.getStorageSync('token')
+  var key = _makeDedupKey(method, fullUrl, data)
+  if (!options._skipDedup) {
+    var cached = _tryDedup(method, fullUrl, data)
+    if (cached) {
+      return cached
+    }
+  }
+
+  var _promise = new Promise((resolve, reject) => {
+    uni.request({
+      url: fullUrl,
+      method: method,
+      data: data,
+      header: {
+        'Content-Type': 'application/json',
+        'Authorization': token ? 'Bearer ' + token : ''
+      },
+      success: function(res) {
+        if (res.statusCode === 401) {
+          uni.login({
+            provider: 'weixin',
+            success: function(loginRes) {
+              if (loginRes && loginRes.code) {
+                silentLogin(loginRes.code).then(function(sr) {
+                  if (sr && sr.data && sr.data.token) {
+                    uni.setStorageSync('token', sr.data.token)
+                    uni.setStorageSync('openid', sr.data.openid)
+                    resolve(sr.data)
+                  } else {
+                    uni.showToast({ title: '登录已过期,请重新进入', icon: 'none' })
+                    reject(res.data)
+                  }
+                })
+              }
+            }
+          })
+          return
+        }
+        if (res.data && res.data.code === 200) {
+          resolve(res.data)
+        } else {
+          var msg = res.data && res.data.message ? res.data.message : '请求失败'
+          uni.showToast({ title: msg, icon: 'none' })
+          reject(res.data)
+        }
+      },
+      fail: function(err) {
+        console.error('[API] 网络错误 ' + method + ' ' + fullUrl, err)
+        uni.showToast({ title: '网络请求失败', icon: 'none' })
+        reject(err)
+      }
+    })
+  })
+
+  if (!options._skipDedup) {
+    _registerDedup(method, fullUrl, data, _promise)
+  }
+
+  return _promise
+}
+
+// ===== 认证 =====
+export const silentLogin = (code, options) => {
+  return request('/api/auth/silent-login', 'POST', { code }, options || {})
+}
+
+export const wechatLogin = (code, userInfo) => {
+  return request('/api/auth/wechat-login', 'POST', { code, nickname: userInfo.nickname, avatar: userInfo.avatar })
+}
+
+export const getUserInfo = () => {
+  return request('/api/user/info', 'POST', {})
+}
+
+// ===== 减脂专题 API =====
+// 体重
+export const getWeightList = (memberId, startDate, endDate) => {
+  var data = {}
+  if (memberId) data.memberId = memberId
+  if (startDate) data.startDate = startDate
+  if (endDate) data.endDate = endDate
+  return request('/api/fatloss/weight/list', 'POST', data)
+}
+
+export const createWeightRecord = (data) => {
+  return request('/api/fatloss/weight/create', 'POST', data)
+}
+
+export const updateWeightRecord = (data) => {
+  return request('/api/fatloss/weight/update', 'POST', data)
+}
+
+export const deleteWeightRecord = (id) => {
+  return request('/api/fatloss/weight/delete', 'POST', { id: id })
+}
+
+// 饮食
+export const getDietList = (memberId, startDate, endDate) => {
+  var data = {}
+  if (memberId) data.memberId = memberId
+  if (startDate) data.startDate = startDate
+  if (endDate) data.endDate = endDate
+  return request('/api/fatloss/diet/list', 'POST', data)
+}
+
+export const createDietRecord = (data) => {
+  return request('/api/fatloss/diet/create', 'POST', data)
+}
+
+export const deleteDietRecord = (id) => {
+  return request('/api/fatloss/diet/delete', 'POST', { id: id })
+}
+
+export const getCaloriesSummary = (date) => {
+  var data = {}
+  if (date) data.date = date
+  return request('/api/fatloss/diet/calories-summary', 'POST', data)
+}
+
+// 运动
+export const getExerciseList = (memberId, startDate, endDate) => {
+  var data = {}
+  if (memberId) data.memberId = memberId
+  if (startDate) data.startDate = startDate
+  if (endDate) data.endDate = endDate
+  return request('/api/fatloss/exercise/list', 'POST', data)
+}
+
+export const createExerciseRecord = (data) => {
+  return request('/api/fatloss/exercise/create', 'POST', data)
+}
+
+export const deleteExerciseRecord = (id) => {
+  return request('/api/fatloss/exercise/delete', 'POST', { id: id })
+}
+
+// 目标
+export const getActiveGoal = () => {
+  return request('/api/fatloss/goal/active', 'POST', {})
+}
+
+export const createGoal = (data) => {
+  return request('/api/fatloss/goal/create', 'POST', data)
+}
+
+export const updateGoal = (data) => {
+  return request('/api/fatloss/goal/update', 'POST', data)
+}
+
+export const completeGoal = (id) => {
+  return request('/api/fatloss/goal/complete', 'POST', { id: id })
+}
+
+export const abandonGoal = (id) => {
+  return request('/api/fatloss/goal/abandon', 'POST', { id: id })
+}
+
+// ===== AI 教练(走主系统 AiGateway) =====
+export const aiCoachSend = (data) => {
+  return request('/api/ai/health-coach/send', 'POST', data)
+}

+ 32 - 0
xiaofat-miniprogram/utils/format.js

@@ -0,0 +1,32 @@
+/**
+ * 时间格式化工具 — iOS 安全版本
+ */
+
+export function parseDate(str) {
+  if (!str) return null
+  if (str instanceof Date) return str
+  var normalized = String(str).replace(' ', 'T').trim()
+  var d = new Date(normalized)
+  return isNaN(d.getTime()) ? null : d
+}
+
+export function formatDateTime(str) {
+  var d = parseDate(str)
+  if (!d) return ''
+  function pad(n) { return n < 10 ? '0' + n : '' + n }
+  return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) +
+    ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
+}
+
+export function formatDate(str) {
+  var d = parseDate(str)
+  if (!d) return ''
+  function pad(n) { return n < 10 ? '0' + n : '' + n }
+  return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
+}
+
+export function today() {
+  var d = new Date()
+  function pad(n) { return n < 10 ? '0' + n : '' + n }
+  return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
+}