From fdca37a8b5e6284eb746d4ac3a1a5fc548d0f6a3 Mon Sep 17 00:00:00 2001 From: zeyan <258785420@qq.com> Date: Thu, 30 Oct 2025 22:43:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- niucloud/app/api/route/pay.php | 1 - .../app/listener/pay/PayCreateListener.php | 53 +++++ .../app/listener/pay/PaySuccessListener.php | 66 ++++++ .../api/apiService/CoachStudentService.php | 65 +++++- uniapp/api/apiRoute.js | 15 +- uniapp/components/order-list-card/index.vue | 206 +++++++++++++++++- uniapp/pages-market/clue/clue_info.vue | 32 ++- 7 files changed, 416 insertions(+), 22 deletions(-) diff --git a/niucloud/app/api/route/pay.php b/niucloud/app/api/route/pay.php index ac88efb5..efbf9c94 100755 --- a/niucloud/app/api/route/pay.php +++ b/niucloud/app/api/route/pay.php @@ -45,5 +45,4 @@ Route::group('pay',function () { Route::post('close', 'pay.Pay/close'); })->middleware(ApiChannel::class) - ->middleware(ApiCheckToken::class, true)//表示验证登录 ->middleware(ApiLog::class); diff --git a/niucloud/app/listener/pay/PayCreateListener.php b/niucloud/app/listener/pay/PayCreateListener.php index 30762249..7fdeed05 100755 --- a/niucloud/app/listener/pay/PayCreateListener.php +++ b/niucloud/app/listener/pay/PayCreateListener.php @@ -11,14 +11,67 @@ namespace app\listener\pay; +use app\model\order_table\OrderTable; /** * 支付单据创建事件 */ class PayCreateListener { + /** + * 处理支付创建事件 + * @param array $params + * @return array|null + */ public function handle(array $params) { + $trade_type = $params['trade_type'] ?? ''; + $trade_id = $params['trade_id'] ?? 0; + // 根据不同的业务类型处理支付创建 + switch ($trade_type) { + case 'school_order_table': + return $this->handleOrderPayment($trade_id); + + // TODO: 添加其他业务类型的支付创建处理 + // case 'recharge': + // return $this->handleRechargePayment($trade_id); + + default: + return null; + } + } + + /** + * 处理订单支付创建 + * @param int $trade_id 订单ID + * @return array|null + */ + private function handleOrderPayment(int $trade_id) + { + // 查询订单信息 + $order = OrderTable::where('id', $trade_id)->find(); + + if (!$order) { + return null; + } + + // TODO: 根据订单信息构建支付数据 + // 返回格式必须包含以下字段: + // - main_type: 支付主体类型(如 'member') + // - main_id: 支付主体ID(如会员ID) + // - money: 支付金额 + // - trade_type: 业务类型 + // - trade_id: 业务ID + // - body: 支付描述 + + return [ + 'main_type' => 'member', // TODO: 从订单获取会员类型 + 'main_id' => $order->student_id ?? 0, // TODO: 从订单获取会员ID + 'money' => $order->order_amount ?? 0, + 'trade_type' => 'school_order_table', + 'trade_id' => $trade_id, + 'body' => '订单支付 - ' . ($order->course_id_name ?? '课程') + ]; } } diff --git a/niucloud/app/listener/pay/PaySuccessListener.php b/niucloud/app/listener/pay/PaySuccessListener.php index e3569c9b..3752f2af 100755 --- a/niucloud/app/listener/pay/PaySuccessListener.php +++ b/niucloud/app/listener/pay/PaySuccessListener.php @@ -12,6 +12,9 @@ namespace app\listener\pay; use app\service\core\pay\CoreAccountService; +use app\model\order_table\OrderTable; +use app\service\api\apiService\OrderTableService; +use think\facade\Log; /** * 支付异步回调事件 @@ -23,6 +26,69 @@ class PaySuccessListener if (isset($pay_info['out_trade_no']) && !empty($pay_info['out_trade_no'])) { //账单记录添加 (new CoreAccountService())->addPayLog($pay_info); + + // 处理业务逻辑 + $this->handleBusinessLogic($pay_info); + } + } + + /** + * 处理业务逻辑 + * @param array $pay_info 支付信息 + */ + private function handleBusinessLogic(array $pay_info) + { + $trade_type = $pay_info['trade_type'] ?? ''; + $trade_id = $pay_info['trade_id'] ?? 0; + + // 根据不同的业务类型处理支付成功逻辑 + switch ($trade_type) { + case 'school_order_table': + $this->handleOrderPaymentSuccess($trade_id, $pay_info); + break; + + // TODO: 添加其他业务类型的支付成功处理 + // case 'recharge': + // $this->handleRechargeSuccess($trade_id, $pay_info); + // break; + + default: + Log::info('PaySuccessListener: 未处理的业务类型 - ' . $trade_type); + break; + } + } + + /** + * 处理订单支付成功 + * @param int $trade_id 订单ID + * @param array $pay_info 支付信息 + */ + private function handleOrderPaymentSuccess(int $trade_id, array $pay_info) + { + try { + // 更新订单状态为已支付 + $order = OrderTable::where('id', $trade_id)->find(); + + if (!$order) { + Log::error('PaySuccessListener: 订单不存在 - ID:' . $trade_id); + return; + } + + // TODO: 更新订单状态 + // 确认订单表的字段名和状态值 + $order->order_status = 'paid'; + $order->payment_time = date('Y-m-d H:i:s'); + $order->payment_id = $pay_info['out_trade_no'] ?? ''; + $order->save(); + + // TODO: 调用订单服务处理支付成功后的业务逻辑 + // 例如:分配课程、创建合同签署记录等 + $orderArray = $order->toArray(); + (new OrderTableService())->handlePaymentSuccess($orderArray); + + Log::info('PaySuccessListener: 订单支付成功处理完成 - 订单ID:' . $trade_id); + } catch (\Exception $e) { + Log::error('PaySuccessListener: 订单支付成功处理异常 - ' . $e->getMessage()); } } } diff --git a/niucloud/app/service/api/apiService/CoachStudentService.php b/niucloud/app/service/api/apiService/CoachStudentService.php index e9642cde..921ae97f 100755 --- a/niucloud/app/service/api/apiService/CoachStudentService.php +++ b/niucloud/app/service/api/apiService/CoachStudentService.php @@ -187,18 +187,32 @@ class CoachStudentService extends BaseApiService } /** - * 获取教练负责的学员ID集合 + * 获取教练负责的学员ID集合(带缓存) * @param int $coachId 教练ID * @param array $data 查询条件 * @return array */ private function getCoachStudentIds($coachId, $data = []) { - // 1. 从 school_student_courses 表中查询 main_coach_id 或 education_id 是当前教练的学员 + // 检查缓存 + $cacheKey = "coach_student_ids:{$coachId}"; + $cachedIds = cache($cacheKey); + + if ($cachedIds !== null && is_array($cachedIds)) { + return $cachedIds; + } + + // 1. 从 school_student_courses 表中查询相关学员 $courseStudentIds = Db::table('school_student_courses') ->where(function($query) use ($coachId) { $query->where('main_coach_id', $coachId) - ->whereOr('education_id', $coachId); + ->whereOr('education_id', $coachId) + ->whereOr(function($q) use ($coachId) { + // 查询 assistant_ids 字段中包含当前教练ID的记录 + $q->whereNotNull('assistant_ids') + ->where('assistant_ids', '<>', '') + ->whereRaw("FIND_IN_SET(?, assistant_ids)", [$coachId]); + }); }) ->column('student_id'); @@ -207,7 +221,13 @@ class CoachStudentService extends BaseApiService ->leftJoin('school_course_schedule cs', 'pcs.schedule_id = cs.id') ->where(function($query) use ($coachId) { $query->where('cs.education_id', $coachId) - ->whereOr('cs.coach_id', $coachId); + ->whereOr('cs.coach_id', $coachId) + ->whereOr(function($q) use ($coachId) { + // 查询 assistant_ids 字段中包含当前教练ID的记录 + $q->whereNotNull('cs.assistant_ids') + ->where('cs.assistant_ids', '<>', '') + ->whereRaw("FIND_IN_SET(?, cs.assistant_ids)", [$coachId]); + }); }) ->where('pcs.person_type', 'student') ->where('pcs.deleted_at', 0) @@ -217,8 +237,12 @@ class CoachStudentService extends BaseApiService // 3. 合并并去重学生ID $allStudentIds = array_merge($courseStudentIds, $scheduleStudentIds); $uniqueStudentIds = array_unique($allStudentIds); + $result = array_values($uniqueStudentIds); + + // 4. 缓存结果(1小时 = 3600秒) + cache($cacheKey, $result, 3600); - return array_values($uniqueStudentIds); + return $result; } /** @@ -387,13 +411,13 @@ class CoachStudentService extends BaseApiService if (empty($token)) { return 0; } - + // 去掉Bearer前缀 $token = str_replace('Bearer ', '', $token); - + // 使用项目的TokenAuth类解析token,类型为personnel(员工端) $tokenInfo = \core\util\TokenAuth::parseToken($token, 'personnel'); - + if (!empty($tokenInfo)) { // 从jti中提取用户ID(格式:用户ID_类型) $jti = $tokenInfo['jti'] ?? ''; @@ -404,10 +428,33 @@ class CoachStudentService extends BaseApiService } } } - + return 0; } catch (\Exception $e) { return 0; } } + + /** + * 清除教练学员关系缓存 + * @param int|array $coachId 教练ID或教练ID数组 + * @return bool + */ + public static function clearCoachStudentCache($coachId) + { + try { + if (is_array($coachId)) { + // 批量清除缓存 + foreach ($coachId as $id) { + cache("coach_student_ids:{$id}", null); + } + } else { + // 清除单个教练缓存 + cache("coach_student_ids:{$coachId}", null); + } + return true; + } catch (\Exception $e) { + return false; + } + } } \ No newline at end of file diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js index 33ccf058..96540b51 100755 --- a/uniapp/api/apiRoute.js +++ b/uniapp/api/apiRoute.js @@ -106,10 +106,6 @@ export default { }, //↓↓↓↓↓↓↓↓↓↓↓↓-----微信绑定相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓ - // 获取微信openid(支持小程序和公众号) - async getWechatOpenid(data = {}) { - return await http.post('/personnel/getWechatOpenid', data) - }, // 绑定微信openid到人员 async bindWechatOpenid(data = {}) { return await http.post('/personnel/bindWechatOpenid', data) @@ -528,6 +524,17 @@ export default { async updateOrderPaymentVoucher(data = {}) { return await http.post('/updateOrderPaymentVoucher', data) }, + + // 微信在线支付 - 创建支付订单 + async createWechatPayment(data = {}) { + return await http.post('/pay', data) + }, + + // 获取微信openid(使用code换取) + async getWechatOpenid(data = {}) { + return await http.post('/personnel/getWechatOpenid', data) + }, + //↓↓↓↓↓↓↓↓↓↓↓↓-----课程预约相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓ // 获取可预约课程列表 async getAvailableCourses(data = {}) { diff --git a/uniapp/components/order-list-card/index.vue b/uniapp/components/order-list-card/index.vue index 2ceabce9..4f93de4d 100755 --- a/uniapp/components/order-list-card/index.vue +++ b/uniapp/components/order-list-card/index.vue @@ -553,13 +553,207 @@ export default { }, /** - * 微信在线代付处理(本阶段只定义函数) + * 微信在线代付处理 */ - processWechatOnlinePayment(order) { - // TODO: 后续实现微信在线代付功能 - uni.showToast({ - title: '微信在线代付功能开发中', - icon: 'none' + async processWechatOnlinePayment(order) { + try { + uni.showLoading({ title: '正在发起支付...' }) + + // 获取用户的微信openid(异步方法) + const openid = await this.getWechatOpenid() + + if (!openid) { + uni.hideLoading() + uni.showModal({ + title: '提示', + content: '未获取到微信授权信息,请重新登录或授权', + showCancel: false, + success: () => { + // 可选:跳转到登录页面 + // uni.navigateTo({ url: '/pages/student/login/login' }) + } + }) + return + } + + console.log('发起微信支付 - 订单ID:', order._raw?.id || order.id) + console.log('发起微信支付 - OpenID:', openid.substring(0, 10) + '***') + + // 调用后端创建微信支付订单 + const res = await apiRoute.createWechatPayment({ + type: 'wechatpay', + trade_type: 'school_order_table', + trade_id: order._raw?.id || order.id, + openid: openid, + return_url: '', + quit_url: '', + buyer_id: '', + voucher: '' + }) + + uni.hideLoading() + + if (res.code !== 1 || !res.data) { + console.error('创建支付订单失败:', res) + uni.showToast({ title: res.msg || '发起支付失败', icon: 'none' }) + return + } + + console.log('支付参数:', res.data) + + // 唤起微信支付 + await this.callWechatPay(res.data) + + // 支付成功后刷新订单列表 + uni.showToast({ title: '支付成功', icon: 'success' }) + await this.refresh() + this.$emit('payment-success', order) + + } catch (error) { + uni.hideLoading() + console.error('微信支付异常:', error) + + if (error.message?.includes('cancel') || error.message?.includes('取消')) { + uni.showToast({ title: '已取消支付', icon: 'none' }) + } else { + uni.showToast({ title: error.message || '支付失败', icon: 'none' }) + } + } + }, + + /** + * 获取微信openid + * @returns {Promise} 微信openid + */ + async getWechatOpenid() { + try { + // 首先尝试从本地存储获取 + const userInfo = uni.getStorageSync('userInfo') + let openid = '' + + // #ifdef MP-WEIXIN + openid = userInfo?.weapp_openid || userInfo?.openid || '' + if (openid) { + console.log('本地获取小程序openid成功') + return openid + } + + console.log('本地无openid,使用 uni.login 获取...') + + // 调用 uni.login 获取 code + const loginRes = await new Promise((resolve, reject) => { + uni.login({ + success: (res) => resolve(res), + fail: (err) => reject(err) + }) + }) + + if (!loginRes.code) { + console.error('uni.login 获取 code 失败') + return '' + } + + console.log('uni.login 获取 code 成功:', loginRes.code.substring(0, 10) + '***') + + // 使用 code 调用接口换取 openid + const res = await apiRoute.getWechatOpenid({ + code: loginRes.code, + type: 'miniprogram' + }) + + if (res.code === 1 && res.data?.openid) { + openid = res.data.openid + // 更新本地存储 + if (userInfo) { + userInfo.weapp_openid = openid + uni.setStorageSync('userInfo', userInfo) + } + console.log('接口获取小程序openid成功') + return openid + } else { + console.error('接口换取openid失败:', res.msg || '未知错误') + return '' + } + // #endif + + // #ifdef H5 + openid = userInfo?.wx_openid || userInfo?.openid || '' + if (openid) { + console.log('本地获取公众号openid成功') + return openid + } + + console.log('本地无openid,从URL获取code...') + + // 如果本地没有,从URL参数获取code(公众号授权后会跳转回来带上code参数) + const urlParams = new URLSearchParams(window.location.search) + const code = urlParams.get('code') + + if (code) { + console.log('从URL获取code成功,调用接口换取openid...') + const res = await apiRoute.getWechatOpenid({ + code: code, + type: 'official' + }) + + if (res.code === 1 && res.data?.openid) { + openid = res.data.openid + // 更新本地存储 + if (userInfo) { + userInfo.wx_openid = openid + uni.setStorageSync('userInfo', userInfo) + } + console.log('接口获取公众号openid成功') + return openid + } else { + console.error('接口换取openid失败:', res.msg || '未知错误') + return '' + } + } else { + console.error('URL中未找到code参数,需要进行微信授权') + // TODO: 这里可以引导用户进行微信授权 + return '' + } + // #endif + + console.error('无法获取openid,可能需要重新微信授权') + return '' + } catch (error) { + console.error('获取openid异常:', error) + return '' + } + }, + + /** + * 调用微信支付 + */ + callWechatPay(payParams) { + return new Promise((resolve, reject) => { + // #ifdef MP-WEIXIN + wx.requestPayment({ + timeStamp: payParams.timeStamp, + nonceStr: payParams.nonceStr, + package: payParams.package, + signType: payParams.signType || 'MD5', + paySign: payParams.paySign, + success: (res) => { + console.log('微信支付成功:', res) + resolve({ code: 1, msg: '支付成功' }) + }, + fail: (err) => { + console.error('微信支付失败:', err) + if (err.errMsg?.includes('cancel')) { + reject(new Error('用户取消支付')) + } else { + reject(new Error('微信支付失败')) + } + } + }) + // #endif + + // #ifndef MP-WEIXIN + reject(new Error('当前环境不支持微信支付')) + // #endif }) }, diff --git a/uniapp/pages-market/clue/clue_info.vue b/uniapp/pages-market/clue/clue_info.vue index f0d7964c..b0af878e 100755 --- a/uniapp/pages-market/clue/clue_info.vue +++ b/uniapp/pages-market/clue/clue_info.vue @@ -132,7 +132,11 @@ /> - +