rk
4 天以前 74b0af6814b96378201ea27d205e054bf01d0306
代码生成
已添加2个文件
已修改15个文件
646 ■■■■ 文件已修改
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java 167 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/constants/Constants.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/utils/ImageWatermarkUtil.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/PricingRule.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/ActiveOrderTipVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/AddrService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/NoticeService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/OrdersService.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/AddrServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/NoticeServiceImpl.java 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/PricingRuleServiceImpl.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/resources/application-dev.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/AddrApi.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ConfigApi.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java
@@ -11,7 +11,9 @@
import com.doumee.core.model.ApiResponse;
import com.doumee.core.utils.DateUtil;
import com.doumee.core.utils.FtpUtil;
import com.doumee.core.utils.ImageWatermarkUtil;
import com.doumee.core.utils.aliyun.ALiYunUtil;
import com.doumee.core.utils.geocode.MapUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -47,14 +49,18 @@
    @Autowired
    private SystemDictDataBiz systemDictDataBiz;
    @ApiOperation(value = "上传OBS", notes = "上传OBS", httpMethod = "POST", position = 6)
    @ApiOperation(value = "上传OSS", notes = "上传OSS,可传入经纬度自动添加时间+地址水印", httpMethod = "POST", position = 6)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "folder", value = "文件夹", required = true, paramType = "query", dataType = "String", dataTypeClass = String.class),
            @ApiImplicitParam(name = "file", value = "文件夹", required = true, paramType = "query", dataType = "String", dataTypeClass = String.class),
            @ApiImplicitParam(name = "file", value = "文件", required = true, paramType = "query", dataType = "String", dataTypeClass = String.class),
            @ApiImplicitParam(name = "latitude", value = "纬度(非必填,传入时添加水印)", paramType = "query", dataType = "Double"),
            @ApiImplicitParam(name = "longitude", value = "经度(非必填,传入时添加水印)", paramType = "query", dataType = "Double"),
    })
    @PostMapping(value = "/upload", headers = "content-type=multipart/form-data")
    @ResponseBody
    public void upload(HttpServletRequest request, HttpServletResponse response, String folder,String flag) throws Exception {
    public void upload(HttpServletRequest request, HttpServletResponse response, String folder, String flag,
                       @RequestParam(required = false) Double latitude,
                       @RequestParam(required = false) Double longitude) throws Exception {
        Date d1 = new Date();
        log.error("总得上传文件成功=============开始========="+DateUtil.getPlusTime2(d1));
        if(Objects.isNull(folder)){
@@ -75,6 +81,23 @@
                    System.out.println(file.getSize());
                    String originname = file.getOriginalFilename();
                    is = file.getInputStream();
                    // ç»çº¬åº¦æ°´å°å¤„理
                    InputStream uploadStream = is;
                    if (latitude != null && longitude != null) {
                        try {
                            JSONObject geoResult = MapUtil.reverseGeocode(latitude, longitude);
                            String address = geoResult != null ? geoResult.getString("formatted_addresses") : "";
                            String timeText = DateUtil.getPlusTime2(new Date());
                            uploadStream = ImageWatermarkUtil.addWatermark(is, timeText, address, latitude, longitude);
                            log.info("图片水印添加成功: time={}, address={}", timeText, address);
                        } catch (Exception e) {
                            log.warn("水印处理失败,使用原图上传: {}", e.getMessage());
                            try { is.reset(); } catch (Exception ignored) {}
                            uploadStream = is;
                        }
                    }
                    String endType = ".jpg";
                    if(originname.indexOf(".")>0){
                        endType=originname.substring(originname.lastIndexOf("."),originname.length());
@@ -87,14 +110,12 @@
                    ALiYunUtil obs = new ALiYunUtil(systemDictDataBiz.queryByCode(Constants.OSS,Constants.ENDPOINT).getCode(),systemDictDataBiz.queryByCode(Constants.OSS,Constants.ACCESS_ID).getCode(),
                            systemDictDataBiz.queryByCode(Constants.OSS,Constants.ACCESS_KEY).getCode());
                    r = obs.uploadOnlineObject(file.getInputStream(),systemDictDataBiz.queryByCode(Constants.OSS,Constants.BUCKETNAME).getCode(), fileName,null);
                    r = obs.uploadOnlineObject(uploadStream,systemDictDataBiz.queryByCode(Constants.OSS,Constants.BUCKETNAME).getCode(), fileName,null);
                    if(r){
                        context.put("success", true);
                        context.put("code", 200);
                        context.put("errno",0);
                        JSONObject fileJSON = new JSONObject();
//                        fileJSON.put("prefixPath", prefixPath);
//                        fileJSON.put("folder", folder);
                        fileJSON.put("url", prefixPath+fileName);
                        fileJSON.put("imgaddr", fName);
                        fileJSON.put("imgname", fileName);
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java
@@ -7,15 +7,57 @@
import com.alipay.v3.Configuration;
import com.alipay.v3.api.AlipayFundTransUniApi;
import com.alipay.v3.model.*;
import com.doumee.config.mybatis.SpringUtils;
import com.doumee.core.constants.ResponseStatus;
import com.doumee.core.exception.BusinessException;
import com.doumee.dao.dto.AlipayTransferDTO;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import com.wechat.pay.java.core.http.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@org.springframework.context.annotation.Configuration
public class AlipayFundTransUniTransfer {
    @Autowired
    private AlipayProperties alipayProperties;
    private static AlipayConfig alipayConfig;
    public static AlipayFundTransUniTransfer me() {
        return SpringUtils.get().getBean(AlipayFundTransUniTransfer.class);
    }
    @PostConstruct
    void init() {
        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");
            alipayConfig = config;
            log.info("支付宝配置初始化成功, appId={}", config.getAppId());
        } catch (Exception e) {
            log.error("支付宝配置初始化失败: {}", e.getMessage(), e);
        }
    }
    public static void main(String[] args) throws ApiException  {
        downLoadCertificates();
@@ -60,45 +102,120 @@
        }
    }
    public static void testTransger( ) throws ApiException  {
//    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);
//        }
//    }
        // åˆå§‹åŒ–SDK
    /**
     * å•笔转账到支付宝账户
     *
     * @param dto è½¬è´¦å‚æ•°
     * @return æ”¯ä»˜å®è½¬è´¦å•号
     */
    public static String transfer(AlipayTransferDTO dto) throws ApiException {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        // åˆå§‹åŒ–alipay参数(全局设置一次)
        defaultClient.setAlipayConfig(getAlipayConfig());
        // æž„造请求参数以调用接口
        defaultClient.setAlipayConfig(alipayConfig);
        AlipayFundTransUniApi api = new AlipayFundTransUniApi();
        AlipayFundTransUniTransferModel data = new AlipayFundTransUniTransferModel();
        // è®¾ç½®å•†å®¶ä¾§å”¯ä¸€è®¢å•号
        data.setOutBizNo("202606300001");
        data.setOutBizNo(dto.getOutBizNo());
        // è®¾ç½®è®¢å•总金额
        data.setTransAmount("1");
        // è®¾ç½®æè¿°ç‰¹å®šçš„业务场景
        data.setOutBizNo(dto.getOutBizNo());
        data.setTransAmount(dto.getTransAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
        data.setBizScene("DIRECT_TRANSFER");
        // è®¾ç½®ä¸šåŠ¡äº§å“ç 
        data.setProductCode("TRANS_ACCOUNT_NO_PWD");
        // è®¾ç½®è½¬è´¦ä¸šåŠ¡çš„æ ‡é¢˜
        data.setOrderTitle("201905代发");
        data.setOrderTitle("佣金报酬");
        // è®¾ç½®åŽŸæ”¯ä»˜å®ä¸šåŠ¡å•å·
        data.setOriginalOrderId("20190620110075000006640000063056");
        // è®¾ç½®æ”¶æ¬¾æ–¹ä¿¡æ¯
        data.setOrderTitle("佣金报酬");
        Participant payeeInfo = new Participant();
        payeeInfo.setIdentity("15345690849");
        payeeInfo.setName("江萍");
        payeeInfo.setIdentity(dto.getPayeeAccount());
        payeeInfo.setName(dto.getPayeeName());
        payeeInfo.setIdentity(dto.getPayeeAccount());
        payeeInfo.setName(dto.getPayeeName());
        payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
        data.setPayeeInfo(payeeInfo);
        // è®¾ç½®ä¸šåŠ¡å¤‡æ³¨
        data.setRemark("201905代发");
        data.setRemark("佣金报酬");
        // è®¾ç½®è½¬è´¦åœºæ™¯åç§°
        data.setRemark(dto.getRemark());
        data.setTransferSceneName("佣金报酬");
        // è®¾ç½®è½¬è´¦åœºæ™¯ä¸ŠæŠ¥ä¿¡æ¯
        List<TransferSceneReportInfo> transferSceneReportInfos = new ArrayList<TransferSceneReportInfo>();
        TransferSceneReportInfo transferSceneReportInfos0 = new TransferSceneReportInfo();
        transferSceneReportInfos0.setInfoType("佣金报酬说明");
        transferSceneReportInfos0.setInfoContent("3月家政服务报酬");
        transferSceneReportInfos0.setInfoContent("佣金报酬");
        transferSceneReportInfos.add(transferSceneReportInfos0);
        data.setTransferSceneReportInfos(transferSceneReportInfos);
        List<TransferSceneReportInfo> reportInfos = new ArrayList<>();
        TransferSceneReportInfo reportInfo = new TransferSceneReportInfo();
        reportInfo.setInfoType("佣金报酬说明");
        reportInfo.setInfoContent("佣金报酬");
        reportInfos.add(reportInfo);
        data.setTransferSceneReportInfos(reportInfos);
        // è®¾ç½®è½¬è´¦ä¸šåŠ¡è¯·æ±‚çš„æ‰©å±•å‚æ•°
        data.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}");
@@ -115,22 +232,28 @@
*/
        try {
            AlipayFundTransUniTransferResponseModel response = api.transfer(data);
            log.info("支付宝转账成功, outBizNo={}, orderId={}", dto.getOutBizNo(), response.getOrderId());
            return response.getOrderId();
        } catch (ApiException e) {
            AlipayFundTransUniTransferDefaultResponse errorObject = (AlipayFundTransUniTransferDefaultResponse) e.getErrorObject();
            System.out.println("调用失败:" + errorObject);
            log.error("支付宝转账失败, outBizNo={}, error={}", dto.getOutBizNo(), errorObject);
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),
                    "转账失败:" + (errorObject != null ? errorObject.toString() : e.getMessage()));
        }
    }
    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;
    }
//    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/core/constants/Constants.java
@@ -390,7 +390,9 @@
        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.closed.status, OrderStatus.cancelling.status}),
        home(6, "首页查询", new int[]{OrderStatus.waitPay.status, OrderStatus.waitDeposit.status, OrderStatus.deposited.status
                , OrderStatus.accepted.status, OrderStatus.delivering.status, OrderStatus.arrived.status})
        ;
        private final int key;
        private final String desc;
