import config from '@/config.js' const BASE_URL = config.API_BASE_URL var _redirectingToLogin = false function _clearAuthAndRedirect() { if (_redirectingToLogin) return _redirectingToLogin = true uni.removeStorageSync('token') uni.removeStorageSync('userId') uni.removeStorageSync('role') uni.removeStorageSync('alumniVerify') uni.removeStorageSync('classId') uni.removeStorageSync('userInfo') try { var app = typeof getApp === 'function' ? getApp() : null if (app && app.$store) { app.$store.commit('logout') } } catch (e) {} uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' }) setTimeout(function() { uni.$emit('showLoginSheet', {}) _redirectingToLogin = false }, 1500) } export const request = (url, method = 'POST', data = {}, options = {}) => { var fullUrl = BASE_URL + url const token = uni.getStorageSync('token') // 过滤空字符串 ID 字段,防止后端 Long.valueOf("") 抛 NumberFormatException var body = data if (data && typeof data === 'object' && !Array.isArray(data)) { body = {} var hasFiltered = false for (var bk in data) { if (Object.prototype.hasOwnProperty.call(data, bk)) { var bv = data[bk] if (bv === '' && /(?:Id|ID|_id)$/.test(bk)) { hasFiltered = true continue } body[bk] = bv } } if (!hasFiltered) body = data } return new Promise((resolve, reject) => { uni.request({ url: fullUrl, method: method, data: body, header: { 'Content-Type': 'application/json', 'Authorization': token ? 'Bearer ' + token : '' }, success: (res) => { if (res.statusCode === 401 || (res.data && res.data.code === 401)) { _clearAuthAndRedirect() reject(res.data || { code: 401, message: '未授权' }) return } if (res.data && res.data.code === 200) { resolve(res.data) } else { // silent:业务型空态(如未入组/未报名)由页面自行展示,不弹全局 toast if (!options.silent) { uni.showToast({ title: (res.data && res.data.message) || '请求失败', icon: 'none' }) } reject(res.data) } }, fail: (err) => { console.error('[API] 网络错误 ' + method + ' ' + fullUrl, err) uni.showToast({ title: '网络请求失败', icon: 'none' }) reject(err) } }) }) } // === API 函数 === // 登录 export const wechatLogin = (code) => { return request('/api/auth/wechat-login', 'POST', { code }) } // 微信手机号快捷登录(getPhoneNumber 授权,自动获取手机号+昵称) export const wechatPhoneLogin = (data) => { return request('/api/auth/wechat-phone-login', 'POST', data) } // 校友验证 export const alumniVerify = (data) => { return request('/api/auth/alumni-verify', 'POST', data) } // 班次 export const joinClass = (code) => { return request('/api/class/join', 'POST', { code }) } export const getClassInfo = () => { return request('/api/class/info', 'POST') } // 课程(L0/L1/L2/L3/L4) export const getCourseList = () => { return request('/api/course/list', 'POST') } export const getMyCourse = (courseId) => { var data = {} if (courseId) { data = { courseId } } return request('/api/course/current', 'POST', data) } export const enterCourse = (courseId) => { return request('/api/course/enter', 'POST', { courseId }) } export const getCourseProgress = () => { return request('/api/course/progress', 'POST') } // 分组 export const joinGroup = (groupId) => { return request('/api/group/join', 'POST', { groupId }) } export const getMyGroup = () => { // 未入组是正常空态,静默交由页面展示引导,不弹全局 toast return request('/api/group/mine', 'POST', {}, { silent: true }) } export const getClassGroups = () => { return request('/api/group/list', 'POST') } export const setSpokesperson = (speakerUid) => { return request('/api/group/spokesperson', 'POST', { speakerUid }) } export const assignRole = (bookType) => { return request('/api/group/assign', 'POST', { bookType }) } // 三本账 export const claimAssignment = (data) => { return request('/api/assignment/claim', 'POST', data) } export const getMyAssignments = () => { return request('/api/assignment/mine', 'POST') } export const getGroupAssignments = () => { return request('/api/assignment/group', 'POST') } // 提交/打卡 export const checkin = (image) => { return request('/api/submission/checkin', 'POST', { image }) } export const getCheckinStatus = () => { return request('/api/submission/checkin/status', 'POST') } export const prepCheck = (data) => { return request('/api/submission/prep-check', 'POST', data) } export const getPrepCheckStatus = () => { return request('/api/submission/prep-check/status', 'POST') } export const uploadSubmission = (data) => { return request('/api/submission/upload', 'POST', data) } export const getMySubmissions = () => { return request('/api/submission/mylist', 'POST') } export const submitTeachingCard = (data) => { return request('/api/submission/teaching-card', 'POST', data) } // 路演 export const uploadRoadmap = (data) => { return request('/api/vote/roadmap/upload', 'POST', data) } // 投票 export const castVote = (data) => { return request('/api/vote/cast', 'POST', data) } export const getVoteResult = (data) => { return request('/api/vote/result', 'POST', data) } export const getVotedStatus = (data) => { return request('/api/vote/voted', 'POST', data) } export const getScoreboard = (classId) => { return request('/api/vote/scoreboard', 'POST', { classId }) } // 行动计划 export const submitPlan = (data) => { return request('/api/plan/submit', 'POST', data) } export const getMyPlan = () => { return request('/api/plan/mine', 'POST') } export const submitSurvey = (data) => { return request('/api/plan/survey', 'POST', data) } export const getMyCoupons = () => { return request('/api/plan/coupons', 'POST') } export const getMyCert = () => { return request('/api/plan/cert', 'POST') } export const verifyCert = (certNo) => { return request('/api/cert/verify', 'POST', { certNo }) } export const redeemCoupon = (data) => { return request('/api/plan/coupon/redeem', 'POST', data) } // 报名 export const getEnrollClasses = () => { return request('/api/enroll/classes', 'POST') } export const createEnrollment = (data) => { return request('/api/enroll/create', 'POST', data) } export const getMyEnrollments = () => { return request('/api/enroll/mine', 'POST') } // 沙龙 export const getSalonList = () => { return request('/api/salon/list', 'POST') } export const createSalonEnroll = (data) => { return request('/api/salon/create', 'POST', data) } export const getMySalons = () => { return request('/api/salon/mine', 'POST') } // 支付 export const createPayOrder = (data) => { return request('/api/pay/create', 'POST', data) } export const getPayStatus = (data) => { return request('/api/pay/status', 'POST', data) } export const cancelPayOrder = (data) => { return request('/api/pay/cancel', 'POST', data) } // 裂变分享 export const getMyPoster = () => { return request('/api/invite/myposter', 'POST') } export const getInviteStats = () => { return request('/api/invite/stats', 'POST') } // 团报(FR-PAY-03) export const createGroupOrder = (data) => { return request('/api/group-order/create', 'POST', data) } export const getMyGroupOrders = () => { return request('/api/group-order/mine', 'POST') } export const getGroupOrderDetail = (data) => { return request('/api/group-order/detail', 'POST', data) } export const payGroupOrder = (data) => { return request('/api/group-order/pay', 'POST', data) } // 案例授权(FR-FISS-04) export const getMyCases = () => { return request('/api/case/mine', 'POST') } export const decideCase = (data) => { return request('/api/case/decide', 'POST', data) } // 课前资料 export const getClassMaterials = () => { return request('/api/class/materials', 'POST') } // 文件上传 export const uploadFile = (filePath) => { return new Promise((resolve, reject) => { const token = uni.getStorageSync('token') uni.uploadFile({ url: BASE_URL + '/api/upload/file', filePath: filePath, name: 'file', header: { 'Authorization': token ? 'Bearer ' + token : '' }, success: (res) => { try { const data = JSON.parse(res.data) if (data.code === 200) { resolve(data) } else { uni.showToast({ title: data.message || '上传失败', icon: 'none' }) reject(data) } } catch (e) { reject(e) } }, fail: (err) => { console.error('[API] 上传失败', err) uni.showToast({ title: '上传失败', icon: 'none' }) reject(err) } }) }) }