rk
4 天以前 e39dda2f25df9680e66c9e0dd3a606149e21bcc5
代码生成
已添加5个文件
已修改25个文件
1590 ■■■■ 文件已修改
server/admin/src/main/java/com/doumee/job/AutoCompleteOrderJob.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/CancelTimeoutOrderJob.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/NoGrabNotifyJob.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/SettleOrdersJob.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/pom.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/db/db_change.sql 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/pom.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/biz/system/impl/OperationConfigBizImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/jwt/WebMvcConfig.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/constants/Constants.java 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Multifile.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Orders.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/OrdersRefund.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/CommentOrderDTO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/OperationConfigDTO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/DriverOrderDetailVO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/MyOrderDetailVO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/ShopCenterVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/ShopSalesStatsVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/OrdersService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 564 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/RevenueServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/resources/application-dev.yml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ConfigApi.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/PaymentCallback.java 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ShopInfoApi.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/AutoCompleteOrderJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * è‡ªåŠ¨å®Œæˆè¶…æ—¶æœªç¡®è®¤çš„å·²é€è¾¾è®¢å•
 * æ ¹æ®è¿è¥é…ç½® AUTO_CONFIRM_RECEIPT(天),将已送达超过该天数的订单自动标记为已完成
 * @author rk
 * @date 2026/04/22
 */
@Slf4j
@Component("autoCompleteOrderJob")
public class AutoCompleteOrderJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            int count = ordersService.autoCompleteOrders();
            jobContext.setHandleSuccessSize(count);
            jobContext.setHandleTotalSize(count);
            jobContext.setContext("自动完成超时订单完成,共完成" + count + "单");
        } catch (Exception e) {
            log.error("自动完成订单任务异常", e);
        }
        return jobContext;
    }
}
server/admin/src/main/java/com/doumee/job/CancelTimeoutOrderJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * è¶…时未支付订单自动取消
 * æ ¹æ®è¿è¥é…ç½® AUTO_CANCEL_TIME(分钟),取消超时未支付订单
 * @author rk
 * @date 2026/04/22
 */
@Slf4j
@Component("cancelTimeoutOrderJob")
public class CancelTimeoutOrderJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            int count = ordersService.cancelTimeoutUnpaidOrders();
            jobContext.setHandleSuccessSize(count);
            jobContext.setHandleTotalSize(count);
            jobContext.setContext("超时未支付订单自动取消完成,共取消" + count + "单");
        } catch (Exception e) {
            log.error("超时取消订单任务异常", e);
        }
        return jobContext;
    }
}
server/admin/src/main/java/com/doumee/job/NoGrabNotifyJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * æ— äººæŠ¢å•短信通知平台人员
 * æ ¹æ®è¿è¥é…ç½® NO_GRAB_NOTIFY_TIME(分钟),对已寄存且无人抢单的订单发送短信通知
 * @author rk
 * @date 2026/04/22
 */
@Slf4j
@Component("noGrabNotifyJob")
public class NoGrabNotifyJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            int count = ordersService.notifyUngrabbedOrders();
            jobContext.setHandleSuccessSize(count);
            jobContext.setHandleTotalSize(count);
            jobContext.setContext("无人抢单短信通知完成,共通知" + count + "单");
        } catch (Exception e) {
            log.error("无人抢单通知任务异常", e);
        }
        return jobContext;
    }
}
server/admin/src/main/java/com/doumee/job/SettleOrdersJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * è®¢å•自动结算定时任务
 * æ ¹æ®è¿è¥é…ç½® SETTLEMENT_DATE,将已完成的待结算订单进行结算
 *
 * @author rk
 * @date 2026/04/22
 */
@Slf4j
@Component("settleOrdersJob")
public class SettleOrdersJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            ordersService.settleOrders();
            jobContext.setContext("订单结算任务执行完成");
        } catch (Exception e) {
            log.error("订单结算任务异常", e);
            jobContext.setContext("订单结算任务异常:" + e.getMessage());
        }
        return jobContext;
    }
}
server/pom.xml
@@ -37,6 +37,9 @@
    <weixin-java-pay.version>4.1.0</weixin-java-pay.version>
    <!-- é˜¿é‡Œäº‘OSS存储 -->
    <aliyun-oss.version>3.8.0</aliyun-oss.version>
    <okhttp3.version>4.9.3</okhttp3.version>
    <kotlin.version>1.5.31</kotlin.version>
    <okio.version>2.10.0</okio.version>
  </properties>
  <dependencies>
    <!-- Spring Boot -->
@@ -244,12 +247,12 @@
      <groupId>com.github.wechatpay-apiv3</groupId>
      <artifactId>wechatpay-java</artifactId>
      <version>0.2.15</version>
      <exclusions>
        <exclusion>
          <artifactId>okhttp</artifactId>
          <groupId>com.squareup.okhttp3</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <!-- OkHttp 4.x éœ€è¦ okio 2.x(Kotlin ç‰ˆæœ¬ï¼‰ -->
    <dependency>
      <groupId>com.squareup.okio</groupId>
      <artifactId>okio</artifactId>
      <version>2.10.0</version>
    </dependency>
    <!--  é˜¿é‡Œäº‘OSS-->
server/services/db/db_change.sql
@@ -5,6 +5,46 @@
-- ============================================================
-- 2026/04/22 è®¢å•退款表新增退款前订单状态字段
-- ============================================================
ALTER TABLE `orders_refund` ADD COLUMN `BEFORE_STATUS` INT NULL DEFAULT NULL COMMENT '退款前订单状态' AFTER `STATUS`;
-- ============================================================
-- 2026/04/22 è®¢å•退款表新增退款金额字段
-- ============================================================
ALTER TABLE `orders_refund` ADD COLUMN `REFUND_AMOUNT` BIGINT NULL DEFAULT NULL COMMENT '退款金额(分)' AFTER `REFUND_REMARK`;
-- ============================================================
-- 2026/04/22 æ–°å¢žè®¢å•自动结算定时任务
-- ============================================================
INSERT INTO `SYSTEM_JOB` VALUES (4, '订单自动结算', '根据运营配置SETTLEMENT_DATE,将已完成的待结算订单进行结算', 'settleOrdersJob', NULL, NULL, 1, 0, 1, 1, '0 0 2 * * ?', '', 0, NULL, 0, 1, NOW(), NULL, NULL, 0);
-- ============================================================
-- 2026/04/22 è¿è¥é…ç½®æ–°å¢žæ— äººæŠ¢å•通知时间和通知人员
-- ============================================================
INSERT INTO `SYSTEM_DICT_DATA` (`DICT_ID`, `CODE`, `LABEL`, `SORT`, `DISABLED`, `CREATE_USER`, `CREATE_TIME`, `DELETED`) VALUES (105, '30', 'NO_GRAB_NOTIFY_TIME', 0, 0, 1, NOW(), 0);
INSERT INTO `SYSTEM_DICT_DATA` (`DICT_ID`, `CODE`, `LABEL`, `SORT`, `DISABLED`, `CREATE_USER`, `CREATE_TIME`, `DELETED`) VALUES (105, '', 'NO_GRAB_NOTIFY_USERS', 0, 0, 1, NOW(), 0);
-- ============================================================
-- 2026/04/22 è®¢å•表增加无人接单是否已短信通知平台字段
-- ============================================================
ALTER TABLE `orders` ADD COLUMN `PLATFORM_SMS_NOTIFIED` INT DEFAULT 0 COMMENT '无人接单是否已短信通知平台:0=否;1=是' AFTER `OUT_TRADE_NO`;
ALTER TABLE `orders` ADD COLUMN `PLATFORM_SMS_NOTIFIED_TIME` DATETIME DEFAULT NULL COMMENT '无人接单已短信通知平台时间' AFTER `PLATFORM_SMS_NOTIFIED`;
-- ============================================================
-- 2026/04/22 æ–°å¢žè®¢å•相关定时任务(三个独立任务)
-- ============================================================
INSERT INTO `SYSTEM_JOB` VALUES (3, '超时取消订单', '根据运营配置AUTO_CANCEL_TIME(分钟),取消超时未支付订单', 'cancelTimeoutOrderJob', NULL, NULL, 1, 0, 1, 1, '0 */1 * * * ?', '', 0, NULL, 0, 1, NOW(), NULL, NULL, 0);
INSERT INTO `SYSTEM_JOB` VALUES (5, '无人抢单通知', '根据运营配置NO_GRAB_NOTIFY_TIME(分钟),对已寄存且无人抢单的订单发送短信通知平台人员', 'noGrabNotifyJob', NULL, NULL, 1, 0, 1, 1, '0 */1 * * * ?', '', 0, NULL, 0, 1, NOW(), NULL, NULL, 0);
INSERT INTO `SYSTEM_JOB` VALUES (6, '自动完成订单', '根据运营配置AUTO_CONFIRM_RECEIPT(天),自动完成超时未确认的已送达订单', 'autoCompleteOrderJob', NULL, NULL, 1, 0, 1, 1, '0 0 */1 * * ?', '', 0, NULL, 0, 1, NOW(), NULL, NULL, 0);
-- ============================================================
-- 2026/04/22 æçŽ°è®°å½•è¡¨å¢žåŠ æ”¯ä»˜å®å®žåå§“åå­—æ®µ
-- ============================================================
ALTER TABLE `withdrawal_orders` ADD COLUMN `ALI_NAME` VARCHAR(50) DEFAULT NULL COMMENT '支付宝实名姓名' AFTER `ALI_ACCOUNT`;
server/services/pom.xml
@@ -22,12 +22,6 @@
      <artifactId>alipay-sdk-java-v3</artifactId>
      <version>3.1.70.ALL</version>
    </dependency>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>4.9.3</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>
</project>
server/services/src/main/java/com/doumee/biz/system/impl/OperationConfigBizImpl.java
@@ -34,6 +34,8 @@
        dto.setInsuranceRate(getValue(Constants.OP_INSURANCE_RATE));
        dto.setOrderAcceptLimit(getValue(Constants.OP_ORDER_ACCEPT_LIMIT));
        dto.setAutoConfirmReceipt(getValue(Constants.OP_AUTO_CONFIRM_RECEIPT));
        dto.setNoGrabNotifyTime(getValue(Constants.OP_NO_GRAB_NOTIFY_TIME));
        dto.setNoGrabNotifyUsers(getValue(Constants.OP_NO_GRAB_NOTIFY_USERS));
        return dto;
    }