@@ -407,6 +409,37 @@
    }
    /**
     * é¦–页订单状态提示枚举
     * status: è®¢å•状态值
     * desc: çŠ¶æ€æè¿°
     * tipTemplate: æç¤ºæ–‡æ¡ˆæ¨¡æ¿ï¼Œå ä½ç¬¦ç”¨ {xxx} è¡¨ç¤º
     */
    @Getter
    @AllArgsConstructor
    public enum OrderStatusTip {
        waitPay(0, "待支付", "请在{timeout}分钟内完成支付,超时订单将自动取消"),
        waitDeposit(1, "待寄存", "订单已支付,请前往门店寄存"),
        deposited(2, "已寄存", null), // å°±åœ°/异地文案不同,动态处理
        accepted(3, "已接单", "已有司机抢单,正前往取件地点"),
        delivering(4, "派送中", "司机已取件,正运往目的地"),
        arrived(5, "待取件", null), // å°±åœ°/异地/有无取件门店文案不同,动态处理
        ;
        private final int status;
        private final String desc;
        private final String tipTemplate;
        public static OrderStatusTip getByStatus(int status) {
            for (OrderStatusTip t : OrderStatusTip.values()) {
                if (t.status == status) {
                    return t;
                }
            }
            return null;
        }
    }
    /**
     * è®¢å•站内信通知枚举
     * title: é€šçŸ¥æ ‡é¢˜
     * content: é€šçŸ¥æ–‡æ¡ˆæ¨¡æ¿ï¼Œå ä½ç¬¦ç”¨ {xxx} è¡¨ç¤º
server/services/src/main/java/com/doumee/core/utils/ImageWatermarkUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,117 @@
package com.doumee.core.utils;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * å›¾ç‰‡æ°´å°å·¥å…·ç±»
 * åœ¨å›¾ç‰‡å·¦ä¸‹è§’叠加时间 + åœ°å€æ°´å°
 *
 * @author rk
 * @date 2026/04/21
 */
