Просмотр исходного кода

Merge remote-tracking branch 'origin/dev_20260630' into dev_20260630

xucaiqin 1 день назад
Родитель
Сommit
be6d0f4e66
16 измененных файлов с 510 добавлено и 108 удалено
  1. 8 0
      sckw-modules-api/sckw-contract-api/src/main/java/com/sckw/contract/api/RemoteContractService.java
  2. 26 0
      sckw-modules/sckw-contract/src/main/java/com/sckw/contract/dubbo/RemoteContractServiceImpl.java
  3. 12 0
      sckw-modules/sckw-contract/src/main/java/com/sckw/contract/repository/KwcContractProxyRepository.java
  4. 4 4
      sckw-modules/sckw-contract/src/main/java/com/sckw/contract/service/operateService/KwcContractTradeService.java
  5. 10 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/controller/KwfDriverController.java
  6. 52 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/dto/DriverRegisterDto.java
  7. 20 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/vo/DriverRegisterLogisticsEntVo.java
  8. 173 0
      sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/service/KwfDriverService.java
  9. 1 5
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/req/GoodsInfoReq.java
  10. 1 37
      sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpGoodsService.java
  11. 0 34
      sckw-modules/sckw-product/src/test/java/com/sckw/product/service/KwpGoodsServiceTest.java
  12. 6 0
      sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/EntInfoReq.java
  13. 56 1
      sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsEnterpriseService.java
  14. 132 27
      sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsMenuService.java
  15. 7 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/WaybillOrderStatusResp.java
  16. 2 0
      sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/app/WaybillOrderService.java

+ 8 - 0
sckw-modules-api/sckw-contract-api/src/main/java/com/sckw/contract/api/RemoteContractService.java

@@ -208,5 +208,13 @@ public interface RemoteContractService {
      */
     List<Long> queryProxyGoodsIds(Long entId, Long proxyEntId);
 
+    /**
+     * 根据代理商企业ID查询代理合同对应的供应商企业ID集合。
+     *
+     * @param proxyEntId 代理商企业ID
+     * @return 供应商企业ID集合
+     */
+    List<Long> querySupplyEntIdsByProxyId(Long proxyEntId);
+
     List<TradeEntInfoResVo> queryPrepaidTradeEntIds(TradeEntListQueryFeignDto queryFeignDto);
 }

+ 26 - 0
sckw-modules/sckw-contract/src/main/java/com/sckw/contract/dubbo/RemoteContractServiceImpl.java

@@ -821,6 +821,32 @@ public class RemoteContractServiceImpl implements RemoteContractService {
         return proxyEntIds;
     }
 
+    /**
+     * 根据代理商企业ID查询代理合同对应的供应商企业ID集合。
+     *
+     * @param proxyEntId 代理商企业ID
+     * @return 供应商企业ID列表,若未找到则返回空列表
+     */
+    @Override
+    public List<Long> querySupplyEntIdsByProxyId(Long proxyEntId) {
+        if (Objects.isNull(proxyEntId)) {
+            log.warn("查询代理合同供应商企业ID失败,代理商企业ID为空");
+            return Collections.emptyList();
+        }
+        List<KwcContractProxy> proxyContracts = kwcContractProxyRepository.queryByProxyId(proxyEntId);
+        if (CollectionUtils.isEmpty(proxyContracts)) {
+            log.debug("代理商[{}]未找到任何关联的代理合同", proxyEntId);
+            return Collections.emptyList();
+        }
+        List<Long> supplyEntIds = proxyContracts.stream()
+                .map(KwcContractProxy::getSupplyId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        log.info("成功查询到代理商[{}]对应的{}个供应商企业ID", proxyEntId, supplyEntIds.size());
+        return supplyEntIds;
+    }
+
     @Override
     public List<ContractVo> tradeList() {
         List<Long> ids = new ArrayList<>();

+ 12 - 0
sckw-modules/sckw-contract/src/main/java/com/sckw/contract/repository/KwcContractProxyRepository.java

@@ -71,4 +71,16 @@ public class KwcContractProxyRepository extends ServiceImpl<KwcContractProxyMapp
                 .eq(KwcContractProxy::getDelFlag, 0)
                 .eq(KwcContractProxy::getSupplyId, supplyId));
     }
+
+    /**
+     * 根据代理商企业ID查询代理合同。
+     *
+     * @param proxyId 代理商企业ID
+     * @return 代理合同集合
+     */
+    public List<KwcContractProxy> queryByProxyId(Long proxyId) {
+        return list(Wrappers.<KwcContractProxy>lambdaQuery()
+                .eq(KwcContractProxy::getDelFlag, 0)
+                .eq(KwcContractProxy::getProxyId, proxyId));
+    }
 }

+ 4 - 4
sckw-modules/sckw-contract/src/main/java/com/sckw/contract/service/operateService/KwcContractTradeService.java