@@ -49,6 +51,8 @@
        saveOrUpdate(Constants.OP_INSURANCE_RATE, "保费比率", dto.getInsuranceRate());
        saveOrUpdate(Constants.OP_ORDER_ACCEPT_LIMIT, "接单数量", dto.getOrderAcceptLimit());
        saveOrUpdate(Constants.OP_AUTO_CONFIRM_RECEIPT, "自动确认收货", dto.getAutoConfirmReceipt());
        saveOrUpdate(Constants.OP_NO_GRAB_NOTIFY_TIME, "无人抢单通知时间", dto.getNoGrabNotifyTime());
        saveOrUpdate(Constants.OP_NO_GRAB_NOTIFY_USERS, "无人抢单短信通知人员", dto.getNoGrabNotifyUsers());
    }
    private String getValue(String label) {
@@ -82,7 +86,9 @@
                || StringUtils.isBlank(dto.getAutoCancelTime())
                || StringUtils.isBlank(dto.getInsuranceRate())
                || StringUtils.isBlank(dto.getOrderAcceptLimit())
                || StringUtils.isBlank(dto.getAutoConfirmReceipt())) {
                || StringUtils.isBlank(dto.getAutoConfirmReceipt())
                || StringUtils.isBlank(dto.getNoGrabNotifyTime())
                || StringUtils.isBlank(dto.getNoGrabNotifyUsers())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "所有配置项均为必填");
        }
    }
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java
@@ -41,16 +41,17 @@
        this.loadAlipayConfig();
    }
    public void loadAlipayConfig() {
        try {
            AlipayConfig config = new AlipayConfig();
            config.setServerUrl("https://openapi.alipay.com");
            config.setAppId("2021006147660139");
            String privateKey  = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEeqqkZztBHfK+vpyBi6ejgDTHZuZ3yiuXds+lRBbMo/g24F5trH+oLHW0gMhSxihFfQBBIpVBXDsPQK4ZkhDWTaOBktnU1UMRoOEiaaZU6EiWy10ePFUmpdXpkCQEp7rc88OwI90p58S3+L+Ckak60WqNwJBdB5YGBaUunryBA78U4zW1KNY7JvoRnZDcFMQiczikwUzhO7EAi0yVrVpsVsc9s87mcS4uOJKx4qb2E83r9RJ1z30db+cIIZRiLP2oNZBLYzgKpOouE+uIgxhQzlh6cOASNZQulXuUjoT/+Y9w4njfl4TmKIXWcJFKIMc6kMiux9tTncpp0TqRwk1tAgMBAAECggEALkSYtJheusnbpRFr95G0i2sggqh3s1PXihZ/dXKgT9Z5GCsj8X3Cng7CNRxykBN73kk+axhCv56Bhej8Vqcv8ddcnqG/TEBgR+Fzws/QTIRau6/uILWic7RvuE2qPbJl7aw1s9/uL/UVPSGFr7CvgltYVUM4e7/Sk1529JCK4XJfoXP5tKJ3OaXssvaFnCKEU8IGQkjRG+lUZJhAHVtClGHtgrhevgRhy2zre5wp2qSa8d/MqrPruSYS02hn9b5Nl6i2PlUS6dGlJ4lrxYTG22ukYYoxAPNPS7gnvmveXonWP7b5tPhKRpZjnoySojz3WECUlhz/v8wM1cDrpq+GQQKBgQDsc7y2rlx4f77a7ORfb5/qWHCOJs1cIzggj0kJ7TgFGe71kbCQ5nywD/Fe5V9OwbW+DCxOME+SrrHeiK4axWiu5si/1JlurJoxNy+4k4ywk3ZA3Nv2aBhlPqfkwDhJ0z7Mgsq2c/YgnVddmSvKZoC39wA77ovks4GDxaBOt8N7PQKBgQDUuPGgzkwcgb60UdaxfMbacrPsW26vDxaE4ceuXo2m8KDiCIqkF2y9r6AdWMTgGGSJwOsk7+FP+21VdRivCg9HcOLWngveUc6xDIuqKHVpemMo3SdCF4Wqf96rRc3VOBr5cfIdWxeorZf5umMyKnIAjAFETOOrK7eLTTmjyLD98QKBgB82S+Plcklpu3zUpnS+nGJn2Du7fYI7F+6cW2zXBn0N5lA+Mgt+kVkAUcFQD9uqkF4M51BO6kIXk10nt6vLAT2NM1S3MKW+XQBAI6l+uKSaYpK/VL3bEdVThwAYK5X7L5/5Z97bwdKeUmkFjhVCoJ0oGrzOiWLgGymUzct2UHSVAoGBAMb+7Cs+Ub0pMrmFBY6r52pbey1Uq0pglvRgMmhQU7sjx50r2GaA81zPer15WVM5/nNPYaoALYqg7jrPe/PjOT/fvpR+7SNg7DZ8QftANfYiY7jKifst/gDt9ePLPS6FedZ4XcJQgOVu34jicAFx64vPbS/zrddm4iEScSVijRBBAoGAXCheERsx8+n16Us/DttXFUa1nc7+D8WR6buM1QMZgQCVF2qp3XtM+FusCKL4+q1+dtag8svLjJFp9QbaAXqX8Zk7rn8wUHbDloPTPy9XWgrPowyL9MPU+e/Rq8Hr6TWPDBd4TU64YzIEfBQYpJXfZbXhVYmK3o7xHXKB1x4vvEM=";
            config.setPrivateKey(privateKey);
            config.setAppCertPath("pay/pro/appCertPublicKey.crt");
            config.setAlipayPublicCertPath("pay/pro/alipayCertPublicKey_RSA2.crt");
            config.setRootCertPath("pay/pro/alipayRootCert.crt");
            config.setAppId(alipayProperties.getAppId());
//            String privateKey  = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEeqqkZztBHfK+vpyBi6ejgDTHZuZ3yiuXds+lRBbMo/g24F5trH+oLHW0gMhSxihFfQBBIpVBXDsPQK4ZkhDWTaOBktnU1UMRoOEiaaZU6EiWy10ePFUmpdXpkCQEp7rc88OwI90p58S3+L+Ckak60WqNwJBdB5YGBaUunryBA78U4zW1KNY7JvoRnZDcFMQiczikwUzhO7EAi0yVrVpsVsc9s87mcS4uOJKx4qb2E83r9RJ1z30db+cIIZRiLP2oNZBLYzgKpOouE+uIgxhQzlh6cOASNZQulXuUjoT/+Y9w4njfl4TmKIXWcJFKIMc6kMiux9tTncpp0TqRwk1tAgMBAAECggEALkSYtJheusnbpRFr95G0i2sggqh3s1PXihZ/dXKgT9Z5GCsj8X3Cng7CNRxykBN73kk+axhCv56Bhej8Vqcv8ddcnqG/TEBgR+Fzws/QTIRau6/uILWic7RvuE2qPbJl7aw1s9/uL/UVPSGFr7CvgltYVUM4e7/Sk1529JCK4XJfoXP5tKJ3OaXssvaFnCKEU8IGQkjRG+lUZJhAHVtClGHtgrhevgRhy2zre5wp2qSa8d/MqrPruSYS02hn9b5Nl6i2PlUS6dGlJ4lrxYTG22ukYYoxAPNPS7gnvmveXonWP7b5tPhKRpZjnoySojz3WECUlhz/v8wM1cDrpq+GQQKBgQDsc7y2rlx4f77a7ORfb5/qWHCOJs1cIzggj0kJ7TgFGe71kbCQ5nywD/Fe5V9OwbW+DCxOME+SrrHeiK4axWiu5si/1JlurJoxNy+4k4ywk3ZA3Nv2aBhlPqfkwDhJ0z7Mgsq2c/YgnVddmSvKZoC39wA77ovks4GDxaBOt8N7PQKBgQDUuPGgzkwcgb60UdaxfMbacrPsW26vDxaE4ceuXo2m8KDiCIqkF2y9r6AdWMTgGGSJwOsk7+FP+21VdRivCg9HcOLWngveUc6xDIuqKHVpemMo3SdCF4Wqf96rRc3VOBr5cfIdWxeorZf5umMyKnIAjAFETOOrK7eLTTmjyLD98QKBgB82S+Plcklpu3zUpnS+nGJn2Du7fYI7F+6cW2zXBn0N5lA+Mgt+kVkAUcFQD9uqkF4M51BO6kIXk10nt6vLAT2NM1S3MKW+XQBAI6l+uKSaYpK/VL3bEdVThwAYK5X7L5/5Z97bwdKeUmkFjhVCoJ0oGrzOiWLgGymUzct2UHSVAoGBAMb+7Cs+Ub0pMrmFBY6r52pbey1Uq0pglvRgMmhQU7sjx50r2GaA81zPer15WVM5/nNPYaoALYqg7jrPe/PjOT/fvpR+7SNg7DZ8QftANfYiY7jKifst/gDt9ePLPS6FedZ4XcJQgOVu34jicAFx64vPbS/zrddm4iEScSVijRBBAoGAXCheERsx8+n16Us/DttXFUa1nc7+D8WR6buM1QMZgQCVF2qp3XtM+FusCKL4+q1+dtag8svLjJFp9QbaAXqX8Zk7rn8wUHbDloPTPy9XWgrPowyL9MPU+e/Rq8Hr6TWPDBd4TU64YzIEfBQYpJXfZbXhVYmK3o7xHXKB1x4vvEM=";
            config.setPrivateKey(alipayProperties.getPrivateKey());
            config.setAppCertPath(alipayProperties.getAppCertPath());
            config.setAlipayPublicCertPath(alipayProperties.getAlipayPublicCertPath());
            config.setRootCertPath(alipayProperties.getRootCertPath());
            alipayConfig = config;
            log.info("支付宝配置初始化成功, appId={}", config.getAppId());
        } catch (Exception e) {
@@ -58,110 +59,6 @@
        }
    }
    public static void main(String[] args) throws ApiException  {
        downLoadCertificates();
    }
    /**
     * API安全加密配置
     */
    private static RSAAutoCertificateConfig rsaAutoCertificateConfig() {
        return new RSAAutoCertificateConfig.Builder()
                // å•†æˆ·å·
                .merchantId("1629568742")
                // å•†æˆ·API证书私钥的存放路径
                .privateKeyFromPath("D://ptzs/apiclient_key.pem")
                // å•†æˆ·API证书序列号
                .merchantSerialNumber("25D19D18217F4588841E5CD1AA0D1533DE8AF84A")
                // APIv3密钥
                .apiV3Key("NJTLJSTZYXZRGScaiwubuzichanbu666")
                .build();
    }
    /**
     * ä¸‹è½½è¯ä¹¦
     */
    public static void downLoadCertificates() {
        OkHttpClient okHttpClient = new OkHttpClient();
        HttpClient httpClient = new DefaultHttpClientBuilder()
                .config(rsaAutoCertificateConfig())
                .okHttpClient(okHttpClient)
                .build();
        HttpHeaders headers = new HttpHeaders();
        headers.addHeader("Accept", MediaType.APPLICATION_JSON.getValue());
        HttpRequest executeSendGetHttpRequest = new HttpRequest.Builder()
                .httpMethod(HttpMethod.GET)
                .url("https://api.mch.weixin.qq.com/v3/certificates")
                .headers(headers)
                .build();
        try {
            HttpResponse<JSONObject> execute = httpClient.execute(executeSendGetHttpRequest, JSONObject.class);
            JSONObject responseBody = execute.getServiceResponse();
            log.info("下载平台证书返回:{}", responseBody.toString());
        } catch (Exception e) {
            log.error("下载平台证书异常", e);
        }
    }
//    public static void testTransger( ) throws ApiException  {
//
//        // åˆå§‹åŒ–SDK
//        ApiClient defaultClient = Configuration.getDefaultApiClient();
//        // åˆå§‹åŒ–alipay参数(全局设置一次)
//        defaultClient.setAlipayConfig(getAlipayConfig());
//        // æž„造请求参数以调用接口
//        AlipayFundTransUniApi api = new AlipayFundTransUniApi();
//        AlipayFundTransUniTransferModel data = new AlipayFundTransUniTransferModel();
//        // è®¾ç½®å•†å®¶ä¾§å”¯ä¸€è®¢å•号
//        data.setOutBizNo("202606300001");
//        // è®¾ç½®è®¢å•总金额
//        data.setTransAmount("1");
//        // è®¾ç½®æè¿°ç‰¹å®šçš„业务场景
//        data.setBizScene("DIRECT_TRANSFER");
//        // è®¾ç½®ä¸šåŠ¡äº§å“ç 
//        data.setProductCode("TRANS_ACCOUNT_NO_PWD");
//        // è®¾ç½®è½¬è´¦ä¸šåŠ¡çš„æ ‡é¢˜
//        data.setOrderTitle("201905代发");
//        // è®¾ç½®åŽŸæ”¯ä»˜å®ä¸šåŠ¡å•å·
//        data.setOriginalOrderId("20190620110075000006640000063056");
//        // è®¾ç½®æ”¶æ¬¾æ–¹ä¿¡æ¯
//        Participant payeeInfo = new Participant();
//        payeeInfo.setIdentity("15345690849");
//        payeeInfo.setName("江萍");
//        payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
//        data.setPayeeInfo(payeeInfo);
//        // è®¾ç½®ä¸šåŠ¡å¤‡æ³¨
//        data.setRemark("201905代发");
//        // è®¾ç½®è½¬è´¦åœºæ™¯åç§°
//        data.setTransferSceneName("佣金报酬");
//
//        // è®¾ç½®è½¬è´¦åœºæ™¯ä¸ŠæŠ¥ä¿¡æ¯
//        List<TransferSceneReportInfo> transferSceneReportInfos = new ArrayList<TransferSceneReportInfo>();
//        TransferSceneReportInfo transferSceneReportInfos0 = new TransferSceneReportInfo();
//        transferSceneReportInfos0.setInfoType("佣金报酬说明");
//        transferSceneReportInfos0.setInfoContent("3月家政服务报酬");
//        transferSceneReportInfos.add(transferSceneReportInfos0);
//        data.setTransferSceneReportInfos(transferSceneReportInfos);
//
//        // è®¾ç½®è½¬è´¦ä¸šåŠ¡è¯·æ±‚çš„æ‰©å±•å‚æ•°
//        data.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}");
//
//       /* // è®¾ç½®ç­¾åä¿¡æ¯
//        SignData signData = new SignData();
//        signData.setOriSign("EqHFP0z4a9iaQ1ep==");
//        signData.setPartnerId("签名被授权方支付宝账号ID");
//        signData.setOriAppId("2021000185629012");
//        signData.setOriOutBizNo("商户订单号");
//        signData.setOriSignType("RSA2");
//        signData.setOriCharSet("UTF-8");
//        data.setSignData(signData);
//*/
//        try {
//            AlipayFundTransUniTransferResponseModel response = api.transfer(data);
//        } catch (ApiException e) {
//            AlipayFundTransUniTransferDefaultResponse errorObject = (AlipayFundTransUniTransferDefaultResponse) e.getErrorObject();
//            System.out.println("调用失败:" + errorObject);
//        }
//    }
    /**
     * å•笔转账到支付宝账户
@@ -220,16 +117,6 @@
        // è®¾ç½®è½¬è´¦ä¸šåŠ¡è¯·æ±‚çš„æ‰©å±•å‚æ•°
        data.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}");
       /* // è®¾ç½®ç­¾åä¿¡æ¯
        SignData signData = new SignData();
        signData.setOriSign("EqHFP0z4a9iaQ1ep==");
        signData.setPartnerId("签名被授权方支付宝账号ID");
        signData.setOriAppId("2021000185629012");
        signData.setOriOutBizNo("商户订单号");
        signData.setOriSignType("RSA2");
        signData.setOriCharSet("UTF-8");
        data.setSignData(signData);
*/
        try {
            AlipayFundTransUniTransferResponseModel response = api.transfer(data);
            log.info("支付宝转账成功, outBizNo={}, orderId={}", dto.getOutBizNo(), response.getOrderId());
@@ -244,16 +131,4 @@
    }
//    private static AlipayConfig getAlipayConfig() {
//        AlipayConfig alipayConfig = new AlipayConfig();
//        alipayConfig.setServerUrl("https://openapi.alipay.com");
//        alipayConfig.setAppId("2021006147660139");
//        String privateKey  = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEeqqkZztBHfK+vpyBi6ejgDTHZuZ3yiuXds+lRBbMo/g24F5trH+oLHW0gMhSxihFfQBBIpVBXDsPQK4ZkhDWTaOBktnU1UMRoOEiaaZU6EiWy10ePFUmpdXpkCQEp7rc88OwI90p58S3+L+Ckak60WqNwJBdB5YGBaUunryBA78U4zW1KNY7JvoRnZDcFMQiczikwUzhO7EAi0yVrVpsVsc9s87mcS4uOJKx4qb2E83r9RJ1z30db+cIIZRiLP2oNZBLYzgKpOouE+uIgxhQzlh6cOASNZQulXuUjoT/+Y9w4njfl4TmKIXWcJFKIMc6kMiux9tTncpp0TqRwk1tAgMBAAECggEALkSYtJheusnbpRFr95G0i2sggqh3s1PXihZ/dXKgT9Z5GCsj8X3Cng7CNRxykBN73kk+axhCv56Bhej8Vqcv8ddcnqG/TEBgR+Fzws/QTIRau6/uILWic7RvuE2qPbJl7aw1s9/uL/UVPSGFr7CvgltYVUM4e7/Sk1529JCK4XJfoXP5tKJ3OaXssvaFnCKEU8IGQkjRG+lUZJhAHVtClGHtgrhevgRhy2zre5wp2qSa8d/MqrPruSYS02hn9b5Nl6i2PlUS6dGlJ4lrxYTG22ukYYoxAPNPS7gnvmveXonWP7b5tPhKRpZjnoySojz3WECUlhz/v8wM1cDrpq+GQQKBgQDsc7y2rlx4f77a7ORfb5/qWHCOJs1cIzggj0kJ7TgFGe71kbCQ5nywD/Fe5V9OwbW+DCxOME+SrrHeiK4axWiu5si/1JlurJoxNy+4k4ywk3ZA3Nv2aBhlPqfkwDhJ0z7Mgsq2c/YgnVddmSvKZoC39wA77ovks4GDxaBOt8N7PQKBgQDUuPGgzkwcgb60UdaxfMbacrPsW26vDxaE4ceuXo2m8KDiCIqkF2y9r6AdWMTgGGSJwOsk7+FP+21VdRivCg9HcOLWngveUc6xDIuqKHVpemMo3SdCF4Wqf96rRc3VOBr5cfIdWxeorZf5umMyKnIAjAFETOOrK7eLTTmjyLD98QKBgB82S+Plcklpu3zUpnS+nGJn2Du7fYI7F+6cW2zXBn0N5lA+Mgt+kVkAUcFQD9uqkF4M51BO6kIXk10nt6vLAT2NM1S3MKW+XQBAI6l+uKSaYpK/VL3bEdVThwAYK5X7L5/5Z97bwdKeUmkFjhVCoJ0oGrzOiWLgGymUzct2UHSVAoGBAMb+7Cs+Ub0pMrmFBY6r52pbey1Uq0pglvRgMmhQU7sjx50r2GaA81zPer15WVM5/nNPYaoALYqg7jrPe/PjOT/fvpR+7SNg7DZ8QftANfYiY7jKifst/gDt9ePLPS6FedZ4XcJQgOVu34jicAFx64vPbS/zrddm4iEScSVijRBBAoGAXCheERsx8+n16Us/DttXFUa1nc7+D8WR6buM1QMZgQCVF2qp3XtM+FusCKL4+q1+dtag8svLjJFp9QbaAXqX8Zk7rn8wUHbDloPTPy9XWgrPowyL9MPU+e/Rq8Hr6TWPDBd4TU64YzIEfBQYpJXfZbXhVYmK3o7xHXKB1x4vvEM=";
//        alipayConfig.setPrivateKey(privateKey);
//        alipayConfig.setAppCertPath("pay/pro/appCertPublicKey.crt");
//        alipayConfig.setAlipayPublicCertPath("pay/pro/alipayCertPublicKey_RSA2.crt");
//        alipayConfig.setRootCertPath("pay/pro/alipayRootCert.crt");
//
//        return alipayConfig;
//    }
}
server/services/src/main/java/com/doumee/config/jwt/WebMvcConfig.java
@@ -160,7 +160,7 @@
            }
            String openid = shop.getOpenid();
            Integer shopId = getTokenId(token);
            Integer isDeleted = dao.queryForObject(" select COALESCE(ISDELETED,0)  from shop_info where id  = ?", Integer.class, shopId);
            Integer isDeleted = dao.queryForObject(" select COALESCE(DELETED,0)  from shop_info where id  = ?", Integer.class, shopId);
            if(isDeleted== Constants.ONE){
                throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"商户已删除,请联系管理员");
            }
