7ee466ebc953bb5640bcf42f2b8e2a87aa471c21..55642c818f14bf8cf52c98e6858014bd8dc3d3a7
3 小时以前 rk
Merge remote-tracking branch 'origin/master'
55642c 对比 | 目录
3 小时以前 rk
代码生成
7eebfc 对比 | 目录
3 小时以前 rk
~~~
b5f866 对比 | 目录
已添加13个文件
已删除6个文件
已修改33个文件
3565 ■■■■■ 文件已修改
server/admin/src/main/java/com/doumee/api/business/IdentityInfoController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/api/business/OrdersController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/api/business/OtherOrdersController.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/db/db_change.sql 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/SendWxMessage.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/constants/Constants.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/utils/huaweiOBS/ObsUtil.java 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/IdentityInfoMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/MemberMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/OrderCommentMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/OtherOrdersMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/DriverInfo.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/IdentityInfo.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/OrderComment.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Orders.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/OrdersRefund.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/OtherOrders.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/ShopInfo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/CommentOrderDTO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/ShopApplyDTO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/StoreOutDTO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/WithdrawalDTO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/PlatformAboutVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/RevenueStatisticsVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/ShopCenterVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/UserCenterVO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/IdentityInfoService.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/MemberService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/OrdersService.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/OtherOrdersService.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/RevenueService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/WithdrawalOrdersService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/BannerServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/IdentityInfoServiceImpl.java 470 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 827 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OtherOrdersServiceImpl.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/RevenueServiceImpl.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/AccountApi.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ConfigApi.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/MemberApi.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/OrdersApi.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/PaymentCallback.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ShopInfoApi.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/WalletApi.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/api/business/IdentityInfoController.java
ÎļþÒÑɾ³ý
server/admin/src/main/java/com/doumee/api/business/OrdersController.java
@@ -122,4 +122,12 @@
        return ApiResponse.success(null);
    }
    @ApiOperation("手动触发订单结算")
    @PostMapping("/settle")
    @RequiresPermissions("business:orders:update")
    public ApiResponse settleOrders() {
        ordersService.settleOrders();
        return ApiResponse.success("结算完成");
    }
}
server/admin/src/main/java/com/doumee/api/business/OtherOrdersController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
package com.doumee.api.business;
import com.doumee.api.BaseController;
import com.doumee.core.annotation.excel.ExcelExporter;
import com.doumee.core.model.ApiResponse;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.dao.business.model.OtherOrders;
import com.doumee.service.business.OtherOrdersService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
 * å…¶ä»–订单管理
 * @author rk
 * @date 2026/04/16
 */
@Api(tags = "其他订单")
@RestController
@RequestMapping("/business/otherOrders")
public class OtherOrdersController extends BaseController {
    @Autowired
    private OtherOrdersService otherOrdersService;
    @ApiOperation("根据ID删除")
    @GetMapping("/delete/{id}")
    @RequiresPermissions("business:otherOrders:delete")
    public ApiResponse deleteById(@PathVariable Integer id) {
        otherOrdersService.deleteById(id);
        return ApiResponse.success(null);
    }
    @ApiOperation("批量删除")
    @GetMapping("/delete/batch")
    @RequiresPermissions("business:otherOrders:delete")
    public ApiResponse deleteByIdInBatch(@RequestParam String ids) {
        String[] idArray = ids.split(",");
        List<Integer> idList = new ArrayList<>();
        for (String id : idArray) {
            idList.add(Integer.valueOf(id));
        }
        otherOrdersService.deleteByIdInBatch(idList);
        return ApiResponse.success(null);
    }
    @ApiOperation("根据ID修改")
    @PostMapping("/updateById")
    @RequiresPermissions("business:otherOrders:update")
    public ApiResponse updateById(@RequestBody OtherOrders otherOrders) {
        otherOrdersService.updateById(otherOrders);
        return ApiResponse.success(null);
    }
    @ApiOperation("分页查询")
    @PostMapping("/page")
    @RequiresPermissions("business:otherOrders:query")
    public ApiResponse<PageData<OtherOrders>> findPage(@RequestBody PageWrap<OtherOrders> pageWrap) {
        return ApiResponse.success(otherOrdersService.findPage(pageWrap));
    }
    @ApiOperation("导出Excel")
    @PostMapping("/exportExcel")
    @RequiresPermissions("business:otherOrders:exportExcel")
    public void exportExcel(@RequestBody PageWrap<OtherOrders> pageWrap, HttpServletResponse response) {
        ExcelExporter.build(OtherOrders.class).export(otherOrdersService.findPage(pageWrap).getRecords(), "其他订单", response);
    }
    @ApiOperation("根据ID查询")
    @GetMapping("/{id}")
    @RequiresPermissions("business:otherOrders:query")
    public ApiResponse findById(@PathVariable Integer id) {
        return ApiResponse.success(otherOrdersService.findById(id));
    }
}
server/services/db/db_change.sql
@@ -5,6 +5,66 @@
-- ============================================================
-- 2026/04/16 è®¢å•结算功能:结算时间字段 + é—¨åº—/司机余额字段
-- ============================================================
ALTER TABLE `orders` ADD COLUMN `SETTLEMENT_TIME` DATETIME DEFAULT NULL COMMENT '结算时间' AFTER `SETTLEMENT_STATUS`;
ALTER TABLE `shop_info` ADD COLUMN `BALANCE` BIGINT DEFAULT 0 COMMENT '当前余额(分)' AFTER `DELIVERY_AREA`;
ALTER TABLE `shop_info` ADD COLUMN `TOTAL_BALANCE` BIGINT DEFAULT 0 COMMENT '历史总金额(分)' AFTER `BALANCE`;
ALTER TABLE `driver_info` ADD COLUMN `BALANCE` BIGINT DEFAULT 0 COMMENT '当前余额(分)' AFTER `SCORE`;
ALTER TABLE `driver_info` ADD COLUMN `TOTAL_BALANCE` BIGINT DEFAULT 0 COMMENT '历史总金额(分)' AFTER `BALANCE`;
-- ============================================================
-- 2026/04/16 å¸æœºè¯„分字段 + è®¢å•评价记录表
-- ============================================================
ALTER TABLE `driver_info` ADD COLUMN `SCORE` DECIMAL(2,1) DEFAULT NULL COMMENT '司机评分' AFTER `DRIVER_LEVEL`;
CREATE TABLE `order_comment` (
  `ID` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
  `DELETED` INT DEFAULT 0 COMMENT '是否已删除 0未删除 1已删除',
  `CREATE_USER` INT DEFAULT NULL COMMENT '创建人编码',
  `CREATE_TIME` DATETIME DEFAULT NULL COMMENT '创建时间',
  `UPDATE_USER` INT DEFAULT NULL COMMENT '更新人编码',
  `UPDATE_TIME` DATETIME DEFAULT NULL COMMENT '更新时间',
  `REMARK` VARCHAR(500) DEFAULT NULL COMMENT '备注',
  `ORDER_ID` INT NOT NULL COMMENT '关联订单主键',
  `ORDER_CODE` VARCHAR(50) DEFAULT NULL COMMENT '订单编号',
  `MEMBER_ID` INT NOT NULL COMMENT '评价人(会员)主键',
  `TARGET_TYPE` INT NOT NULL COMMENT '评价对象类型:1=存件门店;2=取件门店;3=司机',
  `TARGET_ID` INT NOT NULL COMMENT '评价对象主键(shop_info.id或driver_info.id)',
  `SCORE` INT NOT NULL COMMENT '评分1-5',
  `CONTENT` VARCHAR(500) DEFAULT NULL COMMENT '评价内容',
  PRIMARY KEY (`ID`)
) COMMENT='订单评价记录';
-- ============================================================
-- 2026/04/16 æ–°å¢žå…¶ä»–订单记录表
-- ============================================================
CREATE TABLE `other_orders` (
  `ID` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `DELETED` tinyint DEFAULT NULL COMMENT '是否已删除 0未删除 1已删除',
  `CREATE_USER` int DEFAULT NULL COMMENT '创建人编码',
  `CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
  `UPDATE_USER` int DEFAULT NULL COMMENT '更新人编码',
  `UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新时间',
  `REMARK` varchar(2000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
  `TYPE` int DEFAULT NULL COMMENT '订单类型:0=店铺押金订单;1=司机押金订单;2=逾期费用订单;',
  `OBJ_ID` int DEFAULT NULL COMMENT '关联会员主键 åº—铺、会员',
  `PAY_ACCOUNT` bigint DEFAULT NULL COMMENT '实际支付费用',
  `PAY_STATUS` int DEFAULT NULL COMMENT '支付状态:0=未支付;1=已支付;',
  `PAY_TIME` datetime DEFAULT NULL COMMENT '支付时间',
  `CODE` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '订单编号',
  `WX_EXTERNAL_NO` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信平台交易订单号',
  `ORDER_ID` int DEFAULT NULL COMMENT '关联寄存订单主键',
  `OUT_TRADE_NO` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '三方订单号',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='其他订单记录';
-- ============================================================
-- 2026/04/15 è®¢å•表添加核销码字段
-- ============================================================
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java
@@ -4,8 +4,6 @@
import com.alibaba.fastjson.JSONObject;
import com.doumee.api.BaseController;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.config.annotation.EncryptionReq;
import com.doumee.config.annotation.EncryptionResp;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.constants.Constants;
import com.doumee.core.constants.ResponseStatus;
@@ -13,12 +11,7 @@
import com.doumee.core.model.ApiResponse;
import com.doumee.core.utils.DateUtil;
import com.doumee.core.utils.FtpUtil;
import com.doumee.core.utils.azure.AzureBlobUtil;
import com.doumee.core.utils.huaweiOBS.ObsUtil;
import com.doumee.core.utils.qiyeweixin.QywxUtil;
import com.doumee.core.utils.qiyeweixin.model.response.QywxUploadMediaResponse;
import com.doumee.dao.system.model.SystemDictData;
import com.doumee.service.common.EmailService;
import com.doumee.core.utils.aliyun.ALiYunUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -67,7 +60,7 @@
        if(Objects.isNull(folder)){
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"目标文件夹错误");
        }
        String prefixPath = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode();
        String prefixPath = systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode();
        InputStream is = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
@@ -90,10 +83,11 @@
                    String fName =  date+"/"+ UUID.randomUUID()+endType;
                    String fileName = folder+"/"+fName;
                    boolean r =false;
                    ObsUtil obsUtil = new ObsUtil( systemDictDataBiz.queryByCode(Constants.HWY_OBS,Constants.HWY_OBS_ACCESSID).getCode(),
                            systemDictDataBiz.queryByCode(Constants.HWY_OBS,Constants.HWY_OBS_ACCESSKEY).getCode(),
                            systemDictDataBiz.queryByCode(Constants.HWY_OBS,Constants.HWY_OBS_ENDPOINT).getCode());
                    r = obsUtil.uploadFile( systemDictDataBiz.queryByCode(Constants.HWY_OBS,Constants.HWY_OBS_BUCKET).getCode(),is,fileName);//上传
                    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);
                    if(r){
                        context.put("success", true);
                        context.put("code", 200);
server/services/src/main/java/com/doumee/config/wx/SendWxMessage.java
@@ -5,7 +5,6 @@
import com.doumee.core.constants.Constants;
import com.doumee.core.utils.DateUtil;
import com.doumee.core.utils.HttpsUtil;
import com.doumee.dao.business.model.IdentityInfo;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,38 +28,5 @@
    @Autowired
    private SystemDictDataBiz systemDictDataBiz;
    /**
     * ä¿¡æ¯è®¤è¯
     * @param openid
     * @param identityInfo
     * @throws WxErrorException
     */
    public  void  identityInfoMessage(String openid, IdentityInfo identityInfo){
        try{
            String accessToken = WxMiniConfig.wxMaService.getAccessToken();
            log.info("微信小程序->微信消息通知 - è®¤è¯ä¿¡æ¯  -> accessToken:{}",accessToken);
            //这里简单起见我们每次都获取最新的access_token(时间开发中,应该在access_token快过期时再重新获取)
            String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="+accessToken;
            //拼接推送的模版
            WxMsgVO wxMsgVO = new WxMsgVO();
            //用户的openid(要发送给那个用户)
            wxMsgVO.setTouser(openid);
            //订阅消息模板id
            wxMsgVO.setTemplate_id("VJho7-lf-4_WZFfOzenDnX6sOhYBJWwkLExVjBB563U");
            Map<String, TemplateData> m = new HashMap<>(4);
            m.put("thing2", new TemplateData(Constants.equalsInteger(identityInfo.getType(),Constants.ZERO)?"用工认证":Constants.equalsInteger(identityInfo.getType(),Constants.ONE)?"运货认证":"供餐认证"));
            m.put("phrase6", new TemplateData(Constants.equalsInteger(identityInfo.getAuditStatus(),Constants.TWO)?"通过":"未通过"));
            m.put("time12", new TemplateData(DateUtil.formatDate(identityInfo.getAuditTime(),"yyyy-MM-dd HH:mm")));
            wxMsgVO.setPage(mineUrl);
            wxMsgVO.setData(m);
            log.error("微信小程序->微信消息通知 è®¤è¯ä¿¡æ¯ï¼š{}", JSONObject.toJSONString(wxMsgVO));
            String responseEntity  = HttpsUtil.postJson(url,JSONObject.toJSONString(wxMsgVO));
            log.error("微信小程序->微信消息通知 è®¤è¯ä¿¡æ¯ï¼š{}", JSONObject.toJSONString(responseEntity));
        }catch (WxErrorException wxErrorException){
        }
    }
}
server/services/src/main/java/com/doumee/core/constants/Constants.java
@@ -18,11 +18,8 @@
 */
public class Constants {
    public static final String HWY_OBS ="HWY_OBS" ;
    public static final String HWY_OBS_ACCESSID ="HWY_OBS_ACCESSID" ;
    public static final String HWY_OBS_ACCESSKEY ="HWY_OBS_ACCESSKEY" ;
    public static final String HWY_OBS_ENDPOINT ="HWY_OBS_ENDPOINT" ;
    public static final String HWY_OBS_BUCKET ="HWY_OBS_BUCKET" ;
    public static final String OSS ="OSS" ;
    public static final String RESOURCE_PATH ="RESOURCE_PATH" ;
    public static final String QYWX_CORPID = "QYWX_CORPID";
    public static final String QYWX_SECRET = "QYWX_SECRET";
@@ -53,6 +50,11 @@
    public static final String USER_AGREEMENT ="USER_AGREEMENT" ;
    public static final String PRIVACY_AGREEMENT ="PRIVACY_AGREEMENT" ;
    public static final String ACCESS_ID="ACCESS_ID";
    public static final String BUCKETNAME = "BUCKETNAME";
    public static final String ACCESS_KEY = "ACCESS_KEY";
    public static final String ENDPOINT = "ENDPOINT";
    public static final String QYWX = "QYWX";
    public static final  Integer ONE = 1;
@@ -413,7 +415,7 @@
        STORAGE_ORDER("storageOrder", "寄存订单"),
        SHOP_DEPOSIT("shopDeposit", "店铺押金订单"),
        DRIVER_DEPOSIT("driverDeposit", "司机押金订单"),
        OVERDUE_FEE("overdueFee", "逾期费用订单")
        OVERDUE_FEE("overdueFee", "订单逾期费用")
        ;
        private final String key;
@@ -475,8 +477,8 @@
    public enum OrderCombinedStatus {
        waitPay(0, "待支付", new int[]{OrderStatus.waitPay.status}),
        waitDeposit(1, "待核验", new int[]{OrderStatus.waitDeposit.status}),
        waitDeliver(2, "待配送", new int[]{OrderStatus.deposited.status}),
        waitReceive(3, "待收货", new int[]{OrderStatus.accepted.status, OrderStatus.delivering.status, OrderStatus.arrived.status}),
        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})
        ;