@Slf4j
public class ImageWatermarkUtil {
    /**
     * ä¸ºå›¾ç‰‡æ·»åŠ æ°´å°ï¼ˆæ—¶é—´ + åœ°å€ + ç»çº¬åº¦ï¼‰
     *
     * @param imageInput  åŽŸå›¾è¾“å…¥æµ
     * @param timeText    æ—¶é—´æ–‡æ¡ˆï¼ˆå¦‚ "2026-04-21 14:30:00")
     * @param addressText åœ°å€æ–‡æ¡ˆï¼ˆå¦‚ "北京市海淀区xxxè·¯")
     * @param latitude    çº¬åº¦
     * @param longitude   ç»åº¦
     * @return æ·»åŠ æ°´å°åŽçš„è¾“å…¥æµï¼Œæ°´å°å¤±è´¥æ—¶è¿”å›žåŽŸå›¾
     */
    public static InputStream addWatermark(InputStream imageInput, String timeText, String addressText,
                                           Double latitude, Double longitude) {
        try {
            BufferedImage srcImage = ImageIO.read(imageInput);
            if (srcImage == null) {
                log.warn("水印: æ— æ³•读取图片,跳过水印处理");
                imageInput.reset();
                return imageInput;
            }
            int srcWidth = srcImage.getWidth();
            int srcHeight = srcImage.getHeight();
            // å­—体大小按图片宽度自适应(约3%),最小16px
            int fontSize = Math.max(16, srcWidth * 3 / 100);
            int padding = fontSize;
            int lineHeight = fontSize + fontSize / 3;
            // ç»çº¬åº¦è¡Œ
            String locationText = "";
            if (latitude != null && longitude != null) {
                locationText = String.format("经度:%.6f çº¬åº¦:%.6f", longitude, latitude);
            }
            int lineCount = 2 + (locationText.isEmpty() ? 0 : 1);
            // è®¡ç®—水印区域背景高度
            int bgHeight = lineHeight * lineCount + padding;
            // åˆ›å»ºå›¾ç‰‡
            BufferedImage destImage = new BufferedImage(srcWidth, srcHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = destImage.createGraphics();
            // ç»˜åˆ¶åŽŸå›¾
            g2d.drawImage(srcImage, 0, 0, null);
            // ç»˜åˆ¶åŠé€æ˜ŽèƒŒæ™¯ï¼ˆå·¦ä¸‹è§’)
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
            g2d.setColor(Color.BLACK);
            g2d.fillRect(0, srcHeight - bgHeight, srcWidth, bgHeight);
            // ç»˜åˆ¶æ°´å°æ–‡å­—
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.9f));
            g2d.setColor(Color.WHITE);
            Font font = new Font(Font.SANS_SERIF, Font.PLAIN, fontSize);
            g2d.setFont(font);
            // å¼€å¯æŠ—锯齿
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            int textX = padding;
            int textY = srcHeight - bgHeight + padding + fontSize;
            FontMetrics fm = g2d.getFontMetrics();
            int maxTextWidth = srcWidth - padding * 2;
            // ç¬¬ä¸€è¡Œï¼šæ—¶é—´
            g2d.drawString(timeText, textX, textY);
            // ç¬¬äºŒè¡Œï¼šåœ°å€ï¼ˆåœ°å€è¿‡é•¿æ—¶æˆªæ–­ï¼‰
            String displayAddr = addressText;
            while (fm.stringWidth(displayAddr) > maxTextWidth && displayAddr.length() > 0) {
                displayAddr = displayAddr.substring(0, displayAddr.length() - 1);
            }
            g2d.drawString(displayAddr, textX, textY + lineHeight);
            // ç¬¬ä¸‰è¡Œï¼šç»çº¬åº¦
            if (!locationText.isEmpty()) {
                g2d.drawString(locationText, textX, textY + lineHeight * 2);
            }
            g2d.dispose();
            // è¾“出为字节流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(destImage, "jpg", baos);
            return new ByteArrayInputStream(baos.toByteArray());
        } catch (Exception e) {
            log.error("添加水印失败,返回原图", e);
            try {
                imageInput.reset();
            } catch (Exception ignored) {}
            return imageInput;
        }
    }
}
server/services/src/main/java/com/doumee/dao/business/model/PricingRule.java
@@ -58,7 +58,7 @@
    @ApiModelProperty(value = "参数2:type ï¼ˆ0)=  æ”¶è´¹å•ä»· ;type ï¼ˆ1)= é…é€èµ·æ­¥é‡Œç¨‹å…¬é‡Œæ•° ï¼›type ï¼ˆ2)= èµ·é€é‡Œç¨‹ km ï¼› type (3)  = æŠ¼é‡‘ï¼›type (4) = åœ°ç‚¹ç±»åž‹ï¼ˆå¯„件点/取件点/分成比例)")
    private String fieldB;
    @ApiModelProperty(value = "参数3:type ï¼ˆ1)= é…é€èµ·æ­¥é‡Œç¨‹æ¯å…¬é‡Œå•ä»· ï¼›type (2)  = èµ·é€æ—¶é•¿ï¼› type (3)  = æŠ¼é‡‘ï¼›type (4) = åˆ†æˆæ¯”例")
    @ApiModelProperty(value = "参数3:type ï¼ˆ1)= é…é€èµ·æ­¥é‡Œç¨‹æ¯å…¬é‡Œå•ä»· ï¼›type (2)  = èµ·é€æ—¶é•¿ï¼›")
    private String fieldC;
    @ApiModelProperty(value = "参数4:type=1  è¶…出首单里程公里数 ï¼› type =2  ç»­é€é‡Œç¨‹ km;")