@@ -168,11 +168,11 @@
            if(isForbidden == Constants.ONE){
                throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"商户已禁用,请联系管理员");
            }
            String dbOpenid = dao.queryForObject(" select ifnull(openid,'')  from shop where id  = ?", String.class, shopId);
            String dbOpenid = dao.queryForObject(" select ifnull(openid,'')  from shop_info where id  = ?", String.class, shopId);
            if(StringUtils.isBlank(dbOpenid)||!openid.equals(dbOpenid)){
                throw new BusinessException(ResponseStatus.TOKEN_EXCEED_TIME);
            }
            Integer count = dao.queryForObject("select count(1) from shop where id  = ?", Integer.class, shopId);
            Integer count = dao.queryForObject("select count(1) from shop_info where id  = ?", Integer.class, shopId);
            if (count != null && count > 0) {
                request.setAttribute(JwtTokenUtil.SHOP_ID, shop.getId());
                return true;
server/services/src/main/java/com/doumee/core/constants/Constants.java
@@ -82,6 +82,8 @@
    public static final String OP_INSURANCE_RATE = "INSURANCE_RATE";
    public static final String OP_ORDER_ACCEPT_LIMIT = "ORDER_ACCEPT_LIMIT";
    public static final String OP_AUTO_CONFIRM_RECEIPT = "AUTO_CONFIRM_RECEIPT";
    public static final String OP_NO_GRAB_NOTIFY_TIME = "NO_GRAB_NOTIFY_TIME";
    public static final String OP_NO_GRAB_NOTIFY_USERS = "NO_GRAB_NOTIFY_USERS";
    public static boolean WORKORDER_SHE_EMAIL_SENDING = false;
    public static  boolean DEALING_COMPANY_SYNC = false ;
    public static  boolean DEALING_MEMBER_SYNC = false ;
@@ -298,8 +300,8 @@
        LABOR_CONTRACT(11, "有效劳动合同"),
        ORDER_FILE(12,"下单图片"),
        STORE_OUT(13,"门店出库图片"),
        REFUND_TAKE(14,"退款取件图片")
        REFUND_TAKE(14,"退款取件图片"),
        COMMENT_ATTACH(15,"评价附件图片")
        ;
        private final int key;
@@ -350,9 +352,6 @@
        arrived(5, "已到店/已送达/待取件"),
        overdue(6, "存在逾期"), //弃用
        finished(7, "已完成"),
        closed(96, "订单关闭(退款)"),
        cancelOverdue(97, "取消逾期"), //弃用
        cancelling(98, "取消中"),
        cancelled(99, "已取消")
        ;
        private final int status;
@@ -398,7 +397,7 @@
        waitDeliver(2, "待配送", new int[]{OrderStatus.accepted.status}),
        waitReceive(3, "待收货", new int[]{ OrderStatus.delivering.status, OrderStatus.arrived.status}),
        finished(4, "已完成", new int[]{OrderStatus.finished.status}),
        refund(5, "退款", new int[]{OrderStatus.closed.status, OrderStatus.cancelling.status}),
        refund(5, "退款", new int[]{OrderStatus.cancelled.status}),
        home(6, "首页查询", new int[]{OrderStatus.waitPay.status, OrderStatus.waitDeposit.status, OrderStatus.deposited.status
                , OrderStatus.accepted.status, OrderStatus.delivering.status, OrderStatus.arrived.status})
        ;
@@ -458,8 +457,8 @@
        WAIT_PAY("waitPay", "订单待支付", "您的行李订单:{orderNo}已创建,请在{timeout}分钟内完成支付,超时将自动取消"),
        WAIT_VERIFY("waitVerify", "订单待核验", "您的行李订单:{orderNo}已提交,等待门店核验物品信息,存件码{storeCode}"),
        WAIT_GRAB("waitGrab", "订单待抢单", "您的行李订单:{orderNo}已核验,正在为您安排取件司机"),
        WAIT_PICKUP_REMIND("waitPickupRemind", "订单待取件", "订单{orderNo}行李已寄存,请记得在预约取件时间凭取件码前往指定门店取回"),
        WAIT_PICKUP_GRABBED("waitPickupGrabbed", "订单已抢单", "您的行李订单:已有司机{driverName}抢单,正前往取件地点"),
        WAIT_PICKUP_REMIND("waitPickupRemind", "订单待取件", "您的行李订单:{orderNo}已寄存,请记得在预约取件时间凭取件码前往{shopName}门店取回"),
        WAIT_PICKUP_GRABBED("waitPickupGrabbed", "订单已抢单", "您的行李订单:{orderNo}已有司机{driverName}抢单,正前往取件地点"),
        DELIVERING("delivering", "订单配送中", "您的行李订单:{orderNo}已由司机{driverName}取件,正运往目的地"),
        ARRIVED_NO_SHOP("arrivedNoShop", "订单已送达", "您的行李订单:{orderNo}已送达{destination},请及时确认收货"),
        ARRIVED_HAS_SHOP("arrivedHasShop", "订单已送达", "您的行李订单:{orderNo}已送达{destination},请及时取件,取件码{pickupCode}"),
@@ -500,7 +499,7 @@
        WAIT_PICKUP("waitPickup", "订单待取件", "行李订单:{orderNo}已抢单,等待{name}取件"),
        REFUNDING("refunding", "订单退款中", "行李订单:{orderNo}用户提交退款申请,请知悉"),
        DELIVERING("delivering", "订单配送中", "行李订单:{orderNo}已由司机{driverName}取件,正在配送中"),
        ARRIVED("arrived", "已送达", "行李订单:{orderNo}已送达{destination},请联系用户确认签收"),
        ARRIVED("arrived", "订单已送达", "行李订单:{orderNo}已送达{destination},请联系用户确认签收"),
        FINISHED("finished", "订单已完成", "行李订单:{orderNo}已完成,相关订单结算会在{settleDays}个工作日内完成"),
        EVALUATED("evaluated", "订单已评价", "行李订单:{orderNo}用户已完成评价,可查看评价内容"),
        SETTLED("settled", "订单结算", "行李订单:{orderNo}平台已完成结算,金额为{amount}元,请注意查收。")
@@ -531,12 +530,12 @@
    @Getter
    @AllArgsConstructor
    public enum DriverOrderNotify {
        WAIT_DELIVER("waitDeliver", "订单待配送", "您已抢单成功,订单:{orderNo}请按时到{shopName}取件"),
        DELIVERING("delivering", "配送中", "行李订单:{orderNo}已取件,正在配送中,请按时送达"),
        ARRIVED("arrived", "已送达", "行李订单:{orderNo}已送达{destination},请联系用户确认签收"),
        WAIT_DELIVER("waitDeliver", "订单待配送", "您已抢单成功,行李订单:{orderNo}请按时到{shopName}取件"),
        DELIVERING("delivering", "订单配送中", "行李订单:{orderNo}已取件,正在配送中,请按时送达"),
        ARRIVED("arrived", "订单已送达", "行李订单:{orderNo}已送达{destination},请联系用户确认签收"),
        FINISHED("finished", "订单已完成", "行李订单:{orderNo}已完成,相关订单结算会在{settleDays}个工作日内结算"),
        EVALUATED("evaluated", "订单已评价", "行李订单:{orderNo}用户已完成评价,可前往订单查看评价内容"),
        REFUNDING("refunding", "退款中", "行李订单:{orderNo}用户已提交退款申请,该订单任务已取消,请勿前往。"),
        REFUNDING("refunding", "订单退款中", "行李订单:{orderNo}用户已提交退款申请,该订单任务已取消,请勿前往。"),
        SETTLED("settled", "订单已结算", "行李订单:{orderNo}平台已完成结算,金额为{amount}元,请注意查收。"),
        CANCELLED("cancelled", "订单取消成功", "行李订单:{orderNo}已帮您取消,您今日还可主动取消{cancelLimit}次订单,请合理安排接单。")
        ;
@@ -559,6 +558,39 @@
    }
    /**
     * çŸ­ä¿¡é€šçŸ¥æ¨¡æ¿æžšä¸¾
     * templateCode: é˜¿é‡Œäº‘短信模板Code
     * content: çŸ­ä¿¡æ–‡æ¡ˆæ¨¡æ¿ï¼Œå ä½ç¬¦ç”¨ {xxx} è¡¨ç¤º
     */
    @Getter
    @AllArgsConstructor
    public enum SmsNotify {
        PLATFORM_WAIT_GRAB("SMS_505790009", "平台端-待抢单", "您好,订单:{orderNo}已超过{time}分钟无司机抢单,请尽快加急派单,避免客户过久等待。"),
        SHOP_REFUNDING("SMS_505900008", "门店端-退款中", "行李订单:{orderNo}客户已提交退款申请,请尽快处理。"),
        SHOP_WAIT_VERIFY("SMS_505925004", "门店端-待核验", "新行李订单:{orderNo}客户已支付,请尽快核验用户物品信息。"),
        DRIVER_REFUNDING("SMS_505795005", "司机端-退款中", "行李订单:{orderNo}用户已提交退款申请,该订单任务已取消,请勿前往。"),
        DRIVER_WAIT_PICKUP("SMS_505650038", "司机端-待取件", "您已抢单成功,订单{orderNo},请按时到{address}取件。"),
        MEMBER_CANCELLED("SMS_505605028", "会员端-已取消", "您的行李订单:{orderNo}已取消,感谢您的支持,欢迎下次再会。"),
        MEMBER_REFUNDED("SMS_505920002", "会员端-已退款", "您的行李订单:{orderNo}退款已完成,金额{money}元将原路退回,请注意查收。"),
        MEMBER_ARRIVED("SMS_505875004", "会员端-已送达", "您的行李订单:{orderNo}已送到{address},请及时取件,取件码:{code}。"),
        MEMBER_DELIVERING("SMS_505935002", "会员端-配送中", "您的行李订单:{orderNo}已由司机{name}取件,正运往目的地。"),
        VERIFY_CODE("SMS_333770877", "验证码短信", "您的验证码为:{code},请勿泄露于他人!"),
        ;
        private final String templateCode;
        private final String title;
        private final String content;
        public String format(String... params) {
            String result = this.content;
            for (int i = 0; i < params.length - 1; i += 2) {
                result = result.replace("{" + params[i] + "}", params[i + 1]);
            }
            return result;
        }
    }
    /**
     * å¾—到request对象
     *
     * @return
server/services/src/main/java/com/doumee/dao/business/model/Multifile.java
@@ -55,7 +55,7 @@
    @ApiModelProperty(value = "关联对象类型:0=门店其他材料;1=门店内部照片;2=订单寄存图片;3=门店入库图片;4=司机取件图片;5=司机完成图片;6=司机实名认证车辆照片;" +
            "7=司机实名认证驾驶证照片;8=司机实名认证其他图片;" +
            "9=门店门头照;10=社保缴纳证明;11=有效劳动合同;12=下单图片;13=门店出库图片;", example = "1")
            "9=门店门头照;10=社保缴纳证明;11=有效劳动合同;12=下单图片;13=门店出库图片;14=退款图片;15=评价图片", example = "1")
    private Integer objType;
    @ApiModelProperty(value = "文件地址")
server/services/src/main/java/com/doumee/dao/business/model/Orders.java
@@ -66,8 +66,8 @@
    private Integer selfTake;
    @ApiModelProperty(value = "订单状态", example = "0")
    @ExcelColumn(name = "订单状态", valueMapping = "就地寄存状态:0=待支付;1=待寄存;2=已寄存;5=待取件;6=存在逾期;7=已完成;96:订单关闭(退款);97:取消逾期;98=取消中;99=已取消;" +
            "异地寄存状态:0=待支付;1=待寄存;2=已寄存;3=已接单;4=派送中;5=已到店/已送达;6=存在逾期;7=已完成;96:订单关闭(退款);97:取消逾期;98=取消中;99=已取消;")
    @ExcelColumn(name = "订单状态", valueMapping = "就地寄存状态:0=待支付;1=待寄存;2=已寄存;5=待取件;6=存在逾期;7=已完成;99=已取消;" +
            "异地寄存状态:0=待支付;1=待寄存;2=已寄存;3=已接单;4=派送中;5=已到店/已送达;6=存在逾期;7=已完成;99=已取消;")
    private Integer status;
    @ApiModelProperty(value = "是否异常订单:0=否;1=是", example = "0")
@@ -362,6 +362,13 @@
    @ApiModelProperty(value = "三方订单号")
    private String outTradeNo;
    @ApiModelProperty(value = "无人接单是否已短信通知平台:0=否;1=是", example = "0")
    private Integer platformSmsNotified;
    @ApiModelProperty(value = "无人接单已短信通知平台时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date platformSmsNotifiedTime;
    @TableField(exist = false)
    @ApiModelProperty(value = "创建开始时间(查询用)", example = "2026-01-01")
    @JsonFormat(pattern = "yyyy-MM-dd")
server/services/src/main/java/com/doumee/dao/business/model/OrdersRefund.java
@@ -66,8 +66,13 @@
    @ApiModelProperty(value = "平台操作人(type=1使用)", example = "0")
    private Integer userId;
    @ApiModelProperty(value = "退款金额(分)", example = "10000")
    private Long refundAmount;
    @ApiModelProperty(value = "退款状态:0=退款中;1=退款成功;2=退款失败", example = "0")
    private Integer status;
    @ApiModelProperty(value = "退款前订单状态", example = "1")
    private Integer beforeStatus;
}
server/services/src/main/java/com/doumee/dao/dto/CommentOrderDTO.java
@@ -7,6 +7,8 @@
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
 * è®¢å•评价请求
@@ -41,4 +43,7 @@
    @ApiModelProperty(value = "评价内容")
    private String content;
    @Size(max = 3, message = "最多上传3张图片")
    @ApiModelProperty(value = "图片地址列表,最多3å¼ ")
    private List<String> images;
}
server/services/src/main/java/com/doumee/dao/dto/OperationConfigDTO.java
@@ -37,4 +37,10 @@
    @ApiModelProperty(value = "自动确认收货(天)", required = true)
    private String autoConfirmReceipt;
    @ApiModelProperty(value = "无人抢单通知时间(分钟)", required = true)
    private String noGrabNotifyTime;
    @ApiModelProperty(value = "无人抢单短信通知人员主键(多个以,分割)", required = true)
    private String noGrabNotifyUsers;
}
server/services/src/main/java/com/doumee/dao/vo/DriverOrderDetailVO.java
@@ -5,6 +5,7 @@
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
@@ -76,6 +77,44 @@
    @ApiModelProperty(value = "下单附件图片全路径列表")
    private List<String> orderImages;
    // ---- å®žæ—¶ç»çº¬åº¦ï¼ˆå¼‚地寄存按状态返回) ----
    @ApiModelProperty(value = "存件门店经度")
    private Double depositShopLng;
    @ApiModelProperty(value = "存件门店纬度")
    private Double depositShopLat;
    @ApiModelProperty(value = "取件点经度")
    private Double takeLng;
    @ApiModelProperty(value = "取件点纬度")
    private Double takeLat;
    @ApiModelProperty(value = "司机当前经度")
    private Double driverLng;
    @ApiModelProperty(value = "司机当前纬度")
    private Double driverLat;
    // ---- è¯„价信息 ----
    @ApiModelProperty(value = "是否已评价:0=否 1=是")
    private Integer commentStatus;
    @ApiModelProperty(value = "评价时间")
    @com.fasterxml.jackson.annotation.JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date commentTime;
    @ApiModelProperty(value = "评价内容")
    private String commentContent;
    @ApiModelProperty(value = "评价附件图片")
    private List<String> commentImages;
    @ApiModelProperty(value = "司机评分")
    private Integer driverScore;
    @Data
    @ApiModel("物品项")
    public static class OrderItem implements Serializable {
server/services/src/main/java/com/doumee/dao/vo/MyOrderDetailVO.java
@@ -26,6 +26,9 @@
    @ApiModelProperty(value = "订单状态")
    private Integer status;
    @ApiModelProperty(value = "订单状态文案")
    private String statusName;
    @ApiModelProperty(value = "订单状态描述")
    private String statusDesc;
@@ -87,6 +90,9 @@
    // ---- å–件信息 ----
    @ApiModelProperty(value = "取件门店主键", example = "1")
    private Integer takeShopId;
    @ApiModelProperty(value = "取件门店名称(有取件门店时返回)")
    private String takeShopName;
@@ -133,6 +139,22 @@
    @ApiModelProperty(value = "逾期费用(分)")
    private Long overdueFee;
    // ---- é€€æ¬¾ï¼ˆstatus=96/99 æ—¶è¿”回) ----
    @ApiModelProperty(value = "退款金额(分)")
    private Long refundAmount;
    @ApiModelProperty(value = "退款申请时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date refundApplyTime;
    @ApiModelProperty(value = "退款时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date refundTime;
    @ApiModelProperty(value = "退款备注")
    private String refundRemark;
    // ---- æ ‡è®° ----
    @ApiModelProperty(value = "是否异常: 0=否 1=是")
@@ -151,4 +173,59 @@
    @ApiModelProperty(value = "物品明细列表")
    private List<OrderItemVO> detailList;
    // ---- å¸æœºä¿¡æ¯ ----
    @ApiModelProperty(value = "司机主键")
    private Integer driverId;
    @ApiModelProperty(value = "司机姓名")
    private String driverName;
    @ApiModelProperty(value = "司机手机号")
    private String driverPhone;
    // ---- å®žæ—¶ç»çº¬åº¦ï¼ˆå¼‚地寄存按状态返回) ----
    @ApiModelProperty(value = "存件门店经度")
    private Double depositShopLng;
    @ApiModelProperty(value = "存件门店纬度")
    private Double depositShopLat;
    @ApiModelProperty(value = "取件点经度")
    private Double takeLng;
    @ApiModelProperty(value = "取件点纬度")
    private Double takeLat;
    @ApiModelProperty(value = "司机当前经度")
    private Double driverLng;
    @ApiModelProperty(value = "司机当前纬度")
    private Double driverLat;
    // ---- è¯„价信息 ----
    @ApiModelProperty(value = "是否已评价:0=否 1=是")
    private Integer commentStatus;
    @ApiModelProperty(value = "评价时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date commentTime;
    @ApiModelProperty(value = "评价内容")
    private String commentContent;
    @ApiModelProperty(value = "评价附件图片")
    private List<String> commentImages;
    @ApiModelProperty(value = "存件门店评分")
    private Integer depositScore;
    @ApiModelProperty(value = "取件门店评分")
    private Integer takeScore;
    @ApiModelProperty(value = "司机评分")
    private Integer driverScore;
}
server/services/src/main/java/com/doumee/dao/vo/ShopCenterVO.java
@@ -30,4 +30,10 @@
    @ApiModelProperty(value = "待收货订单数量")
    private Integer waitReceiveCount;
    @ApiModelProperty(value = "支付宝提现账户")
    private String aliAccount;
    @ApiModelProperty(value = "支付宝实名姓名")
    private String aliName;
}
server/services/src/main/java/com/doumee/dao/vo/ShopSalesStatsVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("门店销售统计")
public class ShopSalesStatsVO {
    @ApiModelProperty(value = "销售额(分)")
    private Long salesAmount;
    @ApiModelProperty(value = "结算利润(分)")
    private Long settlementProfit;
    @ApiModelProperty(value = "订单总数")
    private Integer orderCount;
    @ApiModelProperty(value = "在库订单数")
    private Integer storageCount;
}
server/services/src/main/java/com/doumee/service/business/OrdersService.java
@@ -122,7 +122,7 @@
    MyOrderDetailVO findMyOrderDetail(Integer id, Integer memberId);
    /**
     * ä¼šå‘˜å–消订单(仅异地寄存)
     * ä¼šå‘˜å–消订单
     *
     * @param orderId  è®¢å•主键
     * @param memberId ä¼šå‘˜ä¸»é”®
@@ -381,4 +381,29 @@
     */
    ActiveOrderTipVO getActiveOrderTip(Integer memberId);
    /**
     * å–消超时未支付订单
     * æ ¹æ®è¿è¥é…ç½® AUTO_CANCEL_TIME(分钟),将创建时间超过该值且仍为待支付的订单批量取消
     *
     * @return å–消的订单数量
     */
    int cancelTimeoutUnpaidOrders();
    /**
     * é€šçŸ¥å¹³å°äººå‘˜ï¼šå·²å¯„存订单超时无人抢单
     * æ ¹æ®è¿è¥é…ç½® NO_GRAB_NOTIFY_TIME(分钟),扫描异地已寄存(status=2)且超时的订单,
     * ç»™é…ç½®çš„通知人员发送短信(PLATFORM_WAIT_GRAB),并标记已通知防止重复
     *
     * @return é€šçŸ¥çš„订单数量
     */
    int notifyUngrabbedOrders();
    /**
     * è‡ªåŠ¨å®Œæˆè¶…æ—¶æœªç¡®è®¤çš„è®¢å•
     * æ ¹æ®è¿è¥é…ç½® AUTO_CONFIRM_RECEIPT(天),将已送达(status=5)超过该天数的订单自动标记为已完成
     *
     * @return è‡ªåŠ¨å®Œæˆçš„è®¢å•æ•°é‡
     */
    int autoCompleteOrders();
}
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java
@@ -9,6 +9,7 @@
import com.doumee.dao.vo.ShopCenterVO;
import com.doumee.dao.vo.ShopDetailVO;
import com.doumee.dao.vo.ShopNearbyVO;
import com.doumee.dao.vo.ShopSalesStatsVO;
import com.doumee.dao.vo.ShopWebDetailVO;
import java.math.BigDecimal;
@@ -199,4 +200,13 @@
     */
    ShopLoginVO shopSilentLogin(Integer memberId);
    /**
     * é—¨åº—销售统计
     *
     * @param shopId  é—¨åº—主键
     * @param period  ç»Ÿè®¡å‘¨æœŸï¼š0=今日, 1=本月, 2=上月
     * @return é”€å”®ç»Ÿè®¡
     */
    ShopSalesStatsVO getShopSalesStats(Integer shopId, Integer period);
}
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -15,9 +15,11 @@
import com.doumee.dao.business.DriverInfoMapper;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.MultifileMapper;
import com.doumee.dao.business.OrderCommentMapper;
import com.doumee.dao.business.SmsrecordMapper;
import com.doumee.dao.business.CategoryMapper;
import com.doumee.dao.business.OrdersMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.OrdersDetailMapper;
import com.doumee.dao.business.RevenueMapper;
import com.doumee.biz.system.SystemDictDataBiz;
@@ -26,6 +28,8 @@
import com.doumee.dao.business.model.Category;
import com.doumee.dao.business.model.DriverInfo;
import com.doumee.dao.business.model.OrderLog;
import com.doumee.dao.business.model.OrderComment;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.Multifile;
import com.doumee.dao.business.model.Smsrecord;
@@ -43,6 +47,7 @@
import com.doumee.service.business.NoticeService;
import com.alibaba.fastjson.JSONObject;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -59,6 +64,7 @@
 * @author rk
 * @date 2026/04/08
 */