@@ -2566,10 +2566,10 @@ public class KwcContractTradeService {
             log.debug("供应商登录,使用登录企业ID[{}]和企业类型[{}]进行查询",
                     contractTradeOrderDto.getEntId(), contractTradeOrderDto.getEntType());
         } else if (Objects.equals(LoginUserHolder.getEntTypes(), CooperateTypeEnum.PURCHASER.getCode())) {
-            // 采购商登录时,企业ID从商品表获取,忽略前端传入的企业ID
-//            if (Objects.isNull(contractTradeOrderDto.getGoodsId())) {
-//                throw new BusinessException("商品id不能为空!");
-//            }
+            // 采购商登录时,企业ID从商品表获取,忽略前端传入的企业ID,兼容前端之前可能不会传商品id的情况,增加校验
+            if (Objects.isNull(contractTradeOrderDto.getGoodsId()) && Objects.equals(contractTradeOrderDto.getEntType(), 1)) {
+                throw new BusinessException("商品id不能为空!");
+            }
             KwpGoods kwpGoods = goodsInfoService.getGoodsById(contractTradeOrderDto.getGoodsId());
             if (Objects.isNull(kwpGoods) || Objects.isNull(kwpGoods.getEntId())) {
                 throw new BusinessException("商品不存在或商品企业信息缺失!");

+ 10 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/controller/KwfDriverController.java

@@ -170,6 +170,16 @@ public class KwfDriverController {
         return driverService.add(params);
     }
 
+    /**
+     * 司机自助注册
+     */
+    @Operation(summary = "司机注册", description = "司机端自助注册,需短信验证码")
+    @PostMapping("/register")
+    public HttpResult register(@Valid @RequestBody DriverRegisterDto params) {
+        return driverService.register(params);
+    }
+
+
     /**
      * @param params {}
      * @desc 更新

+ 52 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/dto/DriverRegisterDto.java

@@ -0,0 +1,52 @@
+package com.sckw.fleet.model.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+/**
+ * 司机注册入参
+ */
+@Data
+public class DriverRegisterDto {
+
+    /**
+     * 所属物流企业ID
+     */
+    @NotNull(message = "所属物流公司不能为空")
+    @Schema(description = "所属物流公司ID")
+    private Long entId;
+
+    /**
+     * 司机姓名
+     */
+    @NotBlank(message = "姓名不能为空")
+    @Schema(description = "司机姓名")
+    private String name;
+
+    /**
+     * 身份证号
+     */
+    @NotBlank(message = "身份证号不能为空")
+    @Pattern(regexp = "[0-9A-Za-z]{18}", message = "身份证号码格式不正确")
+    @Schema(description = "身份证号")
+    private String idcard;
+
+    /**
+     * 手机号
+     */
+    @NotBlank(message = "电话号码不能为空")
+    @Pattern(regexp = "^1[3456789]\\d{9}$", message = "电话号码格式不正确")
+    @Schema(description = "手机号")
+    private String phone;
+
+    /**
+     * 短信验证码
+     */
+    @NotBlank(message = "验证码不能为空")
+    @Schema(description = "短信验证码")
+    private String captcha;
+}

+ 20 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/model/vo/DriverRegisterLogisticsEntVo.java

@@ -0,0 +1,20 @@
+package com.sckw.fleet.model.vo;
+
+import lombok.Data;
+
+/**
+ * 司机注册-所属物流企业选项
+ */
+@Data
+public class DriverRegisterLogisticsEntVo {
+
+    /**
+     * 企业ID
+     */
+    private Long entId;
+
+    /**
+     * 企业名称
+     */
+    private String firmName;
+}

+ 173 - 0
sckw-modules/sckw-fleet/src/main/java/com/sckw/fleet/service/KwfDriverService.java

@@ -583,6 +583,179 @@ public class KwfDriverService {
         return HttpResult.ok(result.getMsg(), driver);
     }
 
+    /**
+     * 司机自助注册。
+     * <p>
+     * 面向司机端注册页,无需登录态。整体流程参照 {@link #add(KwfDriverDto)},但差异如下:
+     * <ul>
+     *     <li>必须校验短信验证码(类型:{@link DictEnum#SMS_REGISTER})</li>
+     *     <li>手机号、身份证号全局唯一,已存在则直接返回业务错误</li>
+     *     <li>所属物流企业由前端传入 entId,不依赖 {@link LoginUserHolder}</li>
+     *     <li>注册后认证状态为「临时」({@link Global#NUMERICAL_TWO}),待后续补全证件</li>
+     *     <li>同步创建 system 用户并绑定司机角色({@link #registerUserEdit(DriverRegisterDto, Long)})</li>
+     * </ul>
+     * </p>
+     *
+     * @param params 注册入参(物流企业、姓名、身份证号、手机号、短信验证码)
+     * @return 注册成功返回司机档案;校验失败返回对应错误信息
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public HttpResult register(DriverRegisterDto params) {
+        log.info("司机自助注册开始,entId={}, phone={}, name={}, idcard={}",
+                params.getEntId(), maskPhone(params.getPhone()), params.getName(), maskIdcard(params.getIdcard()));
+
+        // 1. 校验短信验证码(失败抛 SystemException,触发事务回滚)
+        validateRegisterCaptcha(params.getPhone(), params.getCaptcha());
+        log.info("司机自助注册短信验证码校验通过,phone={}", maskPhone(params.getPhone()));
+
+        // 2. 校验所属物流企业是否存在
+        EntCacheResDto entCache = remoteSystemService.queryEntCacheById(params.getEntId());
+        if (entCache == null) {
+            log.warn("司机自助注册失败,所属物流企业不存在,entId={}, phone={}", params.getEntId(), maskPhone(params.getPhone()));
+            return HttpResult.error("所属物流公司不存在");
+        }
+        log.info("司机自助注册物流企业校验通过,entId={}, firmName={}", params.getEntId(), entCache.getFirmName());
+
+        // 3. 校验手机号唯一性
+        List<KwfDriver> phoneDrivers = driverDao.findDriver(new HashMap<String, Object>() {{
+            put("phone", params.getPhone());
+        }});
+        if (CollectionUtils.isNotEmpty(phoneDrivers)) {
+            log.warn("司机自助注册失败,手机号已存在,phone={}, existDriverId={}",
+                    maskPhone(params.getPhone()), phoneDrivers.get(Global.NUMERICAL_ZERO).getId());
+            return HttpResult.error("电话号码已存在");
+        }
+
+        // 4. 校验身份证号唯一性
+        List<KwfDriver> idcardDrivers = driverDao.findDriver(new HashMap<String, Object>() {{
+            put("idcard", params.getIdcard());
+        }});
+        if (CollectionUtils.isNotEmpty(idcardDrivers)) {
+            log.warn("司机自助注册失败,身份证号已存在,idcard={}, existDriverId={}",
+                    maskIdcard(params.getIdcard()), idcardDrivers.get(Global.NUMERICAL_ZERO).getId());
+            return HttpResult.error("身份证号已存在");
+        }
+
+        // 5. 构建司机档案(默认密码规则与后台新增司机一致:手机号 + MD5(手机号))
+        KwfDriver driver = new KwfDriver();
+        driver.setName(params.getName());
+        driver.setPhone(params.getPhone());
+        driver.setIdcard(params.getIdcard());
+        driver.setEntId(params.getEntId());
+        driver.setAuthStatus(Global.NUMERICAL_TWO);
+        String salt = PasswordUtils.generateSalt();
+        String md5 = PasswordUtils.md5(params.getPhone());
+        driver.setPassword(PasswordUtils.entryptPassword(params.getPhone() + md5, salt));
+        driver.setSalt(salt);
+        driver.setBusinessStatus(Global.NO);
+        driver.setStatus(Global.NO);
+
+
+        // 6. 落库司机主表
+        if (driverDao.insert(driver) <= 0) {
+            log.error("司机自助注册失败,司机档案入库失败,phone={}, entId={}", maskPhone(params.getPhone()), params.getEntId());
+            return HttpResult.error("司机注册失败");
+        }
+        log.info("司机自助注册司机档案入库成功,driverId={}, phone={}, entId={}",
+                driver.getId(), maskPhone(params.getPhone()), params.getEntId());
+
+        // 7. 建立司机与企业关联关系
+        driverEntEdit(driver);
+        log.info("司机自助注册企业关联完成,driverId={}, entId={}", driver.getId(), params.getEntId());
+
+        // 8. 同步 system 用户及司机角色
+        registerUserEdit(params, driver.getId());
+        log.info("司机自助注册系统用户同步完成,driverId={}, phone={}", driver.getId(), maskPhone(params.getPhone()));
+
+        // 9. 注册成功后清除验证码,防止重复使用
+        String captchaKey = StringUtils.format(RedisConstant.MESSAGE_SMS_VERIFY_CODE_VALUE_KEY,
+                DictEnum.SMS_REGISTER.getValue(), params.getPhone());
+        RedissonUtils.delete(captchaKey);
+
+        log.info("司机自助注册成功,driverId={}, phone={}, entId={}, firmName={}",
+                driver.getId(), maskPhone(params.getPhone()), params.getEntId(), entCache.getFirmName());
+        return HttpResult.ok("注册成功", driver);
+    }
+
+    /**
+     * 校验司机注册短信验证码。
+     * <p>验证码存储在 Redis,key 规则与 {@link DictEnum#SMS_REGISTER} 及手机号关联。</p>
+     *
+     * @param phone   注册手机号
+     * @param captcha 用户输入的验证码
+     */
+    private void validateRegisterCaptcha(String phone, String captcha) {
+        String key = StringUtils.format(RedisConstant.MESSAGE_SMS_VERIFY_CODE_VALUE_KEY,
+                DictEnum.SMS_REGISTER.getValue(), phone);
+        String smsCaptcha = RedissonUtils.getString(key);
+        if (StringUtils.isBlank(smsCaptcha)) {
+            log.warn("司机自助注册验证码已过期或不存在,phone={}", maskPhone(phone));
+            throw new SystemException(HttpStatus.UN_LOGIN_CODE, "验证码已过期,请重新获取!");
+        }
+        if (!captcha.equals(smsCaptcha)) {
+            log.warn("司机自助注册验证码错误,phone={}", maskPhone(phone));
+            throw new SystemException(HttpStatus.UN_LOGIN_CODE, "验证码错误");
+        }
+    }
+
+    /**
+     * 司机注册后同步 system 用户信息。
+     * <p>
+     * 调用远程 {@link RemoteUserService#saveUser(KwsUserReqDto)}:
+     * 按手机号幂等创建/更新用户,并自动绑定所属企业下的「司机」角色。
+     * </p>
+     *
+     * @param params   注册入参
+     * @param driverId 已入库的司机档案主键
+     */
+    private void registerUserEdit(DriverRegisterDto params, Long driverId) {
+        log.info("开始同步司机注册系统用户,driverId={}, phone={}, entId={}",
+                driverId, maskPhone(params.getPhone()), params.getEntId());
+        KwsUserReqDto kwsUserReqDto = new KwsUserReqDto();
+        String salt = PasswordUtils.generateSalt();
+        kwsUserReqDto.setSystemType(SystemTypeEnum.DRIVER.getCode());
+        kwsUserReqDto.setEntId(params.getEntId());
+        kwsUserReqDto.setAccount(params.getPhone());
+        kwsUserReqDto.setPassword(PasswordUtils.entryptPassword(params.getPhone() + PasswordUtils.md5(params.getPhone()), salt));
+        kwsUserReqDto.setName(params.getName());
+        kwsUserReqDto.setPhone(params.getPhone());
+        kwsUserReqDto.setPhoto("");
+        kwsUserReqDto.setEmail("");
+        kwsUserReqDto.setClientId("");
+        kwsUserReqDto.setIsMain(0);
+        kwsUserReqDto.setSalt(salt);
+        kwsUserReqDto.setRemark("司机注册");
+        kwsUserReqDto.setStatus(0);
+        Date date = new Date();
+        kwsUserReqDto.setCreateBy(LoginUserHolder.getUserId());
+        kwsUserReqDto.setCreateTime(date);
+        kwsUserReqDto.setUpdateBy(LoginUserHolder.getUserId());
+        kwsUserReqDto.setUpdateTime(date);
+        kwsUserReqDto.setDelFlag(0);
+        kwsUserReqDto.setDriverId(driverId);
+        remoteUserService.saveUser(kwsUserReqDto);
+    }
+
+    /**
+     * 手机号脱敏,用于日志输出(保留前3后4)。
+     */
+    private String maskPhone(String phone) {
+        if (StringUtils.isBlank(phone) || phone.length() < 7) {
+            return phone;
+        }
+        return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
+    }
+
+    /**
+     * 身份证号脱敏,用于日志输出(保留前6后4)。
+     */
+    private String maskIdcard(String idcard) {
+        if (StringUtils.isBlank(idcard) || idcard.length() < 10) {
+            return idcard;
+        }
+        return idcard.substring(0, 6) + "********" + idcard.substring(idcard.length() - 4);
+    }
+
     /**
      * 全局事务下子步骤若仅用 {@link HttpResult} 表示失败而不抛异常,TM 仍会提交;失败时必须抛异常以驱动回滚。
      */

+ 1 - 5
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/req/GoodsInfoReq.java

@@ -23,11 +23,7 @@ public class GoodsInfoReq implements Serializable {
      */
     @Schema(description = "企业id")
     private String entId;
-    /**
-     * 代理商id
-     */
-    @Schema(description = "代理商id")
-    private String proxyEntId;
+
     /**
      * 商品名称
      */

+ 1 - 37
sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpGoodsService.java

@@ -1491,10 +1491,8 @@ public class KwpGoodsService {
 
     public GoodsInfoResp getGoods(GoodsInfoReq req) {
         log.info("查询商品信息,请求参数 :{}", JSON.toJSONString(req));
-        //查询上商品信息
         Long entId = parseLongParam(req.getEntId(), "企业id");
-        Long proxyEntId = parseLongParam(req.getProxyEntId(), "代理商id");
-        List<KwpGoods> goods = queryGoodsInfoList(req, entId, proxyEntId);
+        List<KwpGoods> goods = kwpGoodsRepository.queryByEntIdAndGoodsName(entId, req.getGoodsName());
         log.info("查询商品信息,返回条数 :{}", goods.size());
         if (org.apache.commons.collections4.CollectionUtils.isEmpty(goods)) {
             return new GoodsInfoResp();
@@ -1509,40 +1507,6 @@ public class KwpGoodsService {
         return goodsInfoResp;
     }
 
-    /**
-     * 查询商品信息列表。企业id和代理商id同时传入时,按代理合同商品限定商品范围;代理商id未传入时保持原有查询逻辑。
-     *
-     * @param req        查询商品请求参数
-     * @param entId      企业id
-     * @param proxyEntId 代理商id
-     * @return 商品信息列表
-     */
-    private List<KwpGoods> queryGoodsInfoList(GoodsInfoReq req, Long entId, Long proxyEntId) {
-        if (!isProxyGoodsQuery(req)) {
-            log.info("查询商品信息,请求参数 :{}", JSON.toJSONString(req));
-            return kwpGoodsRepository.queryByEntIdAndGoodsName(entId, req.getGoodsName());
-        }
-        List<Long> proxyGoodsIds = remoteContractService.queryProxyGoodsIds(entId, proxyEntId);
-        log.info("查询代理商品信息,企业id :{}, 代理商id :{},条数:{}", entId, proxyEntId, proxyGoodsIds.size());
-        if (CollUtil.isEmpty(proxyGoodsIds)) {
-            log.info("查询代理商品信息为空");
-            return Collections.emptyList();
-        }
-        return kwpGoodsRepository.queryByIdsAndGoodsName(proxyGoodsIds, req.getGoodsName());
-    }
-
-    /**
-     * 判断是否按代理合同商品查询。只有企业id与代理商id同时存在时才切换新逻辑,避免影响原有调用方。
-     *
-     * @param req 查询商品请求参数
-     * @return true-按代理合同商品查询;false-按原有企业商品查询
-     */
-    static boolean isProxyGoodsQuery(GoodsInfoReq req) {
-        return Objects.nonNull(req)
-                && StringUtils.isNotBlank(req.getEntId())
-                && StringUtils.isNotBlank(req.getProxyEntId());
-    }
-
     /**
      * 安全转换Long参数,避免非法字符串导致系统异常。
      *

+ 0 - 34
sckw-modules/sckw-product/src/test/java/com/sckw/product/service/KwpGoodsServiceTest.java

@@ -1,34 +0,0 @@
-package com.sckw.product.service;
-
-import com.sckw.product.model.vo.req.GoodsInfoReq;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * 商品服务单元测试。
- */
-public class KwpGoodsServiceTest {
-
-    /**
-     * 企业id和代理商id同时存在时,应按代理合同商品查询。
-     */
-    @Test
-    public void shouldUseProxyGoodsQueryWhenEntIdAndProxyEntIdExist() {
-        GoodsInfoReq req = new GoodsInfoReq();
-        req.setEntId("1001");
-        req.setProxyEntId("2001");
-
-        Assert.assertTrue(KwpGoodsService.isProxyGoodsQuery(req));
-    }
-
-    /**
-     * 代理商id为空时,应保持原有企业商品查询逻辑。
-     */
-    @Test
-    public void shouldKeepOriginalGoodsQueryWhenProxyEntIdMissing() {
-        GoodsInfoReq req = new GoodsInfoReq();
-        req.setEntId("1001");
-
-        Assert.assertFalse(KwpGoodsService.isProxyGoodsQuery(req));
-    }
-}

+ 6 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/model/vo/req/EntInfoReq.java

@@ -36,4 +36,10 @@ public class EntInfoReq implements Serializable {
      */
     @Schema(description = "企业名称")
     private String entName;
+
+    /**
+     * 代理商id(不为空时按代理合同查询对应供应商企业)
+     */
+    @Schema(description = "代理商id")
+    private String agentId;
 }

+ 56 - 1
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsEnterpriseService.java

@@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import com.sckw.contract.api.RemoteContractService;
 import com.sckw.core.exception.BusinessException;
 import com.sckw.core.exception.SystemException;
 import com.sckw.core.model.base.BaseModel;
@@ -50,6 +51,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.redisson.api.RSet;
@@ -125,6 +127,10 @@ public class KwsEnterpriseService {
     private KwsSpecialDao specialDao;
     @Autowired
     private RemoteBaseService remoteBaseService;
+
+    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
+    private RemoteContractService remoteContractService;
+
     private final KwsEntTypeRepository kwsEntTypeRepository;
     private final KwsEnterpriseRepository kwsEnterpriseRepository;
 
@@ -1551,16 +1557,22 @@ public class KwsEnterpriseService {
         if (req.getEntType().contains(req.getLoginEntType())) {
             //登录客户的企业类型和查询的类型是一直的说明只能查询自己和自己的下级
             //发起方的公司信息
+            log.info("发起方的公司信息登录客户=%s,发起方=%s,查询=%s", req.getEntId(), req.getLoginEntType(), req.getEntType());
             return getEntInfoResp(req);
         }
         //被邀请方的企业信息
         //根据类型查询企业id
+        log.info("被邀请方的企业信息登录客户=%s,发起方=%s,查询=%s", req.getEntId(), req.getLoginEntType(), req.getEntType());
         return getInfoResp(req);
     }
 
     @NotNull
     private EntInfoResp getInfoResp(EntInfoReq req) {
         EntInfoResp entInfoResp = buildEntInfoResp(req);
+        if (org.apache.commons.lang3.StringUtils.isNotBlank(req.getAgentId())) {
+            log.info("被邀请方按代理商查询供应商企业,agentId={}", req.getAgentId());
+            return getProxySupplyEntInfoResp(req, entInfoResp);
+        }
         List<KwsEntType> kwsEntTypes = kwsEntTypeRepository.queryByType(req.getEntType());
         if (CollectionUtils.isEmpty(kwsEntTypes)) {
             return entInfoResp;
@@ -1582,9 +1594,52 @@ public class KwsEnterpriseService {
         return entInfoResp;
     }
 
+    /**
+     * 按代理商id查询代理合同关联的供应商企业。
+     */
+    private EntInfoResp getProxySupplyEntInfoResp(EntInfoReq req, EntInfoResp entInfoResp) {
+        Long agentId = parseAgentId(req.getAgentId());
+        List<Long> supplyEntIds = remoteContractService.querySupplyEntIdsByProxyId(agentId);
+        log.info("按代理商查询供应商企业,agentId={}, supplyEntCount={}", agentId, supplyEntIds.size());
+        if (CollectionUtils.isEmpty(supplyEntIds)) {
+            return entInfoResp;
+        }
+        Set<Long> supplyEntIdSet = new HashSet<>(supplyEntIds);
+        List<KwsEntType> kwsEntTypes = kwsEntTypeRepository.queryByEntIdsAndType(supplyEntIdSet, req.getEntType());
+        if (CollectionUtils.isEmpty(kwsEntTypes)) {
+            return entInfoResp;
+        }
+        Set<Long> matchedEntIds = kwsEntTypes.stream().map(KwsEntType::getEntId).collect(Collectors.toSet());
+        List<KwsEnterprise> kwsEnterprises = kwsEnterpriseRepository.queryByEntIds(matchedEntIds, req.getEntName());
+        if (CollectionUtils.isEmpty(kwsEnterprises)) {
+            return entInfoResp;
+        }
+        List<EntInfoResp.EntInfo> entInfos = kwsEnterprises.stream().map(k -> {
+            EntInfoResp.EntInfo info = new EntInfoResp.EntInfo();
+            info.setEntId(String.valueOf(k.getId()));
+            info.setEntName(k.getFirmName());
+            return info;
+        }).collect(Collectors.toList());
+        entInfoResp.setEntInfos(entInfos);
+        return entInfoResp;
+    }
+
+    private Long parseAgentId(String agentId) {
+        try {
+            return Long.valueOf(agentId);
+        } catch (NumberFormatException e) {
+            log.warn("代理商id格式不正确,agentId={}", agentId, e);
+            throw new BusinessException("代理商id格式不正确");
+        }
+    }
+
     @NotNull
     private EntInfoResp getEntInfoResp(EntInfoReq req) {
         EntInfoResp entInfoResp = buildEntInfoResp(req);
+        if (org.apache.commons.lang3.StringUtils.isNotBlank(req.getAgentId())) {
+            log.info("发起方按代理商查询供应商企业,agentId={}", req.getAgentId());
+            return getProxySupplyEntInfoResp(req, entInfoResp);
+        }
         if (org.apache.commons.lang3.StringUtils.isBlank(req.getEntId())) {
             throw new BusinessException("企业id不能为空");
         }
@@ -1684,7 +1739,7 @@ public class KwsEnterpriseService {
     }
 
     public List<EntInfo> queryEnterpriseByPageType(EnterprisePageQueryReqVo req) {
-        log.info("查询企业信息请求参数:{}", JSON.toJSONString(req));
+        log.info("根据企业类型查询企业信息请求参数:{}", JSON.toJSONString(req));
 
         Long loginEntId = LoginUserHolder.getEntId();
         if (Objects.isNull(loginEntId)) {

+ 132 - 27
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsMenuService.java

@@ -138,29 +138,29 @@ public class KwsMenuService {
             throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
         }
 
-        List<FindEntListResVo> list = kwsEnterpriseDao.findList(new FindListReqVo());
-        if (CollectionUtils.isNotEmpty(list)) {
-            List<Long> entIds = list.stream().map(FindEntListResVo::getId).distinct().toList();
-            List<KwsMenuRights> kwsMenuRightsList = new ArrayList<>();
-            Date date = new Date();
-            for (Long entId : entIds) {
-                KwsMenuRights kwsMenuRights = new KwsMenuRights();
-                kwsMenuRights.setEntId(entId);
-                kwsMenuRights.setMenuId(menuId);
-                kwsMenuRights.setId(new IdWorker(1L).nextId());
-                kwsMenuRights.setDelFlag(Global.NO);
-                kwsMenuRights.setStatus(Global.NO);
-                kwsMenuRights.setCreateBy(LoginUserHolder.getUserId());
-                kwsMenuRights.setUpdateBy(LoginUserHolder.getUserId());
-                kwsMenuRights.setCreateTime(date);
-                kwsMenuRights.setUpdateTime(date);
-                kwsMenuRightsList.add(kwsMenuRights);
-            }
-
-            if (kwsMenuRightsDao.saveBatch(kwsMenuRightsList) < kwsMenuRightsList.size()) {
-                throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
-            }
-        }
+//        List<FindEntListResVo> list = kwsEnterpriseDao.findList(new FindListReqVo());
+//        if (CollectionUtils.isNotEmpty(list)) {
+//            List<Long> entIds = list.stream().map(FindEntListResVo::getId).distinct().toList();
+//            List<KwsMenuRights> kwsMenuRightsList = new ArrayList<>();
+//            Date date = new Date();
+//            for (Long entId : entIds) {
+//                KwsMenuRights kwsMenuRights = new KwsMenuRights();
+//                kwsMenuRights.setEntId(entId);
+//                kwsMenuRights.setMenuId(menuId);
+//                kwsMenuRights.setId(new IdWorker(1L).nextId());
+//                kwsMenuRights.setDelFlag(Global.NO);
+//                kwsMenuRights.setStatus(Global.NO);
+//                kwsMenuRights.setCreateBy(LoginUserHolder.getUserId());
+//                kwsMenuRights.setUpdateBy(LoginUserHolder.getUserId());
+//                kwsMenuRights.setCreateTime(date);
+//                kwsMenuRights.setUpdateTime(date);
+//                kwsMenuRightsList.add(kwsMenuRights);
+//            }
+//
+//            if (kwsMenuRightsDao.saveBatch(kwsMenuRightsList) < kwsMenuRightsList.size()) {
+//                throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
+//            }
+//        }
 
     }
 
@@ -665,10 +665,13 @@ public class KwsMenuService {
             finalList = menuList;
         }
         if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.COMPANY.getCode()) ) {
-//            if (Boolean.TRUE.equals(reqVo.getIncludeButton())) {
-//
-//            }
-            finalList = supplementButtons(finalList, findMenuTreePojo.getEntTypeList(),Integer.valueOf(reqVo.getClientType()));
+            if (needRightsBasedButtons(reqVo)) {
+                log.info("企业端 PC 且未指定菜单类型,按钮需基于 kws_menu_rights 权限查询");
+                finalList = supplementButtonsWithRights(finalList, findMenuTreePojo, Global.NUMERICAL_TWO);
+            } else {
+                log.info("企业端 PC 菜单类型,菜单权限查询");
+                finalList = supplementButtons(finalList, findMenuTreePojo.getEntTypeList(), Integer.valueOf(reqVo.getClientType()));
+            }
         }
 
 
@@ -747,6 +750,108 @@ public class KwsMenuService {
         findMenuTreePojo.setRoleIds(Collections.singletonList(LoginUserHolder.getCurrentRoleId()));
     }
 
+    /**
+     * 企业端 PC 且未指定菜单类型时,按钮需基于 kws_menu_rights 权限查询。
+     */
+    private boolean needRightsBasedButtons(FindMenuTreeReqVo reqVo) {
+        return reqVo.getType() == null
+                && org.apache.commons.lang3.StringUtils.equals(reqVo.getClientType(), String.valueOf(Global.NUMERICAL_TWO));
+    }
+
+    /**
+     * 基于菜单权限表补充按钮节点(企业端 PC 场景专用)。
+     * <p>
+     * 适用场景:{@link #findTree(FindMenuTreeReqVo)} 中企业端(systemType=2)、PC 端(clientType=2)、
+     * 且未指定菜单类型(type 为空)时调用,与 {@link #supplementButtons} 的区别在于:
+     * <ul>
+     *     <li>supplementButtons:直接从 kws_menu 查按钮,不做权限过滤</li>
+     *     <li>本方法:先查 kws_menu_rights 获取有权 menuId,再关联 kws_menu 查按钮</li>
+     * </ul>
+     * </p>
+     * <p>处理步骤:</p>
+     * <ol>
+     *     <li>从已有菜单列表中提取目录节点(type=1)作为按钮父级</li>
+     *     <li>按 roleIds / entId 查询 kws_menu_rights,得到有权限的 menuId 集合</li>
+     *     <li>在 kws_menu 中查询 type=2、clientType 匹配、parentId 属于目录且 id 在权限集合内的按钮</li>
+     *     <li>按 using_roles 过滤企业类型后,去重合并到菜单列表</li>
+     * </ol>
+     *
+     * @param menuList   已查询出的菜单列表(不含或已含部分按钮)
+     * @param pojo       查询条件,含 roleIds、entId、entTypeList
+     * @param clientType 客户端类型(企业端 PC 为 2)
+     * @return 合并按钮后的菜单列表
+     */
+    private List<KwsMenuResVo> supplementButtonsWithRights(List<KwsMenuResVo> menuList, FindMenuTreePojo pojo, Integer clientType) {
+        log.info("开始基于权限表补充按钮,clientType={}, entId={}, roleIds={}, 原菜单数={}",
+                clientType, pojo.getEntId(), LoginUserHolder.getCurrentRoleId(), menuList.size());
+
+        // 1. 提取目录节点 id,作为按钮的 parentId 查询范围
+        List<Long> menuTypeIds = menuList.stream()
+                .filter(m -> Objects.equals(m.getType(), MenuTypeEnum.DIRECTORY.getCode()))
+                .map(KwsMenuResVo::getId)
+                .toList();
+        if (CollectionUtils.isEmpty(menuTypeIds)) {
+            log.info("基于权限表补充按钮结束,未找到目录节点,跳过按钮查询");
+            return menuList;
+        }
+        log.debug("基于权限表补充按钮,目录节点数={}, directoryIds={}", menuTypeIds.size(), menuTypeIds);
+
+        // 2. 查询 kws_menu_rights,获取当前用户/角色有权限的 menuId
+        LambdaQueryWrapper<KwsMenuRights> rightsWrapper = Wrappers.lambdaQuery(KwsMenuRights.class)
+                .eq(KwsMenuRights::getDelFlag, Global.NO)
+                .select(KwsMenuRights::getMenuId);
+        if (Objects.nonNull(LoginUserHolder.getCurrentRoleId())) {
+            rightsWrapper.eq(KwsMenuRights::getRoleId,LoginUserHolder.getCurrentRoleId());
+        }
+        if (pojo.getEntId() != null) {
+            rightsWrapper.eq(KwsMenuRights::getEntId, pojo.getEntId());
+        }
+        List<KwsMenuRights> rightsRows = kwsMenuRightsMpMapper.selectList(rightsWrapper);
+        Set<Long> authorizedMenuIds = rightsRows.stream()
+                .map(KwsMenuRights::getMenuId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (authorizedMenuIds.isEmpty()) {
+            log.info("基于权限表补充按钮结束,kws_menu_rights 未查到授权菜单,entId={}, roleIds={}",
+                    pojo.getEntId(), LoginUserHolder.getCurrentRoleId());
+            return menuList;
+        }
+        log.debug("基于权限表补充按钮,授权菜单数={}", authorizedMenuIds.size());
+
+        // 3. 关联 kws_menu 查询有权限的按钮节点
+        LambdaQueryWrapper<KwsMenu> buttonWrapper = Wrappers.lambdaQuery(KwsMenu.class)
+                .eq(KwsMenu::getDelFlag, Global.NO)
+                .eq(KwsMenu::getType, MenuTypeEnum.BUTTON.getCode())
+                .eq(KwsMenu::getClientType, clientType)
+                .in(KwsMenu::getParentId, menuTypeIds)
+                .in(KwsMenu::getId, authorizedMenuIds)
+                .orderByAsc(KwsMenu::getSort);
+        appendUsingRolesFilter(buttonWrapper, pojo.getEntTypeList());
+
+        List<KwsMenu> buttons = kwsMenuMpMapper.selectList(buttonWrapper);
+        if (CollectionUtils.isEmpty(buttons)) {
+            log.info("基于权限表补充按钮结束,kws_menu 未匹配到按钮,clientType={}, entTypeList={}",
+                    clientType, pojo.getEntTypeList());
+            return menuList;
+        }
+        log.debug("基于权限表补充按钮,查询到按钮数={}", buttons.size());
+
+        // 4. 去重合并,避免与 findList 已返回的按钮重复
+        Set<Long> existingIds = menuList.stream().map(KwsMenuResVo::getId).collect(Collectors.toSet());
+        List<KwsMenuResVo> result = new ArrayList<>(menuList);
+        int addedCount = 0;
+        for (KwsMenu btn : buttons) {
+            if (!existingIds.contains(btn.getId())) {
+                KwsMenuResVo vo = new KwsMenuResVo();
+                BeanUtils.copyProperties(btn, vo);
+                result.add(vo);
+                addedCount++;
+            }
+        }
+        log.info("基于权限表补充按钮完成,新增按钮数={}, 合并后菜单总数={}", addedCount, result.size());
+        return result;
+    }
+
     /**
      * 补充按钮节点到菜单列表中
      * <p>

+ 7 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/model/param/WaybillOrderStatusResp.java

@@ -207,6 +207,13 @@ public class WaybillOrderStatusResp implements Serializable {
      */
     @Schema(description = "状态描述")
     private String statusDesc;
+
+    /**
+     * 是否展示打印按钮(待离场状态展示)
+     */
+    @Schema(description = "是否展示打印按钮")
+    private Boolean showPrintButton;
+
     /**
      * 订单余量
      */

+ 2 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/app/WaybillOrderService.java

@@ -933,6 +933,8 @@ public class WaybillOrderService {
         } else {
             wbOrderResp.setStatusDesc("未知状态");
         }
+        //打印按钮
+        wbOrderResp.setShowPrintButton(Objects.equals(wbOrder.getStatus(), CarWaybillV1Enum.WAIT_LEAVE.getCode()));
 
         return wbOrderResp;