api.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /**
  2. * API请求工具类
  3. */
  4. // 开发环境配置
  5. // 真机调试时,请将 DEV_BASE_URL 改为你电脑的局域网IP地址
  6. // 例如:const DEV_BASE_URL = 'http://192.168.1.3:8080'
  7. // 可以通过命令 ipconfig (Windows) 或 ifconfig (Mac/Linux) 查看IP地址
  8. const DEV_BASE_URL = 'http://192.168.1.3:8080'
  9. // 生产环境配置(部署后的服务器地址)
  10. const PROD_BASE_URL = 'https://your-domain.com'
  11. // 根据环境自动选择BASE_URL
  12. // 在微信开发者工具中使用 localhost,真机调试使用局域网IP
  13. const BASE_URL = DEV_BASE_URL
  14. /**
  15. * 获取本地存储的token(避免循环依赖)
  16. */
  17. const getToken = () => {
  18. return uni.getStorageSync('user_token') || null
  19. }
  20. /**
  21. * 统一请求封装
  22. * 自动添加token到请求头
  23. * @param {object} options - 请求配置对象
  24. * @returns {Promise} 请求Promise
  25. */
  26. const request = (options) => {
  27. return new Promise((resolve, reject) => {
  28. // 获取token并添加到请求头
  29. const token = getToken()
  30. const header = options.header || {}
  31. if (token) {
  32. header['Authorization'] = `Bearer ${token}`
  33. }
  34. uni.request({
  35. url: `${BASE_URL}${options.url}`,
  36. method: options.method || 'GET',
  37. data: options.data || {},
  38. header: header,
  39. success: (res) => {
  40. // 统一处理响应
  41. if (res.statusCode === 200) {
  42. resolve(res.data)
  43. } else if (res.statusCode === 401) {
  44. // token过期或未登录
  45. uni.showToast({
  46. title: '登录已过期,请重新登录',
  47. icon: 'none',
  48. duration: 2000
  49. })
  50. // 清除token
  51. uni.removeStorageSync('user_token')
  52. uni.removeStorageSync('user_info')
  53. // 延迟后显示登录弹窗
  54. setTimeout(() => {
  55. uni.showModal({
  56. title: '登录提示',
  57. content: '登录已过期,请前往"模拟排名"或"强势池"页面重新登录',
  58. confirmText: '去登录',
  59. cancelText: '取消',
  60. success: (modalRes) => {
  61. if (modalRes.confirm) {
  62. // 跳转到模拟排名页面
  63. uni.switchTab({
  64. url: '/pages/rank/rank'
  65. })
  66. }
  67. }
  68. })
  69. }, 2000)
  70. reject(new Error('未授权'))
  71. } else {
  72. reject(new Error(res.data.message || '服务暂不可用'))
  73. }
  74. },
  75. fail: (err) => {
  76. reject(new Error('网络异常'))
  77. }
  78. })
  79. })
  80. }
  81. /**
  82. * 第一步:微信静默登录接口(检查是否为老用户)
  83. * @param {object} params - { loginCode }
  84. * @returns {Promise} 返回 { isSign, token?, code? }
  85. */
  86. export const wxSilentLoginApi = (params) => {
  87. return request({
  88. url: '/v1/auth/wx/silent-login',
  89. method: 'POST',
  90. header: {
  91. 'content-type': 'application/json'
  92. },
  93. data: params
  94. })
  95. }
  96. /**
  97. * 第二步:手机号授权登录接口(新用户验证手机号)
  98. * @param {object} params - { loginCode, phoneCode, encryptedData, iv }
  99. * @returns {Promise} 返回 { isSign, token?, openid?, unionid?, phoneNumber? }
  100. */
  101. export const wxPhoneLoginApi = (params) => {
  102. return request({
  103. url: '/v1/auth/wx/phone-verify',
  104. method: 'POST',
  105. header: {
  106. 'content-type': 'application/json'
  107. },
  108. data: params
  109. })
  110. }
  111. /**
  112. * 第三步:完善用户信息接口(首次登录)
  113. * @param {object} params - { openid, unionid, phoneNumber, nickname, avatarUrl }
  114. * @returns {Promise} 返回 { token }
  115. */
  116. export const wxCompleteUserInfoApi = (params) => {
  117. return request({
  118. url: '/v1/auth/wx/register',
  119. method: 'POST',
  120. header: {
  121. 'content-type': 'application/json'
  122. },
  123. data: params
  124. })
  125. }
  126. /**
  127. * 获取用户完整信息接口
  128. * @returns {Promise} 返回用户信息
  129. */
  130. export const getUserInfoApi = () => {
  131. return request({
  132. url: '/v1/user/info',
  133. method: 'GET'
  134. })
  135. }
  136. /**
  137. * 文件上传配置
  138. */
  139. export const uploadFile = {
  140. url: `${BASE_URL}/v1/file/upload`
  141. }
  142. /**
  143. * 更新用户资料
  144. * @param {object} data - 用户资料
  145. * @param {string} data.nickname - 昵称
  146. * @param {string} data.avatar - 头像URL
  147. * @returns {Promise} 返回更新结果
  148. */
  149. export const updateUserProfile = (data) => {
  150. return request({
  151. url: '/v1/user/profile',
  152. method: 'PUT',
  153. header: {
  154. 'content-type': 'application/json'
  155. },
  156. data: data
  157. })
  158. }
  159. /**
  160. * 模糊搜索(联想建议)
  161. * @param {string} keyword - 搜索关键词
  162. * @returns {Promise} 返回搜索建议列表
  163. */
  164. export const getSuggestions = (keyword) => {
  165. return request({
  166. url: '/v1/stock/suggestion',
  167. method: 'GET',
  168. data: { keyword }
  169. })
  170. }
  171. /**
  172. * 股票详情查询
  173. * @param {string} keyword - 股票代码或名称
  174. * @returns {Promise} 返回股票详情信息
  175. */
  176. export const searchStocks = (keyword) => {
  177. return request({
  178. url: '/v1/stock/search',
  179. method: 'POST',
  180. header: {
  181. 'content-type': 'application/json'
  182. },
  183. data: { keyword }
  184. })
  185. }
  186. /**
  187. * 获取用户模拟资产(需要登录)
  188. * @returns {Promise} 返回用户资产信息
  189. */
  190. export const getUserPortfolio = () => {
  191. return request({
  192. url: '/v1/user/portfolio',
  193. method: 'GET'
  194. })
  195. }
  196. /**
  197. * 获取模拟交易排行榜
  198. * @returns {Promise} 返回排行榜数据
  199. */
  200. export const getLeaderboard = () => {
  201. return request({
  202. url: '/v1/rank/leaderboard',
  203. method: 'GET'
  204. })
  205. }