@Slf4j
@Service
public class DriverInfoServiceImpl implements DriverInfoService {
@@ -88,6 +94,12 @@
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private ShopInfoMapper shopInfoMapper;
    @Autowired
    private OrderCommentMapper orderCommentMapper;
    @Autowired
    private RevenueMapper revenueMapper;
@@ -1114,10 +1126,59 @@
            }
        }
        // å¼‚地寄存实时经纬度(按状态返回)
        if (Constants.ONE.equals(order.getType())) {
            // å¸æœºè‡ªèº«ç»çº¬åº¦
            vo.setDriverLng(driverLng);
            vo.setDriverLat(driverLat);
            // status=3(已接单):返回存件门店经纬度
            if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.accepted.getStatus())) {
                if (order.getDepositShopId() != null) {
                    ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
                    if (depositShop != null) {
                        vo.setDepositShopLng(depositShop.getLongitude());
                        vo.setDepositShopLat(depositShop.getLatitude());
                    }
                }
            }
            // status=4(配送中):返回取件点经纬度
            if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())) {
                if (order.getTakeShopId() != null) {
                    ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
                    if (takeShop != null) {
                        vo.setTakeLng(takeShop.getLongitude());
                        vo.setTakeLat(takeShop.getLatitude());
                    }
                } else if (order.getTakeLgt() != null && order.getTakeLat() != null) {
                    vo.setTakeLng(order.getTakeLgt().doubleValue());
                    vo.setTakeLat(order.getTakeLat().doubleValue());
                }
            }
        }
        // ä¸‹å•附件图片
        String imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                + systemDictDataBiz.queryByCode(Constants.OSS, Constants.MEMBER_FILES).getCode();
        vo.setOrderImages(getFileUrls(orderId, Constants.FileType.ORDER_FILE.getKey(), imgPrefix));
        // è¯„价信息
        vo.setCommentStatus(order.getCommentStatus());
        if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
            vo.setCommentTime(order.getCommentTime());
            // æŸ¥è¯¢å¸æœºè¯„价记录
            OrderComment driverComment = orderCommentMapper.selectOne(new QueryWrapper<OrderComment>().lambda()
                    .eq(OrderComment::getOrderId, orderId)
                    .eq(OrderComment::getTargetType, Constants.THREE)
                    .eq(OrderComment::getDeleted, Constants.ZERO)
                    .last("limit 1"));
            if (driverComment != null) {
                vo.setDriverScore(driverComment.getScore());
                vo.setCommentContent(driverComment.getContent());
            }
            // è¯„价附件图片
            vo.setCommentImages(getFileUrls(orderId, Constants.FileType.COMMENT_ATTACH.getKey(), imgPrefix));
        }
        return vo;
    }
@@ -1276,6 +1337,12 @@
        sendDriverNotice(driverId, Constants.DriverOrderNotify.WAIT_DELIVER, orderId,
                "orderNo", order.getCode(),
                "shopName", shopName != null ? shopName : "");
        // çŸ­ä¿¡é€šçŸ¥å¸æœºï¼šæŠ¢å•成功,待取件
        String pickupAddress = order.getDepositShopAddress() != null ? order.getDepositShopAddress() : order.getDepositLocation();
        sendSmsNotify(driver.getTelephone(), Constants.SmsNotify.DRIVER_WAIT_PICKUP,
                "orderNo", order.getCode(),
                "address", pickupAddress != null ? pickupAddress : "");
    }
    @Override
@@ -1355,6 +1422,14 @@
        // é€šçŸ¥å¸æœºï¼šå·²å–件配送中
        sendDriverNotice(driverId, Constants.DriverOrderNotify.DELIVERING, orderId,
                "orderNo", order.getCode());
        // çŸ­ä¿¡é€šçŸ¥ä¼šå‘˜ï¼šå¸æœºå·²å–件,配送中
        Member deliveringMember = memberMapper.selectById(order.getMemberId());
        if (deliveringMember != null) {
            sendSmsNotify(deliveringMember.getTelephone(), Constants.SmsNotify.MEMBER_DELIVERING,
                    "orderNo", order.getCode(),
                    "name", driver.getName());
        }
    }
    @Override