server/services/src/main/java/com/doumee/dao/vo/ActiveOrderTipVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
 * é¦–页进行中最新订单提示VO
 * @author rk
 * @date 2026/04/21
 */
@Data
@ApiModel("首页进行中最新订单提示")
public class ActiveOrderTipVO implements Serializable {
    @ApiModelProperty(value = "订单主键")
    private Integer orderId;
    @ApiModelProperty(value = "订单状态")
    private Integer status;
    @ApiModelProperty(value = "状态提示文案")
    private String tip;
    @ApiModelProperty(value = "支付倒计时毫秒(仅status=0时返回,-1表示已超时)")
    private Long payCountdown;
}
server/services/src/main/java/com/doumee/service/business/AddrService.java
@@ -39,8 +39,10 @@
    /**
     * æŸ¥è¯¢ä¼šå‘˜åœ°å€åˆ—表(含省市区信息)
     * @param memberId ä¼šå‘˜ä¸»é”®
     * @param keyword æ¨¡ç³Šæœç´¢å…³é”®è¯ï¼ˆåŒ¹é…åœ°å€/名称/手机号),可为null
     */
    List<Addr> findListWithArea(Integer memberId);
    List<Addr> findListWithArea(Integer memberId, String keyword);
    /**
     * æ ¹æ®ID查询地址(含省市区信息)
server/services/src/main/java/com/doumee/service/business/NoticeService.java
@@ -34,4 +34,19 @@
    PageData<Notice> findPage(PageWrap<Notice> pageWrap);
    long count(Notice notice);
    /**
     * æ ‡è®°é€šçŸ¥ä¸ºå·²è¯»
     * @param id é€šçŸ¥ä¸»é”®
     * @param userId ç”¨æˆ·ä¸»é”®ï¼ˆæ ¡éªŒå½’属)
     */
    void readNotice(Integer id, Integer userId);
    /**
     * æ ‡è®°è¯¥ç”¨æˆ·æ‰€æœ‰é€šçŸ¥ä¸ºå·²è¯»
     * @param userType ç”¨æˆ·ç±»åž‹
     * @param userId ç”¨æˆ·ä¸»é”®
     */
    void readAllNotice(Integer userType, Integer userId);
}
server/services/src/main/java/com/doumee/service/business/OrdersService.java
@@ -9,15 +9,7 @@
import com.doumee.dao.dto.DispatchDTO;
import com.doumee.dao.dto.CommentOrderDTO;
import com.doumee.dao.dto.MyOrderDTO;
import com.doumee.dao.vo.MyOrderDetailVO;
import com.doumee.dao.vo.MyOrderVO;
import com.doumee.dao.vo.OrderDetailVO;
import com.doumee.dao.vo.OrderDispatchVO;
import com.doumee.dao.vo.OrderSummaryVO;
import com.doumee.dao.vo.OverdueFeeVO;
import com.doumee.dao.vo.PayResponse;
import com.doumee.dao.vo.PriceCalculateVO;
import com.doumee.dao.vo.EstimatedDeliveryResultVO;
import com.doumee.dao.vo.*;
import java.math.BigDecimal;
import java.util.List;
@@ -380,4 +372,13 @@
                                                         Double fromLat, Double fromLng,
                                                         Double toLat, Double toLng);
    /**
     * æŸ¥è¯¢ä¼šå‘˜é¦–页进行中最新订单提示
     * æŸ¥è¯¢status in (0,1,2,3,4,5)的订单,按创建时间倒序取第一条
     *
     * @param memberId ä¼šå‘˜ä¸»é”®
     * @return æœ€æ–°è¿›è¡Œä¸­è®¢å•提示,无订单返回null
     */
    ActiveOrderTipVO getActiveOrderTip(Integer memberId);
}
server/services/src/main/java/com/doumee/service/business/impl/AddrServiceImpl.java
@@ -171,7 +171,7 @@
    }
    @Override
    public List<Addr> findListWithArea(Integer memberId) {
    public List<Addr> findListWithArea(Integer memberId, String keyword) {
        MPJLambdaWrapper<Addr> wrapper = new MPJLambdaWrapper<Addr>()
                .selectAll(Addr.class)
                .select("a3.name", Addr::getDistrictName)
@@ -186,8 +186,14 @@
                .leftJoin("areas a2 on a2.id = a3.PARENT_ID")
                .leftJoin("areas a1 on a1.id = a2.PARENT_ID")
                .eq(Addr::getDeleted, Constants.ZERO)
                .eq(Addr::getMemberId, memberId)
                .orderByDesc(Addr::getIsDefault)
                .eq(Addr::getMemberId, memberId);
        if (StringUtils.isNotBlank(keyword)) {
            wrapper.and(w -> w
                    .like(Addr::getAddr, keyword)
                    .or().like(Addr::getName, keyword)
                    .or().like(Addr::getPhone, keyword));
        }
        wrapper.orderByDesc(Addr::getIsDefault)
                .orderByDesc(Addr::getCreateTime);
        return addrMapper.selectJoinList(Addr.class, wrapper);
    }
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java
@@ -332,7 +332,7 @@
                member.setReceiveNum(Constants.ZERO.longValue());
                member.setPublishNum(Constants.ZERO.longValue());
                member.setAutoReceiveStatus(Constants.ONE);
                member.setUseIdentity(Constants.ZERO);
                member.setUserType(Constants.ZERO);
                memberMapper.insert(member);
            }
            String token = JwtTokenUtil.generateTokenForRedis(member.getId(), Constants.ZERO, JSONObject.toJSONString(member), redisTemplate);
