liujie
2026-09-14 a910819c1853081735356c7a2c5e05a2953602ab
update
7个文件已修改
130 ■■■■■ 已修改文件
backend/src/main/java/com/tourguide/modules/order/service/MiniTourOrderCommandService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/tourguide/modules/user/service/GuideAccountService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/tourguide/modules/user/service/GuideUserService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mingdaojingjiang_guide_app/manifest.json 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mingdaojingjiang_guide_app/pages/workbench/index.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mingdaojingjiang_guide_app/services/auth.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mingdaojingjiang_uniapp/mingdaojingjiang_uniapp/pages/order-pending-pay/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/tourguide/modules/order/service/MiniTourOrderCommandService.java
@@ -166,6 +166,7 @@
        int unpaidCancelMinutes = scenicSpot == null || scenicSpot.getUnpaidCancelMinutes() == null
                || scenicSpot.getUnpaidCancelMinutes() < 1
                ? DEFAULT_UNPAID_CANCEL_MINUTES : scenicSpot.getUnpaidCancelMinutes();
        cancelExpiredUnpaidIfNeeded(order, unpaidCancelMinutes);
        return TourOrderResponse.from(order)
                .enrich(scenicSpot, meetingPoint, pricing, guide)
                .withUnpaidCancelMinutes(unpaidCancelMinutes)