@@ -1567,4 +1642,48 @@
        return vo;
    }
    /**
     * å‘送短信通知(失败不影响主业务)
     */
    private void sendSmsNotify(String phone, Constants.SmsNotify smsNotify, String... paramPairs) {
        if (StringUtils.isBlank(phone)) {
            return;
        }
        String content = smsNotify.format(paramPairs);
        try {
            JSONObject templateParam = new JSONObject();
            for (int i = 0; i < paramPairs.length - 1; i += 2) {
                templateParam.put(paramPairs[i], paramPairs[i + 1]);
            }
            boolean result = AliSmsService.sendSms(phone, smsNotify.getTemplateCode(),
                    templateParam.toJSONString());
            if (result) {
                log.info("短信发送成功: phone={}, template={}", phone, smsNotify.name());
            } else {
                log.warn("短信发送失败: phone={}, template={}", phone, smsNotify.name());
            }
            // å­˜å‚¨çŸ­ä¿¡è®°å½•
            Smsrecord record = new Smsrecord();
            record.setPhone(phone);
            record.setContent(content);
            record.setType(Constants.ONE); // 1=订单通知
            record.setStatus(result ? Constants.ONE : Constants.ZERO);
            record.setCreateTime(new Date());
            record.setDeleted(Constants.ZERO);
            smsrecordMapper.insert(record);
        } catch (Exception e) {
            log.error("短信发送异常: phone={}, template={}, error={}", phone, smsNotify.name(), e.getMessage());
            try {
                Smsrecord record = new Smsrecord();
                record.setPhone(phone);
                record.setContent(content);
                record.setType(Constants.ONE);
                record.setStatus(Constants.ZERO);
                record.setCreateTime(new Date());
                record.setDeleted(Constants.ZERO);
                smsrecordMapper.insert(record);
            } catch (Exception ignored) {}
        }
    }
}
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -19,6 +19,7 @@
import com.doumee.core.utils.DateUtil;
import com.doumee.core.utils.geocode.MapUtil;
import com.doumee.core.utils.Utils;
import com.doumee.core.utils.aliyun.AliSmsService;
import com.doumee.dao.business.*;
import com.doumee.dao.business.model.*;
import com.doumee.dao.system.SystemUserMapper;
@@ -43,6 +44,7 @@
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
@@ -59,6 +61,7 @@
 * @author rk
 * @date 2026/04/10
 */
@Slf4j
@Service
public class OrdersServiceImpl implements OrdersService {
@@ -126,6 +129,9 @@
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private SmsrecordMapper smsrecordMapper;
    @Autowired
    private WxPayV3Service wxPayV3Service;
@@ -680,7 +686,7 @@
                if (takeShop == null || Constants.equalsInteger(takeShop.getDeleted(), Constants.ONE)) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "取件店铺不存在");
                }
                if (takeShop.getStatus() == null || !Constants.equalsInteger(takeShop.getStatus(), Constants.ONE)) {
                if (takeShop.getStatus() == null || Constants.equalsInteger(takeShop.getStatus(), Constants.ONE)) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "取件店铺已停业,请选择其他门店");
                }
                takeLat = BigDecimal.valueOf(takeShop.getLatitude());
@@ -822,6 +828,10 @@
        // è–ªé…¬è®¡ç®—与占比存储
        calculateAndSetFeeAllocation(orders, depositShop, takeShop);
        // æ— äººæŽ¥å•通知相关初始化
        orders.setPlatformSmsNotified(Constants.ZERO);
        orders.setPlatformSmsNotifiedTime(now);
        ordersMapper.insert(orders);
        Integer orderId = orders.getId();
@@ -892,6 +902,7 @@
        // 5. é‡æ–°ç”Ÿæˆç¬¬ä¸‰æ–¹è®¢å•编号(避免重复)
        String orderTradeNo = generateOrderTradeNo();
        orders.setOutTradeNo(orderTradeNo);
        orders.setPlatformSmsNotifiedTime(new Date());
        orders.setUpdateTime(new Date());
        ordersMapper.updateById(orders);
        // 6. å”¤èµ·å¾®ä¿¡æ”¯ä»˜
@@ -1034,9 +1045,6 @@
        // å–消/退款状态时查询退款记录
        Integer status = order.getStatus();
        if (status != null && (status == Constants.OrderStatus.overdue.getStatus()
                || status == Constants.OrderStatus.closed.getStatus()
                || status == Constants.OrderStatus.cancelOverdue.getStatus()
                || status == Constants.OrderStatus.cancelling.getStatus()
                || status == Constants.OrderStatus.cancelled.getStatus())) {
            OrdersRefund ordersRefund = ordersRefundMapper.selectOne(
                    new QueryWrapper<OrdersRefund>().lambda()
@@ -1124,6 +1132,8 @@
                .eq(Orders::getId, order.getId())
                .set(Orders::getIsUrgent, Constants.ONE)
                .set(Orders::getPlatformRewardAmount, urgentFeeFen)
                .set(Orders::getPlatformSmsNotified, Constants.ZERO) // é‡ç½®é€šçŸ¥çŠ¶æ€ä¸ºæœªé€šçŸ¥
                .set(Orders::getPlatformSmsNotifiedTime, new Date()) // é‡ç½®é€šçŸ¥åŸºå‡†æ—¶é—´ä¸ºå½“前
                .set(Orders::getUpdateTime, new Date());
        // å¼‚地寄存且有取件门店时,生成司机核销码
@@ -1597,13 +1607,14 @@
        vo.setExpectedDepositTime(order.getExpectedDepositTime());
        vo.setExpectedTakeTime(order.getExpectedTakeTime());
        vo.setArriveTime(order.getArriveTime());
        vo.setStatusName(Constants.OrderStatus.getDescByKey(order.getStatus(),order.getType()));
        // è´¹ç”¨ï¼ˆåˆ†ï¼‰
        vo.setBasicAmount(order.getBasicAmount());
        vo.setDeclaredAmount(order.getDeclaredAmount());
        vo.setDeclaredFee(order.getDeclaredFee());
        vo.setUrgentAmount(order.getUrgentAmount());
        vo.setActualPayAmount(order.getPayAmount());
        vo.setActualPayAmount(Constants.equalsInteger(order.getPayStatus(), Constants.ONE)?order.getPayAmount():order.getEstimatedAmount());
        // æ ‡è®°
        vo.setExceptionStatus(order.getExceptionStatus());
@@ -1630,6 +1641,7 @@
        // å–件信息
        if (order.getTakeShopId() != null) {
            vo.setTakeShopId(order.getTakeShopId());
            ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
            if (takeShop != null) {
                vo.setTakeShopName(takeShop.getName());
@@ -1643,6 +1655,16 @@
        // å–件联系人
        vo.setTakeUser(order.getTakeUser());
        vo.setTakePhone(order.getTakePhone());
        // å¸æœºä¿¡æ¯
        if (order.getAcceptDriver() != null) {
            DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
            if (driver != null) {
                vo.setDriverId(driver.getId());
                vo.setDriverName(driver.getName());
                vo.setDriverPhone(driver.getTelephone());
            }
        }
        // ç‰©å“ç±»åž‹åç§°
        if (order.getGoodType() != null) {
@@ -1663,11 +1685,67 @@
                        .eq(OrdersDetail::getDeleted, Constants.ZERO));
        vo.setDetailList(buildDetailList(details));
        // é€¾æœŸä¿¡æ¯
        OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
        vo.setOverdue(overdueInfo.getOverdue());
        vo.setOverdueDays(overdueInfo.getOverdueDays());
        vo.setOverdueFee(overdueInfo.getOverdueFee());
        Integer orderStatus = order.getStatus();
        if(Constants.equalsInteger(orderStatus, Constants.FIVE)){
            // é€¾æœŸä¿¡æ¯
            OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
            if (Constants.ONE.equals(order.getType())
                    && order.getTakeShopId() != null) {
                // å¼‚地寄存 + æœ‰å–件门店:
                // æ ¹æ®è¡ŒæŽè½¬ç§»åˆ°åº—æ—¶é—´(arriveTime)当天晚上12点判断是否逾期
                if (order.getArriveTime() != null) {
                    Calendar arriveCal = Calendar.getInstance();
                    arriveCal.setTime(order.getArriveTime());
                    arriveCal.set(Calendar.HOUR_OF_DAY, 23);
                    arriveCal.set(Calendar.MINUTE, 59);
                    arriveCal.set(Calendar.SECOND, 59);
                    Date arriveEndOfDay = arriveCal.getTime();
                    boolean isOverdue = new Date().after(arriveEndOfDay);
                    vo.setOverdue(isOverdue);
                    if (isOverdue) {
                        vo.setOverdueDays(overdueInfo.getOverdueDays() > 0 ? overdueInfo.getOverdueDays() : 1);
                        vo.setOverdueFee(overdueInfo.getOverdueFee());
                    } else {
                        vo.setOverdueDays(0);
                        vo.setOverdueFee(0L);
                    }
                } else {
                    vo.setOverdue(false);
                    vo.setOverdueDays(0);
                    vo.setOverdueFee(0L);
                }
            } else if (Constants.ZERO.equals(order.getType())) {
                // å°±åœ°å¯„存:保持原逻辑
                vo.setOverdue(overdueInfo.getOverdue());
                vo.setOverdueDays(overdueInfo.getOverdueDays());
                vo.setOverdueFee(overdueInfo.getOverdueFee());
            }
        } else {
            vo.setOverdue(false);
            vo.setOverdueDays(0);
            vo.setOverdueFee(0L);
        }
        // é€€æ¬¾ä¿¡æ¯ï¼ˆstatus=96关闭/99取消时返回)
        if (orderStatus != null &&
                (Constants.equalsInteger(orderStatus, Constants.OrderStatus.cancelled.getStatus()))) {
            vo.setRefundApplyTime(order.getCancelTime());
            // æŸ¥è¯¢é€€æ¬¾è®°å½•获取退款金额和备注
            OrdersRefund ordersRefund = ordersRefundMapper.selectOne(
                    new QueryWrapper<OrdersRefund>().lambda()
                            .eq(OrdersRefund::getOrderId, order.getId())
                            .eq(OrdersRefund::getDeleted, Constants.ZERO)
                            .orderByDesc(OrdersRefund::getCreateTime)
                            .last("limit 1"));
            if (ordersRefund != null) {
                vo.setRefundAmount(ordersRefund.getRefundAmount() != null
                        ? ordersRefund.getRefundAmount() : order.getRefundAmount());
                vo.setRefundRemark(ordersRefund.getRemark());
                vo.setRefundTime(ordersRefund.getRefundTime());
            } else {
                vo.setRefundAmount(order.getRefundAmount());
            }
        }
        // æ ¸é”€ç 
        Integer status = order.getStatus();
@@ -1687,6 +1765,68 @@
        } else {
            // é—¨åº—端:始终返回会员核销码
            vo.setMemberVerifyCode(order.getMemberVerifyCode());
        }
        // å¼‚地寄存经纬度(就地寄存不返回)
        if (Constants.ONE.equals(order.getType())) {
            // status=3(已接单):返回存件门店经纬度 + å¸æœºç»çº¬åº¦
            if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
                ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
                if (depositShop != null) {
                    vo.setDepositShopLng(depositShop.getLongitude());
                    vo.setDepositShopLat(depositShop.getLatitude());
                }
                if (order.getAcceptDriver() != null) {
                    DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
                    if (driver != null) {
                        vo.setDriverLng(driver.getLongitude());
                        vo.setDriverLat(driver.getLatitude());
                    }
                }
            }
            // status=4(配送中):返回取件点经纬度 + å¸æœºç»çº¬åº¦
            if (Constants.equalsInteger(status, Constants.OrderStatus.delivering.getStatus())) {
                // å–件点经纬度(优先取件门店,否则订单上的取件坐标)
                if (order.getTakeShopId() != null) {
                    ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
                    if (takeShop != null) {
                        vo.setTakeLng(takeShop.getLongitude());
                        vo.setTakeLat(takeShop.getLatitude());
                    }
                } else if (order.getTakeLgt() != null && order.getTakeLat() != null) {
                    vo.setTakeLng(order.getTakeLgt().doubleValue());
                    vo.setTakeLat(order.getTakeLat().doubleValue());
                }
                if (order.getAcceptDriver() != null) {
                    DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
                    if (driver != null) {
                        vo.setDriverLng(driver.getLongitude());
                        vo.setDriverLat(driver.getLatitude());
                    }
                }
            }
        }
        // è¯„价信息
        vo.setCommentStatus(order.getCommentStatus());
        if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
            vo.setCommentTime(order.getCommentTime());
            // æŸ¥è¯¢è¯„价记录,获取各对象评分
            List<OrderComment> comments = orderCommentMapper.selectList(new QueryWrapper<OrderComment>().lambda()
                    .eq(OrderComment::getOrderId, order.getId())
                    .eq(OrderComment::getDeleted, Constants.ZERO));
            for (OrderComment c : comments) {
                if (Constants.equalsInteger(c.getTargetType(), Constants.ONE)) {
                    vo.setDepositScore(c.getScore());
                    vo.setCommentContent(c.getContent());
                } else if (Constants.equalsInteger(c.getTargetType(), Constants.TWO)) {
                    vo.setTakeScore(c.getScore());
                } else if (Constants.equalsInteger(c.getTargetType(), Constants.THREE)) {
                    vo.setDriverScore(c.getScore());
                }
            }
            // è¯„价附件图片
            vo.setCommentImages(getFileUrls(order.getId(), Constants.FileType.COMMENT_ATTACH.getKey(), imgPrefix));
        }
        return vo;
@@ -1716,11 +1856,42 @@
            order.setCancelTime(now);
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员取消订单(待支付)", reason, memberId);
            // çŸ­ä¿¡é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已取消
            Member cancelMember1 = memberMapper.selectById(memberId);
            sendSmsNotify(cancelMember1 != null ? cancelMember1.getTelephone() : null,
                    Constants.SmsNotify.MEMBER_CANCELLED, "orderNo", order.getCode());
            return;
        }
        // å¾…寄存:直接取消,全额退款(不限订单类型)
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
            // å…ˆæ ‡è®°è®¢å•已取消
            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
            order.setCancelTime(now);
            order.setRefundAmount(order.getPayAmount());
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员取消订单(待寄存,全额退款)", reason, memberId);
            // é€šçŸ¥ä¼šå‘˜ï¼šå·²å–消
            sendOrderNotice(memberId, Constants.MemberOrderNotify.CANCELLED, orderId,
                    "orderNo", order.getCode());
            // çŸ­ä¿¡é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已取消
            Member cancelMember2 = memberMapper.selectById(memberId);
            sendSmsNotify(cancelMember2 != null ? cancelMember2.getTelephone() : null,
                    Constants.SmsNotify.MEMBER_CANCELLED, "orderNo", order.getCode());
            // è°ƒç”¨å¾®ä¿¡é€€æ¬¾V3,全额退款
            com.wechat.pay.java.service.refund.model.Refund refundResult;
            try {
                refundResult = wxPayV3Service.refund(order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount(),
                        "订单退款", wxPayProperties.getV3RefundNotifyUrl());
            } catch (Exception e) {
                log.error("待寄存订单退款调用异常, orderId={}", orderId, e);
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款失败,请稍后重试");
            }
            com.wechat.pay.java.service.refund.model.Status refundStatus = refundResult.getStatus();
            // è®°å½•退款信息
            OrdersRefund refund = new OrdersRefund();
            refund.setOrderId(orderId);