server/services/src/main/java/com/doumee/core/utils/huaweiOBS/ObsUtil.java
ÎļþÒÑɾ³ý
server/services/src/main/java/com/doumee/dao/business/IdentityInfoMapper.java
ÎļþÒÑɾ³ý
server/services/src/main/java/com/doumee/dao/business/MemberMapper.java
@@ -15,15 +15,4 @@
 */
public interface MemberMapper extends MPJBaseMapper<Member> {
    @Select(" select * , " +
            " ifnull((select r.level from receive_weight r where r.RECEIVE_MAX > RECEIVE_NUM and RECEIVE_NUM > r.RECEIVE_MIN limit 1  ),0) as level ," +
            "  ifnull( (select CONVERT( ST_Distance_Sphere ( POINT ( ii.lgt, ii.lat ), POINT ( #{lgt}, #{lat}  )) /1000,DECIMAL(15,2)) from identity_info ii where ii.AUDIT_STATUS = 2 and type = 0 and ii.member_id = ID limit  1 ),0) as distance " +
            " from member  " +
            " where  id in (   " +
            "  select ii.member_id from identity_info ii where ii.AUDIT_STATUS = 2 and type = #{orderType}  " +
            "  and ( CONVERT( ST_Distance_Sphere ( POINT ( ii.lgt, ii.lat ), POINT ( #{lgt}, #{lat} )) /1000,DECIMAL(15,2))) < 100 " +
            "  ) and RELEASE_MEMBER_ID != #{releaseMemberId} " +
            " order by level , score desc , distance asc  ")
    List<Member> getList(@Param("lgt") BigDecimal lgt, @Param("lat") BigDecimal lat, @Param("orderType") Integer orderType,  @Param("releaseMemberId") Integer releaseMemberId);
}
server/services/src/main/java/com/doumee/dao/business/OrderCommentMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.doumee.dao.business;
import com.doumee.dao.business.model.OrderComment;
import com.github.yulichang.base.MPJBaseMapper;
/**
 * è®¢å•评价记录Mapper
 * @author rk
 * @date 2026/04/16
 */
public interface OrderCommentMapper extends MPJBaseMapper<OrderComment> {
}
server/services/src/main/java/com/doumee/dao/business/OtherOrdersMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
package com.doumee.dao.business;
import com.doumee.dao.business.model.OtherOrders;
import com.github.yulichang.base.MPJBaseMapper;
public interface OtherOrdersMapper extends MPJBaseMapper<OtherOrders> {
}
server/services/src/main/java/com/doumee/dao/business/model/DriverInfo.java
@@ -10,6 +10,7 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -131,6 +132,15 @@
    @ApiModelProperty(value = "司机定级:( 5 - 1 S A B C D )", example = "1")
    private Integer driverLevel;
    @ApiModelProperty(value = "司机评分")
    private BigDecimal score;
    @ApiModelProperty(value = "当前余额(分)")
    private Long balance;
    @ApiModelProperty(value = "历史总金额(分)")
    private Long totalBalance;
    @ApiModelProperty(value = "车辆照片列表")
    @TableField(exist = false)
    private List<Multifile> carImgList = new ArrayList<>();
server/services/src/main/java/com/doumee/dao/business/model/IdentityInfo.java
ÎļþÒÑɾ³ý
server/services/src/main/java/com/doumee/dao/business/model/OrderComment.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package com.doumee.dao.business.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
 * è®¢å•评价记录
 * @author rk
 * @date 2026/04/16
 */
@Data
@ApiModel("订单评价记录")
@TableName("`order_comment`")
public class OrderComment {
    @TableId(type = IdType.AUTO)
    @ApiModelProperty(value = "主键", example = "1")
    private Integer id;
    @ApiModelProperty(value = "是否已删除 0未删除 1已删除", example = "0")
    private Integer deleted;
    @ApiModelProperty(value = "创建人编码", example = "1")
    private Integer createUser;
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @ApiModelProperty(value = "更新人编码", example = "1")
    private Integer updateUser;
    @ApiModelProperty(value = "更新时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    @ApiModelProperty(value = "备注")
    private String remark;
    @ApiModelProperty(value = "关联订单主键", example = "1")
    private Integer orderId;
    @ApiModelProperty(value = "订单编号")
    private String orderCode;
    @ApiModelProperty(value = "评价人(会员)主键", example = "1")
    private Integer memberId;
    @ApiModelProperty(value = "评价对象类型:1=存件门店;2=取件门店;3=司机", example = "1")
    private Integer targetType;
    @ApiModelProperty(value = "评价对象主键(shop_info.id或driver_info.id)", example = "1")
    private Integer targetId;
    @ApiModelProperty(value = "评分1-5", example = "5")
    private Integer score;
    @ApiModelProperty(value = "评价内容")
    private String content;
}
server/services/src/main/java/com/doumee/dao/business/model/Orders.java
@@ -337,7 +337,7 @@
    @ExcelColumn(name = "异常费用")
    private Long exceptionAmount;
    @ApiModelProperty(value = "异常补偿/补偿司机(分)")
    @ApiModelProperty(value = "异常补偿司机(分)")
    @ExcelColumn(name = "异常补偿")
    private Long exceptionFee;
@@ -348,6 +348,10 @@
    @ApiModelProperty(value = "结算状态:0=待结算;1=已结算;")
    private Integer settlementStatus;
    @ApiModelProperty(value = "结算时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date settlementTime;
    @ApiModelProperty(value = "三方订单号")
    private String outTradeNo;
server/services/src/main/java/com/doumee/dao/business/model/OrdersRefund.java
@@ -47,7 +47,7 @@
    @ApiModelProperty(value = "订单主键", example = "1")
    private Integer orderId;
    @ApiModelProperty(value = "退款方式:0=未存件直接取消;1=平台直接取消;2=已存件申请取消", example = "0")
    @ApiModelProperty(value = "退款方式:0=未存件直接取消;1=平台直接取消;2=已存件申请取消;3=订单完成退款", example = "0")
    private Integer type;
    @ApiModelProperty(value = "取消原因")
server/services/src/main/java/com/doumee/dao/business/model/OtherOrders.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.doumee.dao.business.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.doumee.core.annotation.excel.ExcelColumn;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
 * å…¶ä»–订单记录
 * @author rk
 * @date 2026/04/16
 */
@Data
@ApiModel("其他订单记录")
@TableName("`other_orders`")
public class OtherOrders {
    @TableId(type = IdType.AUTO)
    @ApiModelProperty(value = "主键", example = "1")
    @ExcelColumn(name = "主键")
    private Integer id;
    @ApiModelProperty(value = "是否已删除 0未删除 1已删除", example = "0")
    @ExcelColumn(name = "是否已删除")
    private Integer deleted;
    @ApiModelProperty(value = "创建人编码", example = "1")
    @ExcelColumn(name = "创建人编码")
    private Integer createUser;
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ExcelColumn(name = "创建时间", width = 16, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @ApiModelProperty(value = "更新人编码", example = "1")
    @ExcelColumn(name = "更新人编码")
    private Integer updateUser;
    @ApiModelProperty(value = "更新时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    @ApiModelProperty(value = "备注")
    @ExcelColumn(name = "备注")
    private String remark;
    @ApiModelProperty(value = "订单类型:0=店铺押金订单;1=司机押金订单;2=逾期费用订单", example = "0")
    @ExcelColumn(name = "订单类型", valueMapping = "0=店铺押金订单;1=司机押金订单;2=逾期费用订单;")
    private Integer type;
    @ApiModelProperty(value = "关联会员主键", example = "1")
    @ExcelColumn(name = "关联会员主键")
    private Integer memberId;
    @ApiModelProperty(value = "实际支付费用")
    @ExcelColumn(name = "实际支付费用")
    private Long payAccount;
    @ApiModelProperty(value = "支付状态:0=未支付;1=已支付", example = "0")
    @ExcelColumn(name = "支付状态", valueMapping = "0=未支付;1=已支付;")
    private Integer payStatus;
    @ApiModelProperty(value = "支付时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ExcelColumn(name = "支付时间", width = 16, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date payTime;
    @ApiModelProperty(value = "订单编号")
    @ExcelColumn(name = "订单编号")
    private String code;
    @ApiModelProperty(value = "微信平台交易订单号")
    @ExcelColumn(name = "微信平台交易订单号")
    private String wxExternalNo;
    @ApiModelProperty(value = "关联寄存订单主键", example = "1")
    @ExcelColumn(name = "关联寄存订单主键")
    private Integer orderId;
    @ApiModelProperty(value = "三方订单号")
    @ExcelColumn(name = "三方订单号")
    private String outTradeNo;
}
server/services/src/main/java/com/doumee/dao/business/model/ShopInfo.java
@@ -156,6 +156,12 @@
    @ApiModelProperty(value = "配送范围(km)")
    private BigDecimal deliveryArea;
    @ApiModelProperty(value = "当前余额(分)")
    private Long balance;
    @ApiModelProperty(value = "历史总金额(分)")
    private Long totalBalance;
    @ApiModelProperty(value = "支付宝提现账户")
    private String aliAccount;
server/services/src/main/java/com/doumee/dao/dto/CommentOrderDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package com.doumee.dao.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
/**
 * è®¢å•评价请求
 *
 * @author rk
 * @date 2026/04/16
 */
@Data
@ApiModel("订单评价请求")
public class CommentOrderDTO {
    @NotNull(message = "订单主键不能为空")
    @ApiModelProperty(value = "订单主键", required = true)
    private Integer orderId;
    @NotNull(message = "存件门店评分不能为空")
    @Min(value = 1, message = "评分最低1星")
    @Max(value = 5, message = "评分最高5星")
    @ApiModelProperty(value = "存件门店评分1-5", required = true, example = "5")
    private Integer depositScore;
    @Min(value = 1, message = "评分最低1星")
    @Max(value = 5, message = "评分最高5星")
    @ApiModelProperty(value = "取件门店评分1-5(异地寄存且有取件门店时必填)", example = "5")
    private Integer takeScore;
    @Min(value = 1, message = "评分最低1星")
    @Max(value = 5, message = "评分最高5星")
    @ApiModelProperty(value = "司机评分1-5(异地寄存订单必填)", example = "5")
    private Integer driverScore;
    @ApiModelProperty(value = "评价内容")
    private String content;
}
server/services/src/main/java/com/doumee/dao/dto/ShopApplyDTO.java
@@ -100,4 +100,8 @@
    @ApiModelProperty(value = "社保缴纳证明(个人类型必填,最多3张)")
    @Size(max = 3, message = "社保缴纳证明最多3å¼ ")
    private List<String> socialSecurityImgs;
    @ApiModelProperty(value = "支付宝提现账户", required = true)
    @NotBlank(message = "支付宝提现账户不能为空")
    private String aliAccount;
}
server/services/src/main/java/com/doumee/dao/dto/StoreOutDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.doumee.dao.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
 * é—¨åº—确认出库请求
 *
 * @author rk
 * @date 2026/04/16
 */
@Data
@ApiModel("门店确认出库请求")
public class StoreOutDTO {
    @NotNull(message = "订单主键不能为空")
    @ApiModelProperty(value = "订单主键", required = true)
    private Integer orderId;
    @Size(max = 3, message = "最多上传3张图片")
    @ApiModelProperty(value = "出库图片地址列表,最多3å¼ ")
    private List<String> images;
    @ApiModelProperty(value = "出库备注")
    private String remark;
}
server/services/src/main/java/com/doumee/dao/dto/WithdrawalDTO.java
@@ -1,10 +1,12 @@
package com.doumee.dao.dto;
import com.doumee.dao.business.model.Member;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
@@ -17,13 +19,13 @@
@ApiModel("提现申请")
public class WithdrawalDTO {
    @ApiModelProperty(value = "提现金额 (单位:元)")
    @ApiModelProperty(value = "提现金额 (单位:元)", required = true)
    @NotNull(message = "提现金额不能为空")
    @DecimalMin(value = "0.01", message = "提现金额不能小于0.01元")
    private BigDecimal amount;
    @ApiModelProperty(value = "真实姓名")
    private String name;
    @ApiModelProperty(value = "支付宝提现账户", required = true)
    @NotBlank(message = "支付宝提现账户不能为空")
    private String aliAccount;
    @ApiModelProperty(value = "用户信息",hidden = true)
    private Member member;
}
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java
@@ -91,4 +91,9 @@
    @ApiModelProperty(value = "物品明细列表")
    private List<OrderItemVO> detailList;
    // ---- é—¨åº—端 ----
    @ApiModelProperty(value = "当前门店角色:1=存件门店;2=取件门店(仅门店端返回)")
    private Integer shopRole;
}
server/services/src/main/java/com/doumee/dao/vo/PlatformAboutVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * å¹³å°ä¿¡æ¯ï¼ˆå…³äºŽæˆ‘们、协议等)
 *
 * @author rk
 * @date 2026/04/16
 */
@Data
@ApiModel("平台信息")
public class PlatformAboutVO {
    @ApiModelProperty(value = "关于我们")
    private String aboutUs;
    @ApiModelProperty(value = "用户协议")
    private String userAgreement;
    @ApiModelProperty(value = "隐私协议")
    private String privacyAgreement;
    @ApiModelProperty(value = "服务介绍")
    private String serverIntroduce;
}
server/services/src/main/java/com/doumee/dao/vo/RevenueStatisticsVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * æ”¶ç›Šç»Ÿè®¡VO
 * @author rk
 * @date 2026/04/16
 */
@Data
@ApiModel("收益统计")
public class RevenueStatisticsVO {
    @ApiModelProperty(value = "当前账户余额(分)")
    private Long balance;
    @ApiModelProperty(value = "待结算金额(分)")
    private Long pendingAmount;
    @ApiModelProperty(value = "累计提现金额(分)")
    private Long totalWithdrawn;
    @ApiModelProperty(value = "提现中金额(分)")
    private Long withdrawingAmount;
}
server/services/src/main/java/com/doumee/dao/vo/ShopCenterVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class ShopCenterVO {
    @ApiModelProperty(value = "门店头像全路径")
    private String fullCoverImg;
    @ApiModelProperty(value = "门店头像半路径")
    private String coverImg;
    @ApiModelProperty(value = "门店名称")
    private String shopName;
    @ApiModelProperty(value = "联系人名称")
    private String linkName;
    @ApiModelProperty(value = "门店类型:0=个人;1=企业")
    private Integer companyType;
    @ApiModelProperty(value = "是否有消息")
    private Boolean hasMessage;
    @ApiModelProperty(value = "待核验订单数量")
    private Integer waitDepositCount;
    @ApiModelProperty(value = "待收货订单数量")
    private Integer waitReceiveCount;
}
server/services/src/main/java/com/doumee/dao/vo/UserCenterVO.java
@@ -1,6 +1,5 @@
package com.doumee.dao.vo;
import com.doumee.core.annotation.excel.ExcelColumn;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -13,39 +12,28 @@
@Data
public class UserCenterVO {
    @ApiModelProperty(value = "发布任务总数 - å‘单方使用")
    private Integer releaseTaskTotal;
    @ApiModelProperty(value = "会员头像全路径")
    private String fullCoverImage;
    @ApiModelProperty(value = "待接单任务总数 - å‘单方使用")
    private Integer waitReceiveTotal;
    @ApiModelProperty(value = "会员头像半路径")
    private String coverImage;
    @ApiModelProperty(value = "进行中任务总数 - å‘单方使用")
    private Integer doingTotal;
    @ApiModelProperty(value = "会员昵称")
    private String nickName;
    @ApiModelProperty(value = "待评价任务总数 - å‘单方使用")
    private Integer waitCommentTotal;
    @ApiModelProperty(value = "姓名")
    private String name;
    @ApiModelProperty(value = "进行中任务数量 - æŽ¥å•方使用")
    private Integer taskingTotal;
    @ApiModelProperty(value = "联系电话")
    private String telephone;
    @ApiModelProperty(value = "完成任务数量 - æŽ¥å•方使用")
    private Integer doneTotal;
    @ApiModelProperty(value = "是否有消息")
    private Boolean hasMessage;
    @ApiModelProperty(value = "已接单任务数量 - æŽ¥å•方使用")
    private Integer receiveTotal;
    @ApiModelProperty(value = "待支付订单数量")
    private Integer waitPayCount;
    @ApiModelProperty(value = "服务介绍")
    private String serverIntroduce;
    @ApiModelProperty(value = "待收货订单数量")
    private Integer waitReceiveCount;
    @ApiModelProperty(value = "关于我们")
    private String aboutUs;
    @ApiModelProperty(value = "用户协议")
    private String userAgreement;
    @ApiModelProperty(value = "隐私协议")
    private String privacyAgreement;
    @ApiModelProperty(value = "客服电话 å¤šä¸ªä»¥,分割")
    private String serverPhone;
}
server/services/src/main/java/com/doumee/service/business/IdentityInfoService.java
ÎļþÒÑɾ³ý
server/services/src/main/java/com/doumee/service/business/MemberService.java
@@ -16,6 +16,7 @@
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.MemberDetailVO;
import com.doumee.dao.vo.MemberListVO;
import com.doumee.dao.vo.PlatformAboutVO;
import com.doumee.dao.vo.UserCenterVO;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
@@ -144,9 +145,9 @@
     * @param memberId
     * @return
     */
    Member getMemberInfo(Integer memberId);
    UserCenterVO getMemberInfo(Integer memberId);
    UserCenterVO getPlatformAboutUs();
    PlatformAboutVO getPlatformAboutUs();
    void logOut(String token,Integer memberId);
server/services/src/main/java/com/doumee/service/business/OrdersService.java
@@ -7,6 +7,7 @@
import com.doumee.dao.dto.CalculateRemotePriceDTO;
import com.doumee.dao.dto.CreateOrderDTO;
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;
@@ -130,9 +131,9 @@
    /**
     * ä¼šå‘˜å–消订单(仅异地寄存)
     *
     * @param orderId   è®¢å•主键
     * @param memberId  ä¼šå‘˜ä¸»é”®
     * @param reason    å–消原因
     * @param orderId  è®¢å•主键
     * @param memberId ä¼šå‘˜ä¸»é”®
     * @param reason   å–消原因
     */
    void cancelOrder(Integer orderId, Integer memberId, String reason);
@@ -189,6 +190,7 @@
    /**
     * è®¡ç®—保价费用
     *
     * @param declaredValue æŠ¥ä»·é‡‘额
     * @return ä¿ä»·è´¹ç”¨
     */
@@ -196,6 +198,7 @@
    /**
     * è®¡ç®—就地存取预估费用
     *
     * @param dto å°±åœ°å­˜å–计价请求参数
     * @return ä»·æ ¼è®¡ç®—结果
     */
@@ -203,6 +206,7 @@
    /**
     * è®¡ç®—异地存取预估费用
     *
     * @param dto å¼‚地存取计价请求参数
     * @return ä»·æ ¼è®¡ç®—结果
     */
@@ -210,7 +214,8 @@
    /**
     * åˆ›å»ºè®¢å•并唤起微信支付
     * @param dto åˆ›å»ºè®¢å•请求参数
     *
     * @param dto      åˆ›å»ºè®¢å•请求参数
     * @param memberId å½“前登录会员ID
     * @return æ”¯ä»˜å“åº”
     */
@@ -218,7 +223,8 @@
    /**
     * ç»§ç»­æ”¯ä»˜ï¼ˆå¾…支付订单重新唤起微信支付)
     * @param orderId è®¢å•主键
     *
     * @param orderId  è®¢å•主键
     * @param memberId å½“前登录会员ID
     * @return æ”¯ä»˜å“åº”
     */
@@ -226,6 +232,7 @@
    /**
     * å°ç¨‹åºç«¯-查询我的订单分页
     *
     * @param pageWrap åˆ†é¡µæŸ¥è¯¢å‚数(model含status)
     * @param memberId ä¼šå‘˜ä¸»é”®
     * @return åˆ†é¡µç»“æžœ
@@ -234,6 +241,7 @@
    /**
     * æŸ¥è¯¢è®¢å•超时费用
     *
     * @param orderId è®¢å•主键
     * @return è¶…时费用计算结果
     */
@@ -260,4 +268,91 @@
     */
    void confirmCustomerArrived(Integer orderId, Integer shopId);
}
    /**
     * é€¾æœŸè´¹ç”¨æ”¯ä»˜ï¼ˆå”¤èµ·å¾®ä¿¡æ”¯ä»˜ï¼‰
     *
     * @param orderId  å¯„存订单主键
     * @param memberId å½“前登录会员ID
     * @return æ”¯ä»˜å“åº”
     */
    PayResponse payOverdueFee(Integer orderId, Integer memberId);
    /**
     * é€¾æœŸè´¹ç”¨æ”¯ä»˜å›žè°ƒå¤„理
     *
     * @param outTradeNo å•†æˆ·è®¢å•号
     * @param wxTradeNo  å¾®ä¿¡è®¢å•号
     */
    void handleOverdueFeePayNotify(String outTradeNo, String wxTradeNo);
    /**
     * å–件门店确认出库
     * å°±åœ°å¯„å­˜/异地寄存,存在取件门店,status=5,overdueStatus=0/2,confirmArriveTime不为空
     * å‡ºåº“后订单完成
     *
     * @param orderId è®¢å•主键
     * @param shopId  å½“前操作门店主键
     * @param images  å‡ºåº“图片(最多3张)
     * @param remark  å‡ºåº“备注
     */
    void confirmStoreOut(Integer orderId, Integer shopId, List<String> images, String remark);
    /**
     * è®¡ç®—并更新订单三方收益(存件门店/取件门店/司机)
     * å°±åœ°å¯„存:仅存件门店收益
     * å¼‚地寄存:存件门店 + å¸æœºæ”¶ç›Šï¼›æœ‰å–件门店时加上取件门店收益
     *
     * @param orderId è®¢å•主键
     */
    void calculateAndSaveOrderFees(Integer orderId);
    /**
     * ä¼šå‘˜åˆ é™¤è®¢å•(逻辑删除,仅已完成/已取消/已退款订单可删除)
     *
     * @param orderId  è®¢å•主键
     * @param memberId ä¼šå‘˜ä¸»é”®
     */
    void deleteMyOrder(Integer orderId, Integer memberId);
    /**
     * é—¨åº—支付押金(唤起微信支付)
     *
     * @param shopId é—¨åº—主键
     * @return æ”¯ä»˜å“åº”
     */
    PayResponse payShopDeposit(Integer shopId);
    /**
     * é—¨åº—押金支付回调处理
     *
     * @param outTradeNo å•†æˆ·è®¢å•号
     * @param wxTradeNo  å¾®ä¿¡è®¢å•号
     */
    void handleShopDepositPayNotify(String outTradeNo, String wxTradeNo);
    /**
     * è®¢å•结算:根据 SETTLEMENT_DATE é…ç½®ï¼Œå°†å·²å®Œæˆçš„待结算订单进行结算
     * æ›´æ–°è®¢å•结算状态、Revenue å…¥è´¦çŠ¶æ€ã€é—¨åº—/司机余额
     */
    void settleOrders();
    /**
     * è®¢å•评价
     * status=7且commentStatus=0时可评价,按对象(存件门店/取件门店/司机)分别记录评分
     *
     * @param dto      è¯„价请求
     * @param memberId å½“前登录会员ID
     */
    void commentOrder(CommentOrderDTO dto, Integer memberId);
    /**
     * é—¨åº—端订单分页列表
     * æŸ¥è¯¢å­˜ä»¶é—¨åº—或取件门店等于当前登录门店的订单
     *
     * @param pageWrap åˆ†é¡µæŸ¥è¯¢å‚数(model含status/combinedStatus)
     * @param shopId   å½“前登录门店主键
     * @return åˆ†é¡µç»“æžœ
     */
    PageData<MyOrderVO> findShopOrderPage(PageWrap<MyOrderDTO> pageWrap, Integer shopId);
}
server/services/src/main/java/com/doumee/service/business/OtherOrdersService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
package com.doumee.service.business;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.dao.business.model.OtherOrders;
import java.util.List;
/**
 * å…¶ä»–订单记录Service定义
 * @author rk
 * @date 2026/04/16
 */