server/services/src/main/java/com/doumee/service/business/impl/NoticeServiceImpl.java
@@ -1,8 +1,10 @@
package com.doumee.service.business.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.doumee.core.constants.Constants;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.dao.business.NoticeMapper;
@@ -12,6 +14,7 @@
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
 * æ¶ˆæ¯é€šçŸ¥ä¿¡æ¯Service实现
@@ -86,35 +89,12 @@
        IPage<Notice> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        QueryWrapper<Notice> queryWrapper = new QueryWrapper<>();
        if (pageWrap.getModel() != null) {
            if (pageWrap.getModel().getIsdeleted() != null) {
                queryWrapper.lambda().eq(Notice::getIsdeleted, pageWrap.getModel().getIsdeleted());
            }
            if (pageWrap.getModel().getUserType() != null) {
                queryWrapper.lambda().eq(Notice::getUserType, pageWrap.getModel().getUserType());
            }
            if (pageWrap.getModel().getUserId() != null) {
                queryWrapper.lambda().eq(Notice::getUserId, pageWrap.getModel().getUserId());
            }
            if (pageWrap.getModel().getObjType() != null) {
                queryWrapper.lambda().eq(Notice::getObjType, pageWrap.getModel().getObjType());
            }
            if (pageWrap.getModel().getStatus() != null) {
                queryWrapper.lambda().eq(Notice::getStatus, pageWrap.getModel().getStatus());
            }
            if (pageWrap.getModel().getTitle() != null) {
                queryWrapper.lambda().like(Notice::getTitle, pageWrap.getModel().getTitle());
            }
            if (pageWrap.getModel().getCreateDate() != null) {
                queryWrapper.lambda().ge(Notice::getCreateDate, pageWrap.getModel().getCreateDate());
            }
            Notice model = pageWrap.getModel();
            queryWrapper.lambda().eq(Notice::getIsdeleted,Constants.ZERO);
            queryWrapper.lambda().eq(Objects.nonNull(model.getUserId()),Notice::getUserId,model.getUserId());
            queryWrapper.lambda().eq(Objects.nonNull(model.getUserType()),Notice::getUserType,model.getUserType());
        }
        for (PageWrap.SortData sortData : pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
                queryWrapper.orderByDesc(sortData.getProperty());
            } else {
                queryWrapper.orderByAsc(sortData.getProperty());
            }
        }
        queryWrapper.lambda().orderByAsc(Notice::getStatus).orderByDesc(Notice::getCreateDate);
        return PageData.from(noticeMapper.selectPage(page, queryWrapper));
    }