@@ -1728,23 +1899,22 @@
            refund.setCancelInfo(reason);
            refund.setCreateTime(now);
            refund.setDeleted(Constants.ZERO);
            // è°ƒç”¨å¾®ä¿¡é€€æ¬¾V3,全额退款
            Refund refundResult = wxPayV3Service.refund(order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount(),
                    "订单退款", wxPayProperties.getV3RefundNotifyUrl());
            refund.setBeforeStatus(Constants.OrderStatus.waitDeposit.getStatus());
            refund.setRefundAmount(order.getPayAmount());
            refund.setRefundCode(refundResult.getOutRefundNo());
            refund.setStatus(Constants.ZERO); // é€€æ¬¾ä¸­
            if (com.wechat.pay.java.service.refund.model.Status.SUCCESS.equals(refundStatus)) {
                // é€€æ¬¾æˆåŠŸ
                refund.setStatus(Constants.ONE);
            } else if (com.wechat.pay.java.service.refund.model.Status.PROCESSING.equals(refundStatus)) {
                // é€€æ¬¾ä¸­ï¼Œç­‰å›žè°ƒå¤„理
                refund.setStatus(Constants.ZERO);
            } else {
                // é€€æ¬¾å¤±è´¥/异常(CLOSED / ABNORMAL / å…¶ä»–)
                log.error("待寄存订单退款失败, orderId={}, refundStatus={}", orderId, refundStatus);
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款失败,请联系客服处理");
            }
            ordersRefundMapper.insert(refund);
            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
            order.setCancelTime(now);
            order.setRefundAmount(order.getPayAmount());
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员取消订单(待寄存,全额退款)", reason, memberId);
            // é€šçŸ¥ä¼šå‘˜ï¼šé€€æ¬¾ä¸­
            sendOrderNotice(memberId, Constants.MemberOrderNotify.REFUNDING, orderId,
                    "orderNo", order.getCode());
            return;
        }
@@ -1753,22 +1923,27 @@
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅异地寄存订单可取消");
        }
        // å·²å¯„å­˜/已接单:进入取消中状态
        // å·²å¯„å­˜/已接单:直接将订单类型改为就地寄存
        if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())
                || Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
            order.setStatus(Constants.OrderStatus.cancelling.getStatus());
            order.setCancelTime(now);
            order.setType(Constants.ZERO); // å°±åœ°å¯„å­˜
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员申请取消订单(已寄存/已接单)", reason, memberId);
            // é€šçŸ¥å­˜ä»¶é—¨åº—:退款申请
            saveCancelLog(order, "会员取消异地寄存订单,转为就地寄存", reason, memberId);
            // é€šçŸ¥å­˜ä»¶é—¨åº—
            if (order.getDepositShopId() != null) {
                sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
                ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
                sendSmsNotify(depositShop != null ? depositShop.getLinkPhone() : null,
                        Constants.SmsNotify.SHOP_REFUNDING, "orderNo", order.getCode());
            }
            // é€šçŸ¥å¸æœºï¼šè®¢å•退款中(已接单情况下司机需停止服务)
            if (order.getAcceptDriver() != null && Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.accepted.getStatus())) {
            // é€šçŸ¥å¸æœºï¼šè®¢å•已取消(已接单情况下司机需停止服务)
            if (order.getAcceptDriver() != null && Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
                DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
                sendSmsNotify(driver != null ? driver.getTelephone() : null,
                        Constants.SmsNotify.DRIVER_REFUNDING, "orderNo", order.getCode());
            }
            return;
        }
@@ -1930,6 +2105,15 @@
            sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.WAIT_VERIFY, order.getId(),
                    "orderNo", order.getCode());
        }
        // çŸ­ä¿¡é€šçŸ¥å­˜ä»¶é—¨åº—:有新订单待核验
        if (order.getDepositShopId() != null) {
            ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
            if (depositShop != null) {
                sendSmsNotify(depositShop.getLinkPhone(), Constants.SmsNotify.SHOP_WAIT_VERIFY,
                        "orderNo", order.getCode());
            }
        }
    }
    @Override
@@ -2027,11 +2211,10 @@
        if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "无权操作此订单");
        }
        // ä»…已完成(7)、已取消(99)、已退款(96)可删除
        // ä»…已完成(7)、已取消(99)
        int status = Constants.formatIntegerNum(order.getStatus());
        if (status != Constants.OrderStatus.finished.getStatus()
                && status != Constants.OrderStatus.cancelled.getStatus()
                && status != Constants.OrderStatus.closed.getStatus()) {
                && status != Constants.OrderStatus.cancelled.getStatus()) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前订单状态不可删除");
        }
        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
@@ -2316,6 +2499,9 @@
            orderCommentMapper.insert(driverComment);
        }
        // 4.4 ä¿å­˜è¯„价附件图片(obj_type=15,最多3张)
        saveVerifyImages(order.getId(), dto.getImages(), Constants.FileType.COMMENT_ATTACH.getKey(), null);
        // 5. æ›´æ–°é—¨åº—/司机平均评分
        updateTargetScore(Constants.ONE, order.getDepositShopId());
        if (isRemote && order.getTakeShopId() != null) {
@@ -2559,7 +2745,9 @@
            refundRecord.setType(3); // å‡ºåº“退款
            refundRecord.setCreateTime(now);
            refundRecord.setRefundRemark(remark);
            refundRecord.setRefundAmount(order.getRefundAmount());
            refundRecord.setDeleted(Constants.ZERO);
            refundRecord.setBeforeStatus(order.getStatus());
            ordersRefundMapper.insert(refundRecord);
            // è°ƒç”¨å¾®ä¿¡é€€æ¬¾V3(放在最后,确保前置操作全部成功)
@@ -2674,7 +2862,7 @@
        Long takeShopFee = 0L;
        Long driverFee = 0L;
        if (Constants.equalsInteger(order.getType(), Constants.TWO)) {
        if (Constants.equalsInteger(order.getType(), Constants.ONE)) {
            // å¼‚地寄存:存件门店 + å¸æœº
            driverFee = new BigDecimal(totalAmount)
                    .multiply(driverRate)
@@ -2819,6 +3007,15 @@
            sendShopNotice(order.getTakeShopId(), Constants.ShopOrderNotify.ARRIVED, order.getId(),
                    "orderNo", order.getCode(),
                    "destination", destination);
        }
        // çŸ­ä¿¡é€šçŸ¥ä¼šå‘˜ï¼šè¡ŒæŽå·²é€è¾¾
        Member arrivedMember = memberMapper.selectById(order.getMemberId());
        if (arrivedMember != null) {
            sendSmsNotify(arrivedMember.getTelephone(), Constants.SmsNotify.MEMBER_ARRIVED,
                    "orderNo", order.getCode(),
                    "address", destination,
                    "code", order.getMemberVerifyCode() != null ? order.getMemberVerifyCode() : "");
        }
    }
@@ -2993,12 +3190,6 @@
        }
        if (Constants.equalsInteger(status, Constants.OrderStatus.cancelled.getStatus())) {
            return "订单已取消,感谢您的支持,欢迎下次再会!";
        }
        if (Constants.equalsInteger(status, Constants.OrderStatus.cancelling.getStatus())) {
            return "退款申请已提交,平台会尽快为您处理退款";
        }
        if (Constants.equalsInteger(status, Constants.OrderStatus.closed.getStatus())) {
            return "退款已成功原路返回,请注意查收";
        }
        return "";
    }
@@ -3294,4 +3485,293 @@
        return vo;
    }
    @Override
    public int cancelTimeoutUnpaidOrders() {
        // èŽ·å–è¶…æ—¶é…ç½®ï¼ˆåˆ†é’Ÿï¼‰
        String autoCancelTimeStr = operationConfigBiz.getConfig().getAutoCancelTime();
        if (StringUtils.isBlank(autoCancelTimeStr)) {
            log.info("未配置超时取消时间,跳过");
            return 0;
        }
        int autoCancelMinutes;
        try {
            autoCancelMinutes = Integer.parseInt(autoCancelTimeStr);
        } catch (NumberFormatException e) {
            log.warn("超时取消时间配置异常: {}", autoCancelTimeStr);
            return 0;
        }
        if (autoCancelMinutes <= 0) {
            return 0;
        }
        // æŸ¥è¯¢æ‰€æœ‰è¶…时未支付订单:status=0 ä¸” åˆ›å»ºæ—¶é—´ + é…ç½®åˆ†é’Ÿæ•° < å½“前时间
        Date deadline = new Date(System.currentTimeMillis() - (long) autoCancelMinutes * 60 * 1000);
        List<Orders> timeoutOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getStatus, Constants.OrderStatus.waitPay.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO)
                .lt(Orders::getCreateTime, deadline));
        if (timeoutOrders == null || timeoutOrders.isEmpty()) {
            return 0;
        }
        int count = 0;
        Date now = new Date();
        for (Orders order : timeoutOrders) {
            try {
                order.setStatus(Constants.OrderStatus.cancelled.getStatus());
                order.setCancelTime(now);
                order.setUpdateTime(now);
                ordersMapper.updateById(order);
                // å†™å…¥æ“ä½œæ—¥å¿—
                OrderLog orderLog = new OrderLog();
                orderLog.setOrderId(order.getId());
                orderLog.setTitle("系统自动取消(超时未支付)");
                orderLog.setLogInfo("订单超时" + autoCancelMinutes + "分钟未支付,系统自动取消");
                orderLog.setObjType(Constants.ORDER_LOG_CANCEL);
                orderLog.setOrderStatus(Constants.OrderStatus.cancelled.getStatus());
                orderLog.setOptUserType(3); // 3=系统
                orderLog.setCreateTime(now);
                orderLog.setDeleted(Constants.ZERO);
                orderLogService.create(orderLog);
                // çŸ­ä¿¡é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已取消
                if (order.getMemberId() != null) {
                    Member member = memberMapper.selectById(order.getMemberId());
                    sendSmsNotify(member != null ? member.getTelephone() : null,
                            Constants.SmsNotify.MEMBER_CANCELLED, "orderNo", order.getCode());
                }
                count++;
            } catch (Exception e) {
                log.error("取消超时订单异常, orderId={}, error={}", order.getId(), e.getMessage());
            }
        }
        log.info("超时未支付订单自动取消完成,共取消{}单", count);
        return count;
    }
    @Override
    public int notifyUngrabbedOrders() {
        // èŽ·å–æ— äººæŠ¢å•é€šçŸ¥æ—¶é—´é…ç½®ï¼ˆåˆ†é’Ÿï¼‰
        String noGrabTimeStr = operationConfigBiz.getConfig().getNoGrabNotifyTime();
        if (StringUtils.isBlank(noGrabTimeStr)) {
            return 0;
        }
        int noGrabMinutes;
        try {
            noGrabMinutes = Integer.parseInt(noGrabTimeStr);
        } catch (NumberFormatException e) {
            log.warn("无人抢单通知时间配置异常: {}", noGrabTimeStr);
            return 0;
        }
        if (noGrabMinutes <= 0) {
            return 0;
        }
        // èŽ·å–é€šçŸ¥äººå‘˜ä¸»é”®åˆ—è¡¨
        String noGrabUsers = operationConfigBiz.getConfig().getNoGrabNotifyUsers();
        if (StringUtils.isBlank(noGrabUsers)) {
            return 0;
        }
        List<String> userIdStrList = Arrays.asList(noGrabUsers.split(","));
        List<Integer> userIds = new ArrayList<>();
        for (String idStr : userIdStrList) {
            if (StringUtils.isNotBlank(idStr.trim())) {
                userIds.add(Integer.parseInt(idStr.trim()));
            }
        }
        if (userIds.isEmpty()) {
            return 0;
        }
        // æŸ¥è¯¢é€šçŸ¥äººå‘˜æ‰‹æœºå·
        List<String> notifyPhones = new ArrayList<>();
        for (Integer userId : userIds) {
            SystemUser user = systemUserMapper.selectById(userId);
            if (user != null && StringUtils.isNotBlank(user.getMobile())) {
                notifyPhones.add(user.getMobile());
            }
        }
        if (notifyPhones.isEmpty()) {
            log.warn("无人抢单通知人员均无有效手机号");
            return 0;
        }
        // æŸ¥è¯¢å¼‚地已寄存(status=2)、未通知(platformSmsNotified=0或null)、超时的订单
        Date deadline = new Date(System.currentTimeMillis() - (long) noGrabMinutes * 60 * 1000);
        List<Orders> ungrabbedOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getType, Constants.ONE) // å¼‚地寄存
                .eq(Orders::getStatus, Constants.OrderStatus.deposited.getStatus()) // å·²å¯„å­˜
                .eq(Orders::getDeleted, Constants.ZERO)
                .ne(Orders::getPlatformSmsNotified, Constants.ONE) // æœªé€šçŸ¥
                .lt(Orders::getPlatformSmsNotifiedTime, deadline)); // é€šçŸ¥åŸºå‡†æ—¶é—´è¶…过配置时间
        if (ungrabbedOrders == null || ungrabbedOrders.isEmpty()) {
            return 0;
        }
        int count = 0;
        Date now = new Date();
        for (Orders order : ungrabbedOrders) {
            try {
                // ç»™æ‰€æœ‰é€šçŸ¥äººå‘˜å‘短信
                for (String phone : notifyPhones) {
                    sendSmsNotify(phone, Constants.SmsNotify.PLATFORM_WAIT_GRAB,
                            "orderNo", order.getCode(),
                            "time", String.valueOf(noGrabMinutes));
                }
                // æ ‡è®°å·²é€šçŸ¥ + è®°å½•通知时间
                order.setPlatformSmsNotified(Constants.ONE);
                order.setPlatformSmsNotifiedTime(now);
                order.setUpdateTime(now);
                ordersMapper.updateById(order);
                count++;
            } catch (Exception e) {
                log.error("无人抢单短信通知异常, orderId={}, error={}", order.getId(), e.getMessage());
            }
        }
        log.info("无人抢单短信通知完成,共通知{}单", count);
        return count;
    }
    @Override
    public int autoCompleteOrders() {
        // èŽ·å–è‡ªåŠ¨ç¡®è®¤æ”¶è´§å¤©æ•°é…ç½®
        String autoConfirmDaysStr = operationConfigBiz.getConfig().getAutoConfirmReceipt();
        if (StringUtils.isBlank(autoConfirmDaysStr)) {
            return 0;
        }
        int autoConfirmDays;
        try {
            autoConfirmDays = Integer.parseInt(autoConfirmDaysStr);
        } catch (NumberFormatException e) {
            log.warn("自动确认收货天数配置异常: {}", autoConfirmDaysStr);
            return 0;
        }
        if (autoConfirmDays <= 0) {
            return 0;
        }
        // æŸ¥è¯¢å·²é€è¾¾(status=5)且送达时间超过配置天数的订单
        Date deadline = new Date(System.currentTimeMillis() - (long) autoConfirmDays * 24 * 60 * 60 * 1000);
        List<Orders> orders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getStatus, Constants.OrderStatus.arrived.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO)
                .isNotNull(Orders::getArriveTime)
                .lt(Orders::getArriveTime, deadline));
        if (orders == null || orders.isEmpty()) {
            return 0;
        }
        int count = 0;
        Date now = new Date();
        for (Orders order : orders) {
            try {
                // é€¾æœŸæœªæ”¯ä»˜çš„不自动完成
                if (Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
                    continue;
                }
                // æ›´æ–°è®¢å•状态为已完成
                order.setStatus(Constants.OrderStatus.finished.getStatus());
                order.setFinishTime(now);
                order.setUpdateTime(now);
                ordersMapper.updateById(order);
                // é‡Šæ”¾æ ¸é”€ç 
                if (StringUtils.isNotBlank(order.getMemberVerifyCode())) {
                    releaseVerifyCode(order.getMemberVerifyCode());
                }
                // ç”Ÿæˆæ”¶ç›Šè®°å½•
                calculateAndSaveOrderFees(order.getId());
                generateRevenueRecords(order.getId());
                // è®°å½•操作日志
                OrderLog orderLog = new OrderLog();
                orderLog.setOrderId(order.getId());
                orderLog.setTitle("系统自动完成");
                orderLog.setLogInfo("订单已送达超过" + autoConfirmDays + "天未确认,系统自动完成");
                orderLog.setObjType(Constants.ORDER_LOG_CANCEL);
                orderLog.setOrderStatus(Constants.OrderStatus.finished.getStatus());
                orderLog.setOptUserType(3); // 3=系统
                orderLog.setCreateTime(now);
                orderLog.setDeleted(Constants.ZERO);
                orderLogService.create(orderLog);
                // é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已完成
                sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.FINISHED, order.getId(),
                        "orderNo", order.getCode());
                // é€šçŸ¥å­˜ä»¶é—¨åº—和取件门店
                String settleDays = operationConfigBiz.getConfig().getSettlementDate();
                notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                        "orderNo", order.getCode(),
                        "settleDays", settleDays != null ? settleDays : "7");
                // é€šçŸ¥å¸æœº
                if (order.getAcceptDriver() != null) {
                    sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, order.getId(),
                            "orderNo", order.getCode(),
                            "settleDays", settleDays != null ? settleDays : "7");
                }
                count++;
            } catch (Exception e) {
                log.error("自动完成订单异常, orderId={}, error={}", order.getId(), e.getMessage());
            }
        }
        log.info("自动完成超时订单完成,共完成{}单", count);
        return count;
    }
    /**
     * å‘送短信通知(失败不影响主业务)
     * @param phone       æŽ¥æ”¶æ‰‹æœºå·
     * @param smsNotify   çŸ­ä¿¡æ¨¡æ¿æžšä¸¾
     * @param paramPairs  æ¨¡æ¿å‚数,key-value äº¤æ›¿ä¼ å…¥ï¼Œå¦‚ "orderNo", "XL202604220001"
     */
    private void sendSmsNotify(String phone, Constants.SmsNotify smsNotify, String... paramPairs) {
        if (StringUtils.isBlank(phone)) {
            return;
        }
        String content = smsNotify.format(paramPairs);
        try {
            JSONObject templateParam = new JSONObject();
            for (int i = 0; i < paramPairs.length - 1; i += 2) {
                templateParam.put(paramPairs[i], paramPairs[i + 1]);
            }
            boolean result = AliSmsService.sendSms(phone, smsNotify.getTemplateCode(),
                    templateParam.toJSONString());
            if (result) {
                log.info("短信发送成功: phone={}, template={}", phone, smsNotify.name());
            } else {
                log.warn("短信发送失败: phone={}, template={}", phone, smsNotify.name());
            }
            // å­˜å‚¨çŸ­ä¿¡è®°å½•
            Smsrecord record = new Smsrecord();
            record.setPhone(phone);
            record.setContent(content);
            record.setType(Constants.ONE); // 1=订单通知
            record.setStatus(result ? Constants.ONE : Constants.ZERO); // 1=已发送, 0=发送失败
            record.setCreateTime(new Date());
            record.setDeleted(Constants.ZERO);
            smsrecordMapper.insert(record);
        } catch (Exception e) {
            log.error("短信发送异常: phone={}, template={}, error={}", phone, smsNotify.name(), e.getMessage());
            // å¼‚常也记录
            try {
                Smsrecord record = new Smsrecord();
                record.setPhone(phone);
                record.setContent(content);
                record.setType(Constants.ONE);
                record.setStatus(Constants.ZERO); // å‘送失败
                record.setCreateTime(new Date());
                record.setDeleted(Constants.ZERO);
                smsrecordMapper.insert(record);
            } catch (Exception ignored) {}
        }
    }
}
server/services/src/main/java/com/doumee/service/business/impl/RevenueServiceImpl.java
@@ -173,8 +173,7 @@
        if (shop == null || Constants.equalsInteger(shop.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "门店不存在");
        }
        Integer memberId = shop.getRegionMemberId();
        return buildRevenueStatistics(memberId, Constants.TWO, shop.getBalance());
        return buildRevenueStatistics(shop.getId(), Constants.TWO, shop.getBalance());
    }
    @Override
