diff --git a/DIAGNOSIS_VIEW_RECORD_FEATURE.md b/DIAGNOSIS_VIEW_RECORD_FEATURE.md new file mode 100644 index 00000000..6bd3ad34 --- /dev/null +++ b/DIAGNOSIS_VIEW_RECORD_FEATURE.md @@ -0,0 +1,218 @@ +# 诊单查看记录功能 + +## 功能概述 +记录用户查看和确认诊单的行为,包括查看次数、查看时间、确认状态等信息。 + +## 数据库表结构 + +### la_diagnosis_view_records 表 +```sql +CREATE TABLE `la_diagnosis_view_records` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` int(11) NOT NULL DEFAULT '0' COMMENT '查看用户ID', + `diagnosis_id` int(11) NOT NULL DEFAULT '0' COMMENT '诊单ID', + `patient_id` int(11) NOT NULL DEFAULT '0' COMMENT '患者ID', + `share_user_id` int(11) NOT NULL DEFAULT '0' COMMENT '分享用户ID', + `view_count` int(11) NOT NULL DEFAULT '1' COMMENT '查看次数', + `first_view_time` int(11) NOT NULL DEFAULT '0' COMMENT '首次查看时间', + `last_view_time` int(11) NOT NULL DEFAULT '0' COMMENT '最后查看时间', + `is_confirmed` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已确认 0-未确认 1-已确认', + `confirmed_time` int(11) NOT NULL DEFAULT '0' COMMENT '确认时间', + `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', + `delete_time` int(11) DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`), + KEY `idx_user_diagnosis` (`user_id`, `diagnosis_id`), + KEY `idx_diagnosis` (`diagnosis_id`), + KEY `idx_patient` (`patient_id`), + KEY `idx_share_user` (`share_user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='诊单查看记录表'; +``` + +## 字段说明 + +| 字段 | 类型 | 说明 | +|------|------|------| +| user_id | int | 查看用户ID(当前登录用户) | +| diagnosis_id | int | 诊单ID | +| patient_id | int | 患者ID(从诊单获取) | +| share_user_id | int | 分享用户ID(生成二维码的用户) | +| view_count | int | 查看次数(每次打开页面+1) | +| first_view_time | int | 首次查看时间戳 | +| last_view_time | int | 最后查看时间戳 | +| is_confirmed | tinyint | 是否已确认(0-未确认 1-已确认) | +| confirmed_time | int | 确认时间戳 | + +## 后端实现 + +### 1. 模型文件 +`server/app/common/model/DiagnosisViewRecord.php` + +### 2. 控制器方法 +`server/app/api/controller/TcmController.php` + +#### diagnosisDetail - 获取诊单详情 +```php +GET /api/tcm/diagnosisDetail +参数: +- id: 诊单ID(必填) +- user_id: 查看用户ID(选填,默认0) +- share_user_id: 分享用户ID(选填,默认0) + +功能: +1. 获取诊单详情 +2. 自动记录查看行为 +3. 如果是首次查看,创建新记录 +4. 如果已有记录,更新查看次数和最后查看时间 +``` + +#### confirmDiagnosis - 确认诊单 +```php +POST /api/tcm/confirmDiagnosis +参数: +- id: 诊单ID(必填) +- user_id: 查看用户ID(选填,默认0) +- share_user: 分享用户ID(选填,默认0) + +功能: +1. 更新查看记录的确认状态 +2. 记录确认时间 +3. 如果没有查看记录,创建一条已确认的记录 +``` + +### 3. 逻辑层方法 +`server/app/adminapi/logic/tcm/DiagnosisLogic.php` + +#### diagnosisDetailWithRecord +- 获取诊单详情并记录查看行为 +- 调用 `recordDiagnosisView` 记录查看 + +#### recordDiagnosisView +- 查找是否已有记录 +- 首次查看:创建新记录 +- 再次查看:更新查看次数和最后查看时间 + +#### confirmDiagnosisRecord +- 更新确认状态 +- 记录确认时间 + +## 前端实现 + +### 小程序页面 +`TUICallKit-Vue3/pages/order/monad/monad.vue` + +#### 参数解析 +```javascript +const parsePageParams = (options) => { + // 解析scene参数(扫码进入) + // 解析普通参数(跳转进入) + // 获取当前登录用户ID + return params; +}; +``` + +#### 加载诊单详情 +```javascript +const res = await proxy.apiUrl({ + url: '/api/tcm/diagnosisDetail', + method: 'GET', + data: { + id: diagnosisId, + user_id: params.user_id || 0, + share_user_id: params.share_user || 0 + } +}); +``` + +#### 确认诊单 +```javascript +const res = await proxy.apiUrl({ + url: '/api/tcm/confirmDiagnosis', + method: 'POST', + data: { + id: params.id, + user_id: params.user_id || 0, + share_user: params.share_user || 0 + } +}); +``` + +## 使用流程 + +### 1. 生成二维码 +管理员在后台点击"小程序二维码"按钮 +- 传递参数:`diagnosis_id`, `patient_id`, `share_user_id` +- 生成scene: `id=4&share_user=1` + +### 2. 用户扫码 +用户使用微信扫描二维码 +- 小程序接收参数:`scene: "id%3D4%26share_user%3D1"` +- 解析参数:`{ id: "4", share_user: "1" }` + +### 3. 查看诊单 +小程序加载诊单详情 +- 调用 `/api/tcm/diagnosisDetail` +- 传递:`id`, `user_id`, `share_user_id` +- 后端自动记录查看行为 + +### 4. 确认诊单 +用户点击"确认诊单"按钮 +- 调用 `/api/tcm/confirmDiagnosis` +- 传递:`id`, `user_id`, `share_user` +- 后端更新确认状态 + +## 数据统计 + +### 可统计的数据 +1. 每个诊单的查看次数 +2. 每个用户查看了哪些诊单 +3. 诊单的确认率 +4. 分享效果(通过share_user_id统计) +5. 首次查看到确认的时间间隔 + +### 查询示例 + +#### 查询某个诊单的查看记录 +```php +$records = DiagnosisViewRecord::where('diagnosis_id', $diagnosisId) + ->with(['user', 'shareUser']) + ->select(); +``` + +#### 查询某个用户的查看记录 +```php +$records = DiagnosisViewRecord::where('user_id', $userId) + ->with(['diagnosis']) + ->select(); +``` + +#### 统计诊单查看次数 +```php +$viewCount = DiagnosisViewRecord::where('diagnosis_id', $diagnosisId) + ->sum('view_count'); +``` + +#### 统计确认率 +```php +$total = DiagnosisViewRecord::where('diagnosis_id', $diagnosisId)->count(); +$confirmed = DiagnosisViewRecord::where('diagnosis_id', $diagnosisId) + ->where('is_confirmed', 1) + ->count(); +$rate = $total > 0 ? ($confirmed / $total * 100) : 0; +``` + +## 注意事项 + +1. **用户ID获取**: 需要在小程序中实现用户登录,获取当前用户ID +2. **匿名访问**: 如果允许匿名访问,user_id可以为0 +3. **数据清理**: 建议定期清理过期的查看记录 +4. **性能优化**: 查看记录表可能增长较快,注意添加索引 +5. **隐私保护**: 查看记录涉及用户行为,注意数据安全 + +## TODO + +- [ ] 在小程序中实现用户登录功能 +- [ ] 在 `parsePageParams` 中添加获取用户ID的逻辑 +- [ ] 添加查看记录的管理界面 +- [ ] 添加数据统计报表 +- [ ] 实现查看记录的导出功能 diff --git a/DIAGNOSIS_VIEW_RECORD_INSTALL.md b/DIAGNOSIS_VIEW_RECORD_INSTALL.md new file mode 100644 index 00000000..2dfaeefa --- /dev/null +++ b/DIAGNOSIS_VIEW_RECORD_INSTALL.md @@ -0,0 +1,223 @@ +# 诊单查看记录功能 - 安装指南 + +## 安装步骤 + +### 1. 创建数据库表 + +执行以下SQL语句创建诊单查看记录表: + +```bash +# 方式1: 使用MySQL客户端执行 +mysql -u root -p your_database < server/database/diagnosis_view_records.sql + +# 方式2: 在phpMyAdmin或其他数据库管理工具中执行 +# 打开文件: server/database/diagnosis_view_records.sql +# 复制SQL语句并执行 +``` + +SQL文件位置: `server/database/diagnosis_view_records.sql` + +### 2. 验证表创建 + +登录数据库,检查表是否创建成功: + +```sql +SHOW TABLES LIKE 'la_diagnosis_view_records'; +DESC la_diagnosis_view_records; +``` + +### 3. 文件清单 + +确认以下文件已创建: + +#### 后端文件 +- ✅ `server/database/diagnosis_view_records.sql` - 数据库表结构 +- ✅ `server/app/common/model/DiagnosisViewRecord.php` - 模型文件 +- ✅ `server/app/api/controller/TcmController.php` - 控制器(已更新) +- ✅ `server/app/adminapi/logic/tcm/DiagnosisLogic.php` - 逻辑层(已更新) + +#### 前端文件 +- ✅ `TUICallKit-Vue3/pages/order/monad/monad.vue` - 小程序页面(已更新) + +#### 文档文件 +- ✅ `DIAGNOSIS_VIEW_RECORD_FEATURE.md` - 功能说明文档 +- ✅ `DIAGNOSIS_VIEW_RECORD_INSTALL.md` - 安装指南(本文件) +- ✅ `MINI_PROGRAM_SCENE_PARAMS.md` - Scene参数说明 + +### 4. 配置小程序用户ID获取 + +在小程序中添加用户ID获取逻辑: + +```javascript +// TUICallKit-Vue3/pages/order/monad/monad.vue +// 在 parsePageParams 函数中添加 + +const parsePageParams = (options) => { + const params = {}; + + // ... 现有的解析逻辑 ... + + // 获取当前登录用户ID + try { + // 方式1: 从本地存储获取 + const userInfo = uni.getStorageSync('userInfo'); + params.user_id = userInfo?.id || 0; + + // 方式2: 从全局状态获取 + // const app = getApp(); + // params.user_id = app.globalData.userId || 0; + + // 方式3: 从Vuex获取 + // import { useUserStore } from '@/stores/user'; + // const userStore = useUserStore(); + // params.user_id = userStore.userId || 0; + } catch (error) { + console.error('获取用户ID失败:', error); + params.user_id = 0; + } + + return params; +}; +``` + +### 5. 测试功能 + +#### 5.1 测试查看记录 +1. 在管理后台生成小程序二维码 +2. 使用微信扫描二维码 +3. 小程序打开诊单详情页 +4. 检查数据库 `la_diagnosis_view_records` 表,应该有一条新记录 + +```sql +SELECT * FROM la_diagnosis_view_records ORDER BY id DESC LIMIT 1; +``` + +#### 5.2 测试重复查看 +1. 再次扫描同一个二维码 +2. 检查数据库,`view_count` 应该增加,`last_view_time` 应该更新 + +```sql +SELECT id, user_id, diagnosis_id, view_count, + FROM_UNIXTIME(first_view_time) as first_view, + FROM_UNIXTIME(last_view_time) as last_view +FROM la_diagnosis_view_records +WHERE diagnosis_id = 4; +``` + +#### 5.3 测试确认功能 +1. 在小程序中点击"确认诊单"按钮 +2. 检查数据库,`is_confirmed` 应该变为1,`confirmed_time` 应该有值 + +```sql +SELECT id, diagnosis_id, is_confirmed, + FROM_UNIXTIME(confirmed_time) as confirmed_at +FROM la_diagnosis_view_records +WHERE diagnosis_id = 4; +``` + +### 6. API接口测试 + +#### 测试 diagnosisDetail 接口 +```bash +curl -X GET "http://your-domain/api/tcm/diagnosisDetail?id=4&user_id=1&share_user_id=2" +``` + +预期响应: +```json +{ + "code": 1, + "msg": "success", + "data": { + "id": 4, + "patient_name": "张三", + // ... 其他诊单信息 + } +} +``` + +#### 测试 confirmDiagnosis 接口 +```bash +curl -X POST "http://your-domain/api/tcm/confirmDiagnosis" \ + -H "Content-Type: application/json" \ + -d '{"id": 4, "user_id": 1, "share_user": 2}' +``` + +预期响应: +```json +{ + "code": 1, + "msg": "确认成功", + "data": [] +} +``` + +### 7. 常见问题 + +#### Q1: 表创建失败 +**A:** 检查数据库权限,确保有CREATE TABLE权限 + +#### Q2: 查看记录没有创建 +**A:** 检查以下几点: +- 确认表已创建 +- 检查PHP错误日志 +- 确认 `DiagnosisViewRecord` 模型文件存在 +- 检查数据库连接配置 + +#### Q3: user_id 总是0 +**A:** 需要在小程序中实现用户登录功能,并在 `parsePageParams` 中获取用户ID + +#### Q4: 确认功能不工作 +**A:** 检查: +- 确认接口已添加到 `$notNeedLogin` 数组 +- 检查POST参数是否正确传递 +- 查看PHP错误日志 + +### 8. 数据库索引优化 + +如果查看记录表数据量较大,建议添加以下索引: + +```sql +-- 已在建表语句中包含,如果没有可手动添加 +ALTER TABLE la_diagnosis_view_records +ADD INDEX idx_user_diagnosis (user_id, diagnosis_id); + +ALTER TABLE la_diagnosis_view_records +ADD INDEX idx_diagnosis (diagnosis_id); + +ALTER TABLE la_diagnosis_view_records +ADD INDEX idx_patient (patient_id); + +ALTER TABLE la_diagnosis_view_records +ADD INDEX idx_share_user (share_user_id); +``` + +### 9. 数据清理 + +定期清理过期的查看记录(可选): + +```sql +-- 删除30天前的未确认记录 +DELETE FROM la_diagnosis_view_records +WHERE is_confirmed = 0 +AND create_time < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY)); + +-- 或使用软删除 +UPDATE la_diagnosis_view_records +SET delete_time = UNIX_TIMESTAMP() +WHERE is_confirmed = 0 +AND create_time < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY)); +``` + +### 10. 下一步 + +功能安装完成后,可以: +1. 实现查看记录的管理界面 +2. 添加数据统计报表 +3. 实现查看记录的导出功能 +4. 添加消息通知(如:诊单被查看时通知医生) + +## 相关文档 + +- [功能说明文档](./DIAGNOSIS_VIEW_RECORD_FEATURE.md) +- [Scene参数说明](./MINI_PROGRAM_SCENE_PARAMS.md) +- [小程序二维码功能](./QRCODE_README.md) diff --git a/MINI_PROGRAM_SCENE_PARAMS.md b/MINI_PROGRAM_SCENE_PARAMS.md new file mode 100644 index 00000000..fa43e206 --- /dev/null +++ b/MINI_PROGRAM_SCENE_PARAMS.md @@ -0,0 +1,146 @@ +# 小程序二维码Scene参数说明 + +## 概述 +小程序通过扫描二维码进入时,参数通过 `scene` 字段传递,格式为URL编码的键值对字符串。 + +## 后端生成 + +### DiagnosisLogic.php +```php +// 构建scene参数 +$scene = "id={$diagnosisId}&share_user={$shareUserId}"; + +// 调用微信接口 +$data = [ + 'scene' => $scene, // 例如: "id=4&share_user=1" + 'page' => 'pages/order/monad/monad', + 'check_path' => false, + 'env_version' => 'release', + 'width' => 280 +]; +``` + +### 微信返回的scene格式 +微信会自动对scene进行URL编码,小程序接收到的格式为: +``` +scene: "id%3D4%26share_user%3D1" +``` + +## 前端解析 + +### monad.vue 解析逻辑 + +```javascript +// 解析页面参数(支持普通参数和scene参数) +const parsePageParams = (options) => { + const params = {}; + + // 如果有scene参数(扫码进入),解析scene + if (options.scene) { + try { + // URL解码scene参数: "id%3D4%26share_user%3D1" -> "id=4&share_user=1" + const decodedScene = decodeURIComponent(options.scene); + + // 解析键值对 + decodedScene.split('&').forEach(item => { + const [key, value] = item.split('='); + if (key && value) { + params[key] = value; + } + }); + } catch (error) { + console.error('解析scene参数失败:', error); + } + } + + // 合并普通参数(普通跳转进入) + Object.keys(options).forEach(key => { + if (key !== 'scene' && options[key]) { + params[key] = options[key]; + } + }); + + return params; +}; +``` + +### 使用示例 + +```javascript +// 在onMounted或需要的地方调用 +const pages = getCurrentPages(); +const currentPage = pages[pages.length - 1]; +const params = parsePageParams(currentPage.options); + +console.log('解析后的参数:', params); +// 输出: { id: "4", share_user: "1" } + +// 使用参数 +const diagnosisId = params.id; +const shareUserId = params.share_user; +``` + +## 两种进入方式 + +### 1. 扫码进入(scene参数) +```javascript +// currentPage.options +{ + scene: "id%3D4%26share_user%3D1" +} + +// 解析后 +{ + id: "4", + share_user: "1" +} +``` + +### 2. 普通跳转进入(直接参数) +```javascript +// currentPage.options +{ + id: "4", + share_user: "1" +} + +// 解析后 +{ + id: "4", + share_user: "1" +} +``` + +## 注意事项 + +1. **scene长度限制**: 微信小程序scene参数最大长度为32个字符(编码前) +2. **URL编码**: 微信会自动对scene进行URL编码,前端需要使用 `decodeURIComponent` 解码 +3. **参数格式**: 使用 `key=value&key2=value2` 格式,不要使用JSON +4. **兼容性**: 解析函数同时支持scene参数和普通参数,确保两种进入方式都能正常工作 + +## 调试方法 + +### 查看原始参数 +```javascript +const pages = getCurrentPages(); +const currentPage = pages[pages.length - 1]; +console.log('原始options:', currentPage.options); +``` + +### 查看解析结果 +```javascript +const params = parsePageParams(currentPage.options); +console.log('解析后的参数:', params); +console.log('诊单ID:', params.id); +console.log('分享用户ID:', params.share_user); +``` + +## 完整流程 + +1. **管理后台**: 点击"小程序二维码"按钮 +2. **后端生成**: 调用 `generateMiniProgramQrcode` 接口 +3. **构建scene**: `id=4&share_user=1` +4. **微信编码**: 微信自动编码为 `id%3D4%26share_user%3D1` +5. **用户扫码**: 小程序接收到 `options.scene = "id%3D4%26share_user%3D1"` +6. **前端解析**: 使用 `parsePageParams` 解析为 `{ id: "4", share_user: "1" }` +7. **加载数据**: 使用解析后的参数加载诊单详情 diff --git a/TUICallKit-Vue3/App.vue b/TUICallKit-Vue3/App.vue index 67038e43..e818eea5 100644 --- a/TUICallKit-Vue3/App.vue +++ b/TUICallKit-Vue3/App.vue @@ -16,13 +16,16 @@ onLaunch(() => { success: function (loginRes) { console.log('xx'); console.log(loginRes.code); + if( uni.getStorageSync('token')){ + userinfo(loginRes.code); + }else{ wxcode(loginRes.code) + } + // 获取用户信息 } }); - - }) const wxcode = async (code) => { @@ -36,11 +39,30 @@ const wxcode = async (code) => { }, }, false) // 不显示加载中 uni.setStorageSync('token', res.data.token) - uni.setStorageSync('userData', res.data) + uni.setStorageSync('userData', res.data) console.log('请求结果:', res.data) } catch (err) { + } + +} +const userinfo = async (code) => { + + try { + // 通过 proxy 调用全局的 apiUrl + const res = await proxy.apiUrl({ + url: '/api/user/info', + method: 'POST', + }, false) // 不显示加载中 + if(res.code==-1){ + + wxcode(code) + } + uni.setStorageSync('userData', res.data) + } catch (err) { + + } } diff --git a/TUICallKit-Vue3/main.js b/TUICallKit-Vue3/main.js index 229c8a52..63dff635 100644 --- a/TUICallKit-Vue3/main.js +++ b/TUICallKit-Vue3/main.js @@ -4,7 +4,7 @@ import App from './App' import Vue from 'vue' import './uni.promisify.adaptor' Vue.config.productionTip = false -Vue.prototype.$url = 'http://www.d.com' +Vue.prototype.$url = 'http://api.zzzhengyangtang.cn' Vue.prototype.apiUrl =function apiurl(promise, Loading = true){ let token=uni.getStorageSync('token') promise.header={ @@ -55,7 +55,7 @@ app.$mount() import { createSSRApp } from 'vue' // 定义全局基础URL -const baseUrl = 'http://www.d.com' +const baseUrl = 'http://api.zzzhengyangtang.cn' // 封装Vue3版本的apiUrl方法 function apiUrl(promise, Loading = true) { diff --git a/TUICallKit-Vue3/pages.json b/TUICallKit-Vue3/pages.json index f80b60a6..aad98ed5 100644 --- a/TUICallKit-Vue3/pages.json +++ b/TUICallKit-Vue3/pages.json @@ -1,9 +1,9 @@ { "pages": [ { - "path": "pages/index/login", + "path": "pages/index/video", "style": { - "navigationBarTitleText": "uni-app" + "navigationBarTitleText": "甄养堂" } }, { @@ -15,7 +15,9 @@ { "path": "TUICallKit/src/Components/TUICallKit", "style": { - "navigationBarTitleText": "视频面诊" + "navigationBarTitleText": "视频面诊", + "navigationBarHidden": true, + "navigationStyle": "custom" } }, { diff --git a/TUICallKit-Vue3/pages/index/login.vue b/TUICallKit-Vue3/pages/index/login.vue deleted file mode 100644 index 0030cb3a..00000000 --- a/TUICallKit-Vue3/pages/index/login.vue +++ /dev/null @@ -1,320 +0,0 @@ - - - - - diff --git a/TUICallKit-Vue3/pages/index/video.vue b/TUICallKit-Vue3/pages/index/video.vue new file mode 100644 index 00000000..c7a825ea --- /dev/null +++ b/TUICallKit-Vue3/pages/index/video.vue @@ -0,0 +1,578 @@ + + + + + diff --git a/TUICallKit-Vue3/pages/order/monad/monad.vue b/TUICallKit-Vue3/pages/order/monad/monad.vue index 684e7c4a..e86d856f 100644 --- a/TUICallKit-Vue3/pages/order/monad/monad.vue +++ b/TUICallKit-Vue3/pages/order/monad/monad.vue @@ -1,6 +1,16 @@