diff --git a/app.js b/app.js index 31f3b43..677f798 100644 --- a/app.js +++ b/app.js @@ -4,9 +4,143 @@ App(injectApp()({ }, async onLaunch() { + if (wx.canIUse('getUpdateManager')) { + const updateManager = wx.getUpdateManager(); + updateManager.onCheckForUpdate(function (res) { + if (res.hasUpdate) { + updateManager.onUpdateReady(function () { + wx.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用?', + success(res) { + if (res.confirm) { + updateManager.applyUpdate(); + } + } + }); + }); + } + }); + } // 等待登陆完成以后请求自动携带token await waitLogin() // 三方登录本地缓存 console.log(wx.getStorageSync('jdwx-userinfo')) }, + + //内容安全 + async checkdata(txt = '', checkType, mediaUrl = '') { + try { + if (!checkType || (checkType !== 2 && checkType !== 3)) { + throw new Error('checkType必须为2(图片检测)或3(文本检测)') + } + + if (checkType === 3 && !txt) { + throw new Error('文本检测时content不能为空') + } + + if (checkType === 2 && !mediaUrl) { + throw new Error('图片检测时mediaUrl不能为空') + } + + const postdata = { + content: txt, + checkType: checkType, + mediaUrl: mediaUrl + } + + const data = await gatewayHttpClient.request('/wx/v1/api/app/content/check', 'post', postdata) + + if (data.code === 200) { + if (checkType == 3) { + return data.data.suggest === 'pass' ? 1 : 2 + } + + if (checkType == 2) { + return data.data.id || null + } + } else { + console.error('内容检测API调用失败:', data) + return 2 + } + } catch (error) { + console.error('checkdata函数执行错误:', error) + return 2 + } + }, +// 轮询检查图片是否合规 +//imgurl 为本地图片地址 +async checkimage(imgurl){ + wx.showLoading({ + title: '正在检查图片...', + mask: true + }); + try{ + const upfileData = await gatewayHttpClient.uploadFile(imgurl, 'image'); + const checkid = await this.checkdata('',2,upfileData.data.url) + let retryCount = 0; + const maxRetries = 5; + while (retryCount < maxRetries) { + await new Promise(resolve => setTimeout(resolve, 1000)); // 间隔1秒 + + const passcode = await this.checkSafetyResults(checkid); + + // 检查具体的错误码 + switch (passcode) { + case 100: + wx.hideLoading(); + return upfileData.data; + case 20001: + wx.hideLoading(); + await gatewayHttpClient.deleteFile(upfileData.data.id); + this.showwarning('图片时政内容,请重新选择'); + return null; + case 20002: + wx.hideLoading(); + await gatewayHttpClient.deleteFile(upfileData.data.id); + this.showwarning('图片含有色情内容,请重新选择'); + return null; + case 20006: + wx.hideLoading(); + await gatewayHttpClient.deleteFile(upfileData.data.id); + this.showwarning('图片含有违法犯罪内容,请重新选择'); + return null; + case 21000: + wx.hideLoading(); + await gatewayHttpClient.deleteFile(upfileData.data.id); + this.showwarning('图片非法内容,请重新选择'); + return null; + default: + break + } + retryCount++; + } + + // 5次超时,返回失败 + wx.hideLoading(); + await gatewayHttpClient.deleteFile(upfileData.data.id)//删除图片 + wx.showToast({ + title: '图片检查超时,请重试', + icon: 'none' + }); + return null; + } catch (error) { + wx.hideLoading(); + console.error('图片检查失败:', error); + wx.showToast({ + title: '检查失败,请重试', + icon: 'none' + }); + return null; + } +}, +showwarning(txt){ + wx.showModal({ + title: '提示', + content: txt, + showCancel: false, + success: () => { + } + }); +} })) diff --git a/pages/index/index.js b/pages/index/index.js index c38b65f..8fd55dc 100644 --- a/pages/index/index.js +++ b/pages/index/index.js @@ -1,4 +1,5 @@ -import { injectPage, adManager } from '@jdmini/api' +import { injectPage, adManager,waitLogin } from '@jdmini/api' +const { requireLogin, httpClient, IS_DEV } = require('../../utils/index.js') Page(injectPage({ //showInterstitialAd:true// 插屏广告 @@ -10,5 +11,22 @@ Page(injectPage({ async showRewardedVideoAd() { const isEnded = await adManager.createAndShowRewardedVideoAd(this) console.log(isEnded) + }, + choosePhoto(){ + wx.chooseMedia({ + count: 1, + mediaType: ['image'], + sourceType: ['album'], + success(res) { + const tempFiles = res.tempFiles + console.log(tempFiles) + // tempFiles.forEach(file => { + // that.addWatermarkToPhoto(file.tempFilePath) + // }) + } + }) + } + + })) \ No newline at end of file diff --git a/pages/index/index.wxml b/pages/index/index.wxml index df29459..aa60595 100644 --- a/pages/index/index.wxml +++ b/pages/index/index.wxml @@ -1 +1 @@ -null \ No newline at end of file +首页 diff --git a/project.config.json b/project.config.json index 5427f07..960d67c 100644 --- a/project.config.json +++ b/project.config.json @@ -1,6 +1,6 @@ { "compileType": "miniprogram", - "libVersion": "trial", + "libVersion": "3.10.1", "packOptions": { "ignore": [], "include": [] diff --git a/project.private.config.json b/project.private.config.json index 53467e2..378c6b4 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -19,5 +19,6 @@ "checkInvalidKey": true, "ignoreDevUnusedFiles": true }, - "libVersion": "3.10.1" + "libVersion": "3.10.1", + "condition": {} } \ No newline at end of file diff --git a/utils/api.js b/utils/api.js new file mode 100644 index 0000000..5728e12 --- /dev/null +++ b/utils/api.js @@ -0,0 +1,12 @@ +/** + * API 接口定义 + * 所有业务 API 接口的统一管理 + */ + +const { get, post, put, del, uploadFile } = require('./request.js') + +/** + * =============================== + * 相关接口 + * =============================== + */ diff --git a/utils/auth.js b/utils/auth.js new file mode 100644 index 0000000..4d6437b --- /dev/null +++ b/utils/auth.js @@ -0,0 +1,269 @@ +/** + * 用户认证工具模块 + * 统一管理登录状态检查、登录跳转等 + */ + +/** + * 检查用户是否已登录 + * @returns {Boolean} 是否已登录 + */ +function isLoggedIn() { + const userId = wx.getStorageSync('userId') + const token = wx.getStorageSync('token') + return !!(userId && token) +} + +/** + * 获取用户信息 + * @returns {Object|null} 用户信息对象或null + */ +function getUserInfo() { + if (!isLoggedIn()) { + return null + } + + const userInfo = wx.getStorageSync('userInfo') + return userInfo || null +} + +/** + * 获取用户ID + * @returns {String|null} 用户ID或null + */ +function getUserId() { + return wx.getStorageSync('userId') || null +} + +/** + * 获取Token + * @returns {String|null} Token或null + */ +function getToken() { + return wx.getStorageSync('token') || null +} + +/** + * 检查登录状态,未登录则跳转登录页 + * @param {Object} options 配置项 + * @param {String} options.returnUrl 登录成功后返回的页面URL + * @param {String} options.returnType 返回类型: navigateTo/redirectTo/switchTab + * @param {Function} options.success 已登录时的回调 + * @param {Function} options.fail 未登录时的回调 + * @returns {Boolean} 是否已登录 + */ +function checkLogin(options = {}) { + const { + returnUrl = '', + returnType = 'navigateTo', + success, + fail + } = options + + if (isLoggedIn()) { + // 已登录 + success && success() + return true + } else { + // 未登录,跳转登录页 + fail && fail() + + // 保存返回信息 + if (returnUrl) { + wx.setStorageSync('returnTo', { + url: returnUrl, + type: returnType + }) + } + + wx.navigateTo({ + url: '/pages/login/login' + }) + + return false + } +} + +/** + * 要求登录后执行操作 + * @param {Function} callback 登录后执行的回调 + * @param {Object} options 配置项 + */ +function requireLogin(callback, options = {}) { + return checkLogin({ + ...options, + success: callback, + fail: () => { + wx.showToast({ + title: '请先登录', + icon: 'none', + duration: 2000 + }) + } + }) +} + +/** + * 保存用户信息到本地 + * @param {Object} data 用户数据 + * @param {String} data.userId 用户ID + * @param {String} data.token Token + * @param {Object} data.userInfo 用户信息 + */ +function saveUserData(data) { + const { userId, token, userInfo } = data + + if (userId) { + wx.setStorageSync('userId', userId) + } + + if (token) { + wx.setStorageSync('token', token) + } + + if (userInfo) { + wx.setStorageSync('userInfo', userInfo) + } +} + +/** + * 清除用户登录信息 + */ +function clearUserData() { + wx.removeStorageSync('userId') + wx.removeStorageSync('token') + wx.removeStorageSync('userInfo') +} + +/** + * 跳转到登录页面(带返回功能) + * 参考 checkin.js 的实现,显示弹窗确认后跳转登录页 + * @param {Object} options 配置项 + * @param {String} options.message 提示信息 + * @param {String} options.returnUrl 登录成功后返回的页面URL(不传则自动获取当前页面) + * @param {String} options.returnType 返回类型: navigateTo/redirectTo/switchTab + * @param {Boolean} options.showCancel 是否显示取消按钮 + * @param {Function} options.onCancel 取消回调 + */ +function navigateToLogin(options = {}) { + const { + message = '请先登录', + returnUrl = '', + returnType = 'navigateTo', + showCancel = true, + onCancel = null + } = options + + // 自动获取当前页面路径 + let finalReturnUrl = returnUrl + if (!finalReturnUrl) { + const pages = getCurrentPages() + if (pages.length > 0) { + const currentPage = pages[pages.length - 1] + finalReturnUrl = '/' + currentPage.route + // 添加页面参数 + if (currentPage.options && Object.keys(currentPage.options).length > 0) { + const params = Object.keys(currentPage.options) + .map(key => `${key}=${currentPage.options[key]}`) + .join('&') + finalReturnUrl += '?' + params + } + } + } + + wx.showModal({ + title: '提示', + content: message, + confirmText: '去登录', + cancelText: '返回', + showCancel: showCancel, + success: (res) => { + if (res.confirm) { + // 保存返回信息,登录成功后返回当前页面 + if (finalReturnUrl) { + wx.setStorageSync('returnTo', { + type: returnType, + url: finalReturnUrl + }) + } + wx.redirectTo({ + url: '/pages/login/login' + }) + } else { + // 点击取消 + if (onCancel) { + onCancel() + } + } + } + }) +} + +/** + * 退出登录 + * @param {Object} options 配置项 + * @param {String} options.redirectUrl 退出后跳转的页面 + * @param {Boolean} options.confirm 是否需要确认 + * @param {Function} options.success 成功回调 + */ +function logout(options = {}) { + const { + redirectUrl = '/pages/home/home', + confirm = true, + success + } = options + + const doLogout = () => { + clearUserData() + + wx.showToast({ + title: '已退出登录', + icon: 'success', + duration: 1500 + }) + + setTimeout(() => { + success && success() + + // 跳转到指定页面 + if (redirectUrl.startsWith('/pages/home/') || + redirectUrl.startsWith('/pages/index/') || + redirectUrl.startsWith('/pages/record/') || + redirectUrl.startsWith('/pages/settings/')) { + wx.reLaunch({ + url: redirectUrl + }) + } else { + wx.redirectTo({ + url: redirectUrl + }) + } + }, 1500) + } + + if (confirm) { + wx.showModal({ + title: '退出登录', + content: '确定要退出登录吗?', + success: (res) => { + if (res.confirm) { + doLogout() + } + } + }) + } else { + doLogout() + } +} + +module.exports = { + isLoggedIn, + getUserInfo, + getUserId, + getToken, + checkLogin, + requireLogin, + navigateToLogin, + saveUserData, + clearUserData, + logout +} diff --git a/utils/config.js b/utils/config.js new file mode 100644 index 0000000..48dd7bc --- /dev/null +++ b/utils/config.js @@ -0,0 +1,36 @@ +/** + * API 配置文件 + * 统一管理开发环境和生产环境的配置 + */ + +// 开发模式开关 +const IS_DEV = false + +// 开发环境配置 +const DEV_CONFIG = { + apiBase: 'http://localhost:3001/api', + timeout: 30000, + enableLog: true +} + +// 生产环境配置 +const PROD_CONFIG = { + apiBase: '/mp/jd-haiba', // jd-haiba是模块名 + timeout: 30000, + enableLog: false +} + +// 当前环境配置 +const CONFIG = IS_DEV ? DEV_CONFIG : PROD_CONFIG + +module.exports = { + IS_DEV, + API_BASE: CONFIG.apiBase, + TIMEOUT: CONFIG.timeout, + ENABLE_LOG: CONFIG.enableLog, + + // 切换环境方法(用于调试) + switchEnv: (isDev) => { + return isDev ? DEV_CONFIG : PROD_CONFIG + } +} diff --git a/utils/httpClient.js b/utils/httpClient.js new file mode 100644 index 0000000..36c1ddc --- /dev/null +++ b/utils/httpClient.js @@ -0,0 +1,88 @@ +/** + * 统一的 HTTP 客户端 + * 自动根据环境切换使用 gatewayHttpClient 或 HttpClient + */ + +import { gatewayHttpClient, HttpClient } from '@jdmini/api' +const { IS_DEV, API_BASE } = require('./config.js') + +// 开发环境使用 HttpClient +const devHttpClient = IS_DEV ? new HttpClient({ + baseURL: API_BASE, + timeout: 30000, +}) : null + +/** + * 统一的 HTTP 请求方法 + * @param {String} url - 请求路径(相对路径,不包含 baseURL) + * @param {String} method - 请求方法 GET/POST/PUT/DELETE + * @param {Object} data - 请求数据 + * @param {Object} options - 额外选项 + * @returns {Promise} + */ +function request(url, method = 'GET', data = {}, options = {}) { + // 确保 URL 以 / 开头 + const apiUrl = url.startsWith('/') ? url : `/${url}` + + if (IS_DEV) { + // 开发环境:使用 HttpClient + console.log(`[DEV] ${method} ${API_BASE}${apiUrl}`, data) + return devHttpClient.request(apiUrl, method, data, options) + } else { + // 生产环境:使用 gatewayHttpClient + // gatewayHttpClient 需要完整的路径:mp/jd-haiba/user/login + const fullUrl = `${API_BASE}${apiUrl}` + console.log(`[PROD] ${method} ${fullUrl}`, data) + + // gatewayHttpClient.request 的参数顺序:(url, method, data, options) + return gatewayHttpClient.request(fullUrl, method, data, options) + } +} + +/** + * GET 请求 + */ +function get(url, params = {}, options = {}) { + return request(url, 'GET', params, options) +} + +/** + * POST 请求 + */ +function post(url, data = {}, options = {}) { + return request(url, 'POST', data, options) +} + +/** + * PUT 请求 + */ +function put(url, data = {}, options = {}) { + return request(url, 'PUT', data, options) +} + +/** + * DELETE 请求 + */ +function del(url, data = {}, options = {}) { + return request(url, 'DELETE', data, options) +} + +/** + * 上传文件(暂时保留原有逻辑) + */ +function upload(filePath, formData = {}) { + return gatewayHttpClient.uploadFile(filePath,formData) +} + +module.exports = { + request, + get, + post, + put, + del, + upload, + + // 导出原始客户端供特殊场景使用 + gatewayHttpClient, + devHttpClient +} diff --git a/utils/index.js b/utils/index.js new file mode 100644 index 0000000..4922715 --- /dev/null +++ b/utils/index.js @@ -0,0 +1,27 @@ +/** + * Utils 统一导出 + * 方便各个页面统一引入 + */ + +const config = require('./config.js') +const request = require('./request.js') +const api = require('./api.js') +const auth = require('./auth.js') +const httpClient = require('./httpClient.js') + +module.exports = { + // 配置 + ...config, + + // 请求方法 + ...request, + + // API接口 + ...api, + + // 认证 + ...auth, + + // 统一 HTTP 客户端 + httpClient +} diff --git a/utils/request.js b/utils/request.js new file mode 100644 index 0000000..7d8a16b --- /dev/null +++ b/utils/request.js @@ -0,0 +1,278 @@ +/** + * 统一请求模块 + * 封装所有 API 请求,支持拦截器、错误处理等 + */ + +const { API_BASE, TIMEOUT, ENABLE_LOG, IS_DEV } = require('./config.js') +const httpClient = require('./httpClient.js') + +/** + * 统一请求方法 + * @param {Object} options 请求配置 + * @param {String} options.url 请求路径(相对路径,会自动拼接 API_BASE) + * @param {String} options.method 请求方法 GET/POST/PUT/DELETE + * @param {Object} options.data 请求数据 + * @param {Object} options.header 自定义请求头 + * @param {Boolean} options.showLoading 是否显示加载提示 + * @param {String} options.loadingText 加载提示文字 + * @param {Boolean} options.showError 是否显示错误提示 + * @returns {Promise} + */ +function request(options = {}) { + const { + url, + method = 'GET', + data = {}, + header = {}, + showLoading = false, + loadingText = '加载中...', + showError = true + } = options + + // 显示加载提示 + if (showLoading) { + wx.showLoading({ + title: loadingText, + mask: true + }) + } + + // 拼接完整URL + const fullUrl = url.startsWith('http') ? url : `${API_BASE}${url}` + + // 构建请求头 + const requestHeader = { + 'Content-Type': 'application/json', + ...header + } + + // 添加 token(如果存在) + const token = wx.getStorageSync('token') + if (token) { + requestHeader['Authorization'] = `Bearer ${token}` + } + + // 日志输出 + if (ENABLE_LOG) { + console.log('[API Request]', { + url: fullUrl, + method, + data, + header: requestHeader + }) + } + + return new Promise((resolve, reject) => { + wx.request({ + url: fullUrl, + method, + data, + header: requestHeader, + timeout: TIMEOUT, + success: (res) => { + // 隐藏加载提示 + if (showLoading) { + wx.hideLoading() + } + + // 日志输出 + if (ENABLE_LOG) { + console.log('[API Response]', res.data) + } + + // 统一处理响应 + const { statusCode, data: resData } = res + + // HTTP 状态码检查 + if (statusCode >= 200 && statusCode < 300) { + // 业务状态码检查 + if (resData.code === 200 || resData.success === true) { + resolve(resData) + } else { + // 业务错误 + const errorMsg = resData.msg || resData.message || '请求失败' + if (showError) { + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000 + }) + } + reject({ + code: resData.code, + msg: errorMsg, + data: resData + }) + } + } else { + // HTTP 错误 + const errorMsg = `服务器错误 (${statusCode})` + if (showError) { + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000 + }) + } + reject({ + code: statusCode, + msg: errorMsg, + data: res + }) + } + }, + fail: (err) => { + // 隐藏加载提示 + if (showLoading) { + wx.hideLoading() + } + + // 日志输出 + if (ENABLE_LOG) { + console.error('[API Error]', err) + } + + // 网络错误 + const errorMsg = err.errMsg || '网络请求失败' + if (showError) { + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000 + }) + } + + reject({ + code: -1, + msg: errorMsg, + data: err + }) + } + }) + }) +} + +/** + * GET 请求 + */ +function get(url, data = {}, options = {}) { + // 直接使用 httpClient,它会自动处理环境切换 + return httpClient.get(url, data, options) +} + +/** + * POST 请求 + */ +function post(url, data = {}, options = {}) { + // 直接使用 httpClient,它会自动处理环境切换 + return httpClient.post(url, data, options) +} + +/** + * PUT 请求 + */ +function put(url, data = {}, options = {}) { + // 直接使用 httpClient,它会自动处理环境切换 + return httpClient.put(url, data, options) +} + +/** + * DELETE 请求 + */ +function del(url, data = {}, options = {}) { + // 直接使用 httpClient,它会自动处理环境切换 + return httpClient.del(url, data, options) +} + +/** + * 上传文件 + * @param {String} url 上传地址 + * @param {String} filePath 文件路径 + * @param {Object} formData 额外的表单数据 + * @param {Object} options 其他配置 + */ +function uploadFile(filePath, formData = {}) { + const { + name = 'file', + showLoading = true, + loadingText = '上传中...', + showError = true + } = options + + if (showLoading) { + wx.showLoading({ + title: loadingText, + mask: true + }) + } + + const fullUrl = url.startsWith('http') ? url : `${API_BASE}${url}` + + // 构建请求头 + const header = {} + const token = wx.getStorageSync('token') + if (token) { + header['Authorization'] = `Bearer ${token}` + } + + return new Promise((resolve, reject) => { + wx.uploadFile({ + url: fullUrl, + filePath, + name, + formData, + header, + timeout: TIMEOUT, + success: (res) => { + if (showLoading) { + wx.hideLoading() + } + + const data = JSON.parse(res.data) + if (data.code === 200 || data.success === true) { + resolve(data) + } else { + const errorMsg = data.msg || data.message || '上传失败' + if (showError) { + wx.showToast({ + title: errorMsg, + icon: 'none' + }) + } + reject({ + code: data.code, + msg: errorMsg, + data + }) + } + }, + fail: (err) => { + if (showLoading) { + wx.hideLoading() + } + + const errorMsg = err.errMsg || '上传失败' + if (showError) { + wx.showToast({ + title: errorMsg, + icon: 'none' + }) + } + reject({ + code: -1, + msg: errorMsg, + data: err + }) + } + }) + }) +} + +module.exports = { + request, + get, + post, + put, + del, + uploadFile +}