public interface OtherOrdersService {
    /**
     * åˆ›å»º
     *
     * @param otherOrders å®žä½“对象
     * @return Integer
     */
    Integer create(OtherOrders otherOrders);
    /**
     * ä¸»é”®åˆ é™¤
     *
     * @param id ä¸»é”®
     */
    void deleteById(Integer id);
    /**
     * åˆ é™¤
     *
     * @param otherOrders å®žä½“对象
     */
    void delete(OtherOrders otherOrders);
    /**
     * æ‰¹é‡ä¸»é”®åˆ é™¤
     *
     * @param ids ä¸»é”®é›†
     */
    void deleteByIdInBatch(List<Integer> ids);
    /**
     * ä¸»é”®æ›´æ–°
     *
     * @param otherOrders å®žä½“对象
     */
    void updateById(OtherOrders otherOrders);
    /**
     * æ‰¹é‡ä¸»é”®æ›´æ–°
     *
     * @param otherOrdersList å®žä½“集
     */
    void updateByIdInBatch(List<OtherOrders> otherOrdersList);
    /**
     * ä¸»é”®æŸ¥è¯¢
     *
     * @param id ä¸»é”®
     * @return OtherOrders
     */
    OtherOrders findById(Integer id);
    /**
     * æ¡ä»¶æŸ¥è¯¢å•条记录
     *
     * @param otherOrders å®žä½“对象
     * @return OtherOrders
     */
    OtherOrders findOne(OtherOrders otherOrders);
    /**
     * æ¡ä»¶æŸ¥è¯¢
     *
     * @param otherOrders å®žä½“对象
     * @return List<OtherOrders>
     */
    List<OtherOrders> findList(OtherOrders otherOrders);
    /**
     * åˆ†é¡µæŸ¥è¯¢
     *
     * @param pageWrap åˆ†é¡µå¯¹è±¡
     * @return PageData<OtherOrders>
     */
    PageData<OtherOrders> findPage(PageWrap<OtherOrders> pageWrap);
    /**
     * æ¡ä»¶ç»Ÿè®¡
     *
     * @param otherOrders å®žä½“对象
     * @return long
     */
    long count(OtherOrders otherOrders);
}
server/services/src/main/java/com/doumee/service/business/RevenueService.java
@@ -3,6 +3,7 @@
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.dao.business.model.Revenue;
import com.doumee.dao.vo.RevenueStatisticsVO;
import java.util.List;
@@ -96,4 +97,20 @@
     */
    long count(Revenue revenue);
    /**
     * é—¨åº—收益统计
     *
     * @param shopId é—¨åº—主键
     * @return æ”¶ç›Šç»Ÿè®¡
     */
    RevenueStatisticsVO getShopRevenueStatistics(Integer shopId);
    /**
     * å¸æœºæ”¶ç›Šç»Ÿè®¡
     *
     * @param memberId ä¼šå‘˜ä¸»é”®
     * @return æ”¶ç›Šç»Ÿè®¡
     */
    RevenueStatisticsVO getDriverRevenueStatistics(Integer memberId);
}
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java
@@ -6,6 +6,7 @@
import com.doumee.dao.business.model.Member;
import com.doumee.dao.dto.*;
import com.doumee.dao.vo.ShopLoginVO;
import com.doumee.dao.vo.ShopCenterVO;
import com.doumee.dao.vo.ShopDetailVO;
import com.doumee.dao.vo.ShopNearbyVO;
import com.doumee.dao.vo.ShopWebDetailVO;
@@ -178,6 +179,13 @@
    /**
     * èŽ·å–é—¨åº—ç™»å½•åŽä¿¡æ¯ï¼ˆé—¨åº—ä¸­å¿ƒï¼‰
     * @param shopId é—¨åº—主键
     * @return é—¨åº—中心信息
     */
    ShopCenterVO getShopCenterInfo(Integer shopId);
    /**
     * é—¨åº—密码登录
     * @param dto ç™»å½•请求
     * @return ç™»å½•结果
server/services/src/main/java/com/doumee/service/business/WithdrawalOrdersService.java
@@ -4,6 +4,7 @@
import com.doumee.core.model.PageWrap;
import com.doumee.dao.business.model.WithdrawalOrders;
import com.doumee.dao.dto.WithdrawalApproveDTO;
import com.doumee.dao.dto.WithdrawalDTO;
import java.util.List;
@@ -112,4 +113,20 @@
     */
    void approve(WithdrawalApproveDTO dto);
    /**
     * å¸æœºæçŽ°ç”³è¯·
     *
     * @param dto      æçŽ°å‚æ•°
     * @param memberId ä¼šå‘˜ä¸»é”®
     */
    void applyDriverWithdrawal(WithdrawalDTO dto, Integer memberId);
    /**
     * é—¨åº—提现申请
     *
     * @param dto    æçŽ°å‚æ•°
     * @param shopId é—¨åº—主键
     */
    void applyShopWithdrawal(WithdrawalDTO dto, Integer shopId);
}
server/services/src/main/java/com/doumee/service/business/impl/BannerServiceImpl.java
@@ -228,7 +228,7 @@
    }
    private String getBannerPath() {
        return systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
        return systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.BANNER_FILES).getCode();
    }
}
server/services/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java
@@ -139,7 +139,7 @@
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        if(StringUtils.isNotBlank(category.getIcon())){
            String path  = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode()
            String path  = systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.CATEGORY_FILES).getCode();
            category.setIconFull(path + category.getIcon());
        }
@@ -208,7 +208,7 @@
        queryWrapper.orderByAsc(Category::getSortnum);
        PageData<Category> result =PageData.from(categoryMapper.selectJoinPage(page, Category.class,queryWrapper));
        if(result!=null && result.getRecords()!=null){
            String path  = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode()
            String path  = systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.CATEGORY_FILES).getCode();
            for(Category cate : result.getRecords()){
                try {
@@ -236,7 +236,7 @@
                .orderByAsc(Category::getSortnum)
        );
        if(com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(categoryList)){
            String path  = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode()
            String path  = systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.CATEGORY_FILES).getCode();
            for (Category category:categoryList) {
                if(StringUtils.isNotBlank(category.getIcon())){
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -555,7 +555,7 @@
        // æ‹¼æŽ¥å›¾ç‰‡å‰ç¼€
        String imgPrefix = "";
        try {
            imgPrefix = systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
            imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.DRIVER_FILES).getCode();
        } catch (Exception e) {
            // æœªé…ç½®æ—¶å¿½ç•¥
server/services/src/main/java/com/doumee/service/business/impl/IdentityInfoServiceImpl.java
ÎļþÒÑɾ³ý
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java
@@ -12,12 +12,12 @@
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.IdentityInfoMapper;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.OrdersMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.SmsrecordMapper;
import com.doumee.dao.business.model.IdentityInfo;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.Orders;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.business.model.MemberRevenue;
import com.doumee.dao.business.model.Smsrecord;
@@ -27,6 +27,7 @@
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.MemberDetailVO;
import com.doumee.dao.vo.MemberListVO;
import com.doumee.dao.vo.PlatformAboutVO;
import com.doumee.dao.vo.UserCenterVO;
import com.doumee.service.business.MemberService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -45,6 +46,7 @@
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@@ -64,9 +66,6 @@
    private MemberMapper memberMapper;
    @Autowired
    private IdentityInfoMapper identityInfoMapper;
    @Autowired
    private SmsrecordMapper smsrecordMapper;
    @Autowired
@@ -80,6 +79,9 @@
    @Autowired
    private ShopInfoMapper shopInfoMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Override
@@ -129,8 +131,8 @@
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        if(StringUtils.isNotBlank(member.getCoverImage())){
            String path  = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.MEMBER_FILES).getCode();
            String path  = systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.OSS,Constants.MEMBER_FILES).getCode();
            member.setFullCoverImage(path + member.getCoverImage());
        }
        return member;
@@ -323,8 +325,7 @@
                member.setDeleted(Constants.ZERO);
                member.setOpenid(wxPhoneRequest.getOpenid());
                member.setTelephone(mobile);
                member.setNickName(mobile);
//                member.setCoverImage("1.png");
                member.setNickName(mobile.substring(0, 3) + "****" + mobile.substring(7));
                member.setAmount(Constants.ZERO.longValue());
                member.setTotalAmount(Constants.ZERO.longValue());
                member.setStatus(Constants.ZERO);
@@ -385,75 +386,51 @@
    }