@@ -123,4 +103,26 @@
        QueryWrapper<Notice> wrapper = new QueryWrapper<>(notice);
        return noticeMapper.selectCount(wrapper);
    }
    @Override
    public void readNotice(Integer id, Integer userId) {
        Notice notice = noticeMapper.selectById(id);
        if (notice != null && notice.getUserId().equals(userId)) {
            notice.setStatus(Constants.ONE);
            notice.setEditDate(new java.util.Date());
            noticeMapper.updateById(notice);
        }
    }
    @Override
    public void readAllNotice(Integer userType, Integer userId) {
        noticeMapper.update(new UpdateWrapper<Notice>().lambda()
                .set(Notice::getStatus, Constants.ONE)
                .set(Notice::getEditDate, new java.util.Date())
                .eq(Notice::getUserType, userType)
                .eq(Notice::getUserId, userId)
                .eq(Notice::getStatus, Constants.ZERO)
                .eq(Notice::getIsdeleted, Constants.ZERO));
    }
}
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -420,8 +420,11 @@
        String to = dto.getToLat() + "," + dto.getToLgt();
        JSONObject distanceResult = MapUtil.direction("driving", from, to);
        BigDecimal distance = distanceResult.getBigDecimal("distance");
        // distance å•位为米,转为公里
        // distance å•位为米,转为公里(不足1公里按1公里计算)
        BigDecimal distanceKm = distance.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
        if (distanceKm.compareTo(BigDecimal.ONE) < 0) {
            distanceKm = BigDecimal.ONE;
        }
        // æ”¶é›†æ‰€æœ‰ç‰©å“ç±»åž‹ID
        List<Integer> categoryIds = new ArrayList<>();
@@ -645,8 +648,11 @@
        // ========== 3. æŸ¥è¯¢å¯„件店铺信息 ==========
        ShopInfo depositShop = shopInfoMapper.selectById(dto.getDepositShopId());
        if (depositShop == null) {
        if (depositShop == null || Constants.equalsInteger(depositShop.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寄件店铺不存在");
        }
        if (depositShop.getStatus() == null || !Constants.equalsInteger(depositShop.getStatus(), Constants.ZERO)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寄件店铺已停业,请选择其他门店");
        }
        // ========== 4. è®¡ç®—费用 ==========
@@ -663,9 +669,15 @@
            }
            // å–件点:店铺 or è‡ªé€‰ç‚¹ï¼Œè‡³å°‘提供一组
            if (dto.getTakeShopId() != null) {
                if (dto.getTakeShopId().equals(dto.getDepositShopId())) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异地寄存订单存件门店和取件门店不能相同");
                }
                takeShop = shopInfoMapper.selectById(dto.getTakeShopId());
                if (takeShop == null) {
                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)) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "取件店铺已停业,请选择其他门店");
                }
                takeLat = BigDecimal.valueOf(takeShop.getLatitude());
                takeLgt = BigDecimal.valueOf(takeShop.getLongitude());
