This commit is contained in:
honghefly 2025-12-15 18:46:47 +08:00
parent 86e4c38663
commit 9a32fb8fb8
11 changed files with 867 additions and 4 deletions

134
app.js
View File

@ -4,9 +4,143 @@ App(injectApp()({
}, },
async onLaunch() { 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 // 等待登陆完成以后请求自动携带token
await waitLogin() await waitLogin()
// 三方登录本地缓存 // 三方登录本地缓存
console.log(wx.getStorageSync('jdwx-userinfo')) 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: () => {
}
});
}
})) }))

View File

@ -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({ Page(injectPage({
//showInterstitialAd:true// 插屏广告 //showInterstitialAd:true// 插屏广告
@ -10,5 +11,22 @@ Page(injectPage({
async showRewardedVideoAd() { async showRewardedVideoAd() {
const isEnded = await adManager.createAndShowRewardedVideoAd(this) const isEnded = await adManager.createAndShowRewardedVideoAd(this)
console.log(isEnded) 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)
// })
} }
})
}
})) }))

View File

@ -1 +1 @@
<view>null</view> <view>首页</view>

View File

@ -1,6 +1,6 @@
{ {
"compileType": "miniprogram", "compileType": "miniprogram",
"libVersion": "trial", "libVersion": "3.10.1",
"packOptions": { "packOptions": {
"ignore": [], "ignore": [],
"include": [] "include": []

View File

@ -19,5 +19,6 @@
"checkInvalidKey": true, "checkInvalidKey": true,
"ignoreDevUnusedFiles": true "ignoreDevUnusedFiles": true
}, },
"libVersion": "3.10.1" "libVersion": "3.10.1",
"condition": {}
} }

12
utils/api.js Normal file
View File

@ -0,0 +1,12 @@
/**
* API 接口定义
* 所有业务 API 接口的统一管理
*/
const { get, post, put, del, uploadFile } = require('./request.js')
/**
* ===============================
* 相关接口
* ===============================
*/

269
utils/auth.js Normal file
View File

@ -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
}

36
utils/config.js Normal file
View File

@ -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
}
}

88
utils/httpClient.js Normal file
View File

@ -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
}

27
utils/index.js Normal file
View File

@ -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
}

278
utils/request.js Normal file
View File

@ -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
}