//    @Override
//    public void editUseIdentity(Member member){
//        if(Objects.isNull(member)
//                || Objects.isNull(member.getUseIdentity())){
//            throw new BusinessException(ResponseStatus.BAD_REQUEST);
//        }
//        if(Constants.equalsInteger(member.getUseIdentity(),Constants.ZERO)){
//            member.setUseIdentity(Constants.ZERO);
//            memberMapper.update(new UpdateWrapper<Member>().lambda().set(Member::getUseIdentity,Constants.ZERO).eq(Member::getId,member.getId()));
//        }else{
//            if(identityInfoMapper.selectCount(new QueryWrapper<IdentityInfo>().lambda().eq(IdentityInfo::getDeleted,Constants.ZERO)
//                    .eq(IdentityInfo::getMemberId,member.getId())
//                    .eq(IdentityInfo::getAuditStatus,Constants.TWO))>Constants.ZERO){
//                memberMapper.update(new UpdateWrapper<Member>().lambda().set(Member::getUseIdentity,Constants.ONE).eq(Member::getId,member.getId()));
//            }else{
//                throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"当前无可用接单身份!");
//            }
//        }
//    }
    @Override
    public Member getMemberInfo(Integer memberId){
        Member member  = this.findById(memberId);
    public UserCenterVO getMemberInfo(Integer memberId){
        Member member  = memberMapper.selectById(memberId);
        if(Objects.isNull(member)){
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // ç”¨æˆ·ä¸­å¿ƒæ•°æ®
        UserCenterVO userCenterVO = new UserCenterVO();
        userCenterVO.setNickName(member.getNickName());
        userCenterVO.setName(member.getName());
        userCenterVO.setTelephone(member.getTelephone());
        userCenterVO.setCoverImage(member.getCoverImage());
        if(StringUtils.isNotBlank(member.getCoverImage())){
            String path  = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.MEMBER_FILES).getCode();
            member.setFullCoverImage(path + member.getCoverImage());
            String path  = systemDictDataBiz.queryByCode(Constants.OSS,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.OSS,Constants.MEMBER_FILES).getCode();
            userCenterVO.setFullCoverImage(path + member.getCoverImage());
        }
//        UserCenterVO userCenterVO = new UserCenterVO();
//        userCenterVO.setReleaseTaskTotal(Constants.ZERO);
//        userCenterVO.setWaitReceiveTotal(Constants.ZERO);
//        userCenterVO.setDoingTotal(Constants.ZERO);
//        userCenterVO.setWaitCommentTotal(Constants.ZERO);
//        userCenterVO.setTaskingTotal(Constants.ZERO);
//        userCenterVO.setDoneTotal(Constants.ZERO);
//        userCenterVO.setReceiveTotal(Constants.ZERO);
//        List<Orders> releaseOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda().eq(Orders::getDeleted,Constants.ZERO)
//                .eq(Orders::getReleaseMemberId,member.getId()));
//        if(com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(releaseOrders)){
//            userCenterVO.setReleaseTaskTotal(releaseOrders.size());
//            userCenterVO.setWaitCommentTotal(releaseOrders.stream().filter(i->Constants.equalsInteger(i.getStatus(),Constants.ordersStatus.done.getKey())&&Constants.equalsInteger(i.getCommentStatus(),Constants.ZERO)).collect(Collectors.toList()).size());
//            userCenterVO.setDoingTotal(releaseOrders.stream().filter(i->Constants.equalsInteger(i.getStatus(),Constants.ordersStatus.doing.getKey())).collect(Collectors.toList()).size());
//            userCenterVO.setWaitReceiveTotal(releaseOrders.stream().filter(i->Constants.equalsInteger(i.getStatus(),Constants.ordersStatus.wait.getKey())).collect(Collectors.toList()).size());
//        }
//        List<Orders> acceptOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda().eq(Orders::getDeleted,Constants.ZERO).eq(Orders::getAcceptMemberId,member.getId()));
//        if(com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(acceptOrders)){
//            userCenterVO.setReceiveTotal(acceptOrders.size());
//            userCenterVO.setTaskingTotal(acceptOrders.stream().filter(i->Constants.equalsInteger(i.getStatus(),Constants.ordersStatus.doing.getKey())).collect(Collectors.toList()).size());
//            userCenterVO.setDoneTotal(acceptOrders.stream().filter(i->Constants.equalsInteger(i.getStatus(),Constants.ordersStatus.done.getKey())).collect(Collectors.toList()).size());
//        }
//        member.setUserCenterVO(userCenterVO);
        return member;
        userCenterVO.setHasMessage(false);
        // å¾…支付订单数量
        Long waitPayCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getMemberId, memberId)
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getStatus, Constants.OrderStatus.waitPay.getStatus()));
        userCenterVO.setWaitPayCount(waitPayCount.intValue());
        // å¾…收货订单数量
        int[] waitReceiveStatuses = Constants.OrderCombinedStatus.waitReceive.getStatuses();
        Long waitReceiveCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getMemberId, memberId)
                .eq(Orders::getDeleted, Constants.ZERO)
                .in(Orders::getStatus, Arrays.stream(waitReceiveStatuses).boxed().collect(Collectors.toList())));
        userCenterVO.setWaitReceiveCount(waitReceiveCount.intValue());
        return userCenterVO;
    }
    @Override
    public UserCenterVO getPlatformAboutUs(){
        UserCenterVO userCenterVO = new UserCenterVO();
        userCenterVO.setAboutUs(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.ABOUT_US).getCode()));
        userCenterVO.setUserAgreement(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.USER_AGREEMENT).getCode()));
        userCenterVO.setPrivacyAgreement(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.PRIVACY_AGREEMENT).getCode()));
        userCenterVO.setServerIntroduce(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.SERVER_INTRODUCE).getCode()));
        return userCenterVO;
    public PlatformAboutVO getPlatformAboutUs(){
        PlatformAboutVO vo = new PlatformAboutVO();
        vo.setAboutUs(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.ABOUT_US).getCode()));
        vo.setUserAgreement(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.USER_AGREEMENT).getCode()));
        vo.setPrivacyAgreement(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.PRIVACY_AGREEMENT).getCode()));
        vo.setServerIntroduce(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.SERVER_INTRODUCE).getCode()));
        return vo;
    }
    @Override
@@ -543,8 +520,8 @@
        detail.setArea(member.getArea());
        detail.setAutoReceiveStatus(member.getAutoReceiveStatus());
        if (StringUtils.isNotBlank(member.getCoverImage())) {
            String path = systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.MEMBER_FILES).getCode();
            String path = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.MEMBER_FILES).getCode();
            detail.setFullCoverImage(path + member.getCoverImage());
        }
        return detail;
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -20,9 +20,11 @@
import com.doumee.dao.business.*;
import com.doumee.dao.business.model.*;
import com.doumee.dao.system.SystemUserMapper;
import com.doumee.dao.system.model.SystemDictData;
import com.doumee.dao.system.model.SystemUser;
import com.doumee.dao.dto.CalculateLocalPriceDTO;
import com.doumee.dao.dto.CalculateRemotePriceDTO;
import com.doumee.dao.dto.CommentOrderDTO;
import com.doumee.dao.dto.CreateOrderDTO;
import com.doumee.dao.dto.DispatchDTO;
import com.doumee.dao.dto.MyOrderDTO;
@@ -30,6 +32,7 @@
import com.doumee.dao.vo.*;
import com.doumee.service.business.OrderLogService;
import com.doumee.service.business.OrdersService;
import com.doumee.service.business.AreasService;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
@@ -92,6 +95,15 @@
    private OrdersRefundMapper ordersRefundMapper;
    @Autowired
    private OtherOrdersMapper otherOrdersMapper;
    @Autowired
    private OrderCommentMapper orderCommentMapper;
    @Autowired
    private RevenueMapper revenueMapper;
    @Autowired
    private WxMiniUtilService wxMiniUtilService;
    @Autowired
@@ -107,6 +119,9 @@
    @Autowired
    private OperationConfigBiz operationConfigBiz;
    @Autowired
    private AreasService areasService;
    @Override
    public Integer create(Orders orders) {
@@ -1135,8 +1150,8 @@
    private String getOrdersPrefix() {
        try {
            return systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.ORDERS_FILES).getCode();
            return systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.ORDERS_FILES).getCode();
        } catch (Exception e) {
            return "";
        }