@@ -257,7 +256,7 @@
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        IPage<Revenue> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        QueryWrapper<Revenue> qw = buildRevenueQueryWrapper(pageWrap.getModel(), shop.getRegionMemberId());
        QueryWrapper<Revenue> qw = buildRevenueQueryWrapper(pageWrap.getModel(), shop.getId());
        PageData<Revenue> result = PageData.from(revenueMapper.selectPage(page, qw));
        if (result != null && result.getRecords() != null) {
            for (Revenue model : result.getRecords()) {
@@ -278,7 +277,7 @@
        if (shop == null || Constants.equalsInteger(shop.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        return buildRevenueSummary(queryDTO, shop.getRegionMemberId());
        return buildRevenueSummary(queryDTO, shop.getId());
    }
    private QueryWrapper<Revenue> buildRevenueQueryWrapper(RevenueQueryDTO query, Integer memberId) {
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -32,6 +32,7 @@
import com.doumee.dao.vo.ShopCenterVO;
import com.doumee.dao.vo.ShopLoginVO;
import com.doumee.dao.vo.ShopNearbyVO;
import com.doumee.dao.vo.ShopSalesStatsVO;
import com.doumee.dao.vo.ShopWebDetailVO;
import com.doumee.service.business.AreasService;
import com.doumee.service.business.ShopInfoService;
@@ -996,6 +997,106 @@
                .eq(Orders::getDeleted, Constants.ZERO)
                .in(Orders::getStatus, Constants.OrderStatus.delivering.getStatus(), Constants.OrderStatus.arrived.getStatus()));
        vo.setWaitReceiveCount(waitReceiveCount.intValue());
        // æ”¯ä»˜å®æçŽ°è´¦å·
        vo.setAliAccount(shop.getAliAccount());
        vo.setAliName(shop.getAliName());
        return vo;
    }
    @Override
    public ShopSalesStatsVO getShopSalesStats(Integer shopId, Integer period) {
        // è®¡ç®—时间范围 0=今日 1=本月 2=上月
        Calendar cal = Calendar.getInstance();
        Date startTime;
        Date endTime;
        if (Constants.equalsInteger(period, 2)) {
            // ä¸Šæœˆï¼šä¸Šæœˆ1号00:00:00 ~ æœ¬æœˆ1号00:00:00
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            endTime = cal.getTime();
            cal.add(Calendar.MONTH, -1);
            startTime = cal.getTime();
        } else if (Constants.equalsInteger(period, 1)) {
            // æœ¬æœˆï¼šæœ¬æœˆ1号00:00:00 ~ ä¸‹æœˆ1号00:00:00
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            startTime = cal.getTime();
            cal.add(Calendar.MONTH, 1);
            endTime = cal.getTime();
        } else {
            // ä»Šæ—¥ï¼šä»Šå¤©00:00:00 ~ æ˜Žå¤©00:00:00
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            startTime = cal.getTime();
            cal.add(Calendar.DAY_OF_MONTH, 1);
            endTime = cal.getTime();
        }
        ShopSalesStatsVO vo = new ShopSalesStatsVO();
        // 1. é”€å”®é¢ + è®¢å•数:按订单创建时间,存件门店或取件门店是本门店
        // å­˜ä»¶é—¨åº—
        List<Orders> depositSalesOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .ge(Orders::getCreateTime, startTime)
                .lt(Orders::getCreateTime, endTime)
                .eq(Orders::getDepositShopId, shopId));
        // å–件门店
        List<Orders> takeSalesOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .ge(Orders::getCreateTime, startTime)
                .lt(Orders::getCreateTime, endTime)
                .eq(Orders::getTakeShopId, shopId));
        long salesAmount = depositSalesOrders.stream().mapToLong(o -> o.getTotalAmount() != null ? o.getTotalAmount() : 0L).sum()
                + takeSalesOrders.stream().mapToLong(o -> o.getTotalAmount() != null ? o.getTotalAmount() : 0L).sum();
        vo.setSalesAmount(salesAmount);
        vo.setOrderCount(depositSalesOrders.size() + takeSalesOrders.size());
        // 2. ç»“算利润:按结算时间,根据门店角色取depositShopFee或takeShopFee
        // å­˜ä»¶é—¨åº— = æœ¬é—¨åº— çš„订单,取 depositShopFee
        List<Orders> depositSettleOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getSettlementStatus, Constants.ONE)
                .ge(Orders::getSettlementTime, startTime)
                .lt(Orders::getSettlementTime, endTime)
                .eq(Orders::getDepositShopId, shopId));
        // å–件门店 = æœ¬é—¨åº— çš„订单,取 takeShopFee
        List<Orders> takeSettleOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getSettlementStatus, Constants.ONE)
                .ge(Orders::getSettlementTime, startTime)
                .lt(Orders::getSettlementTime, endTime)
                .eq(Orders::getTakeShopId, shopId));
        long depositFee = depositSettleOrders.stream()
                .mapToLong(o -> o.getDepositShopFee() != null ? o.getDepositShopFee() : 0L).sum();
        long takeFee = takeSettleOrders.stream()
                .mapToLong(o -> o.getTakeShopFee() != null ? o.getTakeShopFee() : 0L).sum();
        vo.setSettlementProfit(depositFee + takeFee);
        // 3. åœ¨åº“订单数
        // 3.1 å­˜ä»¶é—¨åº—=本门店,status in (2已寄存, 5待取件)
        Long depositStorageCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getDepositShopId, shopId)
                .in(Orders::getStatus,
                        Constants.OrderStatus.deposited.getStatus(),
                        Constants.OrderStatus.arrived.getStatus()));
        // 3.2 å–件门店=本门店,status = 5待取件
        Long takeStorageCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getTakeShopId, shopId)
                .eq(Orders::getStatus, Constants.OrderStatus.arrived.getStatus()));
        vo.setStorageCount(depositStorageCount.intValue() + takeStorageCount.intValue());
        return vo;
    }