@@ -173,6 +174,17 @@
                        platformSettingsService.getSettings().getUnacceptedAutoRefundMinutes());
    }
    /** 读取订单时惰性取消已超时的未支付订单,避免等待定时任务扫描延迟。 */
    private void cancelExpiredUnpaidIfNeeded(TourOrder order, int unpaidCancelMinutes) {
        if (!OrderStatus.PENDING_PAYMENT.matches(order.getStatus())) return;
        LocalDateTime baseTime = order.getPlacedAt() == null ? order.getCreatedAt() : order.getPlacedAt();
        if (baseTime == null || baseTime.plusMinutes(unpaidCancelMinutes).isAfter(LocalDateTime.now())) return;
        if (orderMapper.cancelUnpaid(order.getId()) > 0) {
            order.setStatus(OrderStatus.CANCELLED.getCode());
            order.setCancelledAt(LocalDateTime.now());
        }
    }
    @Transactional
    public TourOrderResponse cancel(Long visitorId, Long orderId) {
        TourOrder order = orderMapper.selectForUpdate(orderId);
backend/src/main/java/com/tourguide/modules/user/service/GuideAccountService.java
@@ -1,6 +1,7 @@
package com.tourguide.modules.user.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.tourguide.common.exception.BusinessException;
import com.tourguide.modules.order.domain.OrderStatus;
import com.tourguide.modules.order.entity.TourOrder;
@@ -57,16 +58,17 @@
        if (idCardNo != null && !idCardNo.trim().isEmpty()) {
            guideApplicationMapper.clearIdentityByIdCard(idCardNo);
        }
        user.setUsername("deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24));
        user.setMobile(null);
        user.setCertificateNo(null);
        user.setIdCardNo(null);
        user.setIdCardFrontUrl(null);
        user.setIdCardBackUrl(null);
        user.setStatus(GuideUserService.STATUS_CANCELLED);
        user.setAutoDisableWhitelisted(0);
        user.setAutoDisabled(0);
        guideUserMapper.updateById(user);
        guideUserMapper.update(null, new LambdaUpdateWrapper<GuideUser>()
                .eq(GuideUser::getId, guideId)
                .set(GuideUser::getUsername, "deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24))
                .set(GuideUser::getMobile, null)
                .set(GuideUser::getCertificateNo, null)
                .set(GuideUser::getIdCardNo, null)
                .set(GuideUser::getIdCardFrontUrl, null)
                .set(GuideUser::getIdCardBackUrl, null)
                .set(GuideUser::getStatus, GuideUserService.STATUS_CANCELLED)
                .set(GuideUser::getAutoDisableWhitelisted, 0)
                .set(GuideUser::getAutoDisabled, 0));
        tokenSessionService.remove(payload);
    }
}
backend/src/main/java/com/tourguide/modules/user/service/GuideUserService.java
@@ -1,6 +1,7 @@
package com.tourguide.modules.user.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tourguide.common.api.PageResponse;
import com.tourguide.common.exception.BusinessException;
@@ -332,11 +333,12 @@
    }
    private void anonymizeCancelledGuide(GuideUser user) {
        user.setUsername("deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24));
        user.setMobile(null);
        user.setCertificateNo(null);
        user.setIdCardNo(null);
        userMapper.updateById(user);
        userMapper.update(null, new LambdaUpdateWrapper<GuideUser>()
                .eq(GuideUser::getId, user.getId())
                .set(GuideUser::getUsername, "deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24))
                .set(GuideUser::getMobile, null)
                .set(GuideUser::getCertificateNo, null)
                .set(GuideUser::getIdCardNo, null));
    }
    private void ensureUniqueGuideIdentity(GuideApplication application, GuideUser existingUser) {
mingdaojingjiang_guide_app/manifest.json
@@ -39,6 +39,7 @@
                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
mingdaojingjiang_guide_app/pages/workbench/index.vue
@@ -143,7 +143,7 @@
<script>
import GuideDialog from '../../components/guide-dialog/guide-dialog.vue'
import { getLoginState, isLoggedIn } from '../../services/auth'
import { clearGuideLocalData, getLoginState, isLoggedIn } from '../../services/auth'
import {
  getCurrentGuideReceivingSession,
  getGuideProfile,
@@ -203,27 +203,54 @@
}
function dialPhone(phoneNumber) {
  // App 端(5+):先申请拨号权限,再调用系统拨号界面
  if (typeof plus !== 'undefined' && plus.device && typeof plus.device.dial === 'function') {
    plus.device.dial(phoneNumber, true)
    const doDial = () => plus.device.dial(phoneNumber, true)
    // Android 6.0+ 需运行时申请 CALL_PHONE 权限,iOS 直接拨号
    if (typeof plus.android !== 'undefined' && typeof plus.android.requestPermissions === 'function') {
      plus.android.requestPermissions(['android.permission.CALL_PHONE'], (result) => {
        const denied = [
          ...(result && result.deniedAlways ? result.deniedAlways : []),
          ...(result && result.denied ? result.denied : [])
        ]
        if (denied.length > 0) {
          showPhoneCallError()
          return
        }
        doDial()
      }, () => {
        showPhoneCallError()
      })
    } else {
      doDial()
    }
    return true
  }
  if (typeof uni === 'undefined' || typeof uni.makePhoneCall !== 'function') {
    if (typeof window !== 'undefined' && window.location) {
      window.location.href = `tel:${phoneNumber}`
      return true
    }
    return false
  // H5 端:用 <a href="tel:"> 触发,兼容微信 WebView
  if (typeof window !== 'undefined' && typeof document !== 'undefined' && typeof document.createElement === 'function') {
    const link = document.createElement('a')
    link.setAttribute('href', `tel:${phoneNumber}`)
    link.style.display = 'none'
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    return true
  }
  const result = uni.makePhoneCall({
    phoneNumber,
    fail: handlePhoneCallFailure
  })
  if (result && typeof result.catch === 'function') {
    result.catch(handlePhoneCallFailure)
  // 小程序端:uni.makePhoneCall
  if (typeof uni !== 'undefined' && typeof uni.makePhoneCall === 'function') {
    const result = uni.makePhoneCall({
      phoneNumber,
      fail: handlePhoneCallFailure
    })
    if (result && typeof result.catch === 'function') {
      result.catch(handlePhoneCallFailure)
    }
    return true
  }
  return true
  return false
}
function buildReceivingConfig(session, scenics = [], fallback = {}) {
@@ -594,13 +621,20 @@
        }
        this.countdownSeconds = Math.max(
          0,
          Math.ceil((this.countdownFallbackEndAt - Date.now()) / 1000)
          Math.min(
            leadMinutes * 60,
            Math.ceil((this.countdownFallbackEndAt - Date.now()) / 1000)
          )
        )
        return
      }
      this.countdownFallbackEndAt = null
      const endAt = serving ? startAt + durationMinutes * 60 * 1000 : startAt
      this.countdownSeconds = Math.max(0, Math.ceil((endAt - Date.now()) / 1000))
      const maxSeconds = (serving ? durationMinutes : leadMinutes) * 60
      this.countdownSeconds = Math.max(
        0,
        Math.min(maxSeconds, Math.ceil((endAt - Date.now()) / 1000))
      )
    },
    clearCountdown() {
      if (this.countdownTimer) {
@@ -693,7 +727,8 @@
      })
    },
    resubmit() {
      routeTo('/pages/register-phone/index')
      clearGuideLocalData()
      routeTo('/pages/login/index', 'reLaunch')
    },
    openPolicy() {
      routeTo('/pages/agreements/index')
mingdaojingjiang_guide_app/services/auth.js
@@ -100,7 +100,7 @@
    })
}
function clearGuideLocalData() {
export function clearGuideLocalData() {
  [
    GUIDE_LOGIN_KEY,
    GUIDE_ORDERS_KEY,
mingdaojingjiang_uniapp/mingdaojingjiang_uniapp/pages/order-pending-pay/index.vue
@@ -224,6 +224,7 @@
function updateCountdown() {
  if (!paymentDeadline.value) return
  const previous = remainingSeconds.value
  remainingSeconds.value = Math.max(
    0,
    Math.min(
@@ -231,6 +232,15 @@
      Math.ceil((paymentDeadline.value - Date.now()) / 1000)
    )
  )
  if (previous > 0 && remainingSeconds.value === 0) {
    onCountdownExpired()
  }
}
function onCountdownExpired() {
  if (pageActive && order.value.id && !redirecting) {
    refreshOrderStatus()
  }
}
function parseServerTime(value) {