@@ -1307,6 +1322,102 @@
                vo.setDetailList(buildDetailList(details));
                // é€¾æœŸä¿¡æ¯ï¼ˆä»…待取件状态计算)
                if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
                    OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
                    vo.setOverdue(overdueInfo.getOverdue());
                    vo.setOverdueDays(overdueInfo.getOverdueDays());
                    vo.setOverdueFee(overdueInfo.getOverdueFee());
                }
                voList.add(vo);
            }
        }
        IPage<MyOrderVO> vPage = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        PageData<MyOrderVO> pageData = PageData.from(vPage);
        pageData.setRecords(voList);
        pageData.setTotal(orderPage.getTotal());
        pageData.setPage(orderPage.getCurrent());
        pageData.setCapacity(orderPage.getSize());
        return pageData;
    }
    @Override
    public PageData<MyOrderVO> findShopOrderPage(PageWrap<MyOrderDTO> pageWrap, Integer shopId) {
        MyOrderDTO model = pageWrap.getModel();
        Integer status = model != null ? model.getStatus() : null;
        Integer combinedStatus = model != null ? model.getCombinedStatus() : null;
        // è§£æžåˆå¹¶çŠ¶æ€ä¸ºå…·ä½“çŠ¶æ€åˆ—è¡¨
        List<Integer> statusList = null;
        if (combinedStatus != null) {
            Constants.OrderCombinedStatus combined = Constants.OrderCombinedStatus.getByKey(combinedStatus);
            if (combined != null) {
                statusList = new ArrayList<>();
                for (int s : combined.getStatuses()) {
                    statusList.add(s);
                }
            }
        }
        IPage<Orders> p = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        MPJLambdaWrapper<Orders> wrapper = new MPJLambdaWrapper<Orders>()
                .selectAll(Orders.class)
                .select("s1.name", Orders::getDepositShopName)
                .select("s1.link_name", Orders::getDepositShopLinkName)
                .select("s1.link_phone", Orders::getDepositShopLinkPhone)
                .select("s2.name", Orders::getTakeShopName)
                .select("s2.address", Orders::getTakeShopAddress)
                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
                .eq(Orders::getPayStatus, Constants.ONE)
                .and(w -> w.eq(Orders::getDepositShopId, shopId).or().eq(Orders::getTakeShopId, shopId))
                .eq(status != null, Orders::getStatus, status)
                .in(statusList != null, Orders::getStatus, statusList)
                .orderByDesc(Orders::getCreateTime);
        IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
        List<MyOrderVO> voList = new ArrayList<>();
        if (orderPage != null && orderPage.getRecords() != null) {
            for (Orders o : orderPage.getRecords()) {
                MyOrderVO vo = new MyOrderVO();
                vo.setId(o.getId());
                vo.setCode(o.getCode());
                vo.setType(o.getType());
                vo.setStatus(o.getStatus());
                vo.setCreateTime(o.getCreateTime());
                vo.setExpectedTakeTime(o.getExpectedTakeTime());
                vo.setDepositShopName(o.getDepositShopName());
                vo.setDepositShopLinkName(o.getDepositShopLinkName());
                vo.setDepositShopPhone(o.getDepositShopLinkPhone());
                // é—¨åº—角色:存件门店=1,取件门店=2
                if (Constants.equalsInteger(o.getDepositShopId(), shopId)) {
                    vo.setShopRole(Constants.ONE);
                } else if (Constants.equalsInteger(o.getTakeShopId(), shopId)) {
                    vo.setShopRole(Constants.TWO);
                }
                if (o.getTakeShopId() != null) {
                    vo.setTakeShopName(o.getTakeShopName());
                    vo.setTakeShopAddress(o.getTakeShopAddress());
                } else {
                    vo.setTakeLocation(o.getTakeLocation());
                    vo.setTakeLocationRemark(o.getTakeLocationRemark());
                }
                vo.setTakeUser(o.getTakeUser());
                vo.setTakePhone(o.getTakePhone());
                vo.setDeclaredFee(o.getDeclaredFee());
                vo.setEstimatedAmount(o.getEstimatedAmount());
                List<OrdersDetail> details = ordersDetailMapper.selectList(
                        new QueryWrapper<OrdersDetail>().lambda()
                                .eq(OrdersDetail::getOrderId, o.getId())
                                .eq(OrdersDetail::getDeleted, Constants.ZERO));
                vo.setDetailList(buildDetailList(details));
                if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
                    OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
                    vo.setOverdue(overdueInfo.getOverdue());
@@ -1607,6 +1718,443 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayResponse payOverdueFee(Integer orderId, Integer memberId) {
        // 1. æŸ¥è¯¢å¯„存订单
        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getId, orderId)
                .eq(Orders::getMemberId, memberId)
                .eq(Orders::getDeleted, Constants.ZERO));
        if (order == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
        }
        // 2. æ ¡éªŒçŠ¶æ€ï¼šå¾…å–ä»¶(5) + é€¾æœŸ(1)
        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不支持逾期支付");
        }
        if (!Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不存在逾期费用");
        }
        if (order.getOverdueAmount() == null || order.getOverdueAmount() <= 0) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "逾期费用异常,无法发起支付");
        }
        // 3. æŸ¥è¯¢ä¼šå‘˜
        Member member = memberMapper.selectById(memberId);
        if (member == null || StringUtils.isBlank(member.getOpenid())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "用户信息异常,无法发起支付");
        }
        // 4. åˆ›å»ºé€¾æœŸè´¹ç”¨è®¢å•
        String outTradeNo = generateOrderTradeNo();
        Date now = new Date();
        OtherOrders otherOrders = new OtherOrders();
        otherOrders.setType(Constants.TWO); // 2=逾期费用订单
        otherOrders.setMemberId(memberId);
        otherOrders.setOrderId(orderId);
        otherOrders.setPayAccount(order.getOverdueAmount());
        otherOrders.setPayStatus(Constants.ZERO);
        otherOrders.setCode("OD" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now) + orderId);
        otherOrders.setOutTradeNo(outTradeNo);
        otherOrders.setDeleted(Constants.ZERO);
        otherOrders.setCreateTime(now);
        otherOrdersMapper.insert(otherOrders);
        // 5. å”¤èµ·å¾®ä¿¡æ”¯ä»˜
        return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.OVERDUE_FEE);
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void handleOverdueFeePayNotify(String outTradeNo, String wxTradeNo) {
        // 1. æŸ¥æ‰¾é€¾æœŸè´¹ç”¨è®¢å•
        OtherOrders otherOrders = otherOrdersMapper.selectOne(new QueryWrapper<OtherOrders>().lambda()
                .eq(OtherOrders::getOutTradeNo, outTradeNo)
                .eq(OtherOrders::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (otherOrders == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "逾期费用订单不存在: " + outTradeNo);
        }
        // 2. å¹‚等:已支付则跳过
        if (Constants.equalsInteger(otherOrders.getPayStatus(), Constants.ONE)) {
            return;
        }
        Date now = new Date();
        // 3. æ›´æ–°é€¾æœŸè´¹ç”¨è®¢å•状态
        otherOrders.setPayStatus(Constants.ONE);
        otherOrders.setPayTime(now);
        otherOrders.setWxExternalNo(wxTradeNo);
        otherOrders.setUpdateTime(now);
        otherOrdersMapper.updateById(otherOrders);
        // 4. æ›´æ–°å¯„存订单逾期状态为已支付(2),更新总金额,重算三方收益
        if (otherOrders.getOrderId() != null) {
            Orders order = ordersMapper.selectById(otherOrders.getOrderId());
            if (order != null) {
                order.setOverdueStatus(Constants.TWO); // 2=已支付
                // æ€»é‡‘额 = åŽŸé‡‘é¢ + é€¾æœŸè´¹ç”¨
                Long overdueFee = otherOrders.getPayAccount() != null ? otherOrders.getPayAccount() : 0L;
                long newTotal = (order.getTotalAmount() != null ? order.getTotalAmount() : 0L) + overdueFee;
                order.setTotalAmount(newTotal);
                order.setUpdateTime(now);
                ordersMapper.updateById(order);
                // é‡ç®—三方收益
                calculateAndSaveOrderFees(order.getId());
            }
        }
    }
    @Override
    public void deleteMyOrder(Integer orderId, Integer memberId) {
        Orders order = ordersMapper.selectById(orderId);
        if (order == null || !Constants.equalsInteger(order.getDeleted(), Constants.ZERO)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "无权操作此订单");
        }
        // ä»…已完成(7)、已取消(99)、已退款(96)可删除
        int status = Constants.formatIntegerNum(order.getStatus());
        if (status != Constants.OrderStatus.finished.getStatus()
                && status != Constants.OrderStatus.cancelled.getStatus()
                && status != Constants.OrderStatus.closed.getStatus()) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前订单状态不可删除");
        }
        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                .set(Orders::getDeleted, Constants.ONE)
                .set(Orders::getUpdateTime, new Date())
                .eq(Orders::getId, orderId));
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public PayResponse payShopDeposit(Integer shopId) {
        // 1. æŸ¥è¯¢é—¨åº—信息
        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
        if (shopInfo == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "门店不存在");
        }
        // 2. æ ¡éªŒçŠ¶æ€ï¼šå®¡æ‰¹é€šè¿‡(1)才能支付押金
        if (!Constants.equalsInteger(shopInfo.getAuditStatus(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前门店状态不支持支付押金");
        }
        if (shopInfo.getDepositAmount() == null || shopInfo.getDepositAmount() <= 0) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "押金金额异常,无法发起支付");
        }
        // 3. æŸ¥è¯¢ä¼šå‘˜openid
        Member member = memberMapper.selectById(shopInfo.getRegionMemberId());
        if (member == null || StringUtils.isBlank(member.getOpenid())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "用户信息异常,无法发起支付");
        }
        // 4. åˆ›å»ºæŠ¼é‡‘订单
        String outTradeNo = generateOrderTradeNo();
        Date now = new Date();
        OtherOrders otherOrders = new OtherOrders();
        otherOrders.setType(Constants.ZERO); // 0=店铺押金订单
        otherOrders.setMemberId(shopInfo.getRegionMemberId());
        otherOrders.setPayAccount(shopInfo.getDepositAmount());
        otherOrders.setPayStatus(Constants.ZERO);
        otherOrders.setCode("SD" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now) + shopId);
        otherOrders.setOutTradeNo(outTradeNo);
        otherOrders.setDeleted(Constants.ZERO);
        otherOrders.setCreateTime(now);
        otherOrdersMapper.insert(otherOrders);
        // 5. å”¤èµ·å¾®ä¿¡æ”¯ä»˜
        return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.SHOP_DEPOSIT);
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void handleShopDepositPayNotify(String outTradeNo, String wxTradeNo) {
        // 1. æŸ¥æ‰¾æŠ¼é‡‘订单
        OtherOrders otherOrders = otherOrdersMapper.selectOne(new QueryWrapper<OtherOrders>().lambda()
                .eq(OtherOrders::getOutTradeNo, outTradeNo)
                .eq(OtherOrders::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (otherOrders == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "押金订单不存在: " + outTradeNo);
        }
        // 2. å¹‚等:已支付则跳过
        if (Constants.equalsInteger(otherOrders.getPayStatus(), Constants.ONE)) {
            return;
        }
        Date now = new Date();
        // 3. æ›´æ–°æŠ¼é‡‘订单状态
        otherOrders.setPayStatus(Constants.ONE);
        otherOrders.setPayTime(now);
        otherOrders.setWxExternalNo(wxTradeNo);
        otherOrders.setUpdateTime(now);
        otherOrdersMapper.updateById(otherOrders);
        // 4. æŸ¥è¯¢é—¨åº—信息(通过注册会员主键关联)
        ShopInfo shopInfo = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
                .eq(ShopInfo::getRegionMemberId, otherOrders.getMemberId())
                .eq(ShopInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (shopInfo == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "门店不存在");
        }
        // 5. æ›´æ–°é—¨åº—状态:已支付押金
        shopInfo.setAuditStatus(Constants.THREE); // 3=已支付押金
        shopInfo.setPayStatus(Constants.ONE);
        shopInfo.setPayTime(now);
        shopInfo.setWxExternalNo(wxTradeNo);
        shopInfo.setCode(otherOrders.getCode());
        Member member = memberMapper.selectById(otherOrders.getMemberId());
        if (member != null) {
            shopInfo.setPayMemberOpenId(member.getOpenid());
        }
        shopInfo.setUpdateTime(now);
        shopInfoMapper.updateById(shopInfo);
        // 6. æŠ¼é‡‘支付完成后,若城市未开通则自动开通
        if (shopInfo.getAreaId() != null) {
            Areas shopArea = areasBiz.resolveArea(shopInfo.getAreaId());
            if (shopArea != null && shopArea.getParentId() != null) {
                Areas cityArea = areasBiz.resolveArea(shopArea.getParentId());
                if (cityArea != null && !Constants.equalsInteger(cityArea.getStatus(), Constants.ONE)) {
                    cityArea.setStatus(Constants.ONE);
                    cityArea.setEditDate(now);
                    areasService.updateById(cityArea);
                    areasService.cacheData();
                }
            }
        }
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void settleOrders() {
        // 1. è¯»å–结算天数配置
        SystemDictData settlementConfig = systemDictDataBiz.queryByCode(Constants.OPERATION_CONFIG, Constants.OP_SETTLEMENT_DATE);
        if (settlementConfig == null || StringUtils.isBlank(settlementConfig.getCode())) {
            return;
        }
        int days = Integer.parseInt(settlementConfig.getCode());
        // ç»“算截止时间 = å½“前时间 - N天
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_MONTH, -days);
        Date deadline = cal.getTime();
        // 2. æŸ¥è¯¢å·²å®Œæˆçš„待结算订单(完成时间 <= æˆªæ­¢æ—¶é—´ï¼‰
        List<Orders> ordersList = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getStatus, Constants.OrderStatus.finished.getStatus())
                .eq(Orders::getSettlementStatus, Constants.ZERO)
                .le(Orders::getFinishTime, deadline));
        if (ordersList == null || ordersList.isEmpty()) {
            return;
        }
        Date now = new Date();
        for (Orders order : ordersList) {
            // 3. æ›´æ–°è®¢å•结算状态
            ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                    .set(Orders::getSettlementStatus, Constants.ONE)
                    .set(Orders::getSettlementTime, now)
                    .set(Orders::getUpdateTime, now)
                    .eq(Orders::getId, order.getId()));
            // 4. æŸ¥è¯¢å…³è”的待入账 Revenue è®°å½•
            List<Revenue> revenues = revenueMapper.selectList(new QueryWrapper<Revenue>().lambda()
                    .eq(Revenue::getObjId, order.getId())
                    .eq(Revenue::getObjType, Constants.ZERO)
                    .eq(Revenue::getVaildStatus, Constants.ZERO)
                    .eq(Revenue::getDeleted, Constants.ZERO));
            for (Revenue revenue : revenues) {
                Long amount = revenue.getAmount() != null ? revenue.getAmount() : 0L;
                // æ›´æ–° Revenue ä¸ºå·²å…¥è´¦
                revenueMapper.update(new UpdateWrapper<Revenue>().lambda()
                        .set(Revenue::getVaildStatus, Constants.ONE)
                        .set(Revenue::getUpdateTime, now)
                        .eq(Revenue::getId, revenue.getId()));
                // æ ¹æ® memberType æ›´æ–°ä½™é¢
                if (Constants.equalsInteger(revenue.getMemberType(), Constants.ONE)) {
                    // å¸æœºï¼šé€šè¿‡ memberId æŸ¥ DriverInfo,更新 balance / totalBalance
                    DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                            .eq(DriverInfo::getMemberId, revenue.getMemberId())
                            .eq(DriverInfo::getDeleted, Constants.ZERO)
                            .last("limit 1"));
                    if (driver != null) {
                        driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
                                .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amount)
                                .setSql(" TOTAL_BALANCE = IFNULL(TOTAL_BALANCE, 0) + " + amount)
                                .eq(DriverInfo::getId, driver.getId()));
                    }
                } else if (Constants.equalsInteger(revenue.getMemberType(), Constants.TWO)) {
                    // é—¨åº—:通过 memberId æŸ¥ ShopInfo(regionMemberId),更新 balance / totalBalance
                    ShopInfo shop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
                            .eq(ShopInfo::getRegionMemberId, revenue.getMemberId())
                            .eq(ShopInfo::getDeleted, Constants.ZERO)
                            .last("limit 1"));
                    if (shop != null) {
                        shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda()
                                .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amount)
                                .setSql(" TOTAL_BALANCE = IFNULL(TOTAL_BALANCE, 0) + " + amount)
                                .eq(ShopInfo::getId, shop.getId()));
                    }
                }
            }
        }
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void commentOrder(CommentOrderDTO dto, Integer memberId) {
        // 1. æ ¡éªŒè®¢å•
        Orders order = ordersMapper.selectById(dto.getOrderId());
        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
        }
        if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权评价该订单");
        }
        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.finished.getStatus())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不支持评价");
        }
        if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单已评价");
        }
        // 2. å¼‚地寄存订单:取件门店和司机评分校验
        boolean isRemote = Constants.equalsInteger(order.getType(), Constants.ONE);
        if (isRemote) {
            if (dto.getDriverScore() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异地寄存订单必须评价司机");
            }
            if (order.getTakeShopId() != null && dto.getTakeScore() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "请评价取件门店");
            }
        }
        Date now = new Date();
        // 3. æ›´æ–°è®¢å•评价状态
        order.setCommentStatus(Constants.ONE);
        order.setCommentInfo(dto.getContent());
        order.setCommentDepositLevel(dto.getDepositScore());
        order.setCommentTakeLevel(dto.getTakeScore());
        order.setCommentDriverLevel(dto.getDriverScore());
        order.setCommentTime(now);
        order.setUpdateTime(now);
        ordersMapper.updateById(order);
        // 4. åˆ›å»ºè¯„价记录
        // 4.1 å­˜ä»¶é—¨åº—
        OrderComment depositComment = new OrderComment();
        depositComment.setOrderId(order.getId());
        depositComment.setOrderCode(order.getCode());
        depositComment.setMemberId(memberId);
        depositComment.setTargetType(Constants.ONE); // 1=存件门店
        depositComment.setTargetId(order.getDepositShopId());
        depositComment.setScore(dto.getDepositScore());
        depositComment.setContent(dto.getContent());
        depositComment.setDeleted(Constants.ZERO);
        depositComment.setCreateTime(now);
        orderCommentMapper.insert(depositComment);
        // 4.2 å–件门店(异地寄存且有取件门店)
        if (isRemote && order.getTakeShopId() != null && dto.getTakeScore() != null) {
            OrderComment takeComment = new OrderComment();
            takeComment.setOrderId(order.getId());
            takeComment.setOrderCode(order.getCode());
            takeComment.setMemberId(memberId);
            takeComment.setTargetType(Constants.TWO); // 2=取件门店
            takeComment.setTargetId(order.getTakeShopId());
            takeComment.setScore(dto.getTakeScore());
            takeComment.setContent(dto.getContent());
            takeComment.setDeleted(Constants.ZERO);
            takeComment.setCreateTime(now);
            orderCommentMapper.insert(takeComment);
        }
        // 4.3 å¸æœºï¼ˆå¼‚地寄存)
        if (isRemote && order.getAcceptDriver() != null && dto.getDriverScore() != null) {
            OrderComment driverComment = new OrderComment();
            driverComment.setOrderId(order.getId());
            driverComment.setOrderCode(order.getCode());
            driverComment.setMemberId(memberId);
            driverComment.setTargetType(Constants.THREE); // 3=司机
            driverComment.setTargetId(order.getAcceptDriver());
            driverComment.setScore(dto.getDriverScore());
            driverComment.setContent(dto.getContent());
            driverComment.setDeleted(Constants.ZERO);
            driverComment.setCreateTime(now);
            orderCommentMapper.insert(driverComment);
        }
        // 5. æ›´æ–°é—¨åº—/司机平均评分
        updateTargetScore(Constants.ONE, order.getDepositShopId());
        if (isRemote && order.getTakeShopId() != null) {
            updateTargetScore(Constants.TWO, order.getTakeShopId());
        }
        if (isRemote && order.getAcceptDriver() != null) {
            updateTargetScore(Constants.THREE, order.getAcceptDriver());
        }
    }
    /**
     * æ›´æ–°è¯„价对象(门店/司机)的平均评分
     */
    private void updateTargetScore(Integer targetType, Integer targetId) {
        List<OrderComment> comments = orderCommentMapper.selectList(new QueryWrapper<OrderComment>().lambda()
                .eq(OrderComment::getDeleted, Constants.ZERO)
                .eq(OrderComment::getTargetType, targetType)
                .eq(OrderComment::getTargetId, targetId));
        if (comments.isEmpty()) {
            return;
        }
        double avg = comments.stream()
                .mapToInt(OrderComment::getScore)
                .average()
                .orElse(0.0);
        BigDecimal score = BigDecimal.valueOf(avg).setScale(1, BigDecimal.ROUND_HALF_UP);
        Date now = new Date();
        if (Constants.equalsInteger(targetType, Constants.ONE) || Constants.equalsInteger(targetType, Constants.TWO)) {
            ShopInfo shopInfo = shopInfoMapper.selectById(targetId);
            if (shopInfo != null) {
                shopInfo.setScore(score);
                shopInfo.setUpdateTime(now);
                shopInfoMapper.updateById(shopInfo);
            }
        } else if (Constants.equalsInteger(targetType, Constants.THREE)) {
            DriverInfo driverInfo = driverInfoMapper.selectById(targetId);
            if (driverInfo != null) {
                driverInfo.setScore(score);
                driverInfo.setUpdateTime(now);
                driverInfoMapper.updateById(driverInfo);
            }
        }
    }
    /**
     * å”¤èµ·å¾®ä¿¡æ”¯ä»˜ï¼ˆå…¶ä»–订单)
     */
    private PayResponse wxPayForOtherOrder(OtherOrders otherOrders, String openid, Constants.OrdersAttach ordersAttach) {
        try {
            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            request.setBody(ordersAttach.getName());
            request.setAttach(ordersAttach.getKey());
            request.setOutTradeNo(otherOrders.getOutTradeNo());
            long totalFee = otherOrders.getPayAccount() != null ? otherOrders.getPayAccount() : 0L;
            request.setTotalFee((int) totalFee);
            request.setTimeStart(DateUtil.DateToString(new Date(), "yyyyMMddHHmmss"));
            request.setSpbillCreateIp(Constants.getIpAddr());
            request.setOpenid(openid);
            Object response = WxMiniConfig.wxPayService.createOrder(request);
            PayResponse payResponse = new PayResponse();
            payResponse.setResponse(response);
            payResponse.setOrderId(otherOrders.getId());
            return payResponse;
        } catch (WxPayException e) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "支付调起失败:" + e.getMessage());
        }
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void shopVerifyOrder(String verifyCode, Integer shopId, List<String> images, String remark) {
        if (StringUtils.isBlank(verifyCode)) {
@@ -1663,11 +2211,217 @@
            releaseVerifyCode(verifyCode);
            // ä¿å­˜å‡ºåº“图片(obj_type=13 é—¨åº—出库图片,最多3张)
            saveVerifyImages(order.getId(), images, Constants.FileType.STORE_OUT.getKey(), shopId);
            // ç”Ÿæˆæ”¶ç›Šè®°å½•
            calculateAndSaveOrderFees(order.getId());
            generateRevenueRecords(order.getId());
            // è®°å½•订单日志
            saveShopVerifyLog(order, "门店确认取件", "门店【" + shopName + "】确认取件,订单完成", remark, shopId);
        } else {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许核销");
        }
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void confirmStoreOut(Integer orderId, Integer shopId, List<String> images, String remark) {
        // 1. æŸ¥è¯¢è®¢å•
        Orders order = ordersMapper.selectById(orderId);
        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
        }
        // 2. æ ¡éªŒçŠ¶æ€ï¼šå¾…å–ä»¶(5)
        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许出库");
        }
        // 3. æ ¡éªŒé€¾æœŸçŠ¶æ€ï¼š0=未逾期 æˆ– 2=已支付
        if (order.getOverdueStatus() != null && Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单存在逾期未支付费用,请先完成逾期费用支付");
        }
        // 4. æ ¡éªŒç¡®è®¤åˆ°åº—时间不为空
        if (order.getConfirmArriveTime() == null) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单尚未确认到店,无法出库");
        }
        // 5. æ ¡éªŒé—¨åº—与订单关系
        if (Constants.equalsInteger(order.getType(), Constants.ZERO)) {
            // å°±åœ°å¯„存:取件门店即存件门店
            if (!shopId.equals(order.getDepositShopId())) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不属于当前门店");
            }
        } else {
            // å¼‚地寄存:校验取件门店
            if (order.getTakeShopId() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单无取件门店,无法出库");
            }
            if (!shopId.equals(order.getTakeShopId())) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不属于当前门店");
            }
        }
        // 6. æŸ¥è¯¢é—¨åº—名称
        String shopName = "";
        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
        if (shopInfo != null) {
            shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
        }
        // 7. æ›´æ–°è®¢å•状态为已完成
        Date now = new Date();
        order.setStatus(Constants.OrderStatus.finished.getStatus());
        order.setFinishTime(now);
        order.setUpdateTime(now);
        ordersMapper.updateById(order);
        // 8. é‡Šæ”¾æ ¸é”€ç 
        if (StringUtils.isNotBlank(order.getMemberVerifyCode())) {
            releaseVerifyCode(order.getMemberVerifyCode());
        }
        // 9. ä¿å­˜å‡ºåº“图片(obj_type=13 é—¨åº—出库图片,最多3张)
        saveVerifyImages(order.getId(), images, Constants.FileType.STORE_OUT.getKey(), shopId);
        // 10. å¦‚果存在退款金额,先保存退款记录再调用微信退款
        //    é€€æ¬¾è®°å½•在退款调用前落库,避免退款成功但本地异常导致无记录
        if (order.getRefundAmount() != null && order.getRefundAmount() > 0
                && StringUtils.isNotBlank(order.getOutTradeNo())
                && order.getPayAmount() != null && order.getPayAmount() > 0) {
            OrdersRefund refundRecord = new OrdersRefund();
            refundRecord.setOrderId(orderId);
            refundRecord.setType(3); // å‡ºåº“退款
            refundRecord.setCreateTime(now);
            refundRecord.setRefundRemark(remark);
            refundRecord.setDeleted(Constants.ZERO);
            ordersRefundMapper.insert(refundRecord);
            // è°ƒç”¨å¾®ä¿¡é€€æ¬¾ï¼ˆæ”¾åœ¨æœ€åŽï¼Œç¡®ä¿å‰ç½®æ“ä½œå…¨éƒ¨æˆåŠŸï¼‰
            String refundCode = wxMiniUtilService.wxRefund(
                    order.getOutTradeNo(), order.getPayAmount(), order.getRefundAmount());
            // é€€æ¬¾æˆåŠŸåŽå›žå¡«é€€æ¬¾å•å·å’Œæ—¶é—´
            refundRecord.setRefundCode(refundCode);
            refundRecord.setRefundTime(new Date());
            ordersRefundMapper.updateById(refundRecord);
        }
        // 11. ç”Ÿæˆæ”¶ç›Šè®°å½•
        calculateAndSaveOrderFees(orderId);
        generateRevenueRecords(orderId);
        // 12. è®°å½•订单日志
        String logInfo = "门店【" + shopName + "】确认出库,订单完成";
        if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
            logInfo += ",退款" + Constants.getFormatMoney(order.getRefundAmount()) + "元";
        }
        saveShopVerifyLog(order, "门店确认出库", logInfo, remark, shopId);
    }
    @Override
    public void calculateAndSaveOrderFees(Integer orderId) {
        Orders order = ordersMapper.selectById(orderId);
        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
        }
        Long totalAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0L;
        // è´¹çŽ‡ï¼ˆä¸ºç©ºæ—¶é»˜è®¤0)
        BigDecimal depositRate = order.getDepositShopFeeRata() != null ? order.getDepositShopFeeRata() : BigDecimal.ZERO;
        BigDecimal takeRate = order.getTakeShopFeeRata() != null ? order.getTakeShopFeeRata() : BigDecimal.ZERO;
        BigDecimal driverRate = order.getDriverFeeRata() != null ? order.getDriverFeeRata() : BigDecimal.ZERO;
        Long exceptionFeeVal = order.getExceptionFee() != null ? order.getExceptionFee() : 0L;
        //存件门店收益
        Long depositShopFee = new BigDecimal(totalAmount)
                .multiply(depositRate)
                .setScale(0, RoundingMode.HALF_UP)
                .longValue();
        Long takeShopFee = 0L;
        Long driverFee = 0L;
        if (Constants.equalsInteger(order.getType(), Constants.TWO)) {
            // å¼‚地寄存:存件门店 + å¸æœº
            driverFee = new BigDecimal(totalAmount)
                    .multiply(driverRate)
                    .setScale(0, RoundingMode.HALF_UP)
                    .longValue()
                    + exceptionFeeVal;
            // å¼‚地寄存且有取件门店:加上取件门店收益
            if (order.getTakeShopId() != null) {
                takeShopFee = new BigDecimal(totalAmount)
                        .multiply(takeRate)
                        .setScale(0, RoundingMode.HALF_UP)
                        .longValue();
            }
        }
        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                .eq(Orders::getId, orderId)
                .set(Orders::getDepositShopFee, depositShopFee)
                .set(Orders::getTakeShopFee, takeShopFee)
                .set(Orders::getDriverFee, driverFee)
                .set(Orders::getUpdateTime, new Date()));
    }
    /**
     * ç”Ÿæˆé—¨åº—/司机收益记录(未结算)
     * è®¢å•完成时调用,读取订单上已计算好的费用字段
     */
    private void generateRevenueRecords(Integer orderId) {
        Orders order = ordersMapper.selectById(orderId);
        if (order == null) {
            return;
        }
        Date now = new Date();
        Long depositShopFee = order.getDepositShopFee() != null ? order.getDepositShopFee() : 0L;
        Long takeShopFee = order.getTakeShopFee() != null ? order.getTakeShopFee() : 0L;
        Long driverFee = order.getDriverFee() != null ? order.getDriverFee() : 0L;
        // å­˜ä»¶é—¨åº—收益
        if (depositShopFee > 0 && order.getDepositShopId() != null) {
            ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
            if (depositShop != null && depositShop.getRegionMemberId() != null) {
                revenueMapper.insert(buildRevenue(depositShop.getRegionMemberId(), Constants.TWO,
                        depositShopFee, orderId, order.getCode()));
            }
        }
        // å–件门店收益(异地寄存且有取件门店)
        if (takeShopFee > 0 && order.getTakeShopId() != null) {
            ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
            if (takeShop != null && takeShop.getRegionMemberId() != null) {
                revenueMapper.insert(buildRevenue(takeShop.getRegionMemberId(), Constants.TWO,
                        takeShopFee, orderId, order.getCode()));
            }
        }
        // å¸æœºæ”¶ç›Šï¼ˆå¼‚地寄存)
        if (driverFee > 0 && order.getAcceptDriver() != null) {
            DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
            if (driver != null && driver.getMemberId() != null) {
                revenueMapper.insert(buildRevenue(driver.getMemberId(), Constants.ONE,
                        driverFee, orderId, order.getCode()));
            }
        }
    }
    /**
     * æž„建收益记录
     */
    private Revenue buildRevenue(Integer memberId, Integer memberType, Long amount, Integer orderId, String orderNo) {
        Revenue revenue = new Revenue();
        revenue.setMemberId(memberId);
        revenue.setMemberType(memberType); // 1=司机, 2=门店
        revenue.setType(Constants.ZERO); // 0=完成订单
        revenue.setOptType(Constants.ONE); // 1=收入
        revenue.setAmount(amount);
        revenue.setVaildStatus(Constants.ZERO); // 0=入账中(未结算)
        revenue.setObjId(orderId);
        revenue.setObjType(Constants.ZERO); // 0=订单业务
        revenue.setStatus(Constants.ZERO); // 0=成功
        revenue.setOrderNo(orderNo);
        revenue.setDeleted(Constants.ZERO);
        revenue.setCreateTime(new Date());
        return revenue;
    }
    @Override