@@ -764,6 +776,7 @@
            orders.setTakeLat(takeLat);
            orders.setTakeLgt(takeLgt);
            orders.setIsUrgent(dto.getIsUrgent());
            orders.setDistance(priceResult.getDistance());
        } else {
            // å°±åœ°ï¼šå–件点同寄件店铺
            orders.setTakeShopId(dto.getDepositShopId());
@@ -775,7 +788,12 @@
        // ç‰©å“ä¿¡æ¯
        orders.setGoodType(dto.getGoodType());
        orders.setGoodLevel(goodTypeCategory.getRelationId());
        // æŸ¥è¯¢ç‰©å“çº§åˆ« type = 3
        Category levelCategory = categoryMapper.selectById(goodTypeCategory.getRelationId());
        if(Objects.isNull(levelCategory)){
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(),"未查询到物品等级信息:{}"+goodTypeCategory.getName());
        }
        orders.setGoodLevel(Integer.valueOf(levelCategory.getDetail()));
        // æ‹¼æŽ¥ç‰©å“ä¿¡æ¯ï¼šç‰©å“ç±»åž‹åç§°ã€å°ºå¯¸åç§°*数量(数组字符串)
        List<String> goodsParts = new ArrayList<>();
        for (ItemPriceVO itemVO : priceResult.getItemList()) {
@@ -797,7 +815,6 @@
        }
        orders.setDeclaredFee(priceResult.getInsuranceFee());
        orders.setPrice(priceResult.getItemPrice());
        // è–ªé…¬è®¡ç®—与占比存储
        calculateAndSetFeeAllocation(orders, depositShop, takeShop);
@@ -1307,8 +1324,9 @@
                .eq(PricingRule::getCityId, cityId)
                .eq(PricingRule::getFieldA, String.valueOf(fieldA))
                .last("limit 1"));
        if (rule != null && StringUtils.isNotBlank(rule.getFieldC())) {
            return new BigDecimal(rule.getFieldC());
        if (rule != null && StringUtils.isNotBlank(rule.getFieldB())) {
            // fieldB å­˜å‚¨çš„æ˜¯ç™¾åˆ†æ¯”整数(如15表示15%),转换为小数比例(0.15)
            return new BigDecimal(rule.getFieldB()).divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP);
        }
        return BigDecimal.ZERO;
    }
@@ -1836,6 +1854,7 @@
        order.setPayStatus(Constants.ONE); // å·²æ”¯ä»˜
        order.setPayTime(now);
        order.setWxExternalNo(wxTradeNo);
        order.setPayAmount(order.getTotalAmount());
        order.setUpdateTime(now);
        // ç”Ÿæˆä¼šå‘˜æ ¸é”€ç 
        order.setMemberVerifyCode(generateVerifyCode());