server/services/src/main/resources/application-dev.yml
@@ -95,17 +95,17 @@
alipay:
  pay:
    appId: 2021006146614417
    privateKey: MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC0zIy4Ej8yG34uQ8zECWu12INdTWYuvSudhrf1qtUrPx4FO9vDGc8NdPz63M3y+akWh9o8gsl7qQqOoB7o8gYebLTzF2ZP3ByxgkNRGUwZBIi0iL/28eq/2uDCWnqNBDEeFQpfWeRcErC28LCB6JSI7F6owH78LiU8pNO0pVvqwvcLovlVee4UDtLThBegUxkkHXq4EM9yutkfps1ruDiTGFYyTjS8g9BmNJ7rJoRSKTE85Gl8OYp1SuFbyurPEaFLBW9yGHbsqMqegys2PwV6PxZq3WBbFMiuWI5fane+VL2jyVoV36CYPS2GFjq0stuUHZvPL1uYk+bwAAei7a2JAgMBAAECggEBAJMnTX1gNJl28Qt82YPRWx6F292r6nguNfqftMi6Q4hQdgGyh5rTPcfpKSKRZvrVIz+YURMhLBZ/Ln2Ja78ThdFrjxewOvDS6XO830d5vIs8OnweNYgrvHJtFrR5afeuqr+eRnP3OTuLZtNvMWp6v7J2GFFnY7OjZ2Flkn4cfKSkqnLQnZ/oDKA/a40OclHRmaQZcS5YkGZqeR+NssanUDad89IVz7NU0kXTbaXjbGHyJscLr6hM3otdfrYsboJ5XHLOnnpSLptv3I0HSUeHBSdhAbP5Hlot+xOo6EbeougefmT2FZWaDA+kPHgajCOdZHW3v+13Xp+PioRW0IPcsLUCgYEA+11Fqm+tpP351tdkJyW+weTy0/OnvUuSVQP/0/kvzQ84kjfkxdoyTKe6kT8+K5b5dkCQhGRfWgb7/amKEe1loCQsujf7/6aebUgTevrOs96LErMT1fCqId1t/MWgXVFpzo8n2OTks2r0nleHY7C9VZiPIbGBQDbkJChIFRAiRXsCgYEAuCIg2p4RrlOG/i4/r1xKmOIwba9snTBnAB8/bri913xMy055OVF6P46hC6d/J25QNqYGODBBF4kmiBuco+vtuz6C94Uo3h/oYD3jetmwlAnRWjEl9Z3Pkf5cbE+o5KBZmUa+M7BTrBDutzdRDqX2SANvnoDWVF03teY9lcVNL8sCgYEAoFdeEhtNC/tKfKZG58XnCe4Oi+9YJ6LmRD7Z2RCSUl8MOhFXaHIINeekVfSeptWWab1DsoAIZvgflC6quUbS1bVdpqgBopFZa+JXMtJ8OjaSRipfU9BB5npGJ8C0y/Ib6TxeMbfIvz5RrhOtdIUQMWKwotCE3z5khz/+wxjYk7MCgYBrMesgeo9ehl/7T99hboA7GssIv+yiYhBEoOxjwAc9EK8AWNH3zXg20gjtaPh8cxsdhW/vfCAY3I5jBHgfcfU3YcAK6ymMjtTQWpc46MyEkmafdCdeIx45JvSVVZbEaplewzFtlARSEpV2ciytM276I43yn5ynBpGtQrmtnGxAGwKBgQCy8imu84IhC+BivLRYdsQ+hMsqs0nih4wfVPKWr6K64QO6CZ4riL76lppH8UBG5EXtRd6cjBbORgvQtr22OJxFcj8WuZjBvYvvCOsTE0jGMdiyBqx9k0PsLrtlVZPFP4kj9vDSFNIyAgIOiAmYY4kBtGfhQqRcDgZK+mgHY0zMbg==
    appCertPath: foo/dev/appCertPublicKey_2021006146614417.crt
    alipayPublicCertPath: foo/dev/alipayCertPublicKey_RSA2.crt
    rootCertPath: foo/dev/alipayRootCert.crt
    appId: 2021006147660139
    privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEeqqkZztBHfK+vpyBi6ejgDTHZuZ3yiuXds+lRBbMo/g24F5trH+oLHW0gMhSxihFfQBBIpVBXDsPQK4ZkhDWTaOBktnU1UMRoOEiaaZU6EiWy10ePFUmpdXpkCQEp7rc88OwI90p58S3+L+Ckak60WqNwJBdB5YGBaUunryBA78U4zW1KNY7JvoRnZDcFMQiczikwUzhO7EAi0yVrVpsVsc9s87mcS4uOJKx4qb2E83r9RJ1z30db+cIIZRiLP2oNZBLYzgKpOouE+uIgxhQzlh6cOASNZQulXuUjoT/+Y9w4njfl4TmKIXWcJFKIMc6kMiux9tTncpp0TqRwk1tAgMBAAECggEALkSYtJheusnbpRFr95G0i2sggqh3s1PXihZ/dXKgT9Z5GCsj8X3Cng7CNRxykBN73kk+axhCv56Bhej8Vqcv8ddcnqG/TEBgR+Fzws/QTIRau6/uILWic7RvuE2qPbJl7aw1s9/uL/UVPSGFr7CvgltYVUM4e7/Sk1529JCK4XJfoXP5tKJ3OaXssvaFnCKEU8IGQkjRG+lUZJhAHVtClGHtgrhevgRhy2zre5wp2qSa8d/MqrPruSYS02hn9b5Nl6i2PlUS6dGlJ4lrxYTG22ukYYoxAPNPS7gnvmveXonWP7b5tPhKRpZjnoySojz3WECUlhz/v8wM1cDrpq+GQQKBgQDsc7y2rlx4f77a7ORfb5/qWHCOJs1cIzggj0kJ7TgFGe71kbCQ5nywD/Fe5V9OwbW+DCxOME+SrrHeiK4axWiu5si/1JlurJoxNy+4k4ywk3ZA3Nv2aBhlPqfkwDhJ0z7Mgsq2c/YgnVddmSvKZoC39wA77ovks4GDxaBOt8N7PQKBgQDUuPGgzkwcgb60UdaxfMbacrPsW26vDxaE4ceuXo2m8KDiCIqkF2y9r6AdWMTgGGSJwOsk7+FP+21VdRivCg9HcOLWngveUc6xDIuqKHVpemMo3SdCF4Wqf96rRc3VOBr5cfIdWxeorZf5umMyKnIAjAFETOOrK7eLTTmjyLD98QKBgB82S+Plcklpu3zUpnS+nGJn2Du7fYI7F+6cW2zXBn0N5lA+Mgt+kVkAUcFQD9uqkF4M51BO6kIXk10nt6vLAT2NM1S3MKW+XQBAI6l+uKSaYpK/VL3bEdVThwAYK5X7L5/5Z97bwdKeUmkFjhVCoJ0oGrzOiWLgGymUzct2UHSVAoGBAMb+7Cs+Ub0pMrmFBY6r52pbey1Uq0pglvRgMmhQU7sjx50r2GaA81zPer15WVM5/nNPYaoALYqg7jrPe/PjOT/fvpR+7SNg7DZ8QftANfYiY7jKifst/gDt9ePLPS6FedZ4XcJQgOVu34jicAFx64vPbS/zrddm4iEScSVijRBBAoGAXCheERsx8+n16Us/DttXFUa1nc7+D8WR6buM1QMZgQCVF2qp3XtM+FusCKL4+q1+dtag8svLjJFp9QbaAXqX8Zk7rn8wUHbDloPTPy9XWgrPowyL9MPU+e/Rq8Hr6TWPDBd4TU64YzIEfBQYpJXfZbXhVYmK3o7xHXKB1x4vvEM=
    appCertPath: pay/pro/appCertPublicKey.crt
    alipayPublicCertPath: pay/pro/alipayCertPublicKey_RSA2.crt
    rootCertPath: pay/pro/alipayRootCert.crt
upload:
  type: ftp
# è…¾è®¯åœ°å›¾apikey
tencent_key: WE3BZ-HN6WS-ONDOH-62QCV-MNL6F-5NFNE
tencent_key:
# é«˜å¾·åœ°å›¾apikey
geocode_map_key: 9a6c1f0eff2e5aa91989ca9d4c21e262
server/web/src/main/java/com/doumee/api/web/ConfigApi.java
@@ -1,6 +1,7 @@
package com.doumee.api.web;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.LoginShopRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.constants.Constants;
import com.doumee.core.model.ApiResponse;
@@ -168,6 +169,33 @@
    }
    @LoginShopRequired
    @ApiOperation(value = "门店通知消息分页", notes = "未读优先,时间倒序")
    @PostMapping("/shopNoticePage")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<PageData<Notice>> shopNoticePage(@RequestBody PageWrap<Notice> pageWrap) {
        if (pageWrap.getModel() == null) {
            pageWrap.setModel(new Notice());
        }
        pageWrap.getModel().setUserId(this.getShopId());
        pageWrap.getModel().setUserType(Constants.TWO);
        return ApiResponse.success("查询成功", noticeService.findPage(pageWrap));
    }
    @LoginRequired
    @ApiOperation(value = "门店标记全部已读", notes = "标记当前用户所有未读通知为已读")
    @PostMapping("/shopReadAllNotice")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse shopReadAllNotice() {
        noticeService.readAllNotice(Constants.TWO, this.getShopId());
        return ApiResponse.success("操作成功");
    }
    @LoginRequired
    @ApiOperation(value = "首页进行中订单提示", notes = "返回最新一条进行中订单的状态和提示文案,无订单返回null")
    @GetMapping("/getActiveOrderTip")
server/web/src/main/java/com/doumee/api/web/PaymentCallback.java
@@ -4,11 +4,17 @@
import com.doumee.config.wx.WxPayV3Service;
import com.doumee.core.constants.Constants;
import com.doumee.core.utils.ID;
import com.doumee.core.utils.aliyun.AliSmsService;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.OrdersRefundMapper;
import com.doumee.dao.business.OrdersMapper;
import com.doumee.dao.business.SmsrecordMapper;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.Notice;
import com.doumee.dao.business.model.Orders;
import com.doumee.dao.business.model.OrdersRefund;
import com.doumee.dao.business.model.Smsrecord;
import com.alibaba.fastjson.JSONObject;
import com.doumee.service.business.NoticeService;
import com.doumee.service.business.OrdersService;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
@@ -49,6 +55,12 @@
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private SmsrecordMapper smsrecordMapper;
    @Autowired
    private MemberMapper memberMapper;
    // ==================== V2 å›žè°ƒ ====================
@@ -171,32 +183,84 @@
                            .last("limit 1"));
            if (refundRecord != null) {
                Status refundStatus = refundNotification.getRefundStatus();
                if (Status.SUCCESS.equals(refundStatus)) {
                    refundRecord.setStatus(Constants.ONE); // é€€æ¬¾æˆåŠŸ
                    refundRecord.setRefundTime(new java.util.Date());
                } else if (Status.CLOSED.equals(refundStatus) || Status.ABNORMAL.equals(refundStatus)) {
                    refundRecord.setStatus(Constants.TWO); // é€€æ¬¾å¤±è´¥
                }
                ordersRefundMapper.updateById(refundRecord);
                log.info("退款记录状态已更新, refundRecordId={}, status={}", refundRecord.getId(), refundRecord.getStatus());
                // é€€æ¬¾æˆåŠŸ â†’ é€šçŸ¥ä¼šå‘˜
                if (Status.SUCCESS.equals(refundStatus) && refundRecord.getOrderId() != null) {
                    Orders refundOrder = ordersMapper.selectById(refundRecord.getOrderId());
                    if (refundOrder != null) {
                        Notice notice = new Notice();
                        notice.setUserType(0);
                        notice.setUserId(refundOrder.getMemberId());
                        notice.setTitle(Constants.MemberOrderNotify.REFUNDED.getTitle());
                        notice.setContent(Constants.MemberOrderNotify.REFUNDED.format(
                                "orderNo", refundOrder.getCode(),
                                "amount", String.valueOf(Constants.getFormatMoney(refundOrder.getRefundAmount() != null ? refundOrder.getRefundAmount() : 0L))));
                        notice.setObjId(refundOrder.getId());
                        notice.setObjType(0);
                        notice.setStatus(0);
                        notice.setIsdeleted(Constants.ZERO);
                        notice.setCreateDate(new java.util.Date());
                        noticeService.create(notice);
                // å¹‚等判断:已成功/已失败的不重复处理
                if (!Constants.equalsInteger(refundRecord.getStatus(), Constants.ZERO)) {
                    log.info("退款记录已处理, refundRecordId={}, status={}, è·³è¿‡", refundRecord.getId(), refundRecord.getStatus());
                } else {
                    if (Status.SUCCESS.equals(refundStatus)) {
                        refundRecord.setStatus(Constants.ONE); // é€€æ¬¾æˆåŠŸ
                        refundRecord.setRefundTime(new java.util.Date());
                    } else if (Status.CLOSED.equals(refundStatus) || Status.ABNORMAL.equals(refundStatus)) {
                        refundRecord.setStatus(Constants.TWO); // é€€æ¬¾å¤±è´¥
                        refundRecord.setRemark("微信退款失败: " + refundStatus.name());
                    }
                    // PROCESSING çŠ¶æ€æ— å˜åŒ–ï¼Œä¸æ›´æ–°
                    ordersRefundMapper.updateById(refundRecord);
                    log.info("退款记录状态已更新, refundRecordId={}, status={}", refundRecord.getId(), refundRecord.getStatus());
                    // é€€æ¬¾æˆåŠŸ â†’ é€šçŸ¥ä¼šå‘˜
                    if (Status.SUCCESS.equals(refundStatus) && refundRecord.getOrderId() != null) {
                        Orders refundOrder = ordersMapper.selectById(refundRecord.getOrderId());
                        if (refundOrder != null) {
                            Notice notice = new Notice();
                            notice.setUserType(0);
                            notice.setUserId(refundOrder.getMemberId());
                            notice.setTitle(Constants.MemberOrderNotify.REFUNDED.getTitle());
                            notice.setContent(Constants.MemberOrderNotify.REFUNDED.format(
                                    "orderNo", refundOrder.getCode(),
                                    "amount", String.valueOf(Constants.getFormatMoney(refundOrder.getRefundAmount() != null ? refundOrder.getRefundAmount() : 0L))));
                            notice.setObjId(refundOrder.getId());
                            notice.setObjType(0);
                            notice.setStatus(0);
                            notice.setIsdeleted(Constants.ZERO);
                            notice.setCreateDate(new java.util.Date());
                            noticeService.create(notice);
                            // çŸ­ä¿¡é€šçŸ¥ä¼šå‘˜ï¼šé€€æ¬¾å·²å®Œæˆ
                            Member refundMember = memberMapper.selectById(refundOrder.getMemberId());
                            if (refundMember != null && StringUtils.isNotBlank(refundMember.getTelephone())) {
                                String smsContent = Constants.SmsNotify.MEMBER_REFUNDED.format(
                                        "orderNo", refundOrder.getCode(),
                                        "money", String.valueOf(Constants.getFormatMoney(
                                                refundOrder.getRefundAmount() != null ? refundOrder.getRefundAmount() : 0L)));
                                try {
                                    JSONObject templateParam = new JSONObject();
                                    templateParam.put("orderNo", refundOrder.getCode());
                                    templateParam.put("money", String.valueOf(Constants.getFormatMoney(
                                            refundOrder.getRefundAmount() != null ? refundOrder.getRefundAmount() : 0L)));
                                    boolean smsResult = AliSmsService.sendSms(refundMember.getTelephone(),
                                            Constants.SmsNotify.MEMBER_REFUNDED.getTemplateCode(),
                                            templateParam.toJSONString());
                                    if (smsResult) {
                                        log.info("退款短信发送成功: phone={}", refundMember.getTelephone());
                                    } else {
                                        log.warn("退款短信发送失败: phone={}", refundMember.getTelephone());
                                    }
                                    // å­˜å‚¨çŸ­ä¿¡è®°å½•
                                    Smsrecord smsRecord = new Smsrecord();
                                    smsRecord.setPhone(refundMember.getTelephone());
                                    smsRecord.setContent(smsContent);
                                    smsRecord.setType(Constants.ONE);
                                    smsRecord.setStatus(smsResult ? Constants.ONE : Constants.ZERO);
                                    smsRecord.setCreateTime(new java.util.Date());
                                    smsRecord.setDeleted(Constants.ZERO);
                                    smsrecordMapper.insert(smsRecord);
                                } catch (Exception smsEx) {
                                    log.error("退款短信发送异常: {}", smsEx.getMessage());
                                    try {
                                        Smsrecord smsRecord = new Smsrecord();
                                        smsRecord.setPhone(refundMember.getTelephone());
                                        smsRecord.setContent(smsContent);
                                        smsRecord.setType(Constants.ONE);
                                        smsRecord.setStatus(Constants.ZERO);
                                        smsRecord.setCreateTime(new java.util.Date());
                                        smsRecord.setDeleted(Constants.ZERO);
                                        smsrecordMapper.insert(smsRecord);
                                    } catch (Exception ignored) {}
                                }
                            }
                        }
                    }
                }
            }
server/web/src/main/java/com/doumee/api/web/ShopInfoApi.java
@@ -13,6 +13,7 @@
import com.doumee.dao.vo.ShopDetailVO;
import com.doumee.dao.vo.ShopCenterVO;
import com.doumee.dao.vo.ShopNearbyVO;
import com.doumee.dao.vo.ShopSalesStatsVO;
import com.doumee.dao.vo.ShopWebDetailVO;
import com.doumee.dao.vo.PayResponse;
import com.doumee.service.business.OrdersService;
@@ -80,6 +81,17 @@
    }
    @LoginShopRequired
    @ApiOperation("门店销售统计")
    @GetMapping("/salesStats")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true),
            @ApiImplicitParam(paramType = "query", dataType = "Integer", name = "period", value = "统计周期:0=今日, 1=本月, 2=上月", required = true)
    })
    public ApiResponse<ShopSalesStatsVO> salesStats(@RequestParam Integer period) {
        return ApiResponse.success("查询成功", shopInfoService.getShopSalesStats(getShopId(), period));
    }
    @LoginShopRequired
    @ApiOperation("维护门店信息(支付押金后)")
    @PostMapping("/maintain")
    public ApiResponse maintain(@RequestBody ShopInfoMaintainDTO dto) {