@@ -1799,23 +2553,48 @@
                            + "天,逾期费用" + Constants.getFormatMoney(overdueInfo.getOverdueFee()) + "元",
                    null, shopId);
        } else {
            // æœªé€¾æœŸï¼šå®Œæˆè®¢å•
            order.setStatus(Constants.OrderStatus.finished.getStatus());
            // æœªé€¾æœŸï¼šæ ‡è®°é€¾æœŸçŠ¶æ€ä¸º0,订单保持当前状态
            order.setConfirmArriveTime(now);
            order.setFinishTime(now);
            order.setOverdueStatus(Constants.ZERO);
            // å°±åœ°å¯„存:计算是否需要退款
            if (Constants.equalsInteger(order.getType(), Constants.ZERO) && !CollectionUtils.isEmpty(details)) {
                int actualDays = calcActualDepositDays(now, order.getDepositTime());
                order.setDepositDays(actualDays);
                int estimatedDays = order.getEstimatedDepositDays() != null ? order.getEstimatedDepositDays() : 1;
                int refundDays = estimatedDays - actualDays;
                if (refundDays > 0) {
                    // é€€æ¬¾é‡‘额 = é€€æ¬¾å¤©æ•° Ã— Î£(物品单价 Ã— æ•°é‡)
                    long dailyBaseFee = 0L;
                    for (OrdersDetail d : details) {
                        dailyBaseFee += (d.getUnitPrice() != null ? d.getUnitPrice() : 0L)
                                * (d.getNum() != null ? d.getNum() : 0);
                    }
                    long refundAmount = (long) refundDays * dailyBaseFee;
                    order.setRefundAmount(refundAmount);
                }
            }
            order.setUpdateTime(now);
            ordersMapper.updateById(order);
            // é‡Šæ”¾æ ¸é”€ç 
            if (StringUtils.isNotBlank(order.getMemberVerifyCode())) {
                releaseVerifyCode(order.getMemberVerifyCode());
            // é€€æ¬¾å¯¼è‡´æ€»é‡‘额变化,重算三方收益
            if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
                long newTotal = (order.getTotalAmount() != null ? order.getTotalAmount() : 0L) - order.getRefundAmount();
                order.setTotalAmount(newTotal);
                ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                        .eq(Orders::getId, orderId)
                        .set(Orders::getTotalAmount, newTotal));
                calculateAndSaveOrderFees(orderId);
            }
            // è®°å½•订单日志
            saveShopVerifyLog(order, "确认顾客到店",
                    "门店【" + shopName + "】确认顾客到店,订单完成",
                    null, shopId);
            String logInfo = "门店【" + shopName + "】确认顾客到店,未逾期";
            if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
                logInfo += ",需退款" + Constants.getFormatMoney(order.getRefundAmount()) + "元";
            }
            saveShopVerifyLog(order, "确认顾客到店", logInfo, null, shopId);
        }
    }
@@ -1972,6 +2751,32 @@
    }
    /**
     * è®¡ç®—实际寄存天数(depositTime åˆ° now çš„天数差,最少1天)
     */
    private int calcActualDepositDays(Date now, Date depositTime) {
        if (depositTime == null || now == null) {
            return 1;
        }
        Calendar depositCal = Calendar.getInstance();
        depositCal.setTime(depositTime);
        depositCal.set(Calendar.HOUR_OF_DAY, 0);
        depositCal.set(Calendar.MINUTE, 0);
        depositCal.set(Calendar.SECOND, 0);
        depositCal.set(Calendar.MILLISECOND, 0);
        Calendar nowCal = Calendar.getInstance();
        nowCal.setTime(now);
        nowCal.set(Calendar.HOUR_OF_DAY, 0);
        nowCal.set(Calendar.MINUTE, 0);
        nowCal.set(Calendar.SECOND, 0);
        nowCal.set(Calendar.MILLISECOND, 0);
        long diffMs = nowCal.getTimeInMillis() - depositCal.getTimeInMillis();
        int days = (int) (diffMs / (1000 * 60 * 60 * 24));
        return Math.max(days, 1);
    }
    /**
     * å°±åœ°å¯„存逾期天数计算
     * è¿‡äº†é¢„计取件时间当天的12点后才算一天
     */
server/services/src/main/java/com/doumee/service/business/impl/OtherOrdersServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,131 @@
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.exception.BusinessException;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.OtherOrdersMapper;
import com.doumee.dao.business.model.OtherOrders;
import com.doumee.service.business.OtherOrdersService;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
 * å…¶ä»–订单记录Service实现
 * @author rk
 * @date 2026/04/16
 */