@@ -3122,6 +3141,61 @@
    }
    @Override
    public ActiveOrderTipVO getActiveOrderTip(Integer memberId) {
        // æŸ¥è¯¢çŠ¶æ€ä¸º 0~5 çš„æœ€æ–°ä¸€æ¡è®¢å•
        QueryWrapper<Orders> wrapper = new QueryWrapper<>();
        wrapper.eq("MEMBER_ID", memberId)
                .in("STATUS", 0, 1, 2, 3, 4, 5)
                .orderByDesc("CREATE_TIME")
                .last("LIMIT 1");
        Orders order = ordersMapper.selectOne(wrapper);
        if (order == null) {
            return null;
        }
        ActiveOrderTipVO vo = new ActiveOrderTipVO();
        vo.setOrderId(order.getId());
        vo.setStatus(order.getStatus());
        // æž„建提示文案
        boolean isLocal = Constants.equalsInteger(order.getType(), Constants.ZERO);
        Integer status = order.getStatus();
        String tip = null;
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
            // å¾…支付:提示支付倒计时
            String minutes = "";
            try {
                minutes = operationConfigBiz.getConfig().getAutoCancelTime();
            } catch (Exception ignored) {}
            tip = "请在" + (StringUtils.isNotBlank(minutes) ? minutes : "") + "分钟内完成支付,超时订单将自动取消";
            // è®¡ç®—支付倒计时
            if (StringUtils.isNotBlank(minutes) && order.getCreateTime() != null) {
                long timeoutMs = Long.parseLong(minutes) * 60 * 1000;
                long deadline = order.getCreateTime().getTime() + timeoutMs;
                long remain = deadline - System.currentTimeMillis();
                vo.setPayCountdown(remain > 0 ? remain : -1L);
            } else {
                vo.setPayCountdown(-1L);
            }
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
            tip = "订单已支付,请前往门店寄存";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())) {
            tip = isLocal ? "行李已寄存,请凭取件码前往指定门店取件" : "门店已接单,正在为您安排取件司机";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
            tip = isLocal ? "行李已寄存,请凭取件码前往指定门店取件" : "已有司机抢单,正前往取件地点";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.delivering.getStatus())) {
            tip = "司机已取件,正运往目的地";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
            tip = "行李已送达服务点,请及时前往取件";
        }
        vo.setTip(tip);
        return vo;
    }
    @Override
    public EstimatedDeliveryResultVO calculateEstimatedDelivery(Integer cityId,
                                                                Double fromLat, Double fromLng,
                                                                Double toLat, Double toLng) {
server/services/src/main/java/com/doumee/service/business/impl/PricingRuleServiceImpl.java
@@ -508,6 +508,11 @@
        Date now = new Date();
        for (StoreDepositItemDTO item : request.getItems()) {
            // å…ƒè½¬åˆ†
            String depositFen = String.valueOf(new BigDecimal(item.getDepositAmount())
                    .multiply(new BigDecimal("100"))
                    .setScale(0, BigDecimal.ROUND_HALF_UP)
                    .longValue());
            QueryWrapper<PricingRule> qw = new QueryWrapper<>();
            qw.lambda()
                    .eq(PricingRule::getType, Constants.THREE)
@@ -518,14 +523,14 @@
            PricingRule existing = pricingRuleMapper.selectOne(qw);
            if (existing != null) {
                existing.setFieldB(item.getDepositAmount());
                existing.setFieldB(depositFen);
                existing.setUpdateTime(now);
                pricingRuleMapper.updateById(existing);
            } else {
                PricingRule rule = new PricingRule();
                rule.setType(Constants.THREE);
                rule.setFieldA(String.valueOf(item.getFieldType()));
                rule.setFieldB(item.getDepositAmount());
                rule.setFieldB(depositFen);
                rule.setCityId(request.getCityId());
                rule.setDeleted(Constants.ZERO);
                rule.setCreateTime(now);
server/services/src/main/resources/application-dev.yml
@@ -127,7 +127,7 @@
tencent_key: WE3BZ-HN6WS-ONDOH-62QCV-MNL6F-5NFNE
# é«˜å¾·åœ°å›¾apikey
geocode_map_key: 9a62636d82d6c7c2372e57d80f99287c
geocode_map_key: 9a6c1f0eff2e5aa91989ca9d4c21e262
server/web/src/main/java/com/doumee/api/web/AddrApi.java
@@ -27,13 +27,14 @@
    private AddrService addrService;
    @LoginRequired
    @ApiOperation(value = "查询我的地址列表", notes = "小程序端")
    @ApiOperation(value = "查询我的地址列表", notes = "小程序端,支持按地址/名称/手机号模糊搜索")
    @GetMapping("/list")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
            @ApiImplicitParam(paramType = "query", dataType = "String", name = "keyword", value = "搜索关键词(地址/名称/手机号)"),
    })
    public ApiResponse<List<Addr>> list() {
        return ApiResponse.success("查询成功", addrService.findListWithArea(getMemberId()));
    public ApiResponse<List<Addr>> list(@RequestParam(required = false) String keyword) {
        return ApiResponse.success("查询成功", addrService.findListWithArea(getMemberId(), keyword));
    }
    @LoginRequired
server/web/src/main/java/com/doumee/api/web/ConfigApi.java
@@ -2,16 +2,21 @@
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.constants.Constants;
import com.doumee.core.model.ApiResponse;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.geocode.MapUtil;
import com.doumee.dao.business.model.Areas;
import com.doumee.dao.business.model.Banner;
import com.doumee.dao.business.model.Category;
import com.doumee.dao.business.model.Notice;
import com.doumee.dao.dto.AreasDto;
import com.doumee.dao.dto.CalculateLocalPriceDTO;
import com.doumee.dao.dto.CalculateRemotePriceDTO;
import com.doumee.dao.dto.SameCityCheckDTO;
import com.doumee.dao.vo.PriceCalculateVO;
import com.doumee.dao.vo.ActiveOrderTipVO;
import com.doumee.dao.vo.PlatformAboutVO;
import com.doumee.service.business.*;
import io.swagger.annotations.Api;
@@ -54,6 +59,9 @@
    @Autowired
    private MemberService memberService;
    @Autowired
    private NoticeService noticeService;
    @ApiOperation("全部区划树形查询")
    @PostMapping("/treeList")
@@ -144,5 +152,43 @@
        return ApiResponse.success("查询成功", areasService.getOpenedCityByName(cityName));
    }
    @LoginRequired
    @ApiOperation(value = "会员通知消息分页", notes = "未读优先,时间倒序")
    @PostMapping("/memberNoticePage")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<PageData<Notice>> memberNoticePage(@RequestBody PageWrap<Notice> pageWrap) {
        if (pageWrap.getModel() == null) {
            pageWrap.setModel(new Notice());
        }
        pageWrap.getModel().setUserId(this.getMemberId());
        pageWrap.getModel().setUserType(Constants.ZERO);
        return ApiResponse.success("查询成功", noticeService.findPage(pageWrap));
    }
    @LoginRequired
    @ApiOperation(value = "首页进行中订单提示", notes = "返回最新一条进行中订单的状态和提示文案,无订单返回null")
    @GetMapping("/getActiveOrderTip")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<ActiveOrderTipVO> getActiveOrderTip() {
        return ApiResponse.success("查询成功", ordersService.getActiveOrderTip(this.getMemberId()));
    }
    @LoginRequired
    @ApiOperation(value = "会员标记全部已读", notes = "标记当前用户所有未读通知为已读")
    @PostMapping("/memberReadAllNotice")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse readAllNotice() {
        noticeService.readAllNotice(0, this.getMemberId());
        return ApiResponse.success("操作成功");
    }
}