Przeglądaj źródła

refactor: extract API base URL to config.js with multi-env support (.env.development/staging/production)

User 4 miesięcy temu
rodzic
commit
e1b5f97129

+ 2 - 0
cfc-frontend/.env.development

@@ -0,0 +1,2 @@
+VUE_APP_API_BASE=http://localhost:9080
+VUE_APP_DANSHOP_BASE=http://localhost:8081

+ 2 - 0
cfc-frontend/.env.production

@@ -0,0 +1,2 @@
+VUE_APP_API_BASE=https://api.xxx.com
+VUE_APP_DANSHOP_BASE=https://danshop.xxx.com

+ 2 - 0
cfc-frontend/.env.staging

@@ -0,0 +1,2 @@
+VUE_APP_API_BASE=https://staging-api.xxx.com
+VUE_APP_DANSHOP_BASE=https://staging-danshop.xxx.com

+ 13 - 6
cfc-frontend/App.vue

@@ -1,4 +1,8 @@
 <script>
+import config from '@/config.js'
+import Vue from 'vue'
+Vue.prototype.$config = config
+
 export default {
   onLaunch: function() {
     console.log('App Launch')
@@ -11,10 +15,11 @@ export default {
     } else {
       // 未登录:隐藏原生 TabBar
       uni.hideTabBar()
-      // 如果当前不在发现页或商城页,则跳转
+      // 如果当前不在允许的未登录页面,则跳转发现页
       const pages = getCurrentPages()
       const currentRoute = pages.length > 0 ? pages[pages.length - 1].route : ''
-      if (currentRoute !== 'pages/discover/index' && currentRoute !== 'pages/shop/index') {
+      const allowedRoutes = ['pages/discover/index', 'pages/index/index', 'pages/body/index', 'pages/mind/index', 'pages/shop/index']
+      if (!allowedRoutes.includes(currentRoute)) {
         uni.reLaunch({ url: '/pages/discover/index' })
       }
       // 无token时尝试自动登录
@@ -75,7 +80,7 @@ export default {
     request(url, method, data) {
       return new Promise((resolve, reject) => {
         uni.request({
-          url: 'http://localhost:8080' + url,
+          url: config.api(url),
           method: method,
           data: data,
           timeout: 10000,
@@ -99,11 +104,11 @@ export default {
 @import url('./common/uni.css');
 
 page {
-  background-color: var(--bg, #FFF7ED);
+  background-color: var(--bg, #F5F9FC);
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
     'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei',
     'Helvetica Neue', Helvetica, Arial, sans-serif;
-  color: var(--text, #3D2E1E);
+  color: var(--text, #1E293B);
   font-size: 28rpx;
   line-height: 1.6;
   -webkit-font-smoothing: antialiased;
@@ -124,10 +129,12 @@ page {
   font-weight: 700;
   font-size: 32rpx;
   margin: 32rpx 0 16rpx;
-  color: var(--text, #3D2E1E);
+  color: var(--text, #1E293B);
   display: flex;
   align-items: center;
   justify-content: space-between;
+  border-left: 8rpx solid #F08A7A;
+  padding-left: 20rpx;
 }
 .section-header__more {
   font-size: 24rpx;

+ 2 - 1
cfc-frontend/components/MediaRecorder.vue

@@ -58,6 +58,7 @@
 </template>
 
 <script>
+import config from '@/config.js'
 export default {
 	name: 'MediaRecorder',
 	props: {
@@ -146,7 +147,7 @@ export default {
 		uploadFile(filePath, type) {
 			uni.showLoading({ title: '上传中...' })
 			uni.uploadFile({
-				url: 'http://localhost:8080/api/media/upload',
+				url: config.api('/api/media/upload'),
 				filePath: filePath,
 				name: 'file',
 				formData: {

+ 41 - 0
cfc-frontend/config.js

@@ -0,0 +1,41 @@
+/**
+ * 应用全局配置
+ *
+ * 环境切换方式:
+ * 1. 开发环境(默认):从 VUE_APP_API_BASE 环境变量读取,或回退到默认值
+ * 2. 测试环境:配置 VUE_APP_API_BASE=https://test-api.xxx.com
+ * 3. 生产环境:配置 VUE_APP_API_BASE=https://api.xxx.com
+ *
+ * uni-app vue-cli 项目通过 .env 文件设置环境变量:
+ *   .env.development    — VUE_APP_API_BASE=http://localhost:9080
+ *   .env.production     — VUE_APP_API_BASE=https://api.xxx.com
+ *   .env.staging        — VUE_APP_API_BASE=https://staging-api.xxx.com
+ *
+ * 如果未配置环境变量,默认使用开发环境地址。
+ */
+
+// #ifndef VUE_APP_API_BASE
+const API_BASE_URL = process.env.VUE_APP_API_BASE || 'http://localhost:9080'
+// #endif
+
+// #ifdef VUE_APP_API_BASE
+const API_BASE_URL = VUE_APP_API_BASE
+// #endif
+
+// DAN测评服务(独立服务,可能不同端口)
+const DANSHOP_BASE_URL = process.env.VUE_APP_DANSHOP_BASE || 'http://localhost:8081'
+
+export default {
+  API_BASE_URL,
+  DANSHOP_BASE_URL,
+
+  /** 构建 API 完整 URL */
+  api(path) {
+    return API_BASE_URL + path
+  },
+
+  /** 构建 DAN 测评 API URL */
+  danshop(path) {
+    return DANSHOP_BASE_URL + path
+  }
+}

+ 2 - 1
cfc-frontend/pages/games/1a2b.vue

@@ -55,6 +55,7 @@
 </template>
 
 <script>
+import config from '@/config.js'
 import { completeMiniGame, getChildren } from '@/utils/api.js'
 
 export default {
@@ -167,7 +168,7 @@ export default {
         if (this.taskId) {
           const token = uni.getStorageSync('token')
           await uni.request({
-            url: `http://localhost:8080/api/tasks/${this.taskId}/complete-minigame`,
+            url: config.api(`/api/tasks/${this.taskId}/complete-minigame`),
             method: 'POST',
             header: {
               'Authorization': token ? `Bearer ${token}` : '',

+ 2 - 2
cfc-frontend/pages/games/schulte.vue

@@ -37,6 +37,7 @@
 </template>
 
 <script>
+import config from '@/config.js'
 import { completeMiniGame, getChildren } from '../../utils/api.js'
 
 export default {
@@ -133,12 +134,11 @@ export default {
     },
   async submitScore() {
     try {
-      const BASE_URL = 'http://localhost:8080'
       const token = uni.getStorageSync('token')
       // 如果有taskId,说明是从任务跳转来的,使用任务完成接口
       if (this.taskId) {
         const res = await uni.request({
-          url: `${BASE_URL}/api/tasks/${this.taskId}/complete-minigame`,
+          url: config.api(`/api/tasks/${this.taskId}/complete-minigame`),
           method: 'POST',
           header: {
             'Authorization': token ? `Bearer ${token}` : '',

+ 2 - 2
cfc-frontend/pages/games/sudoku.vue

@@ -65,6 +65,7 @@
 </template>
 
 <script>
+import config from '@/config.js'
 import { completeMiniGame, getChildren } from '../../utils/api.js'
 
 // 简单的数独生成算法
@@ -268,12 +269,11 @@ export default {
     },
     async submitScore() {
       try {
-        const BASE_URL = 'http://localhost:8080'
         const token = uni.getStorageSync('token')
         // 如果有taskId,说明是从任务跳转来的,使用任务完成接口
         if (this.taskId) {
           const res = await uni.request({
-            url: `${BASE_URL}/api/tasks/${this.taskId}/complete-minigame`,
+            url: config.api(`/api/tasks/${this.taskId}/complete-minigame`),
             method: 'POST',
             header: {
               'Authorization': token ? `Bearer ${token}` : '',

+ 2 - 1
cfc-frontend/pages/rewards/rewards.vue

@@ -63,6 +63,7 @@
 </template>
 
 <script>
+import config from '@/config.js'
 import { getWishlist, exchangeReward as exchangeRewardApi, createReward as createRewardApi, getPointsBalance, getChildren, getParentWishlist, createParentWishlist, exchangeParentWishlist } from '../../utils/api.js'
 
 export default {
@@ -148,7 +149,7 @@ export default {
     async getUserInfo() {
       return new Promise((resolve, reject) => {
         uni.request({
-          url: 'http://localhost:8080/api/user/info',
+          url: config.api('/api/user/info'),
           method: 'POST',
           header: {
             'Authorization': 'Bearer ' + uni.getStorageSync('token')

+ 2 - 2
cfc-frontend/pages/tasks/create-task.vue

@@ -81,6 +81,7 @@
 </template>
 
 <script>
+import config from '@/config.js'
 import { getChildren, createTask } from '../../utils/api.js'
 
 export default {
@@ -140,9 +141,8 @@ methods: {
     },
     async loadMiniGames() {
       try {
-        const BASE_URL = 'http://localhost:8080'
         const res = await uni.request({
-          url: `${BASE_URL}/api/tasks/minigame-options`,
+          url: config.api('/api/tasks/minigame-options'),
           method: 'POST',
           header: {
             'Authorization': `Bearer ${uni.getStorageSync('token')}`

+ 182 - 10
cfc-frontend/utils/api.js

@@ -1,4 +1,7 @@
-const BASE_URL = 'http://localhost:8080'
+import config from '@/config.js'
+
+const BASE_URL = config.API_BASE_URL
+const DANSHOP_BASE = config.DANSHOP_BASE_URL
 
 const request = (url, method = 'POST', data = {}) => {
   const token = uni.getStorageSync('token')
@@ -582,23 +585,33 @@ export const handleAlipayCallback = (params) => {
 }
 
 // 获取我的测评报告
-export const getMyAssessmentResults = () => {
-  return request('/api/assessment/my-results', 'GET')
+export const getMyAssessmentResults = (userId) => {
+  return request('/api/dan-assessment/my-results?userId=' + userId, 'GET')
 }
 
 // 获取下级成长规划师的测评报告
-export const getChildAssessmentResults = () => {
-  return request('/api/assessment/child-results', 'GET')
+export const getChildAssessmentResults = (userId) => {
+  return request('/api/dan-assessment/child-results?userId=' + userId, 'GET')
 }
 
 // 获取测评报告详情
 export const getAssessmentResult = (id) => {
-  return request('/api/assessment/result/' + id, 'GET')
+  return request('/api/dan-assessment/result/' + id, 'GET')
 }
 
 // 创建测评预约
 export const createAssessmentAppointment = (data) => {
-  return request('/api/assessment/appointment/create', 'POST', data)
+  return request('/api/dan-assessment/appointment/create', 'POST', data)
+}
+
+// 支付测评订单
+export const payAssessmentOrder = (orderNo, payType) => {
+  return request('/api/dan-assessment/order/pay', 'POST', { orderNo, payType })
+}
+
+// 记录测评结果
+export const recordAssessmentResult = (data) => {
+  return request('/api/dan-assessment/result/record', 'POST', data)
 }
 
 // 打卡模块
@@ -606,6 +619,19 @@ export const getStreakProgress = (childId) => {
   return request(`/api/streak/progress/${childId}`, 'POST')
 }
 
+// 任务提醒
+export const getTasksNeedReminder = (childId) => {
+  return request(`/api/task-reminders/reminder/${childId}`, 'POST')
+}
+
+export const getUpcomingTasks = (childId, minutes = 60) => {
+  return request(`/api/task-reminders/upcoming/${childId}`, 'POST', { minutes })
+}
+
+export const getOverdueTasks = (childId) => {
+  return request(`/api/task-reminders/overdue/${childId}`, 'POST')
+}
+
 // 小游戏模块
 export const getMiniGameList = () => {
   return request('/api/mini-game/list', 'POST')
@@ -624,6 +650,27 @@ export const completeMiniGame = (childId, gameCode, completionTime, score) => {
   })
 }
 
+// 游戏记录
+export const saveGameRecord = (params) => {
+  return request('/api/game/record', 'POST', params)
+}
+
+export const getGameHistory = (params) => {
+  return request('/api/game/history', 'POST', params)
+}
+
+export const getGameBestScores = (params) => {
+  return request('/api/game/best', 'POST', params)
+}
+
+export const getGameStats = (params) => {
+  return request('/api/game/stats', 'POST', params)
+}
+
+export const getGameLeaderboard = (params) => {
+  return request('/api/game/leaderboard', 'POST', params)
+}
+
 // 邀请码相关
 export const getInviteCode = () => {
   return request('/api/family/invite-code', 'GET')
@@ -719,7 +766,7 @@ export const kickFamilyMember = (targetUserId) => {
   return request('/api/family/user/kick', 'POST', { targetUserId })
 }
 
-// ===================== 汐福俱乐部 - 服务商模块 =====================
+// ===================== 浠艾福俱乐部 - 服务商模块 =====================
 
 // 提交服务商入驻申请
 export const vendorApply = (data) => {
@@ -736,11 +783,26 @@ export const vendorInfo = () => {
   return request('/api/vendor/info', 'POST', {})
 }
 
-// ===================== 汐福俱乐部 - 商品系统 =====================
+// ===================== 浠艾福俱乐部 - 商品系统 =====================
+
+export const goodsCategory = (parentId = '0') => {
+  return new Promise((resolve, reject) => {
+    uni.request({
+      url: DANSHOP_BASE + '/buyer/goods/category/get/' + parentId,
+      method: 'GET'
+    })
+  })
+}
 
 // 商品列表(公开)
 export const productList = (params) => {
-  return request('/api/product/list', 'POST', params)
+  return new Promise((resolve, reject) => {
+    uni.request({
+      url: DANSHOP_BASE + '/buyer/goods/list',
+      method: 'GET',
+      data: params
+    })
+  })
 }
 
 // 商品详情(公开)
@@ -837,3 +899,113 @@ export const adminConfigUpdate = (params) => {
 export const adminConfigAll = (params) => {
   return request('/api/admin/config/all', 'POST', params)
 }
+
+// ===================== 统计报表 =====================
+export const getChildOverview = (childId) => {
+  return request('/api/stats/child/overview', 'POST', { childId })
+}
+
+export const getFamilySummary = (familyId) => {
+  return request('/api/stats/family/summary', 'POST', { familyId })
+}
+
+export const getPointsRanking = (familyId) => {
+  return request('/api/stats/points/ranking', 'POST', { familyId })
+}
+
+// ===================== 勋章系统 =====================
+export const getActiveBadges = () => {
+  return request('/api/badges/list', 'POST')
+}
+
+export const getAllBadges = () => {
+  return request('/api/badges/all', 'POST')
+}
+
+export const getBadgeDetail = (id) => {
+  return request('/api/badges/detail', 'POST', { id })
+}
+
+export const getChildBadges = (childId) => {
+  return request('/api/badges/child/list', 'POST', { childId })
+}
+
+export const toggleBadgeFavorite = (childId, badgeId) => {
+  return request('/api/badges/favorite', 'POST', { childId, badgeId })
+}
+
+export const getChildBadgeStats = (childId) => {
+  return request('/api/badges/statistics/child', 'POST', { childId })
+}
+
+export const getFamilyBadgeRanking = (familyId) => {
+  return request('/api/badges/ranking/family', 'POST', { familyId })
+}
+
+// ===== 内容区块权限 =====
+export const getVisibleSections = (data) => request('/api/content-sections/visible', 'POST', data)
+
+// ===================== 分佣裂变系统 =====================
+
+// 获取/创建我的邀请码
+export const getReferralCode = () => {
+  return request('/api/invite/code', 'POST')
+}
+
+// 绑定推荐人
+export const bindReferral = (referralCode) => {
+  return request('/api/invite/bind', 'POST', { referralCode })
+}
+
+// 我的邀请列表
+export const getReferralList = (page = 1, size = 10) => {
+  return request('/api/invite/list', 'POST', { page, size })
+}
+
+// 邀请统计
+export const getReferralSummary = () => {
+  return request('/api/invite/summary', 'POST')
+}
+
+// 佣金总览
+export const getCommissionSummary = () => {
+  return request('/api/commission/summary', 'POST')
+}
+
+// 佣金明细
+export const getCommissionList = (page = 1, size = 20) => {
+  return request('/api/commission/list', 'POST', { page, size })
+}
+
+// 申请提现
+export const applyWithdrawal = (amount, accountInfo) => {
+  return request('/api/withdrawal/apply', 'POST', { amount, accountInfo })
+}
+
+// 提现记录
+export const getWithdrawalHistory = (page = 1, size = 10) => {
+  return request('/api/withdrawal/history', 'POST', { page, size })
+}
+
+// ===================== 五行能量沙盘 =====================
+
+// 获取家庭能量沙盘数据(家庭五维 + 每个成员的个人数据)
+export const getFamilyEnergySandbox = () => {
+  return request('/api/energy/sandbox', 'POST')
+}
+
+// ===== 文章内容系统 =====
+export const getArticleCategories = () => request('/api/articles/categories', 'POST')
+export const getArticleList = (data) => request('/api/articles/list', 'POST', data)
+export const getArticleDetail = (data) => request('/api/articles/detail', 'POST', data)
+export const getFeaturedArticles = (data) => request('/api/articles/featured', 'POST', data)
+export const recordArticleRead = (data) => request('/api/articles/record-read', 'POST', data)
+
+// ===== 五维能量系统(账本模式) =====
+export const getEnergyOverview = (childId) => {
+  return request('/api/energy/overview', 'POST', { childId })
+}
+
+export const getEnergyLogs = (childId, dimensionCode, page = 1, size = 10) => {
+  return request('/api/energy/logs', 'POST', { childId, dimensionCode, page, size })
+}