@Service
public class OtherOrdersServiceImpl implements OtherOrdersService {
    @Autowired
    private OtherOrdersMapper otherOrdersMapper;
    @Override
    public Integer create(OtherOrders otherOrders) {
        otherOrdersMapper.insert(otherOrders);
        return otherOrders.getId();
    }
    @Override
    public void deleteById(Integer id) {
        otherOrdersMapper.update(new UpdateWrapper<OtherOrders>().lambda()
                .set(OtherOrders::getDeleted, Constants.ONE)
                .eq(OtherOrders::getId, id));
    }
    @Override
    public void delete(OtherOrders otherOrders) {
        UpdateWrapper<OtherOrders> deleteWrapper = new UpdateWrapper<>(otherOrders);
        otherOrdersMapper.delete(deleteWrapper);
    }
    @Override
    public void deleteByIdInBatch(List<Integer> ids) {
        if (ids == null || ids.isEmpty()) {
            return;
        }
        otherOrdersMapper.deleteBatchIds(ids);
    }
    @Override
    public void updateById(OtherOrders otherOrders) {
        otherOrdersMapper.updateById(otherOrders);
    }
    @Override
    public void updateByIdInBatch(List<OtherOrders> otherOrdersList) {
        if (otherOrdersList == null || otherOrdersList.isEmpty()) {
            return;
        }
        for (OtherOrders otherOrders : otherOrdersList) {
            this.updateById(otherOrders);
        }
    }
    @Override
    public OtherOrders findById(Integer id) {
        OtherOrders otherOrders = otherOrdersMapper.selectById(id);
        if (Objects.isNull(otherOrders)) {
            throw new BusinessException(com.doumee.core.constants.ResponseStatus.DATA_EMPTY);
        }
        return otherOrders;
    }
    @Override
    public OtherOrders findOne(OtherOrders otherOrders) {
        QueryWrapper<OtherOrders> wrapper = new QueryWrapper<>(otherOrders);
        return otherOrdersMapper.selectOne(wrapper);
    }
    @Override
    public List<OtherOrders> findList(OtherOrders otherOrders) {
        QueryWrapper<OtherOrders> wrapper = new QueryWrapper<>(otherOrders);
        return otherOrdersMapper.selectList(wrapper);
    }
    @Override
    public PageData<OtherOrders> findPage(PageWrap<OtherOrders> pageWrap) {
        IPage<OtherOrders> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        MPJLambdaWrapper<OtherOrders> queryWrapper = new MPJLambdaWrapper<OtherOrders>()
                .selectAll(OtherOrders.class);
        Utils.MP.blankToNull(pageWrap.getModel());
        pageWrap.getModel().setDeleted(Constants.ZERO);
        OtherOrders model = pageWrap.getModel();
        if (model.getType() != null) {
            queryWrapper.eq(OtherOrders::getType, model.getType());
        }
        if (model.getMemberId() != null) {
            queryWrapper.eq(OtherOrders::getMemberId, model.getMemberId());
        }
        if (model.getPayStatus() != null) {
            queryWrapper.eq(OtherOrders::getPayStatus, model.getPayStatus());
        }
        if (model.getOrderId() != null) {
            queryWrapper.eq(OtherOrders::getOrderId, model.getOrderId());
        }
        for (PageWrap.SortData sortData : pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
                queryWrapper.orderByDesc(sortData.getProperty());
            } else {
                queryWrapper.orderByAsc(sortData.getProperty());
            }
        }
        return PageData.from(otherOrdersMapper.selectJoinPage(page, OtherOrders.class, queryWrapper));
    }
    @Override
    public long count(OtherOrders otherOrders) {
        QueryWrapper<OtherOrders> wrapper = new QueryWrapper<>(otherOrders);
        return otherOrdersMapper.selectCount(wrapper);
    }
}
server/services/src/main/java/com/doumee/service/business/impl/RevenueServiceImpl.java
@@ -11,13 +11,21 @@
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.RevenueMapper;
import com.doumee.dao.business.WithdrawalOrdersMapper;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.model.Revenue;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.WithdrawalOrders;
import com.doumee.dao.vo.RevenueStatisticsVO;
import com.doumee.service.business.RevenueService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
@@ -30,6 +38,15 @@
    @Autowired
    private RevenueMapper revenueMapper;
    @Autowired
    private WithdrawalOrdersMapper withdrawalOrdersMapper;
    @Autowired
    private ShopInfoMapper shopInfoMapper;
    @Autowired
    private MemberMapper memberMapper;
    @Override
    public Integer create(Revenue revenue) {
@@ -148,4 +165,74 @@
        return revenueMapper.selectCount(wrapper);
    }
    @Override
    public RevenueStatisticsVO getShopRevenueStatistics(Integer shopId) {
        ShopInfo shop = shopInfoMapper.selectById(shopId);
        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());
    }
    @Override
    public RevenueStatisticsVO getDriverRevenueStatistics(Integer memberId) {
        Member member = memberMapper.selectById(memberId);
        if (member == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "用户不存在");
        }
        return buildRevenueStatistics(memberId, Constants.ONE, member.getAmount());
    }
    /**
     * æž„建收益统计
     *
     * @param memberId   ä¼šå‘˜ä¸»é”®
     * @param memberType ä¼šå‘˜ç±»åž‹ï¼š1=司机;2=门店
     * @param balance    å½“前余额(分)
     */
    private RevenueStatisticsVO buildRevenueStatistics(Integer memberId, Integer memberType, Long balance) {
        RevenueStatisticsVO vo = new RevenueStatisticsVO();
        vo.setBalance(balance != null ? balance : 0L);
        // å¾…结算金额:revenue中 vaildStatus=0 ä¸” type=0(完成订单)的收入之和
        QueryWrapper<Revenue> pendingWrapper = new QueryWrapper<>();
        pendingWrapper.select("IFNULL(SUM(AMOUNT), 0) as amount")
                .eq("MEMBER_ID", memberId)
                .eq("MEMBER_TYPE", memberType)
                .eq("VAILD_STATUS", Constants.ZERO)
                .eq("TYPE", Constants.ZERO)
                .eq("OPT_TYPE", Constants.ONE)
                .eq("DELETED", Constants.ZERO);
        Map<String, Object> pendingResult = revenueMapper.selectMaps(pendingWrapper).stream().findFirst().orElse(null);
        vo.setPendingAmount(pendingResult != null && pendingResult.get("amount") != null
                ? Long.parseLong(pendingResult.get("amount").toString()) : 0L);
        // ç´¯è®¡æçŽ°é‡‘é¢ï¼šwithdrawal_orders中 status=1(提现成功)且 type=0(提现)
        QueryWrapper<WithdrawalOrders> successWrapper = new QueryWrapper<>();
        successWrapper.select("IFNULL(SUM(AMOUNT), 0) as amount")
                .eq("MEMBER_ID", memberId)
                .eq("MEMBER_TYPE", memberType)
                .eq("STATUS", Constants.ONE)
                .eq("TYPE", Constants.ZERO)
                .eq("DELETED", Constants.ZERO);
        Map<String, Object> successResult = withdrawalOrdersMapper.selectMaps(successWrapper).stream().findFirst().orElse(null);
        vo.setTotalWithdrawn(successResult != null && successResult.get("amount") != null
                ? Long.parseLong(successResult.get("amount").toString()) : 0L);
        // æçŽ°ä¸­é‡‘é¢ï¼šwithdrawal_orders中 status=0(提现申请中)且 type=0(提现)
        QueryWrapper<WithdrawalOrders> progressWrapper = new QueryWrapper<>();
        progressWrapper.select("IFNULL(SUM(AMOUNT), 0) as amount")
                .eq("MEMBER_ID", memberId)
                .eq("MEMBER_TYPE", memberType)
                .eq("STATUS", Constants.ZERO)
                .eq("TYPE", Constants.ZERO)
                .eq("DELETED", Constants.ZERO);
        Map<String, Object> progressResult = withdrawalOrdersMapper.selectMaps(progressWrapper).stream().findFirst().orElse(null);
        vo.setWithdrawingAmount(progressResult != null && progressResult.get("amount") != null
                ? Long.parseLong(progressResult.get("amount").toString()) : 0L);
        return vo;
    }
}
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -16,18 +16,24 @@
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.MultifileMapper;
import com.doumee.dao.business.OrdersMapper;
import com.doumee.dao.business.PricingRuleMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.model.Areas;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.Multifile;
import com.doumee.dao.business.model.Orders;
import com.doumee.dao.business.model.PricingRule;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.dto.*;
import com.doumee.dao.system.SystemUserMapper;
import com.doumee.dao.system.model.SystemUser;
import com.doumee.dao.vo.ShopDetailVO;
import com.doumee.dao.vo.ShopCenterVO;
import com.doumee.dao.vo.ShopLoginVO;
import com.doumee.dao.vo.ShopNearbyVO;
import com.doumee.dao.vo.ShopWebDetailVO;
import com.doumee.service.business.AreasService;
import com.doumee.service.business.ShopInfoService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -68,6 +74,15 @@
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private PricingRuleMapper pricingRuleMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private AreasService areasService;
    @Override
    public Integer create(ShopInfo shopInfo) {
        shopInfoMapper.insert(shopInfo);
@@ -299,6 +314,7 @@
            existing.setLegalPersonCard(request.getLegalPersonCard());
            existing.setPassword(encryptedPassword);
            existing.setSalt(salt);
            existing.setAliAccount(request.getAliAccount());
            existing.setAuditStatus(Constants.ZERO);
            existing.setUpdateTime(now);
            existing.setUpdateUser(memberId);
@@ -328,6 +344,7 @@
            shopInfo.setLegalPersonCard(request.getLegalPersonCard());
            shopInfo.setPassword(encryptedPassword);
            shopInfo.setSalt(salt);
            shopInfo.setAliAccount(request.getAliAccount());
            shopInfo.setOpenid(member.getOpenid());
            shopInfo.setAuditStatus(Constants.ZERO);
            shopInfo.setStatus(Constants.ZERO);
@@ -407,6 +424,38 @@
        shopInfo.setAuditRemark(auditDTO.getAuditRemark());
        shopInfo.setAuditUserId(auditDTO.getAuditUser());
        shopInfo.setUpdateTime(now);
        // å®¡æ‰¹é€šè¿‡æ—¶ï¼Œæ ¡éªŒåŸŽå¸‚pricing_rule配置,读取押金金额
        if (Constants.equalsInteger(newAuditStatus, Constants.ONE)) {
            // 1. è§£æžé—¨åº—所在城市
            Areas area = areasBiz.resolveArea(shopInfo.getAreaId());
            if (area == null || area.getParentId() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "门店区划信息异常,无法确定所在城市");
            }
            Integer cityId = area.getParentId();
            // 2. æ ¡éªŒ pricing_rule é…ç½®ï¼ˆåŸŽå¸‚开通在押金支付完成后处理)
            Areas cityArea = areasService.findById(cityId, Constants.ONE);
            if (cityArea == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "城市信息不存在");
            }
            if (!Constants.equalsInteger(cityArea.getStatus(), Constants.ONE)) {
                List<String> errors = validateCityPricingRules(cityId);
                if (!errors.isEmpty()) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),
                            "城市[" + cityArea.getName() + "]尚未开通,定价规则未配置完整:" + String.join(";", errors));
                }
            }
            // 3. ä»ŽPricingRule读取押金金额
            PricingRule pricingRule = pricingRuleMapper.selectOne(new QueryWrapper<PricingRule>().lambda()
                    .eq(PricingRule::getDeleted, Constants.ZERO)
                    .eq(PricingRule::getType, Constants.THREE)
                    .eq(PricingRule::getFieldA, String.valueOf(shopInfo.getCompanyType()))
                    .last("limit 1"));
            if (pricingRule != null && StringUtils.isNotBlank(pricingRule.getFieldB())) {
                shopInfo.setDepositAmount(Long.parseLong(pricingRule.getFieldB()));
            }
        }
        shopInfoMapper.updateById(shopInfo);
    }
