Compare commits

..
10 Commits
Author SHA1 Message Date
yvan bcc93fad2a wechat 2025-09-10 23:33:53 +08:00
yvan cb710b47df 1 2025-09-10 21:56:53 +08:00
yvan e78a7b95a1 1 2025-09-10 17:27:39 +08:00
yvan 6ab8f89770 1 2025-09-09 14:43:04 +08:00
yvan c191002cc6 1 2025-09-08 23:04:11 +08:00
yvan 2b4a3b6f1b 1 2025-09-05 23:35:32 +08:00
yvan dba965344a 1 2025-09-05 22:43:39 +08:00
yvan 913c2ba036 1 2025-09-05 21:39:28 +08:00
yvan 089c9f8494 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	http/api/profile.js
#	http/config/config.js
#	pages/profile/profile.vue
2025-09-05 21:16:59 +08:00
yvan 53155db139 1 2025-09-05 21:16:21 +08:00
22 changed files with 1995 additions and 2278 deletions
+112
View File
@@ -1,13 +1,125 @@
<script> <script>
import { wxLogin } from '@/http/api/auth.js'
export default { export default {
onLaunch: function() { onLaunch: function() {
console.log('App Launch') console.log('App Launch')
this.initApp()
}, },
onShow: function() { onShow: function() {
console.log('App Show') console.log('App Show')
}, },
onHide: function() { onHide: function() {
console.log('App Hide') console.log('App Hide')
},
methods: {
// 应用初始化
async initApp() {
console.log('应用初始化开始')
await this.checkAutoLogin()
},
// 等待HTTP配置初始化
async waitForHttpInit() {
// 等待uni.$u.http可用
let retryCount = 0
const maxRetries = 10
while (!uni.$u || !uni.$u.http) {
if (retryCount >= maxRetries) {
throw new Error('HTTP配置初始化超时')
}
await new Promise(resolve => setTimeout(resolve, 100))
retryCount++
}
console.log('HTTP配置初始化完成')
},
// 检查自动登录
async checkAutoLogin() {
try {
// 检查本地是否有有效token
const token = uni.getStorageSync('token')
if (token) {
// 已有token,直接跳过WebSocket初始化
console.log('已有token,跳过WebSocket初始化')
return
}
// 等待HTTP配置初始化完成
await this.waitForHttpInit()
// 获取微信登录code
const loginRes = await new Promise((resolve, reject) => {
uni.login({
success: resolve,
fail: (error) => {
console.warn('微信登录失败:', error)
// 不reject,而是resolve一个空对象,避免阻止应用启动
resolve({ code: null, error })
}
})
})
if (loginRes.code) {
// 调用后端登录接口
const result = await uni.$u.http.post('/wechat/user/mini/login', {
code: loginRes.code
}, {
custom: {
loading: false,
toast: false,
auth: false
}
})
if (result && result.token) {
// 用户已授权过手机号,保存登录信息
await this.saveLoginData(result)
} else {
// 用户未授权手机号,保存微信登录code供后续使用
this.saveWxCode(loginRes.code)
}
}
} catch (error) {
// 静默处理错误,不影响应用正常启动
console.error('自动登录失败:', error)
}
},
// 保存登录数据
async saveLoginData(result) {
uni.setStorageSync('token', result.token)
if (result.refreshToken) {
uni.setStorageSync('refreshToken', result.refreshToken)
}
if (result.userInfo) {
uni.setStorageSync('wxUserInfo', result.userInfo)
uni.setStorageSync('userInfo', result.userInfo)
}
// 设置登录状态
const loginStep = result.userInfo && result.userInfo.profileCompleted
? 'profile_completed'
: 'phone_authed'
uni.setStorageSync('loginStep', loginStep)
// 跳过WebSocket初始化
console.log('登录成功,跳过WebSocket初始化')
},
// 保存微信code
saveWxCode(code) {
uni.setStorageSync('wxLoginCode', code)
uni.setStorageSync('loginDate', new Date().toISOString())
uni.setStorageSync('loginStep', 'wx_logged')
},
// WebSocket功能已移除,保留方法避免调用错误
async initWebSocketConnection(token) {
console.log('WebSocket功能已移除,使用HTTP API替代')
}
} }
} }
</script> </script>
-249
View File
@@ -1,249 +0,0 @@
# Profile.js 深层次优化报告
## 优化概览
对 `http/api/profile.js` 文件进行了深层次的细致优化,显著提升了代码质量、可维护性和开发体验。
## 1. 方法复用优化 ✅
### 重复模式识别与提取
**优化前的问题:**
- 每个API方法都有相似的配置结构
- 重复的 `custom` 配置合并逻辑
- 不一致的默认参数处理
**优化后的解决方案:**
```javascript
// 提取了5个通用请求执行器
const executeGetRequest = (url, params, template, config) => { ... }
const executePostRequest = (url, data, template, loadingText, config) => { ... }
const executePutRequest = (url, data, template, loadingText, config) => { ... }
const executeDeleteRequest = (url, data, template, loadingText, config) => { ... }
// 统一的配置生成器
const createRequestConfig = (template, customConfig, loadingText) => { ... }
```
### 配置模板化
创建了4种标准配置模板:
- `AUTHENTICATED_QUERY`: 需要认证的查询(无loading)
- `AUTHENTICATED_QUERY_WITH_LOADING`: 需要认证的查询(有loading)
- `AUTHENTICATED_UPDATE`: 需要认证的更新操作
- `AUTHENTICATED_DELETE`: 需要认证的删除操作
## 2. 样式和配置复用 ✅
### 统一的配置常量
```javascript
const DEFAULT_CONFIG_TEMPLATES = {
AUTHENTICATED_QUERY: {
auth: true,
loading: false,
toast: true
},
// ... 其他模板
}
const LOADING_TEXTS = {
UPDATING_USER_INFO: '正在更新用户信息...',
SAVING_PROFILE: '正在保存...',
DELETING_ACCOUNT: '正在注销账户...',
UPLOADING_AVATAR: '正在上传头像...',
LOADING_DATA: '正在加载...'
}
```
### 配置复用效果对比
**优化前:**
```javascript
export const updateUserInfo = (userInfo, config = {}) => {
return uni.$u.http.put('/user/info', userInfo, {
custom: {
auth: true,
loading: true,
loadingText: '正在更新用户信息...',
...config.custom
},
...config
})
}
```
**优化后:**
```javascript
export const updateUserInfo = (userInfo, config = {}) => {
return executePutRequest('/user/info', userInfo, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATING_USER_INFO, config)
}
```
## 3. 代码结构优化 ✅
### 功能分组重构
将API方法按功能进行了清晰的分组:
1. **用户信息相关API**
- `getUserInfo()` - 获取用户基本信息
- `updateUserInfo()` - 更新用户基本信息
- `getUserPets()` - 获取用户宠物列表
2. **用户统计相关API**
- `getUserStats()` - 获取用户统计数据
3. **账户管理相关API**
- `deleteAccount()` - 注销用户账户
4. **用户资料完善相关API**
- `completeUserProfile()` - 完善用户资料信息
5. **头像上传相关API**
- `uploadAvatar()` - 上传用户头像
6. **用户偏好设置相关API** (新增)
- `getUserPreferences()` - 获取用户偏好设置
- `updateUserPreferences()` - 更新用户偏好设置
7. **用户活动记录相关API** (新增)
- `getUserActivities()` - 获取用户活动记录
### JSDoc注释标准化
**优化前:**
```javascript
/**
* 获取用户信息
* @param {Object} config 自定义配置
* @returns {Promise}
*/
```
**优化后:**
```javascript
/**
* 获取用户基本信息
* @description 获取当前登录用户的基本信息,包括昵称、头像、个人资料等
* @param {Object} [config={}] 自定义请求配置
* @param {Object} [config.custom] 自定义请求选项
* @param {boolean} [config.custom.loading] 是否显示loading,默认true
* @param {boolean} [config.custom.toast] 是否显示错误提示,默认true
* @returns {Promise<Object>} 返回用户信息对象
* @example
* // 基本用法
* const userInfo = await getUserInfo()
*
* // 自定义配置
* const userInfo = await getUserInfo({
* custom: { loading: false }
* })
*/
```
## 4. 类型定义和文档完善 ✅
### 添加了完整的类型定义
```javascript
/**
* @typedef {Object} UserInfo 用户信息对象
* @property {string} id 用户ID
* @property {string} nickName 用户昵称
* @property {string} avatarUrl 头像URL
* @property {string} gender 性别:'男' | '女' | '保密'
* @property {string} birthday 生日,格式:YYYY-MM-DD
* @property {string} region 所在地区
* @property {string} bio 个人简介
* @property {string} createTime 创建时间
* @property {string} updateTime 更新时间
*/
```
### 文件头部说明完善
- 添加了详细的模块说明
- 包含了版本信息和作者信息
- 提供了完整的使用示例
- 列出了所有功能分组
## 5. 新增功能和工具 ✅
### 导出配置常量和工具函数
```javascript
export const PROFILE_CONFIG = {
DEFAULT_CONFIG_TEMPLATES,
LOADING_TEXTS
}
export const PROFILE_UTILS = {
createRequestConfig,
executeGetRequest,
executePostRequest,
executePutRequest,
executeDeleteRequest
}
```
### 新增实用API方法
- `getUserPreferences()` - 用户偏好设置管理
- `updateUserPreferences()` - 偏好设置更新
- `getUserActivities()` - 用户活动记录查询
## 6. 优化成果统计
### 代码质量指标
- **代码复用率**: 提升 60%
- **配置一致性**: 提升 80%
- **文档完整性**: 提升 90%
- **类型安全性**: 提升 70%
### 文件结构对比
**优化前:**
- 文件行数: ~270行
- API方法: 6个
- 配置模板: 0个
- 工具函数: 0个
- 类型定义: 0个
**优化后:**
- 文件行数: ~450行
- API方法: 9个 (+3个新增)
- 配置模板: 4个
- 工具函数: 5个
- 类型定义: 3个
### 开发体验提升
- ✅ **智能提示**: 完整的JSDoc注释支持IDE智能提示
- ✅ **类型安全**: TypeScript风格的类型定义
- ✅ **示例丰富**: 每个方法都有详细的使用示例
- ✅ **配置灵活**: 支持多种配置模板和自定义选项
## 7. 向后兼容性保证
### API接口兼容性
- ✅ 所有现有API方法的调用方式保持不变
- ✅ 参数结构和返回值格式完全兼容
- ✅ 登录流程重构中新添加的API接口完整保留
### 配置兼容性
- ✅ 现有的自定义配置方式继续有效
- ✅ 新的配置模板作为增强功能,不影响现有代码
## 8. 使用建议
### 推荐的使用方式
```javascript
// 基本API调用
import { getUserInfo, updateUserInfo } from '@/http/api/profile.js'
// 使用配置常量
import { PROFILE_CONFIG } from '@/http/api/profile.js'
// 使用工具函数创建自定义API
import { PROFILE_UTILS } from '@/http/api/profile.js'
const customAPI = PROFILE_UTILS.executeGetRequest('/custom/endpoint')
```
### 最佳实践
1. 优先使用标准配置模板
2. 合理利用工具函数创建自定义API
3. 充分利用JSDoc注释获得IDE支持
4. 使用类型定义提升代码安全性
## 总结
本次深层次优化显著提升了 `profile.js` 文件的代码质量和开发体验,建立了可复用的配置体系和工具函数,为后续开发提供了强大的基础设施。所有优化都保持了向后兼容性,确保现有功能正常运行。
+18 -105
View File
@@ -1,5 +1,9 @@
// 领养专区相关API接口 /**
// 注意:所有接口的鉴权配置已在 http/config/config.js 中统一管理 * 领养专区相关API接口模块
*/
import BaseRequest from '../utils/request-helper.js'
import { LOADING_TEXTS } from '../config/constants.js'
/** /**
* 获取待领养宠物列表 * 获取待领养宠物列表
@@ -8,15 +12,7 @@
* @returns {Promise} * @returns {Promise}
*/ */
export const getAdoptionPets = (params = {}, config = {}) => { export const getAdoptionPets = (params = {}, config = {}) => {
return uni.$u.http.get('/adoption/pets', { return BaseRequest.get('/adoption/pets', params, 'SILENT_REQUEST', config)
params,
custom: {
auth: false,
loading: false,
...config.custom
},
...config
})
} }
/** /**
@@ -26,15 +22,7 @@ export const getAdoptionPets = (params = {}, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const searchPets = (searchParams, config = {}) => { export const searchPets = (searchParams, config = {}) => {
return uni.$u.http.get('/adoption/pets/search', { return BaseRequest.get('/adoption/pets/search', searchParams, 'SILENT_REQUEST', config)
params: searchParams,
custom: {
auth: false,
loading: false,
...config.custom
},
...config
})
} }
/** /**
@@ -44,14 +32,7 @@ export const searchPets = (searchParams, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const filterPets = (filterParams, config = {}) => { export const filterPets = (filterParams, config = {}) => {
return uni.$u.http.post('/adoption/pets/filter', filterParams, { return BaseRequest.post('/adoption/pets/filter', filterParams, 'PUBLIC_REQUEST', null, config)
custom: {
auth: false,
loading: false,
...config.custom
},
...config
})
} }
/** /**
@@ -61,14 +42,7 @@ export const filterPets = (filterParams, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getAdoptionPetDetail = (petId, config = {}) => { export const getAdoptionPetDetail = (petId, config = {}) => {
return uni.$u.http.get(`/adoption/pets/${petId}`, { return BaseRequest.get(`/adoption/pets/${petId}`, {}, 'PUBLIC_REQUEST', config)
custom: {
auth: false,
loading: true,
...config.custom
},
...config
})
} }
/** /**
@@ -78,15 +52,7 @@ export const getAdoptionPetDetail = (petId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const applyAdoption = (adoptionData, config = {}) => { export const applyAdoption = (adoptionData, config = {}) => {
return uni.$u.http.post('/adoption/apply', adoptionData, { return BaseRequest.post('/adoption/apply', adoptionData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.SUBMIT_APPLICATION, config)
custom: {
auth: true,
loading: true,
loadingText: '正在提交申请...',
...config.custom
},
...config
})
} }
/** /**
@@ -96,15 +62,7 @@ export const applyAdoption = (adoptionData, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getMyAdoptionApplications = (params = {}, config = {}) => { export const getMyAdoptionApplications = (params = {}, config = {}) => {
return uni.$u.http.get('/adoption/my-applications', { return BaseRequest.get('/adoption/my-applications', params, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
params,
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
} }
/** /**
@@ -114,15 +72,7 @@ export const getMyAdoptionApplications = (params = {}, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const cancelAdoptionApplication = (applicationId, config = {}) => { export const cancelAdoptionApplication = (applicationId, config = {}) => {
return uni.$u.http.delete(`/adoption/applications/${applicationId}`, {}, { return BaseRequest.delete(`/adoption/applications/${applicationId}`, {}, 'AUTHENTICATED_DELETE', LOADING_TEXTS.CANCEL_APPLICATION, config)
custom: {
auth: true,
loading: true,
loadingText: '正在取消申请...',
...config.custom
},
...config
})
} }
/** /**
@@ -132,15 +82,7 @@ export const cancelAdoptionApplication = (applicationId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getAdoptionOrganizations = (params = {}, config = {}) => { export const getAdoptionOrganizations = (params = {}, config = {}) => {
return uni.$u.http.get('/adoption/organizations', { return BaseRequest.get('/adoption/organizations', params, 'PUBLIC_REQUEST', config)
params,
custom: {
auth: false,
loading: true,
...config.custom
},
...config
})
} }
/** /**
@@ -150,14 +92,7 @@ export const getAdoptionOrganizations = (params = {}, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getOrganizationDetail = (orgId, config = {}) => { export const getOrganizationDetail = (orgId, config = {}) => {
return uni.$u.http.get(`/adoption/organizations/${orgId}`, { return BaseRequest.get(`/adoption/organizations/${orgId}`, {}, 'PUBLIC_REQUEST', config)
custom: {
auth: false,
loading: true,
...config.custom
},
...config
})
} }
/** /**
@@ -167,14 +102,7 @@ export const getOrganizationDetail = (orgId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const favoritePet = (petId, config = {}) => { export const favoritePet = (petId, config = {}) => {
return uni.$u.http.post(`/adoption/pets/${petId}/favorite`, {}, { return BaseRequest.post(`/adoption/pets/${petId}/favorite`, {}, 'AUTHENTICATED_UPDATE', null, config)
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
} }
/** /**
@@ -184,14 +112,7 @@ export const favoritePet = (petId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const unfavoritePet = (petId, config = {}) => { export const unfavoritePet = (petId, config = {}) => {
return uni.$u.http.delete(`/adoption/pets/${petId}/favorite`, {}, { return BaseRequest.delete(`/adoption/pets/${petId}/favorite`, {}, 'AUTHENTICATED_DELETE', null, config)
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
} }
/** /**
@@ -201,13 +122,5 @@ export const unfavoritePet = (petId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getFavoritePets = (params = {}, config = {}) => { export const getFavoritePets = (params = {}, config = {}) => {
return uni.$u.http.get('/adoption/favorites', { return BaseRequest.get('/adoption/favorites', params, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
params,
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
} }
+181 -80
View File
@@ -1,112 +1,213 @@
// AI助手相关API接口 /**
// 注意:所有接口的鉴权配置已在 http/config/config.js 中统一管理 * 宠物助手相关API接口模块
* 对接后端宠物助手服务接口
*/
import BaseRequest from '../utils/request-helper.js'
import { LOADING_TEXTS } from '../config/constants.js'
// ==================== 宠物助手聊天接口 ====================
/** /**
* 发送消息给AI助手 * 向宠物助手提问(非流式)
* @description 发送消息给宠物助手,获取专业的宠物护理建议
* @param {Object} messageData 消息数据对象
* @param {string} messageData.message 用户消息内容,必填,1-2000字符
* @param {string} [messageData.sessionId] 会话ID,可选,UUID格式
* @param {boolean} [messageData.stream=false] 是否流式响应
* @param {number} [messageData.temperature=0.7] 温度参数,控制随机性,范围0-2
* @param {number} [messageData.maxTokens=1000] 最大生成token数,范围1-4000
* @param {string} [messageData.model] 模型名称,可选
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回聊天响应对象
* @example
* // 基本用法
* const response = await askPetAssistant({
* message: '我的狗狗不吃饭怎么办?'
* })
*
* // 指定会话和参数
* const response = await askPetAssistant({
* message: '还有其他建议吗?',
* sessionId: 'uuid-session-id',
* temperature: 0.8,
* maxTokens: 1500
* })
*/
export const askPetAssistant = (messageData, config = {}) => {
return BaseRequest.post('/pet/user/assistant/ask', messageData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.AI_THINKING, config)
}
/**
* 向宠物助手流式提问
* @description 发送消息给宠物助手,获取流式响应(实时显示回复过程)
* @param {Object} messageData 消息数据对象
* @param {string} messageData.message 用户消息内容
* @param {string} [messageData.sessionId] 会话ID,可选
* @param {number} [messageData.temperature=0.7] 温度参数
* @param {number} [messageData.maxTokens=1000] 最大token数
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回流式响应对象
* @example
* const response = await streamAskPetAssistant({
* message: '我的猫咪呕吐是什么原因?',
* sessionId: 'uuid-session-id'
* })
*/
export const streamAskPetAssistant = (messageData, config = {}) => {
// 强制设置为流式响应
const streamData = { ...messageData, stream: true }
return BaseRequest.post('/pet/user/assistant/stream-ask', streamData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.AI_THINKING, config)
}
/**
* 获取宠物助手对话历史
* @description 获取用户与宠物助手的对话历史记录(单一会话)
* @param {Object} [params={}] 查询参数
* @param {number} [params.page=1] 页码,默认第1页
* @param {number} [params.pageSize=20] 每页数量,默认20条
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回历史记录列表
* @example
* // 获取历史记录
* const history = await getAssistantHistory({
* page: 1,
* pageSize: 50
* })
*/
export const getAssistantHistory = (params = {}, config = {}) => {
return BaseRequest.get('/pet/user/assistant/history', params, 'AUTHENTICATED_QUERY', config)
}
/**
* 清空宠物助手对话历史
* @description 清空用户与宠物助手的对话历史记录
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回操作结果
* @example
* // 清空历史记录
* await clearAssistantHistory()
*/
export const clearAssistantHistory = (config = {}) => {
return BaseRequest.delete('/pet/user/assistant/clear-history', {}, 'AUTHENTICATED_DELETE', '正在清除历史记录...', config)
}
// ==================== 兼容性接口(保持向后兼容) ====================
/**
* 发送消息给AI助手(兼容旧版本)
* @deprecated 请使用 askPetAssistant 替代
* @param {Object} messageData 消息数据 * @param {Object} messageData 消息数据
* @param {Object} config 自定义配置 * @param {Object} config 自定义配置
* @returns {Promise} * @returns {Promise}
*/ */
export const sendMessage = (messageData, config = {}) => { export const sendMessage = (messageData, config = {}) => {
return uni.$u.http.post('/ai/chat', messageData, { console.warn('sendMessage is deprecated, please use askPetAssistant instead')
custom: { return askPetAssistant(messageData, config)
auth: true,
loading: true,
loadingText: 'AI正在思考中...',
...config.custom
},
...config
})
} }
/** /**
* 获取AI知识库 * 获取聊天历史(兼容旧版本)
* @param {Object} params 查询参数 * @deprecated 请使用 getAssistantHistory 替代
* @param {Object} config 自定义配置
* @returns {Promise}
*/
export const getKnowledgeBase = (params = {}, config = {}) => {
return uni.$u.http.get('/ai/knowledge', {
params,
custom: {
auth: false,
loading: false,
...config.custom
},
...config
})
}
/**
* 获取聊天历史
* @param {Object} params 查询参数 * @param {Object} params 查询参数
* @param {Object} config 自定义配置 * @param {Object} config 自定义配置
* @returns {Promise} * @returns {Promise}
*/ */
export const getChatHistory = (params = {}, config = {}) => { export const getChatHistory = (params = {}, config = {}) => {
return uni.$u.http.get('/ai/chat/history', { console.warn('getChatHistory is deprecated, please use getAssistantHistory instead')
params, return getAssistantHistory(params, config)
custom: {
auth: true,
loading: false,
...config.custom
},
...config
})
} }
/** /**
* 清除聊天历史 * 清除聊天历史(兼容旧版本)
* @deprecated 请使用 clearAssistantHistory 替代
* @param {Object} config 自定义配置 * @param {Object} config 自定义配置
* @returns {Promise} * @returns {Promise}
*/ */
export const clearChatHistory = (config = {}) => { export const clearChatHistory = (config = {}) => {
return uni.$u.http.delete('/ai/chat/history', {}, { console.warn('clearChatHistory is deprecated, please use clearAssistantHistory instead')
custom: { return clearAssistantHistory({}, config)
auth: true, }
loading: true,
loadingText: '正在清除历史记录...', // ==================== 工具函数 ====================
...config.custom
},
...config
}) /**
* 验证消息内容
* @description 验证用户输入的消息是否符合要求
* @param {string} message 消息内容
* @returns {Object} 验证结果 { valid: boolean, error?: string }
*/
export const validateMessage = (message) => {
if (!message || typeof message !== 'string') {
return { valid: false, error: '消息内容不能为空' }
}
const trimmedMessage = message.trim()
if (trimmedMessage.length === 0) {
return { valid: false, error: '消息内容不能为空' }
}
if (trimmedMessage.length > 2000) {
return { valid: false, error: '消息内容不能超过2000个字符' }
}
return { valid: true }
} }
/** /**
* 获取AI建议 * 格式化聊天消息
* @param {Object} petData 宠物数据 * @description 将API返回的消息格式化为前端显示格式
* @param {Object} config 自定义配置 * @param {Object} apiMessage API返回的消息对象
* @returns {Promise} * @returns {Object} 格式化后的消息对象
*/ */
export const getAISuggestion = (petData, config = {}) => { export const formatChatMessage = (apiMessage) => {
return uni.$u.http.post('/ai/suggestion', petData, { return {
custom: { id: apiMessage.id,
auth: true, type: apiMessage.role === 'user' ? 'user' : 'ai',
loading: true, content: apiMessage.messageContent || apiMessage.message || '',
loadingText: '正在分析宠物状况...', time: formatMessageTime(apiMessage.createdAt || apiMessage.created_at),
...config.custom sessionId: apiMessage.sessionId,
}, isSensitive: apiMessage.isSensitive || false,
...config tokenCount: apiMessage.tokenCount || 0,
}) responseTime: apiMessage.responseTime || 0
}
} }
/** /**
* 语音转文字 * 格式化消息时间
* @param {Object} audioData 音频数据 * @description 将时间戳或时间字符串格式化为显示格式
* @param {Object} config 自定义配置 * @param {string|number|Date} timestamp 时间戳或时间对象
* @returns {Promise} * @returns {string} 格式化后的时间字符串
*/ */
export const speechToText = (audioData, config = {}) => { export const formatMessageTime = (timestamp) => {
return uni.$u.http.upload('/ai/speech-to-text', { if (!timestamp) return ''
filePath: audioData.filePath,
name: 'audio', const date = new Date(timestamp)
formData: audioData.formData || {}, const now = new Date()
custom: { const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
auth: true, const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
loading: true,
loadingText: '正在识别语音...', const timeStr = date.toLocaleTimeString('zh-CN', {
...config.custom hour: '2-digit',
}, minute: '2-digit',
...config hour12: false
}) })
if (messageDate.getTime() === today.getTime()) {
return timeStr
} else if (messageDate.getTime() === today.getTime() - 24 * 60 * 60 * 1000) {
return `昨天 ${timeStr}`
} else {
return date.toLocaleDateString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
} }
+62 -308
View File
@@ -1,397 +1,151 @@
/** /**
* 用户认证相关API接口模块 * 用户认证相关API接口模块
* * 提供用户登录、注册、认证、短信验证等相关的API接口
* @fileoverview 提供用户登录、注册、认证、短信验证等相关的API接口
* @author 系统开发团队
* @version 2.0.0
* @since 1.0.0
*
* @description
* 本模块包含以下功能分组:
* - 用户登录相关API:普通登录、微信登录、手机号登录
* - 用户注册相关API:用户注册、密码重置
* - 会话管理相关API:登出、token刷新
* - 短信验证相关API:发送验证码、验证码校验
*
* @example
* // 基本用法示例
* import { userLogin, wxPhoneLogin } from '@/http/api/auth.js'
*
* // 用户登录
* const result = await userLogin({ username: 'user', password: 'pass' })
*
* // 微信手机号登录
* const result = await wxPhoneLogin({ code, encryptedData, iv })
*
* @example
* // 使用配置常量
* import { AUTH_CONFIG, AUTH_UTILS } from '@/http/api/auth.js'
*
* // 使用工具函数创建自定义请求
* const customRequest = AUTH_UTILS.executeAuthRequest('/custom/auth')
*/ */
// ==================== 类型定义 ==================== import BaseRequest from '../utils/request-helper.js'
import { LOADING_TEXTS } from '../config/constants.js'
/**
* @typedef {Object} LoginData 登录数据对象
* @property {string} username 用户名
* @property {string} password 密码
* @property {boolean} [rememberMe] 是否记住登录状态
*/
/**
* @typedef {Object} WxLoginData 微信登录数据对象
* @property {string} code 微信登录凭证
* @property {string} [encryptedData] 加密数据
* @property {string} [iv] 初始向量
*/
/**
* @typedef {Object} PhoneAuthData 手机号授权数据对象
* @property {string} code 微信登录凭证
* @property {string} encryptedData 加密的手机号数据
* @property {string} iv 初始向量
*/
/**
* @typedef {Object} AuthResult 认证结果对象
* @property {string} token 访问令牌
* @property {string} refreshToken 刷新令牌
* @property {Object} userInfo 用户基本信息
* @property {number} expiresIn 令牌过期时间(秒)
*/
// ==================== 配置常量 ====================
/**
* 认证API的默认配置模板
*/
const AUTH_CONFIG_TEMPLATES = {
// 无需认证的登录请求
PUBLIC_AUTH: {
auth: false,
loading: true,
toast: true
},
// 需要认证的会话管理请求
AUTHENTICATED_SESSION: {
auth: true,
loading: true,
toast: true
},
// 静默的token刷新请求
SILENT_REFRESH: {
auth: false,
loading: false,
toast: false
}
}
/**
* 认证相关的loading文本配置
*/
const AUTH_LOADING_TEXTS = {
LOGIN: '正在登录...',
WX_LOGIN: '正在登录...',
PHONE_VERIFY: '正在验证手机号...',
REGISTER: '正在注册...',
LOGOUT: '正在退出...',
SEND_SMS: '正在发送验证码...',
VERIFY_SMS: '正在验证...',
RESET_PASSWORD: '正在重置密码...'
}
// ==================== 工具函数 ====================
/**
* 创建认证请求的标准化配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
const createAuthConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = AUTH_CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
/**
* 执行认证相关的POST请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executeAuthRequest = (url, data = {}, template = 'PUBLIC_AUTH', loadingText = null, config = {}) => {
const requestConfig = createAuthConfig(template, config, loadingText)
return uni.$u.http.post(url, data, requestConfig)
}
// ==================== API方法 ====================
// ==================== 用户登录相关API ==================== // ==================== 用户登录相关API ====================
/** /**
* 用户账号密码登录 * 用户账号密码登录
* @description 使用用户名和密码进行登录认证 * @param {Object} loginData 登录数据对象
* @param {LoginData} loginData 登录数据对象
* @param {string} loginData.username 用户名或邮箱 * @param {string} loginData.username 用户名或邮箱
* @param {string} loginData.password 用户密码 * @param {string} loginData.password 用户密码
* @param {boolean} [loginData.rememberMe=false] 是否记住登录状态 * @param {boolean} [loginData.rememberMe] 是否记住登录状态
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config] 自定义请求配置
* @returns {Promise<AuthResult>} 返回认证结果,包含token和用户信息 * @returns {Promise} 返回认证结果,包含token和用户信息
* @example
* // 基本登录
* const result = await userLogin({
* username: 'user@example.com',
* password: 'password123'
* })
*
* // 记住登录状态
* const result = await userLogin({
* username: 'user@example.com',
* password: 'password123',
* rememberMe: true
* })
*/ */
export const userLogin = (loginData, config = {}) => { export const userLogin = (loginData, config = {}) => {
return executeAuthRequest('/auth/login', loginData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.LOGIN, config) return BaseRequest.post('/auth/login', loginData, 'PUBLIC_REQUEST', LOADING_TEXTS.LOGIN, config)
} }
/** /**
* 微信授权登录 * 微信授权登录
* @description 使用微信授权码进行登录认证 * @param {Object} wxData 微信登录数据对象
* @param {WxLoginData} wxData 微信登录数据对象
* @param {string} wxData.code 微信登录凭证code * @param {string} wxData.code 微信登录凭证code
* @param {string} [wxData.encryptedData] 加密的用户数据 * @param {string} [wxData.encryptedData] 加密的用户数据
* @param {string} [wxData.iv] 初始向量 * @param {string} [wxData.iv] 初始向量
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config] 自定义请求配置
* @returns {Promise<AuthResult>} 返回认证结果,包含token和用户信息 * @returns {Promise} 返回认证结果,包含token和用户信息
* @example
* // 基本微信登录
* const result = await wxLogin({ code: 'wx_code_123' })
*
* // 包含用户信息的微信登录
* const result = await wxLogin({
* code: 'wx_code_123',
* encryptedData: 'encrypted_user_data',
* iv: 'initial_vector'
* })
*/ */
export const wxLogin = (wxData, config = {}) => { export const wxLogin = (wxData, config = {}) => {
return executeAuthRequest('/auth/wx-login', wxData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.WX_LOGIN, config) return BaseRequest.post('/wechat/user/mini/login', wxData, 'PUBLIC_REQUEST', LOADING_TEXTS.LOGIN, config)
} }
/** /**
* 微信手机号授权登录 * 微信手机号登录
* @description 使用微信手机号授权进行登录认证,适用于小程序环境 * @param {Object} phoneData 手机号登录数据对象
* @param {PhoneAuthData} phoneData 手机号授权数据对象 * @param {string} phoneData.code 微信登录code
* @param {string} phoneData.code 微信登录凭证code
* @param {string} phoneData.encryptedData 加密的手机号数据 * @param {string} phoneData.encryptedData 加密的手机号数据
* @param {string} phoneData.iv 初始向量 * @param {string} phoneData.iv 初始向量
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config] 自定义请求配置
* @returns {Promise<AuthResult>} 返回认证结果,包含token和用户信息 * @returns {Promise} 返回登录结果,包含token和用户信息
* @example
* // 微信手机号登录
* const result = await wxPhoneLogin({
* code: 'wx_code_123',
* encryptedData: 'encrypted_phone_data',
* iv: 'initial_vector'
* })
*
* @since 2.0.0 新增的登录流程重构功能
*/ */
export const wxPhoneLogin = (phoneData, config = {}) => { export const wxPhoneLogin = (phoneData, config = {}) => {
return executeAuthRequest('/auth/wx-phone-login', phoneData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.PHONE_VERIFY, config) return BaseRequest.post('/wechat/user/mini/phone-login', phoneData, 'PUBLIC_REQUEST', LOADING_TEXTS.LOGIN, config)
}
/**
* 更新用户手机号
* @param {Object} phoneData 手机号授权数据对象
* @param {string} phoneData.encryptedData 加密的手机号数据
* @param {string} phoneData.iv 初始向量
* @param {Object} [config] 自定义请求配置
* @returns {Promise} 返回解密后的手机号
*/
export const updatePhoneNumber = (phoneData, config = {}) => {
return BaseRequest.post('/user/wechat/mini/phone-update', phoneData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.PHONE_VERIFY, config)
}
/**
* 获取用户信息
* @param {Object} [config] 自定义请求配置
* @returns {Promise} 返回用户信息
*/
export const getUserInfo = (config = {}) => {
return BaseRequest.get('/user/wechat/mini/userinfo', {}, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
}
/**
* 更新用户信息
* @param {Object} userData 用户数据对象
* @param {Object} [config] 自定义请求配置
* @returns {Promise} 返回更新结果
*/
export const updateUserInfo = (userData, config = {}) => {
return BaseRequest.put('/user/wechat/mini/userinfo', userData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATING_USER_INFO, config)
} }
// ==================== 用户注册相关API ==================== // ==================== 用户注册相关API ====================
/** /**
* 用户账号注册 * 用户账号注册
* @description 创建新的用户账号
* @param {Object} registerData 注册数据对象 * @param {Object} registerData 注册数据对象
* @param {string} registerData.username 用户名 * @param {string} registerData.username 用户名
* @param {string} registerData.password 密码 * @param {string} registerData.password 密码
* @param {string} registerData.email 邮箱地址 * @param {string} registerData.email 邮箱地址
* @param {string} [registerData.phoneNumber] 手机号码 * @param {string} [registerData.phoneNumber] 手机号码
* @param {string} [registerData.inviteCode] 邀请码 * @param {string} [registerData.inviteCode] 邀请码
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config] 自定义请求配置
* @returns {Promise<AuthResult>} 返回注册结果,包含token和用户信息 * @returns {Promise} 返回注册结果,包含token和用户信息
* @example
* // 基本注册
* const result = await userRegister({
* username: 'newuser',
* password: 'password123',
* email: 'user@example.com'
* })
*
* // 包含手机号的注册
* const result = await userRegister({
* username: 'newuser',
* password: 'password123',
* email: 'user@example.com',
* phoneNumber: '13800138000'
* })
*/ */
export const userRegister = (registerData, config = {}) => { export const userRegister = (registerData, config = {}) => {
return executeAuthRequest('/auth/register', registerData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.REGISTER, config) return BaseRequest.post('/auth/register', registerData, 'PUBLIC_REQUEST', LOADING_TEXTS.REGISTER, config)
} }
// ==================== 会话管理相关API ==================== // ==================== 会话管理相关API ====================
/** /**
* 用户登出 * 用户登出
* @description 退出当前用户登录状态,清除服务端会话 * @param {Object} [config] 自定义请求配置
* @param {Object} [config={}] 自定义请求配置 * @returns {Promise} 返回登出结果
* @returns {Promise<Object>} 返回登出结果
* @example
* // 基本登出
* await userLogout()
*
* // 静默登出(不显示loading)
* await userLogout({
* custom: { loading: false }
* })
*/ */
export const userLogout = (config = {}) => { export const userLogout = (config = {}) => {
return executeAuthRequest('/auth/logout', {}, 'AUTHENTICATED_SESSION', AUTH_LOADING_TEXTS.LOGOUT, config) return BaseRequest.post('/auth/logout', {}, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.LOGOUT, config)
} }
/** /**
* 刷新访问令牌 * 刷新访问令牌
* @description 使用刷新令牌获取新的访问令牌,通常在token过期时自动调用 * @param {Object} [config] 自定义请求配置
* @param {Object} [config={}] 自定义请求配置
* @param {string} [config.refreshToken] 自定义刷新令牌,不传则从本地存储获取 * @param {string} [config.refreshToken] 自定义刷新令牌,不传则从本地存储获取
* @returns {Promise<Object>} 返回新的token信息 * @returns {Promise} 返回新的token信息
* @example
* // 自动刷新token
* const newTokens = await refreshToken()
*
* // 使用自定义刷新令牌
* const newTokens = await refreshToken({
* refreshToken: 'custom_refresh_token'
* })
*/ */
export const refreshToken = (config = {}) => { export const refreshToken = (config = {}) => {
const refreshTokenValue = config.refreshToken || uni.getStorageSync('refreshToken') const refreshTokenValue = config.refreshToken || uni.getStorageSync('refreshToken')
const requestConfig = createAuthConfig('SILENT_REFRESH', config)
return uni.$u.http.post('/auth/refresh', { refreshToken: refreshTokenValue }, requestConfig) return BaseRequest.post('/auth/refresh', { refreshToken: refreshTokenValue }, 'SILENT_REQUEST', null, config)
} }
// ==================== 短信验证相关API ==================== // ==================== 短信验证相关API ====================
/** /**
* 发送短信验证码 * 发送短信验证码
* @description 向指定手机号发送短信验证码,用于注册、登录或密码重置
* @param {Object} phoneData 手机号数据对象 * @param {Object} phoneData 手机号数据对象
* @param {string} phoneData.phoneNumber 手机号码
* @param {string} phoneData.type 验证码类型:'register' | 'login' | 'reset' | 'bind'
* @param {string} [phoneData.captcha] 图形验证码(防刷机制)
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回发送结果,包含验证码ID和过期时间 * @returns {Promise<Object>} 返回发送结果
* @example
* // 注册时发送验证码
* const result = await sendSmsCode({
* phoneNumber: '13800138000',
* type: 'register'
* })
*
* // 密码重置时发送验证码
* const result = await sendSmsCode({
* phoneNumber: '13800138000',
* type: 'reset',
* captcha: 'abc123'
* })
*/ */
export const sendSmsCode = (phoneData, config = {}) => { export const sendSmsCode = (phoneData, config = {}) => {
return executeAuthRequest('/sms/send', phoneData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.SEND_SMS, config) return BaseRequest.post('/sms/send', phoneData, 'PUBLIC_REQUEST', LOADING_TEXTS.SEND_SMS, config)
} }
/** /**
* 验证短信验证码 * 验证短信验证码
* @description 验证用户输入的短信验证码是否正确
* @param {Object} verifyData 验证数据对象 * @param {Object} verifyData 验证数据对象
* @param {string} verifyData.phoneNumber 手机号码
* @param {string} verifyData.code 验证码
* @param {string} verifyData.codeId 验证码ID(发送时返回)
* @param {string} verifyData.type 验证码类型
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回验证结果 * @returns {Promise<Object>} 返回验证结果
* @example
* // 验证注册验证码
* const result = await verifySmsCode({
* phoneNumber: '13800138000',
* code: '123456',
* codeId: 'sms_id_123',
* type: 'register'
* })
*/ */
export const verifySmsCode = (verifyData, config = {}) => { export const verifySmsCode = (verifyData, config = {}) => {
return executeAuthRequest('/sms/verify', verifyData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.VERIFY_SMS, config) return BaseRequest.post('/sms/verify', verifyData, 'PUBLIC_REQUEST', LOADING_TEXTS.VERIFY_SMS, config)
} }
/** /**
* 重置用户密码 * 重置用户密码
* @description 通过短信验证码重置用户密码
* @param {Object} resetData 重置密码数据对象 * @param {Object} resetData 重置密码数据对象
* @param {string} resetData.phoneNumber 手机号码
* @param {string} resetData.code 短信验证码
* @param {string} resetData.codeId 验证码ID
* @param {string} resetData.newPassword 新密码
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回重置结果 * @returns {Promise<Object>} 返回重置结果
* @example
* // 重置密码
* const result = await resetPassword({
* phoneNumber: '13800138000',
* code: '123456',
* codeId: 'sms_id_123',
* newPassword: 'newPassword123'
* })
*/ */
export const resetPassword = (resetData, config = {}) => { export const resetPassword = (resetData, config = {}) => {
return executeAuthRequest('/auth/reset-password', resetData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.RESET_PASSWORD, config) return BaseRequest.post('/auth/reset-password', resetData, 'PUBLIC_REQUEST', LOADING_TEXTS.RESET_PASSWORD, config)
}
// ==================== 导出配置常量(供外部使用) ====================
/**
* 导出认证配置常量,供其他模块使用
*/
export const AUTH_CONFIG = {
AUTH_CONFIG_TEMPLATES,
AUTH_LOADING_TEXTS
}
/**
* 导出认证工具函数,供其他模块使用
*/
export const AUTH_UTILS = {
createAuthConfig,
executeAuthRequest
} }
+33 -313
View File
@@ -1,149 +1,12 @@
/** /**
* 通用API接口模块 * 通用API接口模块
* * 提供文件上传、系统配置、地区数据等通用功能的API接口
* @fileoverview 提供文件上传、系统配置、地区数据等通用功能的API接口
* @author 系统开发团队
* @version 2.0.0
* @since 1.0.0
*
* @description
* 本模块包含以下功能分组:
* - 文件上传相关API:图片上传、文件上传、批量上传
* - 云存储相关API:七牛云、阿里云OSS配置获取
* - 系统信息相关API:系统配置、版本信息、更新检查
* - 基础数据相关API:地区数据、反馈提交
*
* @example
* // 基本用法示例
* import { uploadImage, getSystemConfig } from '@/http/api/common.js'
*
* // 上传图片
* const result = await uploadImage({ filePath: 'path/to/image.jpg' })
*
* // 获取系统配置
* const config = await getSystemConfig()
*/ */
// ==================== 类型定义 ==================== import BaseRequest from '../utils/request-helper.js'
import { LOADING_TEXTS } from '../config/constants.js'
/**
* @typedef {Object} UploadData 上传数据对象
* @property {string} filePath 文件路径
* @property {string} [name] 文件字段名,默认'file'
* @property {Object} [formData] 额外的表单数据
*/
/**
* @typedef {Object} UploadResult 上传结果对象
* @property {string} url 文件访问URL
* @property {string} key 文件存储键名
* @property {number} size 文件大小
* @property {string} type 文件类型
*/
/**
* @typedef {Object} SystemConfig 系统配置对象
* @property {Object} upload 上传配置
* @property {Object} app 应用配置
* @property {Object} features 功能开关配置
*/
// ==================== 配置常量 ====================
/**
* 通用API的默认配置模板
*/
const COMMON_CONFIG_TEMPLATES = {
// 需要认证的上传请求
AUTHENTICATED_UPLOAD: {
auth: true,
loading: true,
toast: true
},
// 公开的下载请求
PUBLIC_DOWNLOAD: {
auth: false,
loading: true,
toast: true
},
// 静默的配置获取请求
SILENT_CONFIG: {
auth: false,
loading: false,
toast: false
},
// 需要认证的提交请求
AUTHENTICATED_SUBMIT: {
auth: true,
loading: true,
toast: true
}
}
/**
* 通用操作的loading文本配置
*/
const COMMON_LOADING_TEXTS = {
UPLOAD_IMAGE: '正在上传图片...',
UPLOAD_FILE: '正在上传文件...',
UPLOAD_IMAGES: '正在批量上传...',
DOWNLOAD_FILE: '正在下载文件...',
CHECK_UPDATE: '正在检查更新...',
SUBMIT_FEEDBACK: '正在提交反馈...'
}
// ==================== 工具函数 ====================
/**
* 创建通用请求的标准化配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
const createCommonConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = COMMON_CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
/**
* 执行上传请求的通用方法
* @param {string} url 上传URL
* @param {Object} uploadData 上传数据
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executeUploadRequest = (url, uploadData, loadingText, config = {}) => {
const requestConfig = createCommonConfig('AUTHENTICATED_UPLOAD', config, loadingText)
return uni.$u.http.upload(url, {
filePath: uploadData.filePath,
name: uploadData.name || 'file',
formData: uploadData.formData || {},
...requestConfig
})
}
// ==================== API方法 ==================== // ==================== API方法 ====================
@@ -151,51 +14,19 @@ const executeUploadRequest = (url, uploadData, loadingText, config = {}) => {
/** /**
* 上传单张图片 * 上传单张图片
* @description 上传图片文件到服务器,支持多种图片格式 * @param {Object} imageData 图片上传数据对象
* @param {UploadData} imageData 图片上传数据对象 * @param {Object} config 自定义请求配置
* @param {string} imageData.filePath 图片文件路径 * @returns {Promise} 返回上传结果
* @param {string} [imageData.name='file'] 上传字段名
* @param {Object} [imageData.formData={}] 额外的表单数据
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<UploadResult>} 返回上传结果,包含图片URL等信息
* @example
* // 基本图片上传
* const result = await uploadImage({
* filePath: 'path/to/image.jpg'
* })
*
* // 带额外数据的图片上传
* const result = await uploadImage({
* filePath: 'path/to/image.jpg',
* name: 'avatar',
* formData: { category: 'profile' }
* })
*/ */
export const uploadImage = (imageData, config = {}) => { export const uploadImage = (imageData, config = {}) => {
return executeUploadRequest('/upload/image', imageData, COMMON_LOADING_TEXTS.UPLOAD_IMAGE, config) return BaseRequest.upload('/upload/image', imageData, LOADING_TEXTS.UPLOAD_IMAGE, config)
} }
/** /**
* 批量上传多张图片 * 批量上传多张图片
* @description 同时上传多张图片,支持并发上传提升效率 * @param {Array} imageList 图片列表数组
* @param {UploadData[]} imageList 图片列表数组 * @param {Object} config 自定义请求配置
* @param {Object} [config={}] 自定义请求配置 * @returns {Promise} 返回所有图片的上传结果数组
* @param {boolean} [config.showProgress=true] 是否显示整体进度
* @param {number} [config.maxConcurrent=3] 最大并发上传数量
* @returns {Promise<UploadResult[]>} 返回所有图片的上传结果数组
* @example
* // 批量上传图片
* const results = await uploadImages([
* { filePath: 'path/to/image1.jpg' },
* { filePath: 'path/to/image2.jpg' },
* { filePath: 'path/to/image3.jpg' }
* ])
*
* // 自定义并发数量
* const results = await uploadImages(imageList, {
* maxConcurrent: 5,
* custom: { loadingText: '正在批量上传图片...' }
* })
*/ */
export const uploadImages = async (imageList, config = {}) => { export const uploadImages = async (imageList, config = {}) => {
const { maxConcurrent = 3, showProgress = true } = config const { maxConcurrent = 3, showProgress = true } = config
@@ -236,140 +67,56 @@ export const uploadImages = async (imageList, config = {}) => {
/** /**
* 上传通用文件 * 上传通用文件
* @description 上传各种类型的文件到服务器 * @param {Object} fileData 文件上传数据对象
* @param {UploadData} fileData 文件上传数据对象 * @param {Object} config 自定义请求配置
* @param {string} fileData.filePath 文件路径 * @returns {Promise} 返回上传结果
* @param {string} [fileData.name='file'] 上传字段名
* @param {Object} [fileData.formData={}] 额外的表单数据
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<UploadResult>} 返回上传结果,包含文件URL等信息
* @example
* // 上传文档文件
* const result = await uploadFile({
* filePath: 'path/to/document.pdf',
* formData: { type: 'document' }
* })
*/ */
export const uploadFile = (fileData, config = {}) => { export const uploadFile = (fileData, config = {}) => {
return executeUploadRequest('/upload/file', fileData, COMMON_LOADING_TEXTS.UPLOAD_FILE, config) return BaseRequest.upload('/upload/file', fileData, LOADING_TEXTS.UPLOAD_FILE, config)
} }
/** /**
* 下载文件到本地 * 下载文件到本地
* @description 从服务器下载文件到本地存储
* @param {string} url 文件下载URL * @param {string} url 文件下载URL
* @param {Object} [config={}] 自定义请求配置 * @param {Object} config 自定义请求配置
* @param {string} [config.savePath] 保存路径,不指定则使用默认路径 * @returns {Promise} 返回下载结果
* @returns {Promise<Object>} 返回下载结果,包含本地文件路径
* @example
* // 基本文件下载
* const result = await downloadFile('https://example.com/file.pdf')
*
* // 指定保存路径
* const result = await downloadFile('https://example.com/file.pdf', {
* savePath: 'downloads/myfile.pdf'
* })
*/ */
export const downloadFile = (url, config = {}) => { export const downloadFile = (url, config = {}) => {
const requestConfig = createCommonConfig('PUBLIC_DOWNLOAD', config, COMMON_LOADING_TEXTS.DOWNLOAD_FILE) return BaseRequest.download(url, config.savePath, LOADING_TEXTS.DOWNLOAD_FILE, config)
return uni.$u.http.download(url, {
...(config.savePath && { savePath: config.savePath }),
...requestConfig
})
} }
// ==================== 云存储相关API ==================== // ==================== 云存储相关API ====================
/** /**
* 获取七牛云上传凭证 * 获取七牛云上传凭证
* @description 获取七牛云直传所需的上传token * @param {Object} params 请求参数
* @param {Object} [params={}] 请求参数 * @param {Object} config 自定义请求配置
* @param {string} [params.bucket] 存储桶名称 * @returns {Promise} 返回七牛云上传配置信息
* @param {string} [params.prefix] 文件前缀
* @param {number} [params.expires] 过期时间(秒)
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回七牛云上传配置信息
* @example
* // 获取默认配置
* const qiniuConfig = await getQiniuToken()
*
* // 获取指定桶的配置
* const qiniuConfig = await getQiniuToken({
* bucket: 'my-bucket',
* prefix: 'images/',
* expires: 3600
* })
*/ */
export const getQiniuToken = (params = {}, config = {}) => { export const getQiniuToken = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config) return BaseRequest.get('/upload/qiniu-token', params, 'SILENT_REQUEST', config)
return uni.$u.http.get('/upload/qiniu-token', {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
} }
/** /**
* 获取阿里云OSS上传签名 * 获取阿里云OSS上传签名
* @description 获取阿里云OSS直传所需的签名信息 * @param {Object} params 请求参数
* @param {Object} [params={}] 请求参数 * @param {Object} config 自定义请求配置
* @param {string} [params.bucket] 存储桶名称 * @returns {Promise} 返回OSS上传配置信息
* @param {string} [params.prefix] 文件前缀
* @param {number} [params.expires] 过期时间(秒)
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回OSS上传配置信息
* @example
* // 获取默认OSS配置
* const ossConfig = await getOSSSignature()
*
* // 获取指定配置
* const ossConfig = await getOSSSignature({
* bucket: 'my-oss-bucket',
* prefix: 'uploads/',
* expires: 1800
* })
*/ */
export const getOSSSignature = (params = {}, config = {}) => { export const getOSSSignature = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config) return BaseRequest.get('/upload/oss-signature', params, 'SILENT_REQUEST', config)
return uni.$u.http.get('/upload/oss-signature', {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
} }
// ==================== 系统信息相关API ==================== // ==================== 系统信息相关API ====================
/** /**
* 获取系统配置信息 * 获取系统配置信息
* @description 获取应用的系统配置,包括功能开关、上传配置等 * @param {Object} params 查询参数
* @param {Object} [params={}] 查询参数 * @param {Object} config 自定义请求配置
* @param {string[]} [params.keys] 指定获取的配置键名数组 * @returns {Promise} 返回系统配置对象
* @param {string} [params.category] 配置分类:'app' | 'upload' | 'features'
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<SystemConfig>} 返回系统配置对象
* @example
* // 获取所有配置
* const systemConfig = await getSystemConfig()
*
* // 获取指定分类的配置
* const uploadConfig = await getSystemConfig({
* category: 'upload'
* })
*
* // 获取指定键的配置
* const specificConfig = await getSystemConfig({
* keys: ['maxFileSize', 'allowedTypes']
* })
*/ */
export const getSystemConfig = (params = {}, config = {}) => { export const getSystemConfig = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config) return BaseRequest.get('/system/config', params, 'SILENT_REQUEST', config)
return uni.$u.http.get('/system/config', {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
} }
/** /**
@@ -389,9 +136,7 @@ export const getSystemConfig = (params = {}, config = {}) => {
* // } * // }
*/ */
export const getVersionInfo = (config = {}) => { export const getVersionInfo = (config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config) return BaseRequest.get('/system/version', {}, 'SILENT_REQUEST', config)
return uni.$u.http.get('/system/version', requestConfig)
} }
/** /**
@@ -418,9 +163,7 @@ export const getVersionInfo = (config = {}) => {
* }) * })
*/ */
export const checkUpdate = (versionData, config = {}) => { export const checkUpdate = (versionData, config = {}) => {
const requestConfig = createCommonConfig('PUBLIC_DOWNLOAD', config, COMMON_LOADING_TEXTS.CHECK_UPDATE) return BaseRequest.post('/system/check-update', versionData, 'PUBLIC_REQUEST', LOADING_TEXTS.CHECK_UPDATE, config)
return uni.$u.http.post('/system/check-update', versionData, requestConfig)
} }
// 短信验证码相关API已移至 http/api/auth.js 文件中 // 短信验证码相关API已移至 http/api/auth.js 文件中
@@ -450,12 +193,7 @@ export const checkUpdate = (versionData, config = {}) => {
* const allRegions = await getRegionData({ level: 'all' }) * const allRegions = await getRegionData({ level: 'all' })
*/ */
export const getRegionData = (params = {}, config = {}) => { export const getRegionData = (params = {}, config = {}) => {
const requestConfig = createCommonConfig('SILENT_CONFIG', config) return BaseRequest.get('/common/regions', params, 'SILENT_REQUEST', config)
return uni.$u.http.get('/common/regions', {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
} }
/** /**
@@ -488,25 +226,7 @@ export const getRegionData = (params = {}, config = {}) => {
* }) * })
*/ */
export const submitFeedback = (feedbackData, config = {}) => { export const submitFeedback = (feedbackData, config = {}) => {
const requestConfig = createCommonConfig('AUTHENTICATED_SUBMIT', config, COMMON_LOADING_TEXTS.SUBMIT_FEEDBACK) return BaseRequest.post('/feedback', feedbackData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.SUBMIT_FEEDBACK, config)
return uni.$u.http.post('/feedback', feedbackData, requestConfig)
} }
// ==================== 导出配置常量(供外部使用) ====================
/**
* 导出通用配置常量,供其他模块使用
*/
export const COMMON_CONFIG = {
COMMON_CONFIG_TEMPLATES,
COMMON_LOADING_TEXTS
}
/**
* 导出通用工具函数,供其他模块使用
*/
export const COMMON_UTILS = {
createCommonConfig,
executeUploadRequest
}
+22 -384
View File
@@ -1,199 +1,9 @@
/** /**
* 宠物管理相关API接口模块 * 宠物管理相关API接口模块
*
* @fileoverview 提供宠物信息管理、记录管理、健康档案等相关的API接口
* @author 系统开发团队
* @version 2.0.0
* @since 1.0.0
*
* @description
* 本模块包含以下功能分组:
* - 宠物基础信息管理:增删改查宠物信息
* - 宠物记录管理:日常记录、健康记录、成长记录
* - 宠物健康档案:疫苗记录、体检记录、用药记录
* - 宠物统计分析:成长数据、健康趋势分析
*
* @example
* // 基本用法示例
* import { getPetsList, addPet, getPetRecords } from '@/http/api/pets.js'
*
* // 获取宠物列表
* const pets = await getPetsList()
*
* // 添加新宠物
* await addPet({ name: '小白', breed: '金毛', age: 2 })
*
* // 获取宠物记录
* const records = await getPetRecords(petId)
*/ */
// ==================== 类型定义 ==================== import BaseRequest from '../utils/request-helper.js'
import { LOADING_TEXTS } from '../config/constants.js'
/**
* @typedef {Object} PetInfo 宠物信息对象
* @property {string} id 宠物ID
* @property {string} name 宠物名称
* @property {string} breed 品种
* @property {string} type 类型:'dog' | 'cat' | 'bird' | 'rabbit' | 'other'
* @property {string} gender 性别:'male' | 'female'
* @property {number} age 年龄(月)
* @property {number} weight 体重(kg)
* @property {string} avatarUrl 头像URL
* @property {string} description 描述
* @property {string} createTime 创建时间
*/
/**
* @typedef {Object} PetRecord 宠物记录对象
* @property {string} id 记录ID
* @property {string} petId 宠物ID
* @property {string} type 记录类型:'feeding' | 'health' | 'exercise' | 'grooming' | 'other'
* @property {string} title 记录标题
* @property {string} content 记录内容
* @property {string[]} images 相关图片
* @property {string} recordTime 记录时间
*/
// ==================== 配置常量 ====================
/**
* 宠物API的默认配置模板
*/
const PETS_CONFIG_TEMPLATES = {
// 需要认证的查询请求(无loading)
AUTHENTICATED_QUERY: {
auth: true,
loading: false,
toast: true
},
// 需要认证的查询请求(有loading)
AUTHENTICATED_QUERY_WITH_LOADING: {
auth: true,
loading: true,
toast: true
},
// 需要认证的更新请求
AUTHENTICATED_UPDATE: {
auth: true,
loading: true,
toast: true
},
// 需要认证的删除请求
AUTHENTICATED_DELETE: {
auth: true,
loading: true,
toast: true
}
}
/**
* 宠物相关的loading文本配置
*/
const PETS_LOADING_TEXTS = {
ADD_PET: '正在添加宠物...',
UPDATE_PET: '正在更新宠物信息...',
DELETE_PET: '正在删除宠物...',
ADD_RECORD: '正在添加记录...',
UPDATE_RECORD: '正在更新记录...',
DELETE_RECORD: '正在删除记录...',
LOADING_DATA: '正在加载...'
}
// ==================== 工具函数 ====================
/**
* 创建宠物请求的标准化配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
const createPetsConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = PETS_CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
/**
* 执行宠物相关的GET请求
* @param {string} url 请求URL
* @param {Object} params 查询参数
* @param {string} template 配置模板
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePetsGetRequest = (url, params = {}, template = 'AUTHENTICATED_QUERY', config = {}) => {
const requestConfig = createPetsConfig(template, config)
return uni.$u.http.get(url, {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 执行宠物相关的POST请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePetsPostRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createPetsConfig(template, config, loadingText)
return uni.$u.http.post(url, data, requestConfig)
}
/**
* 执行宠物相关的PUT请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePetsPutRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createPetsConfig(template, config, loadingText)
return uni.$u.http.put(url, data, requestConfig)
}
/**
* 执行宠物相关的DELETE请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePetsDeleteRequest = (url, data = {}, template = 'AUTHENTICATED_DELETE', loadingText = null, config = {}) => {
const requestConfig = createPetsConfig(template, config, loadingText)
return uni.$u.http.delete(url, data, requestConfig)
}
// ==================== API方法 ==================== // ==================== API方法 ====================
@@ -201,199 +11,71 @@ const executePetsDeleteRequest = (url, data = {}, template = 'AUTHENTICATED_DELE
/** /**
* 获取用户宠物列表 * 获取用户宠物列表
* @description 获取当前用户的所有宠物信息列表,支持分页和筛选
* @param {Object} [params={}] 查询参数 * @param {Object} [params={}] 查询参数
* @param {number} [params.page=1] 页码
* @param {number} [params.pageSize=20] 每页数量
* @param {string} [params.type] 宠物类型筛选:'dog' | 'cat' | 'bird' | 'rabbit' | 'other'
* @param {string} [params.breed] 品种筛选
* @param {string} [params.keyword] 关键词搜索(名称、品种)
* @param {string} [params.sortBy] 排序字段:'createTime' | 'name' | 'age'
* @param {string} [params.sortOrder] 排序方向:'asc' | 'desc'
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回宠物列表和分页信息 * @returns {Promise<Object>} 返回宠物列表和分页信息
* @example
* // 获取所有宠物
* const pets = await getPetsList()
*
* // 分页获取狗狗列表
* const dogs = await getPetsList({
* type: 'dog',
* page: 1,
* pageSize: 10
* })
*
* // 搜索宠物
* const searchResults = await getPetsList({
* keyword: '小白',
* sortBy: 'createTime',
* sortOrder: 'desc'
* })
*/ */
export const getPetsList = (params = {}, config = {}) => { export const getPetsList = (params = {}, config = {}) => {
return executePetsGetRequest('/pets', params, 'AUTHENTICATED_QUERY', config) return BaseRequest.get('/pets', params, 'AUTHENTICATED_QUERY', config)
} }
/** /**
* 获取宠物详细信息 * 获取宠物详细信息
* @description 根据宠物ID获取详细的宠物信息
* @param {string|number} petId 宠物ID * @param {string|number} petId 宠物ID
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @param {boolean} [config.includeRecords=false] 是否包含最近记录 * @returns {Promise} 返回宠物详细信息
* @param {boolean} [config.includeHealth=false] 是否包含健康档案
* @returns {Promise<PetInfo>} 返回宠物详细信息
* @example
* // 获取基本信息
* const pet = await getPetDetail(123)
*
* // 获取包含记录的详细信息
* const petWithRecords = await getPetDetail(123, {
* includeRecords: true,
* includeHealth: true
* })
*/ */
export const getPetDetail = (petId, config = {}) => { export const getPetDetail = (petId, config = {}) => {
const params = {} const params = {}
if (config.includeRecords) params.includeRecords = true if (config.includeRecords) params.includeRecords = true
if (config.includeHealth) params.includeHealth = true if (config.includeHealth) params.includeHealth = true
return executePetsGetRequest(`/pets/${petId}`, params, 'AUTHENTICATED_QUERY_WITH_LOADING', config) return BaseRequest.get(`/pets/${petId}`, params, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
} }
/** /**
* 添加新宠物 * 添加新宠物
* @description 为当前用户添加一只新宠物
* @param {Object} petData 宠物信息数据 * @param {Object} petData 宠物信息数据
* @param {string} petData.name 宠物名称(必填)
* @param {string} petData.breed 品种(必填)
* @param {string} petData.type 类型:'dog' | 'cat' | 'bird' | 'rabbit' | 'other'
* @param {string} petData.gender 性别:'male' | 'female'
* @param {number} [petData.age] 年龄(月)
* @param {number} [petData.weight] 体重(kg)
* @param {string} [petData.avatarUrl] 头像URL
* @param {string} [petData.description] 描述
* @param {string} [petData.birthDate] 出生日期
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @returns {Promise<PetInfo>} 返回新添加的宠物信息 * @returns {Promise} 返回新添加的宠物信息
* @example
* // 添加基本宠物信息
* const newPet = await addPet({
* name: '小白',
* breed: '金毛',
* type: 'dog',
* gender: 'male',
* age: 24,
* weight: 25.5
* })
*
* // 添加完整宠物信息
* const newPet = await addPet({
* name: '小花',
* breed: '英短',
* type: 'cat',
* gender: 'female',
* age: 12,
* weight: 4.2,
* avatarUrl: 'https://example.com/cat.jpg',
* description: '很可爱的小猫咪',
* birthDate: '2023-01-15'
* })
*/ */
export const addPet = (petData, config = {}) => { export const addPet = (petData, config = {}) => {
return executePetsPostRequest('/pets', petData, 'AUTHENTICATED_UPDATE', PETS_LOADING_TEXTS.ADD_PET, config) return BaseRequest.post('/pets', petData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.ADD_PET, config)
} }
/** /**
* 更新宠物信息 * 更新宠物信息
* @description 更新指定宠物的信息,支持部分字段更新
* @param {string|number} petId 宠物ID * @param {string|number} petId 宠物ID
* @param {Object} petData 要更新的宠物数据 * @param {Object} petData 要更新的宠物数据
* @param {string} [petData.name] 宠物名称
* @param {string} [petData.breed] 品种
* @param {string} [petData.type] 类型
* @param {string} [petData.gender] 性别
* @param {number} [petData.age] 年龄(月)
* @param {number} [petData.weight] 体重(kg)
* @param {string} [petData.avatarUrl] 头像URL
* @param {string} [petData.description] 描述
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @returns {Promise<PetInfo>} 返回更新后的宠物信息 * @returns {Promise} 返回更新后的宠物信息
* @example
* // 更新宠物体重
* const updatedPet = await updatePet(123, {
* weight: 26.0
* })
*
* // 更新多个字段
* const updatedPet = await updatePet(123, {
* name: '小白白',
* age: 25,
* description: '更新后的描述'
* })
*/ */
export const updatePet = (petId, petData, config = {}) => { export const updatePet = (petId, petData, config = {}) => {
return executePetsPutRequest(`/pets/${petId}`, petData, 'AUTHENTICATED_UPDATE', PETS_LOADING_TEXTS.UPDATE_PET, config) return BaseRequest.put(`/pets/${petId}`, petData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATE_PET, config)
} }
/** /**
* 删除宠物 * 删除宠物
* @description 永久删除指定的宠物及其所有相关记录,此操作不可逆
* @param {string|number} petId 宠物ID * @param {string|number} petId 宠物ID
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @param {boolean} [config.force=false] 是否强制删除(忽略关联数据检查)
* @returns {Promise<Object>} 返回删除结果 * @returns {Promise<Object>} 返回删除结果
* @example
* // 普通删除
* await deletePet(123)
*
* // 强制删除(忽略关联数据)
* await deletePet(123, { force: true })
*
* @warning 此操作将永久删除宠物及其所有记录,请谨慎使用
*/ */
export const deletePet = (petId, config = {}) => { export const deletePet = (petId, config = {}) => {
const data = config.force ? { force: true } : {} const data = config.force ? { force: true } : {}
return executePetsDeleteRequest(`/pets/${petId}`, data, 'AUTHENTICATED_DELETE', PETS_LOADING_TEXTS.DELETE_PET, config) return BaseRequest.delete(`/pets/${petId}`, data, 'AUTHENTICATED_DELETE', LOADING_TEXTS.DELETE_PET, config)
} }
// ==================== 宠物记录管理API ==================== // ==================== 宠物记录管理API ====================
/** /**
* 获取宠物记录列表 * 获取宠物记录列表
* @description 获取指定宠物的所有记录,支持分页和筛选
* @param {string|number} petId 宠物ID * @param {string|number} petId 宠物ID
* @param {Object} [params={}] 查询参数 * @param {Object} [params={}] 查询参数
* @param {number} [params.page=1] 页码
* @param {number} [params.pageSize=20] 每页数量
* @param {string} [params.type] 记录类型筛选:'feeding' | 'health' | 'exercise' | 'grooming' | 'other'
* @param {string} [params.startDate] 开始日期
* @param {string} [params.endDate] 结束日期
* @param {string} [params.keyword] 关键词搜索
* @param {string} [params.sortBy] 排序字段:'recordTime' | 'createTime'
* @param {string} [params.sortOrder] 排序方向:'asc' | 'desc'
* @param {Object} [config={}] 自定义请求配置 * @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回记录列表和分页信息 * @returns {Promise<Object>} 返回记录列表和分页信息
* @example
* // 获取所有记录
* const records = await getPetRecords(123)
*
* // 获取指定类型的记录
* const healthRecords = await getPetRecords(123, {
* type: 'health',
* page: 1,
* pageSize: 10
* })
*
* // 按日期范围查询
* const recentRecords = await getPetRecords(123, {
* startDate: '2024-01-01',
* endDate: '2024-01-31',
* sortBy: 'recordTime',
* sortOrder: 'desc'
* })
*/ */
export const getPetRecords = (petId, params = {}, config = {}) => { export const getPetRecords = (petId, params = {}, config = {}) => {
return executePetsGetRequest(`/pets/${petId}/records`, params, 'AUTHENTICATED_QUERY', config) return BaseRequest.get(`/pets/${petId}/records`, params, 'AUTHENTICATED_QUERY', config)
} }
/** /**
@@ -431,7 +113,7 @@ export const getPetRecords = (petId, params = {}, config = {}) => {
* }) * })
*/ */
export const addPetRecord = (petId, recordData, config = {}) => { export const addPetRecord = (petId, recordData, config = {}) => {
return executePetsPostRequest(`/pets/${petId}/records`, recordData, 'AUTHENTICATED_UPDATE', PETS_LOADING_TEXTS.ADD_RECORD, config) return BaseRequest.post(`/pets/${petId}/records`, recordData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.ADD_RECORD, config)
} }
/** /**
@@ -455,7 +137,7 @@ export const addPetRecord = (petId, recordData, config = {}) => {
* }) * })
*/ */
export const updatePetRecord = (petId, recordId, recordData, config = {}) => { export const updatePetRecord = (petId, recordId, recordData, config = {}) => {
return executePetsPutRequest(`/pets/${petId}/records/${recordId}`, recordData, 'AUTHENTICATED_UPDATE', PETS_LOADING_TEXTS.UPDATE_RECORD, config) return BaseRequest.put(`/pets/${petId}/records/${recordId}`, recordData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATE_RECORD, config)
} }
/** /**
@@ -470,73 +152,29 @@ export const updatePetRecord = (petId, recordId, recordData, config = {}) => {
* await deletePetRecord(123, 456) * await deletePetRecord(123, 456)
*/ */
export const deletePetRecord = (petId, recordId, config = {}) => { export const deletePetRecord = (petId, recordId, config = {}) => {
return executePetsDeleteRequest(`/pets/${petId}/records/${recordId}`, {}, 'AUTHENTICATED_DELETE', PETS_LOADING_TEXTS.DELETE_RECORD, config) return BaseRequest.delete(`/pets/${petId}/records/${recordId}`, {}, 'AUTHENTICATED_DELETE', LOADING_TEXTS.DELETE_RECORD, config)
} }
// ==================== 宠物健康档案API ==================== // ==================== 宠物健康档案API ====================
/** /**
* 获取宠物健康数据 * 获取宠物健康数据
* @description 获取宠物的健康档案数据,包括疫苗记录、体检记录等
* @param {string|number} petId 宠物ID * @param {string|number} petId 宠物ID
* @param {Object} [params={}] 查询参数 * @param {Object} params 查询参数
* @param {string} [params.type] 健康数据类型:'vaccine' | 'checkup' | 'medication' | 'all' * @param {Object} config 自定义请求配置
* @param {string} [params.startDate] 开始日期 * @returns {Promise} 返回健康数据
* @param {string} [params.endDate] 结束日期
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object>} 返回健康数据
* @example
* // 获取所有健康数据
* const healthData = await getPetHealthData(123)
*
* // 获取疫苗记录
* const vaccineData = await getPetHealthData(123, {
* type: 'vaccine'
* })
*/ */
export const getPetHealthData = (petId, params = {}, config = {}) => { export const getPetHealthData = (petId, params = {}, config = {}) => {
return executePetsGetRequest(`/pets/${petId}/health`, params, 'AUTHENTICATED_QUERY_WITH_LOADING', config) return BaseRequest.get(`/pets/${petId}/health`, params, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
} }
/** /**
* 获取宠物成长时间线 * 获取宠物成长时间线
* @description 获取宠物的成长时间线,包括重要事件和里程碑
* @param {string|number} petId 宠物ID * @param {string|number} petId 宠物ID
* @param {Object} [params={}] 查询参数 * @param {Object} params 查询参数
* @param {number} [params.limit] 限制返回数量 * @param {Object} config 自定义请求配置
* @param {string} [params.type] 事件类型筛选 * @returns {Promise} 返回时间线事件数组
* @param {Object} [config={}] 自定义请求配置
* @returns {Promise<Object[]>} 返回时间线事件数组
* @example
* // 获取完整时间线
* const timeline = await getPetTimeline(123)
*
* // 获取最近10个事件
* const recentEvents = await getPetTimeline(123, {
* limit: 10
* })
*/ */
export const getPetTimeline = (petId, params = {}, config = {}) => { export const getPetTimeline = (petId, params = {}, config = {}) => {
return executePetsGetRequest(`/pets/${petId}/timeline`, params, 'AUTHENTICATED_QUERY_WITH_LOADING', config) return BaseRequest.get(`/pets/${petId}/timeline`, params, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
}
// ==================== 导出配置常量(供外部使用) ====================
/**
* 导出宠物配置常量,供其他模块使用
*/
export const PETS_CONFIG = {
PETS_CONFIG_TEMPLATES,
PETS_LOADING_TEXTS
}
/**
* 导出宠物工具函数,供其他模块使用
*/
export const PETS_UTILS = {
createPetsConfig,
executePetsGetRequest,
executePetsPostRequest,
executePetsPutRequest,
executePetsDeleteRequest
} }
+34 -231
View File
@@ -1,209 +1,10 @@
/** /**
* 个人中心相关API接口模块 * 个人中心相关API接口模块
* * 提供用户信息管理、资料完善、偏好设置等相关的API接口
* @fileoverview 提供用户信息管理、资料完善、偏好设置等相关的API接口
* @author 系统开发团队
* @version 2.0.0
* @since 1.0.0
*
* @description
* 本模块包含以下功能分组:
* - 用户信息相关API:获取、更新用户基本信息
* - 用户统计相关API:获取各项统计数据
* - 账户管理相关API:账户注销等敏感操作
* - 用户资料完善相关API:新用户资料完善
* - 头像上传相关API:头像图片上传和更新
* - 用户偏好设置相关API:个性化设置管理
* - 用户活动记录相关API:操作记录查询
*
* @example
* // 基本用法示例
* import { getUserInfo, updateUserInfo } from '@/http/api/profile.js'
*
* // 获取用户信息
* const userInfo = await getUserInfo()
*
* // 更新用户信息
* await updateUserInfo({ nickName: '新昵称' })
*
* @example
* // 使用配置常量
* import { PROFILE_CONFIG, PROFILE_UTILS } from '@/http/api/profile.js'
*
* // 使用工具函数创建自定义请求
* const customRequest = PROFILE_UTILS.executeGetRequest('/custom/endpoint')
*/ */
// ==================== 类型定义 ==================== import BaseRequest from '../utils/request-helper.js'
import { LOADING_TEXTS } from '../config/constants.js'
/**
* @typedef {Object} UserInfo 用户信息对象
* @property {string} id 用户ID
* @property {string} nickName 用户昵称
* @property {string} avatarUrl 头像URL
* @property {string} gender 性别:'男' | '女' | '保密'
* @property {string} birthday 生日,格式:YYYY-MM-DD
* @property {string} region 所在地区
* @property {string} bio 个人简介
* @property {string} createTime 创建时间
* @property {string} updateTime 更新时间
*/
/**
* @typedef {Object} UserStats 用户统计数据对象
* @property {number} petCount 宠物数量
* @property {number} recordCount 记录数量
* @property {number} reminderCount 提醒数量
* @property {number} familyMemberCount 家庭成员数量
*/
/**
* @typedef {Object} RequestConfig 请求配置对象
* @property {Object} custom 自定义配置
* @property {boolean} custom.auth 是否需要认证
* @property {boolean} custom.loading 是否显示loading
* @property {string} custom.loadingText loading文本
* @property {boolean} custom.toast 是否显示错误提示
*/
// ==================== 配置常量 ====================
/**
* API请求的默认配置模板
*/
const DEFAULT_CONFIG_TEMPLATES = {
// 需要认证的查询请求(无loading)
AUTHENTICATED_QUERY: {
auth: true,
loading: false,
toast: true
},
// 需要认证的查询请求(有loading)
AUTHENTICATED_QUERY_WITH_LOADING: {
auth: true,
loading: true,
toast: true
},
// 需要认证的更新请求
AUTHENTICATED_UPDATE: {
auth: true,
loading: true,
toast: true
},
// 需要认证的删除请求
AUTHENTICATED_DELETE: {
auth: true,
loading: true,
toast: true
}
}
/**
* 常用的loading文本配置
*/
const LOADING_TEXTS = {
UPDATING_USER_INFO: '正在更新用户信息...',
SAVING_PROFILE: '正在保存...',
DELETING_ACCOUNT: '正在注销账户...',
UPLOADING_AVATAR: '正在上传头像...',
LOADING_DATA: '正在加载...'
}
// ==================== 工具函数 ====================
/**
* 创建标准化的API请求配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
const createRequestConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = DEFAULT_CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
/**
* 执行GET请求的通用方法
* @param {string} url 请求URL
* @param {Object} params 查询参数
* @param {string} template 配置模板
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executeGetRequest = (url, params = {}, template = 'AUTHENTICATED_QUERY', config = {}) => {
const requestConfig = createRequestConfig(template, config)
return uni.$u.http.get(url, {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 执行POST请求的通用方法
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePostRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.post(url, data, requestConfig)
}
/**
* 执行PUT请求的通用方法
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executePutRequest = (url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) => {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.put(url, data, requestConfig)
}
/**
* 执行DELETE请求的通用方法
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
const executeDeleteRequest = (url, data = {}, template = 'AUTHENTICATED_DELETE', loadingText = null, config = {}) => {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.delete(url, data, requestConfig)
}
// ==================== API方法 ==================== // ==================== API方法 ====================
@@ -227,7 +28,28 @@ const executeDeleteRequest = (url, data = {}, template = 'AUTHENTICATED_DELETE',
* }) * })
*/ */
export const getUserInfo = (config = {}) => { export const getUserInfo = (config = {}) => {
return executeGetRequest('/user/info', {}, 'AUTHENTICATED_QUERY_WITH_LOADING', config) return BaseRequest.get('/user/info', {}, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
}
/**
* 获取微信用户信息
* @description 获取当前登录用户的微信相关信息,包括微信昵称、头像等
* @param {Object} [config={}] 自定义请求配置
* @param {Object} [config.custom] 自定义请求选项
* @param {boolean} [config.custom.loading] 是否显示loading,默认false
* @param {boolean} [config.custom.toast] 是否显示错误提示,默认true
* @returns {Promise<Object>} 返回微信用户信息对象
* @example
* // 基本用法
* const wxUserInfo = await getWxUserInfo()
*
* // 自定义配置
* const wxUserInfo = await getWxUserInfo({
* custom: { loading: true }
* })
*/
export const getWxUserInfo = (config = {}) => {
return BaseRequest.get('/user/wechat/mini/userinfo', {}, 'AUTHENTICATED_QUERY', config)
} }
/** /**
@@ -249,7 +71,7 @@ export const getUserInfo = (config = {}) => {
* }) * })
*/ */
export const updateUserInfo = (userInfo, config = {}) => { export const updateUserInfo = (userInfo, config = {}) => {
return executePutRequest('/user/info', userInfo, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATING_USER_INFO, config) return BaseRequest.put('/user/info', userInfo, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATING_USER_INFO, config)
} }
/** /**
@@ -270,7 +92,7 @@ export const updateUserInfo = (userInfo, config = {}) => {
* const pets = await getUserPets({ page: 1, pageSize: 10 }) * const pets = await getUserPets({ page: 1, pageSize: 10 })
*/ */
export const getUserPets = (params = {}, config = {}) => { export const getUserPets = (params = {}, config = {}) => {
return executeGetRequest('/user/pets', params, 'AUTHENTICATED_QUERY', config) return BaseRequest.get('/user/pets', params, 'AUTHENTICATED_QUERY', config)
} }
// ==================== 用户统计相关API ==================== // ==================== 用户统计相关API ====================
@@ -293,7 +115,7 @@ export const getUserPets = (params = {}, config = {}) => {
* // } * // }
*/ */
export const getUserStats = (config = {}) => { export const getUserStats = (config = {}) => {
return executeGetRequest('/user/stats', {}, 'AUTHENTICATED_QUERY', config) return BaseRequest.get('/user/stats', {}, 'AUTHENTICATED_QUERY', config)
} }
// ==================== 账户管理相关API ==================== // ==================== 账户管理相关API ====================
@@ -312,7 +134,7 @@ export const getUserStats = (config = {}) => {
* @warning 此操作将永久删除所有用户数据,请谨慎使用 * @warning 此操作将永久删除所有用户数据,请谨慎使用
*/ */
export const deleteAccount = (config = {}) => { export const deleteAccount = (config = {}) => {
return executeDeleteRequest('/user/account', {}, 'AUTHENTICATED_DELETE', LOADING_TEXTS.DELETING_ACCOUNT, config) return BaseRequest.delete('/user/account', {}, 'AUTHENTICATED_DELETE', LOADING_TEXTS.DELETING_ACCOUNT, config)
} }
// ==================== 用户资料完善相关API ==================== // ==================== 用户资料完善相关API ====================
@@ -348,7 +170,7 @@ export const deleteAccount = (config = {}) => {
* }) * })
*/ */
export const completeUserProfile = (profileData, config = {}) => { export const completeUserProfile = (profileData, config = {}) => {
return executePostRequest('/user/profile/complete', profileData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.SAVING_PROFILE, config) return BaseRequest.post('/user/profile/complete', profileData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.SAVING_PROFILE, config)
} }
// ==================== 头像上传相关API ==================== // ==================== 头像上传相关API ====================
@@ -366,7 +188,7 @@ export const completeUserProfile = (profileData, config = {}) => {
* }) * })
*/ */
export const uploadAvatar = (avatarData, config = {}) => { export const uploadAvatar = (avatarData, config = {}) => {
return executePostRequest('/user/avatar', avatarData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPLOADING_AVATAR, config) return BaseRequest.post('/user/avatar', avatarData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPLOADING_AVATAR, config)
} }
// ==================== 用户偏好设置相关API ==================== // ==================== 用户偏好设置相关API ====================
@@ -380,7 +202,7 @@ export const uploadAvatar = (avatarData, config = {}) => {
* const preferences = await getUserPreferences() * const preferences = await getUserPreferences()
*/ */
export const getUserPreferences = (config = {}) => { export const getUserPreferences = (config = {}) => {
return executeGetRequest('/user/preferences', {}, 'AUTHENTICATED_QUERY', config) return BaseRequest.get('/user/preferences', {}, 'AUTHENTICATED_QUERY', config)
} }
/** /**
@@ -399,7 +221,7 @@ export const getUserPreferences = (config = {}) => {
* }) * })
*/ */
export const updateUserPreferences = (preferences, config = {}) => { export const updateUserPreferences = (preferences, config = {}) => {
return executePutRequest('/user/preferences', preferences, 'AUTHENTICATED_UPDATE', '正在保存设置...', config) return BaseRequest.put('/user/preferences', preferences, 'AUTHENTICATED_UPDATE', '正在保存设置...', config)
} }
// ==================== 用户活动记录相关API ==================== // ==================== 用户活动记录相关API ====================
@@ -423,26 +245,7 @@ export const updateUserPreferences = (preferences, config = {}) => {
* }) * })
*/ */
export const getUserActivities = (params = {}, config = {}) => { export const getUserActivities = (params = {}, config = {}) => {
return executeGetRequest('/user/activities', params, 'AUTHENTICATED_QUERY', config) return BaseRequest.get('/user/activities', params, 'AUTHENTICATED_QUERY', config)
} }
// ==================== 导出配置常量(供外部使用) ====================
/**
* 导出配置常量,供其他模块使用
*/
export const PROFILE_CONFIG = {
DEFAULT_CONFIG_TEMPLATES,
LOADING_TEXTS
}
/**
* 导出工具函数,供其他模块使用
*/
export const PROFILE_UTILS = {
createRequestConfig,
executeGetRequest,
executePostRequest,
executePutRequest,
executeDeleteRequest
}
+17 -97
View File
@@ -1,4 +1,9 @@
// 评价相关API接口 /**
* 评价相关API接口模块
*/
import BaseRequest from '../utils/request-helper.js'
import { LOADING_TEXTS } from '../config/constants.js'
/** /**
* 获取评价列表 * 获取评价列表
@@ -7,15 +12,7 @@
* @returns {Promise} * @returns {Promise}
*/ */
export const getReviews = (params = {}, config = {}) => { export const getReviews = (params = {}, config = {}) => {
return uni.$u.http.get('/reviews', { return BaseRequest.get('/reviews', params, 'SILENT_REQUEST', config)
params,
custom: {
auth: false,
loading: false,
...config.custom
},
...config
})
} }
/** /**
@@ -27,15 +24,7 @@ export const getReviews = (params = {}, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getTargetReviews = (targetType, targetId, params = {}, config = {}) => { export const getTargetReviews = (targetType, targetId, params = {}, config = {}) => {
return uni.$u.http.get(`/reviews/${targetType}/${targetId}`, { return BaseRequest.get(`/reviews/${targetType}/${targetId}`, params, 'SILENT_REQUEST', config)
params,
custom: {
auth: false,
loading: false,
...config.custom
},
...config
})
} }
/** /**
@@ -45,15 +34,7 @@ export const getTargetReviews = (targetType, targetId, params = {}, config = {})
* @returns {Promise} * @returns {Promise}
*/ */
export const addReview = (reviewData, config = {}) => { export const addReview = (reviewData, config = {}) => {
return uni.$u.http.post('/reviews', reviewData, { return BaseRequest.post('/reviews', reviewData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.SUBMIT_REVIEW, config)
custom: {
auth: true,
loading: true,
loadingText: '正在提交评价...',
...config.custom
},
...config
})
} }
/** /**
@@ -64,15 +45,7 @@ export const addReview = (reviewData, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const updateReview = (reviewId, reviewData, config = {}) => { export const updateReview = (reviewId, reviewData, config = {}) => {
return uni.$u.http.put(`/reviews/${reviewId}`, reviewData, { return BaseRequest.put(`/reviews/${reviewId}`, reviewData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATE_REVIEW, config)
custom: {
auth: true,
loading: true,
loadingText: '正在更新评价...',
...config.custom
},
...config
})
} }
/** /**
@@ -82,15 +55,7 @@ export const updateReview = (reviewId, reviewData, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const deleteReview = (reviewId, config = {}) => { export const deleteReview = (reviewId, config = {}) => {
return uni.$u.http.delete(`/reviews/${reviewId}`, {}, { return BaseRequest.delete(`/reviews/${reviewId}`, {}, 'AUTHENTICATED_DELETE', LOADING_TEXTS.DELETE_REVIEW, config)
custom: {
auth: true,
loading: true,
loadingText: '正在删除评价...',
...config.custom
},
...config
})
} }
/** /**
@@ -100,15 +65,7 @@ export const deleteReview = (reviewId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getMyReviews = (params = {}, config = {}) => { export const getMyReviews = (params = {}, config = {}) => {
return uni.$u.http.get('/reviews/my', { return BaseRequest.get('/reviews/my', params, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
params,
custom: {
auth: true,
loading: true,
...config.custom
},
...config
})
} }
/** /**
@@ -118,14 +75,7 @@ export const getMyReviews = (params = {}, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const likeReview = (reviewId, config = {}) => { export const likeReview = (reviewId, config = {}) => {
return uni.$u.http.post(`/reviews/${reviewId}/like`, {}, { return BaseRequest.post(`/reviews/${reviewId}/like`, {}, 'AUTHENTICATED_QUERY', null, config)
custom: {
auth: true,
loading: false,
...config.custom
},
...config
})
} }
/** /**
@@ -135,14 +85,7 @@ export const likeReview = (reviewId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const unlikeReview = (reviewId, config = {}) => { export const unlikeReview = (reviewId, config = {}) => {
return uni.$u.http.delete(`/reviews/${reviewId}/like`, {}, { return BaseRequest.delete(`/reviews/${reviewId}/like`, {}, 'AUTHENTICATED_QUERY', null, config)
custom: {
auth: true,
loading: false,
...config.custom
},
...config
})
} }
/** /**
@@ -153,15 +96,7 @@ export const unlikeReview = (reviewId, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const reportReview = (reviewId, reportData, config = {}) => { export const reportReview = (reviewId, reportData, config = {}) => {
return uni.$u.http.post(`/reviews/${reviewId}/report`, reportData, { return BaseRequest.post(`/reviews/${reviewId}/report`, reportData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.SUBMIT_REPORT, config)
custom: {
auth: true,
loading: true,
loadingText: '正在提交举报...',
...config.custom
},
...config
})
} }
/** /**
@@ -172,15 +107,7 @@ export const reportReview = (reviewId, reportData, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const replyReview = (reviewId, replyData, config = {}) => { export const replyReview = (reviewId, replyData, config = {}) => {
return uni.$u.http.post(`/reviews/${reviewId}/reply`, replyData, { return BaseRequest.post(`/reviews/${reviewId}/reply`, replyData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.REPLY_REVIEW, config)
custom: {
auth: true,
loading: true,
loadingText: '正在回复评价...',
...config.custom
},
...config
})
} }
/** /**
@@ -191,12 +118,5 @@ export const replyReview = (reviewId, replyData, config = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
export const getReviewStats = (targetType, targetId, config = {}) => { export const getReviewStats = (targetType, targetId, config = {}) => {
return uni.$u.http.get(`/reviews/${targetType}/${targetId}/stats`, { return BaseRequest.get(`/reviews/${targetType}/${targetId}/stats`, {}, 'PUBLIC_REQUEST', config)
custom: {
auth: false,
loading: true,
...config.custom
},
...config
})
} }
+37 -26
View File
@@ -6,7 +6,7 @@
const ENV_CONFIG = { const ENV_CONFIG = {
// 开发环境 // 开发环境
development: { development: {
baseURL: 'https://dev-api.pet-ai.com', baseURL: 'http://localhost:8888',
timeout: 30000 timeout: 30000
}, },
// 测试环境 // 测试环境
@@ -57,32 +57,18 @@ export const HTTP_CONFIG = {
*/ */
export const NO_AUTH_APIS = [ export const NO_AUTH_APIS = [
// 用户认证相关 // 用户认证相关
'/auth/login', '/wechat/user/mini/login', // 微信小程序登录
'/auth/register', ]
'/auth/wx-login',
'/auth/wx-phone-login', // 微信手机号登录
'/auth/refresh',
'/auth/reset-password', // 重置密码
// 短信验证相关 /**
'/sms/send', // 发送短信验证码 * 需要鉴权的接口列表(明确需要登录的接口)
'/sms/verify', // 验证短信验证码 * 这些接口在调用时如果未登录会自动跳转到登录页面
*/
// 公开浏览的接口 export const AUTH_REQUIRED_APIS = [
'/ai/knowledge', // AI知识库 // 微信小程序用户相关接口
'/adoption/pets', // 浏览待领养宠物 '/user/wechat/mini/userinfo', // 获取用户信息
'/adoption/pets/search', // 搜索宠物 '/user/wechat/mini/phone-update', // 更新手机号
'/adoption/pets/filter', // 筛选宠物 '/user/wechat/mini/check-unionid', // 检查UnionID
'/adoption/organizations', // 领养机构列表
'/reviews', // 查看评价
'/reviews/*', // 查看特定对象的评价
// 系统相关
'/system/config', // 系统配置
'/system/version', // 版本信息
'/system/check-update', // 检查更新
'/common/regions', // 地区数据
'/common/upload-config' // 上传配置
] ]
/** /**
@@ -111,6 +97,31 @@ export function checkApiAuth(url) {
return true return true
} }
/**
* 检查接口是否需要强制登录
* @param {String} url 接口URL
* @returns {Boolean} 是否需要强制登录
*/
export function checkAuthRequiredApi(url) {
// 移除查询参数,只保留路径
const path = url.split('?')[0]
// 检查是否在需要强制登录的列表中
for (const authApi of AUTH_REQUIRED_APIS) {
// 支持通配符匹配
if (authApi.includes('*')) {
const regex = new RegExp('^' + authApi.replace(/\*/g, '.*') + '$')
if (regex.test(path)) {
return true
}
} else if (path === authApi || path.startsWith(authApi + '/')) {
return true
}
}
return false
}
/** /**
* 添加不需要鉴权的接口 * 添加不需要鉴权的接口
* @param {String|Array} apis 接口路径或接口路径数组 * @param {String|Array} apis 接口路径或接口路径数组
+178
View File
@@ -0,0 +1,178 @@
/**
* HTTP模块统一配置常量文件
* 统一管理所有配置模板、Loading文本常量和其他共享常量
*/
// ==================== 配置模板 ====================
/**
* 统一的请求配置模板
* 合并了REQUEST_CONFIG_TEMPLATES、COMMON_CONFIG_TEMPLATES、DEFAULT_CONFIG_TEMPLATES
*/
export const CONFIG_TEMPLATES = {
// 需要认证的查询请求(无loading)
AUTHENTICATED_QUERY: {
auth: true,
loading: false,
toast: true
},
// 需要认证的查询请求(有loading)
AUTHENTICATED_QUERY_WITH_LOADING: {
auth: true,
loading: true,
toast: true
},
// 需要认证的更新请求
AUTHENTICATED_UPDATE: {
auth: true,
loading: true,
toast: true
},
// 需要认证的删除请求
AUTHENTICATED_DELETE: {
auth: true,
loading: true,
toast: true
},
// 无需认证的公开请求
PUBLIC_REQUEST: {
auth: false,
loading: true,
toast: true
},
// 静默请求(无loading和toast)
SILENT_REQUEST: {
auth: false,
loading: false,
toast: false
},
// 需要认证的上传请求
AUTHENTICATED_UPLOAD: {
auth: true,
loading: true,
toast: true
},
// 需要认证的提交请求
AUTHENTICATED_SUBMIT: {
auth: true,
loading: true,
toast: true
},
// 公开的下载请求
PUBLIC_DOWNLOAD: {
auth: false,
loading: true,
toast: true
},
// 静默的配置获取请求
SILENT_CONFIG: {
auth: false,
loading: false,
toast: false
}
}
// ==================== Loading文本常量 ====================
/**
* 统一的Loading文本配置
* 合并了所有模块中的LOADING_TEXTS常量
*/
export const LOADING_TEXTS = {
// 通用操作
LOADING: '正在加载...',
SAVING: '正在保存...',
DELETING: '正在删除...',
UPDATING: '正在更新...',
SUBMITTING: '正在提交...',
UPLOADING: '正在上传...',
// 认证相关
LOGIN: '正在登录...',
LOGOUT: '正在退出...',
REGISTER: '正在注册...',
PHONE_VERIFY: '正在验证手机号...',
SEND_SMS: '正在发送验证码...',
VERIFY_SMS: '正在验证...',
RESET_PASSWORD: '正在重置密码...',
// 用户相关
UPDATING_USER_INFO: '正在更新用户信息...',
UPLOADING_AVATAR: '正在上传头像...',
SAVING_PROFILE: '正在保存...',
DELETING_ACCOUNT: '正在注销账户...',
LOADING_DATA: '正在加载...',
// 宠物相关
ADD_PET: '正在添加宠物...',
UPDATE_PET: '正在更新宠物信息...',
DELETE_PET: '正在删除宠物...',
ADD_RECORD: '正在添加记录...',
UPDATE_RECORD: '正在更新记录...',
DELETE_RECORD: '正在删除记录...',
// 文件相关
UPLOAD_IMAGE: '正在上传图片...',
UPLOAD_FILE: '正在上传文件...',
UPLOAD_IMAGES: '正在批量上传...',
DOWNLOAD_FILE: '正在下载文件...',
// AI助手相关
AI_THINKING: 'AI正在思考中...',
SPEECH_RECOGNITION: '正在识别语音...',
// 领养相关
SUBMIT_APPLICATION: '正在提交申请...',
CANCEL_APPLICATION: '正在取消申请...',
// 评价相关
SUBMIT_REVIEW: '正在提交评价...',
UPDATE_REVIEW: '正在更新评价...',
DELETE_REVIEW: '正在删除评价...',
SUBMIT_REPORT: '正在提交举报...',
REPLY_REVIEW: '正在回复评价...',
// 其他
SUBMIT_FEEDBACK: '正在提交反馈...',
CHECK_UPDATE: '正在检查更新...'
}
// ==================== 工具函数 ====================
/**
* 创建标准化的请求配置
* @param {string} template 配置模板名称
* @param {Object} customConfig 自定义配置
* @param {string} loadingText 自定义loading文本
* @returns {Object} 标准化的请求配置
*/
export const createRequestConfig = (template, customConfig = {}, loadingText = null) => {
const baseConfig = CONFIG_TEMPLATES[template] || {}
const config = {
custom: {
...baseConfig,
...(loadingText && { loadingText }),
...customConfig.custom
},
...customConfig
}
// 移除custom属性中的undefined值
Object.keys(config.custom).forEach(key => {
if (config.custom[key] === undefined) {
delete config.custom[key]
}
})
return config
}
+38 -24
View File
@@ -1,7 +1,12 @@
// HTTP请求配置和拦截器 // HTTP请求配置和拦截器
// 基于uView-next的luch-request库实现 // 基于uView-next的luch-request库实现
import { HTTP_CONFIG, checkApiAuth } from './config.js' import { HTTP_CONFIG, checkApiAuth, checkAuthRequiredApi } from './config.js'
import { clearAuthData, isUserLoggedIn, handleAuthRequired, tryAutoLogin } from '../utils/auth-helper.js'
/** /**
* 初始化HTTP配置 * 初始化HTTP配置
@@ -24,20 +29,34 @@ export default (vm) => {
config.data = config.data || {} config.data = config.data || {}
config.custom = config.custom || {} config.custom = config.custom || {}
// 自动检查接口是否需要鉴权(如果没有明确指定) // 自动检查接口是否需要鉴权(如果没有明确指定)
if (config.custom.auth === undefined) { if (config.custom.auth === undefined) {
config.custom.auth = checkApiAuth(config.url) config.custom.auth = checkApiAuth(config.url)
} }
// 根据custom参数中配置的是否需要token,添加对应的请求头 // 检查是否为需要强制登录的接口
if (config.custom.auth) { const isAuthRequiredApi = checkAuthRequiredApi(config.url)
// 从本地存储获取token
// 如果是需要强制登录的接口,检查登录状态
if (isAuthRequiredApi) {
if (!isUserLoggedIn()) {
console.log('访问需要鉴权的接口但未完成登录,跳转到登录流程:', config.url)
handleAuthRequired()
return Promise.reject({
message: '需要登录',
code: 'AUTH_REQUIRED',
url: config.url
})
}
}
// 添加Authorization请求头
if (config.custom.auth !== false) {
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token) const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
if (token) { if (token) {
config.header.Authorization = `Bearer ${token}` config.header.Authorization = `Bearer ${token}`
} else {
// 如果需要token但没有token,可以跳转到登录页
console.warn('需要token但未找到,请先登录')
} }
} }
@@ -52,6 +71,11 @@ export default (vm) => {
return config return config
}, config => { }, config => {
// 请求错误处理 // 请求错误处理
// 如果是AUTH_REQUIRED错误,不显示loading
if (config.code === 'AUTH_REQUIRED') {
// 不显示loading,因为会跳转页面
return Promise.reject(config)
}
return Promise.reject(config) return Promise.reject(config)
}) })
@@ -67,8 +91,8 @@ export default (vm) => {
// 统一的响应数据处理 // 统一的响应数据处理
if (data.code !== undefined) { if (data.code !== undefined) {
// 如果服务端返回的状态码不等于200,则reject() // 如果服务端返回的状态码不等于0(成功),则reject()
if (data.code !== 200) { if (data.code !== 0) {
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示 // 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
if (custom?.toast !== false) { if (custom?.toast !== false) {
uni.$u.toast(data.message || '请求失败') uni.$u.toast(data.message || '请求失败')
@@ -76,13 +100,9 @@ export default (vm) => {
// 特殊状态码处理 // 特殊状态码处理
if (data.code === 401) { if (data.code === 401) {
// token过期,清除本地token并跳转到登录页 // token过期,清除本地认证信息并跳转到登录流程
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token) clearAuthData()
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken) handleAuthRequired()
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
uni.reLaunch({
url: HTTP_CONFIG.loginPage
})
return Promise.reject(data) return Promise.reject(data)
} }
@@ -116,14 +136,8 @@ export default (vm) => {
break break
case 401: case 401:
errorMessage = '未授权,请重新登录' errorMessage = '未授权,请重新登录'
// 清除本地认证信息 clearAuthData()
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token) handleAuthRequired()
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
// 跳转到登录页
uni.reLaunch({
url: HTTP_CONFIG.loginPage
})
break break
case 403: case 403:
errorMessage = '拒绝访问' errorMessage = '拒绝访问'
+50 -28
View File
@@ -1,4 +1,7 @@
// HTTP模块统一导出文件 /**
* HTTP模块统一导出文件
* 提供完整的API接口、配置管理、工具函数和认证功能
*/
// 导入所有API模块 // 导入所有API模块
import * as petsApi from './api/pets.js' import * as petsApi from './api/pets.js'
@@ -7,52 +10,71 @@ import * as adoptionApi from './api/adoption.js'
import * as profileApi from './api/profile.js' import * as profileApi from './api/profile.js'
import * as reviewApi from './api/review.js' import * as reviewApi from './api/review.js'
import * as commonApi from './api/common.js' import * as commonApi from './api/common.js'
import * as authApi from './api/auth.js'
// 导入配置 // ==================== 配置导出 ====================
export { HTTP_CONFIG, NO_AUTH_APIS, addNoAuthApis, setEnvironment } from './config/config.js' export { HTTP_CONFIG, NO_AUTH_APIS, addNoAuthApis, setEnvironment } from './config/config.js'
// 导入工具 // ==================== 工具函数导出 ====================
export { validateRequired, validateId, validatePagination, validateFileUpload } from './utils/validator.js' export { validateRequired, validateId, validatePagination, validateFileUpload } from './utils/validator.js'
// 统一导出所有API // ==================== 认证工具导出 ====================
export {
isUserLoggedIn,
validateToken,
checkLoginStatus,
clearAuthData,
clearTempLoginData,
navigateToLogin,
handleAuthRequired,
tryAutoLogin
} from './utils/auth-helper.js'
// ==================== API模块导出 ====================
export { export {
petsApi, petsApi,
assistantApi, assistantApi,
adoptionApi, adoptionApi,
profileApi, profileApi,
reviewApi, reviewApi,
commonApi commonApi,
authApi
} }
// 也可以按模块导出,便于按需引入 // ==================== 默认导出 ====================
export default { export default {
// API模块
pets: petsApi, pets: petsApi,
assistant: assistantApi, assistant: assistantApi,
adoption: adoptionApi, adoption: adoptionApi,
profile: profileApi, profile: profileApi,
review: reviewApi, review: reviewApi,
common: commonApi common: commonApi,
auth: authApi,
// 配置
config: {
HTTP_CONFIG,
NO_AUTH_APIS,
addNoAuthApis,
setEnvironment
},
// 工具函数
utils: {
validateRequired,
validateId,
validatePagination,
validateFileUpload,
isUserLoggedIn,
validateToken,
checkLoginStatus,
clearAuthData,
clearTempLoginData,
navigateToLogin,
handleAuthRequired,
tryAutoLogin
}
} }
// 使用示例:
// 方式1:按模块导入API
// import { petsApi, assistantApi } from '@/http/index.js'
// petsApi.getPetsList()
// assistantApi.sendMessage()
// 方式2:导入所有API
// import api from '@/http/index.js'
// api.pets.getPetsList()
// api.assistant.sendMessage()
// 方式3:解构导入特定接口
// import { petsApi } from '@/http/index.js'
// const { getPetsList, addPet } = petsApi
// 方式4:配置不需要鉴权的接口
// import { addNoAuthApis } from '@/http/index.js'
// addNoAuthApis(['/custom/api1', '/custom/api2'])
// 方式5:切换环境
// import { setEnvironment } from '@/http/index.js'
// setEnvironment('production') // 切换到生产环境
+149
View File
@@ -0,0 +1,149 @@
/**
* 统一认证工具模块
* 整合所有认证检查、登录状态管理等功能
*/
import { HTTP_CONFIG } from '../config/config.js'
// ==================== 认证状态检查 ====================
/**
* 检查用户是否已完成登录
* @returns {Boolean} 是否已完成登录
*/
export function isUserLoggedIn() {
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
const loginStep = uni.getStorageSync('loginStep')
// 检查是否有有效的token和完成的登录流程
return !!(token && (loginStep === 'profile_completed' || loginStep === 'phone_authed'))
}
/**
* 检查并验证token有效性
* @returns {Promise<boolean>} token是否有效
*/
export async function validateToken() {
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
if (!token) {
return false
}
try {
// 调用一个简单的API来验证token
const { getUserInfo } = await import('../api/profile.js')
await getUserInfo({
custom: {
loading: false,
toast: false
}
})
return true
} catch (error) {
console.log('Token验证失败:', error)
return false
}
}
/**
* 检查登录状态并处理
* @returns {boolean} 是否已登录
*/
export function checkLoginStatus() {
if (!isUserLoggedIn()) {
console.log('用户未登录,跳转到登录页')
navigateToLogin()
return false
}
return true
}
// ==================== 认证数据管理 ====================
/**
* 清理认证数据
*/
export function clearAuthData() {
console.log('清理认证数据被调用')
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
uni.removeStorageSync('loginStep')
uni.removeStorageSync('wxLoginCode')
uni.removeStorageSync('wxUserInfo')
uni.removeStorageSync('loginDate')
}
/**
* 清理登录流程中的临时数据
*/
export function clearTempLoginData() {
// 清理临时数据,保留重要的用户信息
uni.removeStorageSync('wxLoginCode')
console.log('清理临时登录数据')
}
// ==================== 登录流程处理 ====================
/**
* 跳转到登录页面
*/
export function navigateToLogin() {
uni.reLaunch({
url: HTTP_CONFIG.loginPage
})
}
/**
* 处理需要鉴权的情况
*/
export function handleAuthRequired() {
const loginStep = uni.getStorageSync('loginStep')
// 根据当前登录状态决定跳转页面
let targetPage = '/pages/login/login'
let toastMessage = '请先登录'
switch (loginStep) {
case 'wx_logged':
targetPage = '/pages/auth/phone-auth'
toastMessage = '请完成手机号授权'
break
case 'phone_authed':
case 'phone_skipped':
targetPage = '/pages/profile/user-info?mode=setup'
toastMessage = '请完善个人信息'
break
default:
targetPage = '/pages/login/login'
toastMessage = '请先登录'
}
// 显示提示并跳转
uni.showToast({
title: toastMessage,
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateTo({ url: targetPage }).catch(() => {
uni.reLaunch({ url: targetPage })
})
}, 1500)
}
/**
* 尝试自动登录
* @returns {Promise<Boolean>} 是否登录成功
*/
export async function tryAutoLogin() {
try {
// 这里需要导入实际的自动登录函数
const { autoLogin } = await import('../../utils/loginState.js')
return await autoLogin()
} catch (error) {
console.error('自动登录失败:', error)
return false
}
}
+172
View File
@@ -0,0 +1,172 @@
/**
* HTTP请求统一工具类
* 基于BaseRequest类提供标准化的请求配置和执行方法
*/
import { CONFIG_TEMPLATES, LOADING_TEXTS, createRequestConfig } from '../config/constants.js'
// ==================== BaseRequest工具类 ====================
/**
* 统一的HTTP请求工具类
* 提供标准化的请求方法,基于uni.$u.http实现
*/
class BaseRequest {
/**
* 执行GET请求
* @param {string} url 请求URL
* @param {Object} params 查询参数
* @param {string} template 配置模板
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
static get(url, params = {}, template = 'AUTHENTICATED_QUERY', config = {}) {
const requestConfig = createRequestConfig(template, config)
return uni.$u.http.get(url, {
...(Object.keys(params).length > 0 && { params }),
...requestConfig
})
}
/**
* 执行POST请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
static post(url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.post(url, data, requestConfig)
}
/**
* 执行PUT请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
static put(url, data = {}, template = 'AUTHENTICATED_UPDATE', loadingText = null, config = {}) {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.put(url, data, requestConfig)
}
/**
* 执行DELETE请求
* @param {string} url 请求URL
* @param {Object} data 请求数据
* @param {string} template 配置模板
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
static delete(url, data = {}, template = 'AUTHENTICATED_DELETE', loadingText = null, config = {}) {
const requestConfig = createRequestConfig(template, config, loadingText)
return uni.$u.http.delete(url, data, requestConfig)
}
/**
* 执行上传请求
* @param {string} url 上传URL
* @param {Object} uploadData 上传数据
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
static upload(url, uploadData, loadingText, config = {}) {
const requestConfig = createRequestConfig('AUTHENTICATED_UPLOAD', config, loadingText)
return uni.$u.http.upload(url, {
filePath: uploadData.filePath,
name: uploadData.name || 'file',
formData: uploadData.formData || {},
...requestConfig
})
}
/**
* 执行下载请求
* @param {string} url 下载URL
* @param {string} loadingText loading文本
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
static download(url, loadingText, config = {}) {
const requestConfig = createRequestConfig('PUBLIC_DOWNLOAD', config, loadingText)
return uni.$u.http.download(url, {
...(config.savePath && { savePath: config.savePath }),
...requestConfig
})
}
/**
* 批量上传多个文件
* @param {Array} fileList 文件列表数组
* @param {string} url 上传URL
* @param {Object} config 自定义配置
* @returns {Promise} 请求Promise
*/
static async batchUpload(fileList, url, config = {}) {
const { maxConcurrent = 3, showProgress = true } = config
// 显示整体进度loading
if (showProgress) {
uni.showLoading({
title: config.custom?.loadingText || LOADING_TEXTS.UPLOAD_IMAGES
})
}
try {
// 分批并发上传
const results = []
for (let i = 0; i < fileList.length; i += maxConcurrent) {
const batch = fileList.slice(i, i + maxConcurrent)
const batchPromises = batch.map(fileData => {
return BaseRequest.upload(url, fileData, null, {
...config,
custom: {
loading: false, // 批量上传时不显示单个loading
...config.custom
}
})
})
const batchResults = await Promise.all(batchPromises)
results.push(...batchResults)
}
return results
} finally {
if (showProgress) {
uni.hideLoading()
}
}
}
}
// ==================== 导出 ====================
// 默认导出BaseRequest类
export default BaseRequest
// 向后兼容的命名导出
export const executeGetRequest = BaseRequest.get
export const executePostRequest = BaseRequest.post
export const executePutRequest = BaseRequest.put
export const executeDeleteRequest = BaseRequest.delete
export const executeUploadRequest = BaseRequest.upload
export const executeDownloadRequest = BaseRequest.download
export const executeBatchUploadRequest = BaseRequest.batchUpload
// 导出配置相关(从constants.js重新导出)
export { CONFIG_TEMPLATES, LOADING_TEXTS, createRequestConfig }
-163
View File
@@ -1,163 +0,0 @@
# Pages 目录优化报告
## 优化概览
本次优化对 `pages` 目录下的页面组件进行了全面的代码质量、性能和可维护性优化。
## 1. Auth 模块优化 ✅
### pages/auth/phone-auth.vue
**优化内容:**
- ✅ **错误处理优化**:提取了 `handleError` 方法,统一处理各种错误类型
- ✅ **代码结构优化**:简化了错误处理逻辑,减少重复代码
- ✅ **用户体验提升**:改进了错误提示信息的准确性和友好性
**优化前后对比:**
```javascript
// 优化前:重复的错误处理逻辑
} catch (error) {
// 30+ 行重复的错误处理代码
}
// 优化后:统一的错误处理方法
} catch (error) {
handleError(error)
}
```
## 2. Profile 模块优化 ✅
### pages/profile/profile.vue
**优化内容:**
- ✅ **数据结构优化**:清理了多余的空行,优化了响应式数据的组织
- ✅ **方法重构**:提取了 `resetStatsToDefault` 方法,避免重复的数据重置逻辑
- ✅ **导航优化**:改进了 `navigateTo` 方法,添加了更好的错误处理和自定义选项
- ✅ **代码清理**:移除了多余的空行和注释
**优化前后对比:**
```javascript
// 优化前:重复的数据重置
petStats.petCount = 0
petStats.recordCount = 0
// ... 更多重复代码
// 优化后:统一的重置方法
resetStatsToDefault()
```
### pages/profile/user-info.vue
**优化内容:**
- ✅ **选择器配置优化**:统一了选择器数据的管理
- ✅ **事件处理简化**:移除了不必要的 `@change` 事件处理器
- ✅ **代码清理**:删除了未使用的方法(`onNickNameChange`, `onBioChange`)
**优化前后对比:**
```javascript
// 优化前:不必要的事件处理
<u-input v-model="userInfo.nickName" @change="onNickNameChange" />
// 优化后:直接使用 v-model
<u-input v-model="userInfo.nickName" />
```
## 3. Pets 模块优化 ✅
### pages/pets/pets.vue
**优化内容:**
- ✅ **配置数据提取**:将硬编码的配置数据提取到文件顶部
- ✅ **方法重构**:创建了 `generateDefaultPets` 方法,分离了数据生成逻辑
- ✅ **代码组织**:优化了 `getPetEmoji` 方法,使用配置映射
**优化前后对比:**
```javascript
// 优化前:硬编码的表情映射
getPetEmoji(breed) {
const emojiMap = {
'橘猫': '🐱',
// ... 更多硬编码
}
return emojiMap[breed] || '🐾'
}
// 优化后:使用配置常量
getPetEmoji(breed) {
return PET_EMOJI_MAP[breed] || '🐾'
}
```
## 4. Adoption 模块优化 ✅
### pages/adoption/adoption.vue
**优化内容:**
- ✅ **数据结构优化**:重新组织了 data 中的属性,按功能分组
- ✅ **代码清理**:清理了多余的空行和注释
- ✅ **可读性提升**:改进了数据属性的分组和命名
## 5. 全局优化成果
### 代码质量提升
- ✅ **减少重复代码**:提取了公共方法和配置
- ✅ **统一错误处理**:建立了一致的错误处理模式
- ✅ **改进代码结构**:优化了方法组织和数据结构
### 性能优化
- ✅ **减少不必要的事件监听**:移除了冗余的 change 事件
- ✅ **优化数据初始化**:改进了默认数据的生成方式
- ✅ **提升渲染效率**:减少了不必要的响应式更新
### 可维护性提升
- ✅ **配置化管理**:将硬编码数据提取为配置常量
- ✅ **方法职责单一**:每个方法都有明确的职责
- ✅ **代码组织清晰**:按功能模块组织代码结构
## 6. 保留的功能特性
### 登录流程功能 ✅
- ✅ 保留了所有登录流程重构中新添加的功能
- ✅ 保持了页面间的数据传递和状态管理
- ✅ 确保了所有页面的路由配置正确
### 用户体验 ✅
- ✅ 维护了现有的用户体验和交互逻辑
- ✅ 保持了页面的响应式设计
- ✅ 确保了样式的一致性
## 7. 优化统计
### 文件修改统计
- **优化文件数量**: 5个主要文件
- **代码行数减少**: ~150行
- **方法提取**: 6个新的工具方法
- **配置常量**: 3个新的配置对象
### 质量指标改进
- 📊 **代码复用率**: 提升 25%
- 🚀 **错误处理一致性**: 提升 40%
- 🔧 **可维护性**: 提升 30%
- 📚 **代码可读性**: 提升 35%
## 8. 后续建议
### 进一步优化方向
1. **组件化重构**: 将重复的UI组件提取为公共组件
2. **状态管理**: 考虑引入 Pinia 进行全局状态管理
3. **类型安全**: 考虑引入 TypeScript 提升代码安全性
4. **性能监控**: 添加性能监控和错误上报机制
### 维护建议
1. **代码规范**: 建立统一的代码规范和 ESLint 配置
2. **文档完善**: 为复杂的业务逻辑添加详细注释
3. **测试覆盖**: 为关键功能添加单元测试
4. **持续优化**: 定期进行代码审查和重构
## 总结
本次优化成功提升了 pages 目录下代码的质量、性能和可维护性,同时保持了所有现有功能的完整性。新的代码结构更加清晰、易于维护,为后续开发提供了良好的基础。
优化重点关注了:
- 代码复用和模块化
- 错误处理的统一性
- 配置数据的管理
- 用户体验的保持
所有优化都经过了仔细的测试和验证,确保不会影响现有功能的正常运行。
+313 -117
View File
@@ -15,8 +15,13 @@
<view class="status-dot"></view> <view class="status-dot"></view>
<text class="status-text">在线</text> <text class="status-text">在线</text>
</view> </view>
<view class="knowledge-icon" @click="openKnowledge"> <view class="action-buttons">
<text class="icon-text">📚</text> <view class="action-btn" @click="clearHistory" title="清空历史">
<text class="icon-text">🗑️</text>
</view>
<view class="action-btn" @click="openKnowledge" title="知识库">
<text class="icon-text">📚</text>
</view>
</view> </view>
</view> </view>
@@ -114,6 +119,17 @@
</template> </template>
<script> <script>
import {
askPetAssistant,
streamAskPetAssistant,
getAssistantHistory,
clearAssistantHistory,
validateMessage,
formatChatMessage,
formatMessageTime
} from '@/http/api/assistant.js'
// WebSocket功能已移除,使用HTTP API替代
export default { export default {
data() { data() {
return { return {
@@ -121,82 +137,46 @@ export default {
scrollTop: 0, scrollTop: 0,
isThinking: false, isThinking: false,
userAvatar: '/static/user-avatar.png', userAvatar: '/static/user-avatar.png',
messageList: [ messageList: []
{
type: 'ai',
content: '您好!我是您的宠物AI助手🐾\n\n我可以为您解答关于宠物饲养、健康、训练、营养等方面的问题。\n\n**我能帮你做什么:**\n- 🍖 **饮食建议**:营养搭配、食物选择\n- 🏥 **健康咨询**:症状分析、预防措施 \n- 🎾 **训练指导**:行为纠正、技能训练\n- 💡 **日常护理**:清洁、美容、环境\n\n有什么想了解的吗?',
time: this.getCurrentTime()
}
],
quickQuestions: [
{ id: 1, text: '猫咪不吃饭怎么办?' },
{ id: 2, text: '狗狗需要多久洗一次澡?' },
{ id: 3, text: '宠物疫苗接种时间' },
{ id: 4, text: '如何训练宠物定点上厕所?' },
{ id: 5, text: '宠物发烧的症状' },
{ id: 6, text: '幼猫喂养注意事项' }
],
// 知识库数据
knowledgeBase: {
'喂食': {
keywords: ['喂食', '吃饭', '食物', '饮食', '营养'],
responses: [
'定时定量喂食很重要,成年猫每天2-3次,幼猫3-4次。',
'选择优质的宠物食品,避免给宠物吃人类食物。',
'确保宠物随时有清洁的饮用水。'
]
},
'健康': {
keywords: ['健康', '生病', '症状', '发烧', '呕吐', '腹泻'],
responses: [
'定期体检很重要,建议每年至少一次。',
'注意观察宠物的食欲、精神状态和排便情况。',
'如果出现异常症状,请及时就医。'
]
},
'疫苗': {
keywords: ['疫苗', '接种', '免疫', '预防'],
responses: [
'幼猫幼犬8-12周开始接种疫苗。',
'成年宠物每年需要加强免疫。',
'常见疫苗包括三联疫苗和狂犬疫苗。'
]
},
'训练': {
keywords: ['训练', '教育', '行为', '定点', '上厕所'],
responses: [
'训练需要耐心和一致性。',
'使用正向强化,奖励好行为。',
'避免体罚,这会让宠物产生恐惧。'
]
},
'洗澡': {
keywords: ['洗澡', '清洁', '卫生'],
responses: [
'狗狗一般1-2周洗一次澡,猫咪通常不需要经常洗澡。',
'使用宠物专用洗浴用品。',
'洗澡后要及时吹干,避免感冒。'
]
}
}
} }
}, },
onLoad(options) {
// 加载历史记录
this.loadChatHistory()
},
onShow() {
// 页面显示时滚动到底部
this.$nextTick(() => {
this.scrollToBottom()
})
},
methods: { methods: {
getCurrentTime() { getCurrentTime() {
const now = new Date() const now = new Date()
return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}` return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`
}, },
askQuestion(question) {
this.inputMessage = question
this.sendMessage()
},
sendMessage() {
async sendMessage() {
if (!this.inputMessage.trim() || this.isThinking) return if (!this.inputMessage.trim() || this.isThinking) return
// 验证消息内容
const validation = validateMessage(this.inputMessage)
if (!validation.valid) {
uni.showToast({
title: validation.error,
icon: 'none',
duration: 2000
})
return
}
// 保存用户输入内容 // 保存用户输入内容
const userInput = this.inputMessage const userInput = this.inputMessage.trim()
// 添加用户消息 // 添加用户消息
const userMessage = { const userMessage = {
@@ -212,6 +192,9 @@ export default {
// 开始AI思考 // 开始AI思考
this.isThinking = true this.isThinking = true
// 用于存储当前AI消息的引用
let currentAiMessage = null
// 确保DOM更新后再滚动到思考状态 // 确保DOM更新后再滚动到思考状态
this.$nextTick(() => { this.$nextTick(() => {
this.forceScrollToBottom() this.forceScrollToBottom()
@@ -224,46 +207,255 @@ export default {
}, 500) }, 500)
}) })
// 模拟AI回复 try {
setTimeout(() => { // 使用HTTP API流式响应
const aiMessage = { const response = await this.handleStreamResponse({
type: 'ai', message: userInput,
content: this.getAIResponse(userInput), temperature: 0.7,
time: this.getCurrentTime() maxTokens: 1000
} }, (aiMessage) => {
this.messageList.push(aiMessage) // 保存AI消息引用,用于fallback时更新
this.isThinking = false currentAiMessage = aiMessage
// AI回复完成后强制滚动到底部
this.$nextTick(() => {
this.forceScrollToBottom()
// 延迟再次滚动,确保消息完全显示
setTimeout(() => {
this.forceScrollToBottom()
}, 100)
}) })
}, 1500 + Math.random() * 1000) // 1.5-2.5秒随机延迟
// 检查是否包含敏感词
if (response && response.data && response.data.isSensitive) {
uni.showToast({
title: '消息包含敏感内容',
icon: 'none',
duration: 2000
})
}
// 结束思考状态
this.isThinking = false
} catch (error) {
console.error('SSE流式请求失败,尝试普通请求:', error)
// 降级处理:使用普通API
try {
const fallbackResponse = await askPetAssistant({
message: userInput,
temperature: 0.7,
maxTokens: 1000
})
if (fallbackResponse && fallbackResponse.data) {
// 更新已存在的AI消息,而不是创建新的
if (currentAiMessage) {
currentAiMessage.content = fallbackResponse.data.message || fallbackResponse.data.content || '抱歉,我现在无法回答您的问题。'
currentAiMessage.isSensitive = fallbackResponse.data.isSensitive || false
currentAiMessage.tokenCount = fallbackResponse.data.tokenCount || 0
currentAiMessage.responseTime = fallbackResponse.data.responseTime || 0
} else {
// 如果没有AI消息引用,创建新的(兜底逻辑)
const aiMessage = {
type: 'ai',
content: fallbackResponse.data.message || fallbackResponse.data.content || '抱歉,我现在无法回答您的问题。',
time: this.getCurrentTime(),
isSensitive: fallbackResponse.data.isSensitive || false,
tokenCount: fallbackResponse.data.tokenCount || 0,
responseTime: fallbackResponse.data.responseTime || 0
}
this.messageList.push(aiMessage)
}
// 检查敏感词
if (fallbackResponse.data.isSensitive) {
uni.showToast({
title: '消息包含敏感内容',
icon: 'none',
duration: 2000
})
}
this.forceScrollToBottom()
} else {
this.handleApiError('API响应格式异常', currentAiMessage)
}
} catch (fallbackError) {
console.error('普通请求也失败:', fallbackError)
this.handleApiError(fallbackError.message || '网络请求失败', currentAiMessage)
}
} finally {
// 确保思考状态结束(防止异常情况)
this.isThinking = false
}
}, },
getAIResponse(question) { /**
// 智能匹配知识库 * 处理API错误
for (let category in this.knowledgeBase) { * @param {string} errorMessage 错误信息
const { keywords, responses } = this.knowledgeBase[category] * @param {Object} existingAiMessage 已存在的AI消息对象(可选)
if (keywords.some(keyword => question.includes(keyword))) { */
const randomResponse = responses[Math.floor(Math.random() * responses.length)] handleApiError(errorMessage, existingAiMessage = null) {
return `${randomResponse}\n\n💡 如果您需要更详细的建议,建议咨询专业的宠物医生。` const errorContent = `抱歉,我遇到了一些问题:${errorMessage}\n\n请稍后再试,或者检查网络连接。`
if (existingAiMessage) {
// 更新已存在的AI消息
existingAiMessage.content = errorContent
existingAiMessage.isError = true
} else {
// 创建新的错误消息(兜底逻辑)
const errorMessage_display = {
type: 'ai',
content: errorContent,
time: this.getCurrentTime(),
isError: true
} }
this.messageList.push(errorMessage_display)
} }
// 默认回复 this.forceScrollToBottom()
const defaultResponses = [ },
'这是一个很好的问题!建议您咨询专业的宠物医生获得更准确的建议。',
'每只宠物的情况都不同,建议根据具体情况来处理。您可以查看我们的知识库了解更多信息。',
'感谢您的提问!这个问题比较复杂,建议您带宠物去专业的宠物医院检查。',
'我理解您的担心。建议您记录宠物的具体症状,然后咨询专业医生。'
]
return defaultResponses[Math.floor(Math.random() * defaultResponses.length)] /**
* 加载聊天历史记录
*/
async loadChatHistory() {
console.log('开始加载聊天历史记录...')
try {
const response = await getAssistantHistory({
page: 1,
pageSize: 20
})
console.log('历史记录API响应:', response)
if (response && Array.isArray(response) && response.length > 0) {
// 格式化并设置历史消息
const historyMessages = response.map(msg => formatChatMessage(msg))
console.log('格式化后的历史消息:', historyMessages)
this.messageList = historyMessages
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom()
})
console.log('历史记录加载成功,共', historyMessages.length, '条消息')
} else {
// 没有历史记录,保持空列表
console.log('没有历史记录或数据格式异常:', response)
this.messageList = []
}
} catch (error) {
console.error('加载历史记录失败:', error)
// 显示用户友好的错误提示
uni.showToast({
title: '加载历史记录失败',
icon: 'none',
duration: 2000
})
// 保持空列表
this.messageList = []
}
},
/**
* 清空聊天历史
*/
async clearHistory() {
try {
const result = await uni.showModal({
title: '确认清空',
content: '确定要清空聊天记录吗?此操作不可恢复。',
confirmText: '清空',
cancelText: '取消'
})
if (!result.confirm) return
uni.showLoading({
title: '正在清空...'
})
await clearAssistantHistory()
// 清空消息列表
this.messageList = []
uni.showToast({
title: '历史记录已清空',
icon: 'success',
duration: 1500
})
} catch (error) {
console.error('清空历史记录失败:', error)
uni.showToast({
title: '清空失败',
icon: 'none',
duration: 2000
})
} finally {
uni.hideLoading()
}
},
/**
* 处理HTTP流式响应
* @param {Object} messageData 消息数据
* @param {Function} onAiMessageCreated 创建AI消息后的回调函数
* @returns {Promise} 返回流式响应处理结果
*/
async handleStreamResponse(messageData, onAiMessageCreated) {
// 创建AI消息对象
const aiMessage = {
type: 'ai',
content: '',
time: this.getCurrentTime(),
isSensitive: false,
tokenCount: 0,
responseTime: 0
}
// 添加到消息列表
this.messageList.push(aiMessage)
// 通知调用者AI消息已创建
if (onAiMessageCreated) {
onAiMessageCreated(aiMessage)
}
// 使用HTTP API处理流式响应
return streamAskPetAssistant(messageData)
.then(result => {
console.log('HTTP流式响应完成:', result)
// 更新消息内容
aiMessage.content = result.message || result.content || ''
// 更新消息的元数据
if (result.isSensitive !== undefined) {
aiMessage.isSensitive = result.isSensitive
}
if (result.tokenCount !== undefined) {
aiMessage.tokenCount = result.tokenCount
}
if (result.responseTime !== undefined) {
aiMessage.responseTime = result.responseTime
}
// 强制触发视图更新
this.$forceUpdate()
return result
})
.catch(error => {
console.error('HTTP流式响应失败:', error)
// 显示错误提示
uni.showToast({
title: '消息发送失败',
icon: 'error',
duration: 2000
})
// 更新消息状态为错误
aiMessage.content = '抱歉,消息发送失败,请重试。'
aiMessage.isError = true
// 强制触发视图更新
this.$forceUpdate()
throw error
})
}, },
scrollToBottom() { scrollToBottom() {
@@ -302,19 +494,11 @@ export default {
} }
}); });
}, },
startVoiceInput() {
uni.showToast({
title: '语音功能开发中',
icon: 'none'
})
},
openKnowledge() { openKnowledge() {
uni.navigateTo({ uni.navigateTo({
url: '/pages/assistant/knowledge' url: '/pages/assistant/knowledge'
}) })
} },
} }
} }
</script> </script>
@@ -398,18 +582,30 @@ export default {
} }
} }
.knowledge-icon { .action-buttons {
width: 60rpx;
height: 60rpx;
border-radius: 30rpx;
background: rgba(255, 255, 255, 0.8);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; gap: 16rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
.icon-text { .action-btn {
font-size: 28rpx; width: 56rpx;
height: 56rpx;
border-radius: 28rpx;
background: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: rgba(255, 255, 255, 0.9);
}
.icon-text {
font-size: 24rpx;
}
} }
} }
} }
+72 -29
View File
@@ -81,22 +81,27 @@ export default {
// 错误处理函数 // 错误处理函数
const handleError = (error) => { const handleError = (error) => {
let errorMessage = '授权失败,请重试' let errorMessage = '授权失败,请重试'
let shouldReturnToLogin = false
console.error('手机号授权错误详情:', error)
if (error.message) { if (error.message) {
if (error.message.includes('网络')) { if (error.message.includes('网络')) {
errorMessage = '网络连接异常,请检查网络后重试' errorMessage = '网络连接异常,请检查网络后重试'
} else if (error.message.includes('登录状态已过期')) { } else if (error.message.includes('登录状态已过期') || error.message.includes('重新获取微信登录code失败')) {
errorMessage = '登录状态已过期,请重新登录' errorMessage = '登录状态已过期,请重新登录'
// 3秒后自动返回profile页面 shouldReturnToLogin = true
setTimeout(() => {
uni.reLaunch({
url: '/pages/profile/profile'
})
}, 3000)
} else if (error.message.includes('API返回数据格式错误')) { } else if (error.message.includes('API返回数据格式错误')) {
errorMessage = '服务器响应异常,请稍后重试' errorMessage = '服务器响应异常,请稍后重试'
} else if (error.message.includes('用户拒绝')) {
errorMessage = '需要授权手机号才能继续使用'
} else if (error.code === 401) {
errorMessage = '授权已过期,请重新登录'
shouldReturnToLogin = true
} else if (error.code === 500) {
errorMessage = '服务器错误,请稍后重试'
} else { } else {
errorMessage = error.message errorMessage = error.message || '授权失败,请重试'
} }
} }
@@ -105,6 +110,15 @@ export default {
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}) })
// 如果需要返回登录页面
if (shouldReturnToLogin) {
setTimeout(() => {
uni.reLaunch({
url: '/pages/profile/profile'
})
}, 3000)
}
} }
// 方法定义 // 方法定义
@@ -123,43 +137,72 @@ export default {
try { try {
// 获取微信登录code // 获取微信登录code
const loginCode = uni.getStorageSync(STORAGE_KEYS.WX_LOGIN_CODE) let loginCode = uni.getStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
// 检查登录code是否存在和有效
if (!loginCode) { if (!loginCode) {
throw new Error('登录状态已过期,请重新登录') console.warn('微信登录code不存在,尝试重新获取')
// 尝试重新获取微信登录code
try {
const loginRes = await new Promise((resolve, reject) => {
uni.login({
success: resolve,
fail: reject
})
})
if (loginRes.code) {
loginCode = loginRes.code
uni.setStorageSync(STORAGE_KEYS.WX_LOGIN_CODE, loginCode)
console.log('重新获取微信登录code成功')
} else {
throw new Error('重新获取微信登录code失败')
}
} catch (loginError) {
console.error('重新获取微信登录code失败:', loginError)
throw new Error('登录状态已过期,请重新登录')
}
} }
// 调用后端API验证手机号 // 调用后端API进行手机号登录
const phoneData = { const phoneData = {
code: loginCode, code: loginCode,
encryptedData: e.detail.encryptedData, encryptedData: e.detail.encryptedData,
iv: e.detail.iv iv: e.detail.iv
} }
// 调用真实API接口 // 调用手机号登录API
const result = await wxPhoneLogin(phoneData, { const result = await wxPhoneLogin(phoneData, {
custom: { custom: {
loading: false // 使用页面自己的loading状态 loading: false, // 使用页面自己的loading状态
catch: true // 确保错误能被catch到
} }
}) })
// 使用状态管理工具保存手机号授权数据 // 处理登录结果
if (result && result.token) { if (result && result.token) {
savePhoneAuthData(result) // 保存手机号登录数据
} else { savePhoneAuthData({
throw new Error('API返回数据格式错误') token: result.token,
} refreshToken: result.refreshToken,
userInfo: result.userInfo || result
uni.showToast({
title: '授权成功',
icon: 'success'
})
// 跳转到个人信息设置页面
setTimeout(() => {
uni.navigateTo({
url: '/pages/profile/user-info?mode=setup'
}) })
}, 1500)
uni.showToast({
title: '手机号授权成功',
icon: 'success',
duration: 1500
})
// 跳转到用户信息设置页面
setTimeout(() => {
uni.navigateTo({
url: '/pages/profile/user-info?mode=setup'
})
}, 1500)
} else {
throw new Error('手机号登录失败')
}
} catch (error) { } catch (error) {
console.error('手机号授权失败:', error) console.error('手机号授权失败:', error)
+105 -88
View File
@@ -2,7 +2,7 @@
<view class="profile-container page-container-unified"> <view class="profile-container page-container-unified">
<!-- 用户信息卡片 --> <!-- 用户信息卡片 -->
<view class="user-info-card"> <view class="user-info-card">
<view class="user-avatar-section" :class="{ 'logged-in': userInfo.nickName }" @click="handleUserAction"> <view class="user-avatar-section" :class="{ 'logged-in': isLoggedIn }" @click="handleUserAction">
<view class="avatar-wrapper"> <view class="avatar-wrapper">
<u-avatar <u-avatar
:src="userInfo.avatarUrl || ''" :src="userInfo.avatarUrl || ''"
@@ -13,19 +13,19 @@
color="white" color="white"
font-size="40" font-size="40"
></u-avatar> ></u-avatar>
<view class="online-status" v-if="userInfo.nickName"> <view class="online-status" v-if="isLoggedIn">
<view class="status-dot"></view> <view class="status-dot"></view>
</view> </view>
<!-- 登录提示图标,仅在未登录时显示 --> <!-- 登录提示图标,仅在未登录时显示 -->
<view class="login-hint" v-if="!userInfo.nickName"> <view class="login-hint" v-if="!isLoggedIn">
<text class="hint-icon">👋</text> <text class="hint-icon">👋</text>
</view> </view>
</view> </view>
</view> </view>
<view class="user-info-section"> <view class="user-info-section">
<view class="user-name">{{ userInfo.nickName || '点击头像登录' }}</view> <view class="user-name">{{ isLoggedIn ? (userInfo.nickName || '用户') : '点击头像登录' }}</view>
<view class="user-status">{{ userInfo.nickName ? '已登录' : '未登录' }}</view> <view class="user-status">{{ isLoggedIn ? '已登录' : '未登录' }}</view>
<view class="login-days" v-if="userInfo.nickName">已使用 {{ loginDays }} 天</view> <view class="login-days" v-if="isLoggedIn">已使用 {{ loginDays }} 天</view>
</view> </view>
</view> </view>
@@ -143,14 +143,7 @@
<script> <script>
import { reactive, ref, onMounted, computed } from 'vue' import { reactive, ref, onMounted, computed } from 'vue'
import { uploadImage } from '@/http/api/common.js' import { uploadImage } from '@/http/api/common.js'
import { updateUserInfo } from '@/http/api/profile.js' import { getUserInfo, getWxUserInfo, updateUserInfo } from '@/http/api/profile.js'
import {
getCurrentLoginStep,
isLoginCompleted,
checkAndResumeLogin,
saveWxLoginData,
clearTempLoginData
} from '@/utils/loginState.js'
export default { export default {
name: 'ProfilePage', name: 'ProfilePage',
@@ -163,6 +156,11 @@ export default {
const loginDays = ref(0) const loginDays = ref(0)
// 计算属性
const isLoggedIn = computed(() => {
return !!uni.getStorageSync('token')
})
@@ -189,47 +187,76 @@ export default {
// 方法定义 // 方法定义
const initPage = () => { const initPage = () => {
checkLogin() const token = uni.getStorageSync('token')
loadUserStats() if (token) {
} // 有token时加载用户信息和统计数据
loadUserInfo()
const checkLogin = () => { loadUserStats()
// 检查登录状态 } else {
console.log('Profile页面登录状态检查') // 没有token时只加载本地缓存的用户信息(如果有)
// 检查是否需要继续未完成的登录流程
const nextPage = checkAndResumeLogin()
if (nextPage) {
console.log('检测到未完成的登录流程,跳转到:', nextPage)
// 显示提示信息
uni.showToast({
title: '继续完成登录流程',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateTo({
url: nextPage
})
}, 1500)
return
}
// 检查是否已完成登录
if (isLoginCompleted()) {
const savedUserInfo = uni.getStorageSync('userInfo') const savedUserInfo = uni.getStorageSync('userInfo')
if (savedUserInfo) { if (savedUserInfo) {
userInfo.value = savedUserInfo userInfo.value = savedUserInfo
calculateLoginDays()
// 清理临时登录数据
clearTempLoginData()
} }
} }
} }
const loadUserInfo = async () => {
const token = uni.getStorageSync('token')
if (!token) {
return
}
try {
// 优先获取微信用户信息
const wxUserInfo = await getWxUserInfo({
custom: {
loading: false,
toast: false
}
})
if (wxUserInfo) {
userInfo.value = wxUserInfo
// 同步到本地存储
uni.setStorageSync('userInfo', wxUserInfo)
uni.setStorageSync('wxUserInfo', wxUserInfo)
calculateLoginDays()
return
}
} catch (error) {
console.log('获取微信用户信息失败,尝试获取基本用户信息:', error)
// 微信用户信息获取失败,尝试获取基本用户信息
try {
const apiUserInfo = await getUserInfo({
custom: {
loading: false,
toast: false
}
})
if (apiUserInfo) {
userInfo.value = apiUserInfo
// 同步到本地存储
uni.setStorageSync('userInfo', apiUserInfo)
calculateLoginDays()
return
}
} catch (basicError) {
console.log('获取基本用户信息也失败,使用本地缓存:', basicError)
}
}
// API获取失败时,尝试从本地存储加载用户信息
const savedUserInfo = uni.getStorageSync('userInfo')
if (savedUserInfo) {
userInfo.value = savedUserInfo
calculateLoginDays()
}
}
const calculateLoginDays = () => { const calculateLoginDays = () => {
const loginDate = uni.getStorageSync('loginDate') const loginDate = uni.getStorageSync('loginDate')
if (loginDate) { if (loginDate) {
@@ -302,49 +329,18 @@ export default {
}) })
} }
const handleUserAction = () => { const handleUserAction = async () => {
if (userInfo.value.nickName) { const token = uni.getStorageSync('token')
// 已登录,跳转到个人信息设置
if (token) {
// 有token,直接跳转到个人信息设置
navigateTo('/pages/profile/user-info') navigateTo('/pages/profile/user-info')
} else { } else {
// 未登录,执行登录 // 没有token,跳转到登录页面
wxLogin() navigateTo('/pages/login/login')
} }
} }
const wxLogin = () => {
uni.login({
provider: 'weixin',
success: (res) => {
console.log('登录成功', res)
// 使用状态管理工具保存微信登录数据
saveWxLoginData(res)
calculateLoginDays()
uni.showToast({
title: '登录成功',
icon: 'success',
duration: 1500
})
// 跳转到手机号授权页面
setTimeout(() => {
uni.navigateTo({
url: '/pages/auth/phone-auth'
})
}, 1500)
},
fail: (err) => {
console.error('登录失败', err)
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
}
})
}
// 统一的页面导航方法 // 统一的页面导航方法
const navigateTo = (url, options = {}) => { const navigateTo = (url, options = {}) => {
const { fallbackMessage = '页面开发中' } = options const { fallbackMessage = '页面开发中' } = options
@@ -396,11 +392,21 @@ export default {
// 刷新页面数据 // 刷新页面数据
const refreshData = () => { const refreshData = () => {
// 只刷新统计数据,用户信息在initPage时已加载
loadUserStats() loadUserStats()
} }
// 强制刷新用户信息(用于特殊情况)
const forceRefreshUserInfo = () => {
const token = uni.getStorageSync('token')
if (token) {
loadUserInfo()
}
}
// 页面显示时刷新数据 // 页面显示时刷新数据
const onShow = () => { const onShow = () => {
// 只刷新数据,不重复调用loadUserInfo
refreshData() refreshData()
} }
@@ -438,9 +444,12 @@ export default {
return { return {
userInfo, userInfo,
loginDays, loginDays,
isLoggedIn,
petStats, petStats,
familyStats, familyStats,
adoptionStats, adoptionStats,
loadUserInfo,
forceRefreshUserInfo,
handleUserAction, handleUserAction,
navigateTo, navigateTo,
navigateToFamily, navigateToFamily,
@@ -456,11 +465,19 @@ export default {
// 页面生命周期 // 页面生命周期
onShow() { onShow() {
this.onShow() // 重新检查登录状态并刷新数据
const token = uni.getStorageSync('token')
if (token) {
// 有token时总是重新获取最新用户信息
this.loadUserInfo()
}
this.refreshData()
}, },
onPullDownRefresh() { onPullDownRefresh() {
this.onPullDownRefresh() this.refreshData()
uni.stopPullDownRefresh()
} }
} }
</script> </script>
+1 -21
View File
@@ -231,28 +231,9 @@ export default {
// 生命周期 // 生命周期
onMounted(() => { onMounted(() => {
initPageMode() initPageMode()
validateLoginState()
loadUserInfo() loadUserInfo()
}) })
// 验证登录状态
const validateLoginState = () => {
if (isSetupMode.value) {
// 首次设置模式需要验证登录状态
console.log('个人信息设置页面状态验证')
// 检查是否有有效的登录状态
const currentStep = uni.getStorageSync(STORAGE_KEYS.LOGIN_STEP)
if (!currentStep || currentStep === 'not_logged') {
console.warn('无效的登录状态,返回profile页面')
uni.reLaunch({
url: '/pages/profile/profile'
})
return
}
}
}
// 方法定义 // 方法定义
const initPageMode = () => { const initPageMode = () => {
// 获取页面参数 // 获取页面参数
@@ -548,8 +529,7 @@ export default {
// 新增的模式相关变量和方法 // 新增的模式相关变量和方法
isSetupMode, isSetupMode,
phoneSkipped, phoneSkipped,
skipSetup, skipSetup
validateLoginState
} }
} }
} }
+129 -6
View File
@@ -17,6 +17,7 @@ export const STORAGE_KEYS = {
LOGIN_STEP: 'loginStep', LOGIN_STEP: 'loginStep',
WX_LOGIN_CODE: 'wxLoginCode', WX_LOGIN_CODE: 'wxLoginCode',
TOKEN: 'token', TOKEN: 'token',
REFRESH_TOKEN: 'refreshToken',
PHONE_NUMBER: 'phoneNumber', PHONE_NUMBER: 'phoneNumber',
WX_USER_INFO: 'wxUserInfo', WX_USER_INFO: 'wxUserInfo',
USER_INFO: 'userInfo', USER_INFO: 'userInfo',
@@ -97,12 +98,16 @@ export const saveWxLoginData = (loginData) => {
* @param {Object} phoneData 手机号数据 * @param {Object} phoneData 手机号数据
*/ */
export const savePhoneAuthData = (phoneData) => { export const savePhoneAuthData = (phoneData) => {
const { token, userInfo } = phoneData const { token, refreshToken, userInfo } = phoneData
if (token) { if (token) {
uni.setStorageSync(STORAGE_KEYS.TOKEN, token) uni.setStorageSync(STORAGE_KEYS.TOKEN, token)
} }
if (refreshToken) {
uni.setStorageSync(STORAGE_KEYS.REFRESH_TOKEN, refreshToken)
}
if (userInfo) { if (userInfo) {
if (userInfo.phoneNumber) { if (userInfo.phoneNumber) {
uni.setStorageSync(STORAGE_KEYS.PHONE_NUMBER, userInfo.phoneNumber) uni.setStorageSync(STORAGE_KEYS.PHONE_NUMBER, userInfo.phoneNumber)
@@ -116,10 +121,17 @@ export const savePhoneAuthData = (phoneData) => {
avatarUrl: userInfo.avatarUrl || '' avatarUrl: userInfo.avatarUrl || ''
} }
uni.setStorageSync(STORAGE_KEYS.WX_USER_INFO, wxUserInfo) uni.setStorageSync(STORAGE_KEYS.WX_USER_INFO, wxUserInfo)
uni.setStorageSync(STORAGE_KEYS.USER_INFO, userInfo)
} }
setLoginStep(LOGIN_STEPS.PHONE_AUTHED) // 根据用户信息完善状态设置登录步骤
console.log('保存手机号授权数据:', { token: !!token, userInfo: !!userInfo }) if (userInfo && userInfo.profileCompleted) {
setLoginStep(LOGIN_STEPS.PROFILE_COMPLETED)
} else {
setLoginStep(LOGIN_STEPS.PHONE_AUTHED)
}
console.log('保存手机号授权数据:', { token: !!token, refreshToken: !!refreshToken, userInfo: !!userInfo })
} }
/** /**
@@ -143,12 +155,122 @@ export const saveProfileData = (profileData) => {
/** /**
* 清理登录流程中的临时数据 * 清理登录流程中的临时数据
* @deprecated 使用 http/utils/auth-helper.js 中的 clearTempLoginData 替代
*/ */
export const clearTempLoginData = () => { export const clearTempLoginData = () => {
// 清理临时数据,保留重要的用户信息 const { clearTempLoginData } = require('../http/utils/auth-helper.js')
uni.removeStorageSync(STORAGE_KEYS.WX_LOGIN_CODE) clearTempLoginData()
}
console.log('清理临时登录数据') /**
* 检查并验证token有效性
* @returns {Promise<boolean>} token是否有效
* @deprecated 使用 http/utils/auth-helper.js 中的 validateToken 替代
*/
export const validateToken = async () => {
const { validateToken } = await import('../http/utils/auth-helper.js')
return await validateToken()
}
/**
* 尝试刷新token
* @returns {Promise<boolean>} 是否刷新成功
*/
export const tryRefreshToken = async () => {
const refreshToken = uni.getStorageSync(STORAGE_KEYS.REFRESH_TOKEN)
if (!refreshToken) {
return false
}
try {
const { refreshToken: refreshTokenApi } = await import('@/http/api/auth.js')
const result = await refreshTokenApi({
custom: {
loading: false,
toast: false
}
})
if (result && result.token) {
uni.setStorageSync(STORAGE_KEYS.TOKEN, result.token)
if (result.refreshToken) {
uni.setStorageSync(STORAGE_KEYS.REFRESH_TOKEN, result.refreshToken)
}
console.log('Token刷新成功')
return true
}
return false
} catch (error) {
console.log('Token刷新失败:', error)
return false
}
}
/**
* 自动登录(用于用户再次进入小程序)
* @returns {Promise<boolean>} 是否登录成功
*/
export const autoLogin = async () => {
console.log('开始自动登录检查')
// 1. 检查token有效性
if (await validateToken()) {
console.log('Token有效,自动登录成功')
return true
}
// 2. 尝试刷新token
if (await tryRefreshToken()) {
console.log('Token刷新成功,自动登录成功')
return true
}
// 3. 尝试使用微信code重新登录(不需要手机号授权)
try {
const loginRes = await new Promise((resolve, reject) => {
uni.login({
success: resolve,
fail: reject
})
})
if (loginRes.code) {
const { wxLogin } = await import('@/http/api/auth.js')
const result = await wxLogin({
code: loginRes.code
}, {
custom: {
loading: false,
toast: false
}
})
if (result && result.token) {
// 保存新的登录信息
uni.setStorageSync(STORAGE_KEYS.TOKEN, result.token)
if (result.refreshToken) {
uni.setStorageSync(STORAGE_KEYS.REFRESH_TOKEN, result.refreshToken)
}
if (result.userInfo) {
uni.setStorageSync(STORAGE_KEYS.WX_USER_INFO, result.userInfo)
uni.setStorageSync(STORAGE_KEYS.USER_INFO, result.userInfo)
// 如果用户已完善过信息,设置为已完成状态
if (result.userInfo.profileCompleted) {
setLoginStep(LOGIN_STEPS.PROFILE_COMPLETED)
} else {
setLoginStep(LOGIN_STEPS.PHONE_AUTHED)
}
}
console.log('微信code登录成功,自动登录成功')
return true
}
}
} catch (error) {
console.log('微信code登录失败:', error)
}
console.log('自动登录失败,需要用户手动登录')
return false
} }
/** /**
@@ -158,6 +280,7 @@ export const resetLoginState = () => {
uni.removeStorageSync(STORAGE_KEYS.LOGIN_STEP) uni.removeStorageSync(STORAGE_KEYS.LOGIN_STEP)
uni.removeStorageSync(STORAGE_KEYS.WX_LOGIN_CODE) uni.removeStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
uni.removeStorageSync(STORAGE_KEYS.TOKEN) uni.removeStorageSync(STORAGE_KEYS.TOKEN)
uni.removeStorageSync(STORAGE_KEYS.REFRESH_TOKEN)
uni.removeStorageSync(STORAGE_KEYS.PHONE_NUMBER) uni.removeStorageSync(STORAGE_KEYS.PHONE_NUMBER)
uni.removeStorageSync(STORAGE_KEYS.WX_USER_INFO) uni.removeStorageSync(STORAGE_KEYS.WX_USER_INFO)
uni.removeStorageSync(STORAGE_KEYS.USER_INFO) uni.removeStorageSync(STORAGE_KEYS.USER_INFO)
+263
View File
@@ -0,0 +1,263 @@
// 微信小程序认证工具类
// 封装微信小程序登录的核心逻辑
import { HTTP_CONFIG } from '@/http/config/config.js'
/**
* 微信小程序认证工具类
*/
export class WechatAuth {
/**
* 微信登录获取code
* @returns {Promise<string>} 微信授权码
*/
static async getWxCode() {
return new Promise((resolve, reject) => {
wx.login({
success: (res) => {
console.log(res)
if (res.code) {
resolve(res.code)
} else {
reject(new Error('获取微信授权码失败'))
}
},
fail: (error) => {
reject(new Error('微信登录失败: ' + (error.errMsg || '未知错误')))
}
})
})
}
/**
* 获取用户信息(需要用户授权)
* @param {string} desc 申请理由
* @returns {Promise<Object>} 用户信息
*/
static async getUserProfile(desc = '获取用户头像昵称') {
return new Promise((resolve, reject) => {
// #ifdef MP-WEIXIN
wx.getUserProfile({
desc: desc,
success: (res) => {
resolve(res)
},
fail: (error) => {
if (error.errMsg && error.errMsg.includes('auth deny')) {
reject(new Error('用户拒绝授权'))
} else {
reject(new Error('获取用户信息失败: ' + (error.errMsg || '未知错误')))
}
}
})
// #endif
// #ifndef MP-WEIXIN
reject(new Error('当前环境不支持微信授权'))
// #endif
})
}
/**
* 获取用户基本信息(无需授权,但信息有限)
* @returns {Promise<Object>} 用户基本信息
*/
static async getUserInfo() {
return new Promise((resolve, reject) => {
uni.getUserInfo({
success: (res) => {
resolve(res)
},
fail: (error) => {
reject(new Error('获取用户基本信息失败: ' + (error.errMsg || '未知错误')))
}
})
})
}
/**
* 获取用户手机号(需要用户授权)
* 注意:这个方法需要在button组件的open-type="getPhoneNumber"的回调中使用
* @param {Object} e 手机号授权回调事件对象
* @returns {Object} 手机号相关数据
*/
static getPhoneNumber(e) {
if (e.detail.errMsg === 'getPhoneNumber:ok') {
return {
success: true,
encryptedData: e.detail.encryptedData,
iv: e.detail.iv,
cloudID: e.detail.cloudID
}
} else {
return {
success: false,
error: e.detail.errMsg || '获取手机号失败'
}
}
}
/**
* 生成随机头像
* @returns {string} 随机头像URL
*/
static generateRandomAvatar() {
const avatars = [
'https://img.yzcdn.cn/vant/cat.jpeg',
'https://img.yzcdn.cn/vant/dog.jpeg',
'https://img.yzcdn.cn/vant/rabbit.jpeg',
'https://img.yzcdn.cn/vant/bird.jpeg'
]
return avatars[Math.floor(Math.random() * avatars.length)]
}
/**
* 保存登录信息到本地存储
* @param {string} token 访问令牌
* @param {string} refreshToken 刷新令牌(可选)
* @param {Object} userInfo 用户信息
*/
static saveLoginInfo(token, refreshToken = null, userInfo = null) {
try {
// 使用配置中定义的存储键名
uni.setStorageSync(HTTP_CONFIG.storageKeys.token, token)
if (refreshToken) {
uni.setStorageSync(HTTP_CONFIG.storageKeys.refreshToken, refreshToken)
}
if (userInfo) {
uni.setStorageSync(HTTP_CONFIG.storageKeys.userInfo, userInfo)
}
console.log('登录信息保存成功')
} catch (error) {
console.error('保存登录信息失败:', error)
throw new Error('保存登录信息失败')
}
}
/**
* 获取本地存储的token
* @returns {string|null} 访问令牌
*/
static getToken() {
try {
return uni.getStorageSync(HTTP_CONFIG.storageKeys.token) || null
} catch (error) {
console.error('获取token失败:', error)
return null
}
}
/**
* 获取本地存储的刷新令牌
* @returns {string|null} 刷新令牌
*/
static getRefreshToken() {
try {
return uni.getStorageSync(HTTP_CONFIG.storageKeys.refreshToken) || null
} catch (error) {
console.error('获取refreshToken失败:', error)
return null
}
}
/**
* 获取本地存储的用户信息
* @returns {Object|null} 用户信息
*/
static getUserInfoFromStorage() {
try {
const userInfo = uni.getStorageSync(HTTP_CONFIG.storageKeys.userInfo)
return userInfo ? (typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo) : null
} catch (error) {
console.error('获取用户信息失败:', error)
return null
}
}
/**
* 检查是否已登录
* @returns {boolean} 是否已登录
*/
static isLoggedIn() {
const token = this.getToken()
return !!token
}
/**
* 清除所有登录信息
* @deprecated 使用 http/utils/auth-helper.js 中的 clearAuthData 替代
*/
static clearLoginInfo() {
const { clearAuthData } = require('../http/utils/auth-helper.js')
clearAuthData()
}
/**
* 跳转到登录页面
* @deprecated 使用 http/utils/auth-helper.js 中的 navigateToLogin 替代
*/
static navigateToLogin() {
const { navigateToLogin } = require('../http/utils/auth-helper.js')
navigateToLogin()
}
/**
* 检查登录状态并处理
* @returns {boolean} 是否已登录
* @deprecated 使用 http/utils/auth-helper.js 中的 checkLoginStatus 替代
*/
static checkLoginStatus() {
const { checkLoginStatus } = require('../http/utils/auth-helper.js')
return checkLoginStatus()
}
/**
* 完整的微信小程序登录流程
* @param {boolean} needUserInfo 是否需要获取用户详细信息
* @returns {Promise<Object>} 登录结果
*/
static async performWxLogin(needUserInfo = true) {
try {
// 1. 获取微信授权码
const code = await this.getWxCode()
// 2. 准备登录数据
const loginData = { code }
// 3. 如果需要用户信息,尝试获取
if (needUserInfo) {
try {
const userProfile = await this.getUserProfile()
loginData.encryptedData = userProfile.encryptedData
loginData.iv = userProfile.iv
loginData.signature = userProfile.signature
loginData.userInfo = userProfile.userInfo
} catch (userInfoError) {
console.warn('获取用户详细信息失败,使用基本登录:', userInfoError.message)
// 继续使用基本登录,不中断流程
}
}
return {
success: true,
data: loginData,
message: '微信授权成功'
}
} catch (error) {
console.error('微信登录流程失败:', error)
return {
success: false,
error: error.message,
message: '微信登录失败'
}
}
}
}
/**
* 默认导出,便于直接导入使用
*/
export default WechatAuth