@@ -612,7 +661,7 @@
        // æ‹¼æŽ¥å›¾ç‰‡å‰ç¼€
        String imgPrefix = "";
        try {
            imgPrefix = systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
            imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.SHOP_FILES).getCode();
        } catch (Exception e) {
            // æœªé…ç½®æ—¶å¿½ç•¥
@@ -670,7 +719,7 @@
            if (payMember != null && StringUtils.isNotBlank(payMember.getCoverImage())) {
                String memberPrefix = "";
                try {
                    memberPrefix = systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
                    memberPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                            + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.MEMBER_FILES).getCode();
                } catch (Exception e) {
                    // æœªé…ç½®æ—¶å¿½ç•¥
@@ -861,7 +910,7 @@
     */
    private String getShopPrefix() {
        try {
            return systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
            return systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.SHOP_FILES).getCode();
        } catch (Exception e) {
            return "";
@@ -903,7 +952,37 @@
        return urls;
    }
    @Override
    public ShopCenterVO getShopCenterInfo(Integer shopId) {
        ShopInfo shop = shopInfoMapper.selectById(shopId);
        if (shop == null || Constants.equalsInteger(shop.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        ShopCenterVO vo = new ShopCenterVO();
        vo.setShopName(shop.getName());
        vo.setLinkName(shop.getLinkName());
        vo.setCompanyType(shop.getCompanyType());
        vo.setCoverImg(shop.getCoverImg());
        if (StringUtils.isNotBlank(shop.getCoverImg())) {
            String path = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.SHOP_FILES).getCode();
            vo.setFullCoverImg(path + shop.getCoverImg());
        }
        vo.setHasMessage(false);
        // å¾…核验订单数量(存件门店,status=1)
        Long waitDepositCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDepositShopId, shopId)
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getStatus, Constants.OrderStatus.waitDeposit.getStatus()));
        vo.setWaitDepositCount(waitDepositCount.intValue());
        // å¾…收货订单数量(取件门店,status IN 4,5)
        Long waitReceiveCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getTakeShopId, shopId)
                .eq(Orders::getDeleted, Constants.ZERO)
                .in(Orders::getStatus, Constants.OrderStatus.delivering.getStatus(), Constants.OrderStatus.arrived.getStatus()));
        vo.setWaitReceiveCount(waitReceiveCount.intValue());
        return vo;
    }
    /**
     * å•†æˆ·è´¦å·å¯†ç ç™»å½•
@@ -993,4 +1072,91 @@
        return vo;
    }
    /**
     * æ ¡éªŒåŸŽå¸‚定价规则配置是否完整
     * @param cityId åŸŽå¸‚主键
     * @return é”™è¯¯ä¿¡æ¯åˆ—表,空表示校验通过
     */
    private List<String> validateCityPricingRules(Integer cityId) {
        List<String> errors = new ArrayList<>();
        // type=0 å°±åœ°å­˜å–规则:至少1条,fieldB不为空
        List<PricingRule> type0 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.ZERO)
                .eq(PricingRule::getCityId, cityId));
        if (type0.isEmpty()) {
            errors.add("缺少就地存取规则");
        } else if (type0.stream().allMatch(r -> StringUtils.isBlank(r.getFieldB()))) {
            errors.add("就地存取规则未配置收费单价");
        }
        // type=1 å¼‚地寄送规则:至少1条,fieldB/C/D/E不为空
        List<PricingRule> type1 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.ONE)
                .eq(PricingRule::getCityId, cityId));
        if (type1.isEmpty()) {
            errors.add("缺少异地寄送规则");
        } else if (type1.stream().allMatch(r -> StringUtils.isAnyBlank(r.getFieldB(), r.getFieldC(), r.getFieldD(), r.getFieldE()))) {
            errors.add("异地寄送规则配置不完整");
        }
        // type=2 é¢„计时效:fieldA=1(标速达) å’Œ fieldA=2(极速达) å„1条
        List<PricingRule> type2 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.TWO)
                .eq(PricingRule::getCityId, cityId));
        Map<String, PricingRule> type2Map = type2.stream()
                .collect(Collectors.toMap(PricingRule::getFieldA, r -> r, (a, b) -> a));
        if (!type2Map.containsKey("1")) {
            errors.add("缺少预计时效-标速达配置");
        } else if (StringUtils.isAnyBlank(type2Map.get("1").getFieldB(), type2Map.get("1").getFieldC(),
                type2Map.get("1").getFieldD(), type2Map.get("1").getFieldE())) {
            errors.add("预计时效-标速达配置不完整");
        }
        if (!type2Map.containsKey("2")) {
            errors.add("缺少预计时效-极速达配置");
        } else if (StringUtils.isAnyBlank(type2Map.get("2").getFieldB(), type2Map.get("2").getFieldC(),
                type2Map.get("2").getFieldD(), type2Map.get("2").getFieldE())) {
            errors.add("预计时效-极速达配置不完整");
        }
        // type=3 é—¨åº—注册押金:fieldA=0(企业) å’Œ fieldA=1(个人) å„1条,fieldB不为空
        List<PricingRule> type3 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.THREE)
                .eq(PricingRule::getCityId, cityId));
        Map<String, PricingRule> type3Map = type3.stream()
                .collect(Collectors.toMap(PricingRule::getFieldA, r -> r, (a, b) -> a));
        String[] depositNames = {"企业", "个人"};
        for (int i = 0; i <= 1; i++) {
            String key = String.valueOf(i);
            if (!type3Map.containsKey(key)) {
                errors.add("缺少门店注册押金-" + depositNames[i] + "配置");
            } else if (StringUtils.isBlank(type3Map.get(key).getFieldB())) {
                errors.add("门店注册押金-" + depositNames[i] + "未配置押金金额");
            }
        }
        // type=4 åˆ†æˆæ¯”例:fieldA=0~4 å…±5条,fieldB不为空
        List<PricingRule> type4 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.FOUR)
                .eq(PricingRule::getCityId, cityId));
        String[] shareNames = {"企业寄", "个人寄", "企业取", "个人取", "配送员"};
        Map<String, PricingRule> type4Map = type4.stream()
                .collect(Collectors.toMap(PricingRule::getFieldA, r -> r, (a, b) -> a));
        for (int i = 0; i <= 4; i++) {
            String key = String.valueOf(i);
            if (!type4Map.containsKey(key)) {
                errors.add("缺少分成比例-" + shareNames[i] + "配置");
            } else if (StringUtils.isBlank(type4Map.get(key).getFieldB())) {
                errors.add("分成比例-" + shareNames[i] + "未配置分成比例");
            }
        }
        return errors;
    }
}
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java
@@ -11,12 +11,15 @@
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.DriverInfoMapper;
import com.doumee.dao.business.RevenueMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.WithdrawalOrdersMapper;
import com.doumee.dao.business.model.DriverInfo;
import com.doumee.dao.business.model.Revenue;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.business.model.WithdrawalOrders;
import com.doumee.dao.dto.WithdrawalApproveDTO;
import com.doumee.dao.dto.WithdrawalDTO;
import com.doumee.dao.system.SystemUserMapper;
import com.doumee.dao.system.model.SystemUser;
import com.doumee.service.business.WithdrawalOrdersService;
@@ -25,10 +28,13 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
/**
 * æçŽ°ç”³è¯·è®°å½•Service实现
@@ -49,6 +55,9 @@
    @Autowired
    private DriverInfoMapper driverInfoMapper;
    @Autowired
    private RevenueMapper revenueMapper;
    @Override
    public Integer create(WithdrawalOrders withdrawalOrders) {
@@ -238,15 +247,60 @@
        // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·
        Integer currentUserId = getCurrentUserId();
        Date now = new Date();
        WithdrawalOrders update = new WithdrawalOrders();
        update.setId(dto.getId());
        update.setStatus(dto.getStatus());
        update.setUserId(currentUserId);
        update.setApproveTime(new Date());
        update.setApproveTime(now);
        update.setApproveRemark(dto.getApproveRemark());
        update.setUpdateTime(new Date());
        update.setUpdateTime(now);
        withdrawalOrdersMapper.updateById(update);
        // é©³å›žæ—¶é€€å›žä½™é¢
        if (Constants.TWO.equals(dto.getStatus())) {
            Long amountFen = order.getAmount() != null ? order.getAmount() : 0L;
            if (amountFen > 0 && order.getMemberId() != null) {
                if (Constants.equalsInteger(order.getMemberType(), Constants.ONE)) {
                    // å¸æœºï¼šé€šè¿‡ memberId æŸ¥ DriverInfo,退回 balance
                    DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                            .eq(DriverInfo::getMemberId, order.getMemberId())
                            .eq(DriverInfo::getDeleted, Constants.ZERO)
                            .last("limit 1"));
                    if (driver != null) {
                        driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
                                .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amountFen)
                                .eq(DriverInfo::getId, driver.getId()));
                    }
                } else if (Constants.equalsInteger(order.getMemberType(), Constants.TWO)) {
                    // é—¨åº—:通过 memberId æŸ¥ ShopInfo,退回 balance
                    ShopInfo shop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
                            .eq(ShopInfo::getRegionMemberId, order.getMemberId())
                            .eq(ShopInfo::getDeleted, Constants.ZERO)
                            .last("limit 1"));
                    if (shop != null) {
                        shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda()
                                .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amountFen)
                                .eq(ShopInfo::getId, shop.getId()));
                    }
                }
                // åˆ›å»ºé€€å›ž Revenue è®°å½•
                Revenue revenue = new Revenue();
                revenue.setMemberId(order.getMemberId());
                revenue.setMemberType(order.getMemberType());
                revenue.setType(Constants.TWO); // 2=提现退回
                revenue.setOptType(Constants.ONE); // 1=收入
                revenue.setAmount(amountFen);
                revenue.setVaildStatus(Constants.ONE); // å·²å…¥è´¦
                revenue.setObjId(order.getId());
                revenue.setObjType(Constants.ONE); // 1=提现业务
                revenue.setStatus(Constants.ZERO);
                revenue.setDeleted(Constants.ZERO);
                revenue.setCreateTime(now);
                revenueMapper.insert(revenue);
            }
        }
    }
    private Integer getCurrentUserId() {
@@ -289,4 +343,113 @@
        }
    }
    @Override
    public void applyDriverWithdrawal(WithdrawalDTO dto, Integer memberId) {
        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                .eq(DriverInfo::getMemberId, memberId)
                .eq(DriverInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (driver == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在");
        }
        long amountFen = dto.getAmount().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
        long balance = driver.getBalance() != null ? driver.getBalance() : 0L;
        if (amountFen <= 0) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "提现金额必须大于0");
        }
        if (amountFen > balance) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "提现金额不能超过可用余额");
        }
        String billNo = generateBillNo();
        Date now = new Date();
        // æ‰£å‡å¸æœºä½™é¢
        driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
                .setSql(" BALANCE = IFNULL(BALANCE, 0) - " + amountFen)
                .eq(DriverInfo::getId, driver.getId()));
        // åˆ›å»ºæçŽ°è®°å½•
        WithdrawalOrders order = new WithdrawalOrders();
        order.setMemberId(memberId);
        order.setMemberType(Constants.ONE);
        order.setAmount(amountFen);
        order.setStatus(Constants.ZERO);
        order.setType(Constants.ZERO);
        order.setOutBillNo(billNo);
        order.setAliAccount(dto.getAliAccount());
        order.setDeleted(Constants.ZERO);
        order.setCreateTime(now);
        order.setUpdateTime(now);
        order.setCreateUser(memberId);
        withdrawalOrdersMapper.insert(order);
        // åˆ›å»ºæ”¯å‡º Revenue è®°å½•
        Revenue revenue = new Revenue();
        revenue.setMemberId(memberId);
        revenue.setMemberType(Constants.ONE);
        revenue.setType(Constants.ONE);
        revenue.setOptType(-Constants.ONE);
        revenue.setAmount(amountFen);
        revenue.setVaildStatus(Constants.ONE);
        revenue.setObjId(order.getId());
        revenue.setObjType(Constants.ONE);
        revenue.setStatus(Constants.ZERO);
        revenue.setDeleted(Constants.ZERO);
        revenue.setCreateTime(now);
        revenueMapper.insert(revenue);
    }
    @Override
    public void applyShopWithdrawal(WithdrawalDTO dto, Integer shopId) {
        ShopInfo shop = shopInfoMapper.selectById(shopId);
        if (shop == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        long amountFen = dto.getAmount().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
        long balance = shop.getBalance() != null ? shop.getBalance() : 0L;
        if (amountFen <= 0) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "提现金额必须大于0");
        }
        if (amountFen > balance) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "提现金额不能超过可用余额");
        }
        String billNo = generateBillNo();
        Date now = new Date();
        // æ‰£å‡é—¨åº—余额
        shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda()
                .setSql(" BALANCE = IFNULL(BALANCE, 0) - " + amountFen)
                .eq(ShopInfo::getId, shopId));
        // åˆ›å»ºæçŽ°è®°å½•
        WithdrawalOrders order = new WithdrawalOrders();
        order.setMemberId(shop.getRegionMemberId());
        order.setMemberType(Constants.TWO);
        order.setAmount(amountFen);
        order.setStatus(Constants.ZERO);
        order.setType(Constants.ZERO);
        order.setOutBillNo(billNo);
        order.setAliAccount(dto.getAliAccount());
        order.setDeleted(Constants.ZERO);
        order.setCreateTime(now);
        order.setUpdateTime(now);
        order.setCreateUser(shop.getRegionMemberId());
        withdrawalOrdersMapper.insert(order);
        // åˆ›å»ºæ”¯å‡º Revenue è®°å½•
        Revenue revenue = new Revenue();
        revenue.setMemberId(shop.getRegionMemberId());
        revenue.setMemberType(Constants.TWO);
        revenue.setType(Constants.ONE); // 1=提现支出
        revenue.setOptType(-Constants.ONE); // -1=支出
        revenue.setAmount(amountFen);
        revenue.setVaildStatus(Constants.ONE); // å·²å…¥è´¦
        revenue.setObjId(order.getId());
        revenue.setObjType(Constants.ONE); // 1=提现业务
        revenue.setStatus(Constants.ZERO);
        revenue.setDeleted(Constants.ZERO);
        revenue.setCreateTime(now);
        revenueMapper.insert(revenue);
    }
    private String generateBillNo() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String random = String.valueOf(ThreadLocalRandom.current().nextInt(100000, 999999));
        return "TX" + sdf.format(new Date()) + random;
    }
}
server/web/src/main/java/com/doumee/api/web/AccountApi.java
@@ -61,6 +61,7 @@
        return  ApiResponse.success("操作成功",memberService.wxAuthPhone(wxPhoneRequest));
    }
    @ApiOperation(value = "门店账号密码登录", notes = "小程序端,门店用户通过手机号+密码登录")
    @PostMapping("/shopLogin")
    public ApiResponse<ShopLoginVO> shopLogin(@RequestBody @Validated ShopLoginDTO dto) {
server/web/src/main/java/com/doumee/api/web/ConfigApi.java
@@ -10,7 +10,7 @@
import com.doumee.dao.dto.CalculateRemotePriceDTO;
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.PriceCalculateVO;
import com.doumee.dao.vo.UserCenterVO;
import com.doumee.dao.vo.PlatformAboutVO;
import com.doumee.service.business.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@@ -89,12 +89,9 @@
    @ApiOperation(value = "获取系统配置", notes = "小程序端")
    @GetMapping("/getPlatformAboutUs")
    public ApiResponse<UserCenterVO> getPlatformAboutUs() {
    public ApiResponse<PlatformAboutVO> getPlatformAboutUs() {
        return  ApiResponse.success("查询成功",memberService.getPlatformAboutUs());
    }
    @LoginRequired
    @ApiOperation(value = "计算保价费用", notes = "根据报价金额计算保价费用")
server/web/src/main/java/com/doumee/api/web/MemberApi.java
@@ -42,7 +42,7 @@
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse<Member> getMemberInfo() {
    public ApiResponse<UserCenterVO> getMemberInfo() {
        return  ApiResponse.success("查询成功",memberService.getMemberInfo(getMemberId()));
    }
server/web/src/main/java/com/doumee/api/web/OrdersApi.java
@@ -7,9 +7,12 @@
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.dao.dto.CancelOrderDTO;
import com.doumee.dao.dto.CommentOrderDTO;
import com.doumee.dao.dto.ConfirmArriveDTO;
import com.doumee.dao.dto.CreateOrderDTO;
import com.doumee.dao.dto.DriverVerifyDTO;
import com.doumee.dao.dto.ShopVerifyDTO;
import com.doumee.dao.dto.StoreOutDTO;
import com.doumee.dao.dto.MyOrderDTO;
import com.doumee.dao.vo.MyOrderDetailVO;
import com.doumee.dao.vo.MyOrderVO;
@@ -77,6 +80,16 @@
        return ApiResponse.success("查询成功", ordersService.findMyOrderPage(pageWrap, getMemberId()));
    }
    @LoginShopRequired
    @ApiOperation(value = "门店订单分页", notes = "门店端,查询存件/取件门店为当前门店的订单")
    @PostMapping("/shopPage")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true)
    })
    public ApiResponse<PageData<MyOrderVO>> shopPage(@RequestBody @Validated PageWrap<MyOrderDTO> pageWrap) {
        return ApiResponse.success("查询成功", ordersService.findShopOrderPage(pageWrap, getShopId()));
    }
    @LoginRequired
    @ApiOperation(value = "会员订单详情", notes = "小程序端,查询当前会员的订单详情")
    @GetMapping("/detail/{orderId}")
@@ -108,6 +121,29 @@
    })
    public ApiResponse<OverdueFeeVO> overdueFee(@PathVariable Integer orderId) {
        return ApiResponse.success("查询成功", ordersService.calculateOverdueFee(orderId));
    }
    @LoginRequired
    @ApiOperation(value = "逾期费用支付", notes = "逾期订单唤起微信支付")
    @PostMapping("/payOverdueFee/{orderId}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "orderId", value = "订单主键", required = true),
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<PayResponse> payOverdueFee(@PathVariable Integer orderId) {
        return ApiResponse.success("操作成功", ordersService.payOverdueFee(orderId, getMemberId()));
    }
    @LoginRequired
    @ApiOperation(value = "会员删除订单", notes = "仅已完成/已取消/已退款订单可删除,逻辑删除")
    @PostMapping("/delete/{orderId}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "orderId", value = "订单主键", required = true),
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse deleteOrder(@PathVariable Integer orderId) {
        ordersService.deleteMyOrder(orderId, getMemberId());
        return ApiResponse.success("删除成功");
    }
    @LoginShopRequired
@@ -145,5 +181,38 @@
        return ApiResponse.success("查询成功", ordersService.findShopOrderDetail(orderId, verifyCode));
    }
    @LoginShopRequired
    @ApiOperation(value = "门店确认出库", notes = "取件门店确认出库,订单完成")
    @PostMapping("/storeOut")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true)
    })
    public ApiResponse storeOut(@RequestBody @Validated StoreOutDTO dto) {
        ordersService.confirmStoreOut(dto.getOrderId(), getShopId(), dto.getImages(), dto.getRemark());
        return ApiResponse.success("出库成功");
    }
    @LoginShopRequired
    @ApiOperation(value = "确认顾客到店", notes = "门店确认顾客已到店取件,检查逾期/退款")
    @PostMapping("/confirmArrived")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true)
    })
    public ApiResponse confirmArrived(@RequestBody @Validated ConfirmArriveDTO dto) {
        ordersService.confirmCustomerArrived(dto.getOrderId(), getShopId());
        return ApiResponse.success("操作成功");
    }
    @LoginRequired
    @ApiOperation(value = "订单评价", notes = "已完成且未评价的订单可进行评价")
    @PostMapping("/comment")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse comment(@RequestBody @Validated CommentOrderDTO dto) {
        ordersService.commentOrder(dto, getMemberId());
        return ApiResponse.success("评价成功");
    }
}
server/web/src/main/java/com/doumee/api/web/PaymentCallback.java
@@ -58,12 +58,12 @@
                    }
                    //店铺押金订单
                    case "shopDeposit": {
                        ordersService.handleShopDepositPayNotify(outTradeNo, paymentNo);
                        break;
                    }
                    //逾期费用订单
                    case "overdueFee": {
                        ordersService.handleOverdueFeePayNotify(outTradeNo, paymentNo);
                        break;
                    }
                }
server/web/src/main/java/com/doumee/api/web/ShopInfoApi.java
@@ -11,10 +11,15 @@
import com.doumee.dao.dto.ShopInfoMaintainDTO;
import com.doumee.dao.dto.ShopNearbyDTO;
import com.doumee.dao.vo.ShopDetailVO;
import com.doumee.dao.vo.ShopCenterVO;
import com.doumee.dao.vo.ShopNearbyVO;
import com.doumee.dao.vo.ShopWebDetailVO;
import com.doumee.dao.vo.PayResponse;
import com.doumee.service.business.OrdersService;
import com.doumee.service.business.ShopInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@@ -32,6 +37,9 @@
    @Autowired
    private ShopInfoService shopInfoService;
    @Autowired
    private OrdersService ordersService;
    @LoginRequired
    @ApiOperation("门店入驻申请/修改")
@@ -62,6 +70,16 @@
    }
    @LoginShopRequired
    @ApiOperation("获取门店登录后信息")
    @GetMapping("/getShopInfo")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true)
    })
    public ApiResponse<ShopCenterVO> getShopInfo() {
        return ApiResponse.success("查询成功", shopInfoService.getShopCenterInfo(getShopId()));
    }
    @LoginShopRequired
    @ApiOperation("维护门店信息(支付押金后)")
    @PostMapping("/maintain")
    public ApiResponse maintain(@RequestBody ShopInfoMaintainDTO dto) {
@@ -76,4 +94,14 @@
        return ApiResponse.success(shopInfoService.getShopMaintainInfo(this.getMemberId()));
    }
    @LoginShopRequired
    @ApiOperation("门店支付押金")
    @PostMapping("/payDeposit")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true)
    })
    public ApiResponse<PayResponse> payDeposit() {
        return ApiResponse.success("操作成功", ordersService.payShopDeposit(getShopId()));
    }
}
server/web/src/main/java/com/doumee/api/web/WalletApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
package com.doumee.api.web;/**
* Created by IntelliJ IDEA.
* @Author : Rk
* @create 2026/4/16 18:05
*/public class WalletApi {
}