diff --git a/njzscloud-svr/pom.xml b/njzscloud-svr/pom.xml index 2ed0d55..f7afa26 100644 --- a/njzscloud-svr/pom.xml +++ b/njzscloud-svr/pom.xml @@ -105,7 +105,7 @@ com.github.binarywang weixin-java-pay - 4.7.0 + 4.8.0 diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/constant/BizObj.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/constant/BizObj.java index df668ac..7ce408a 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/constant/BizObj.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/constant/BizObj.java @@ -18,6 +18,7 @@ public enum BizObj implements DictStr { ShiGongDanWei("ShiGongDanWei", "施工单位"), ChaiQian("ChaiQian", "拆迁公司"), SheQu("SheQu", "社区/村/街道"), + QiTa("QiTa", "其他"), // 清运方 QiYe("QiYe", "企业"), @@ -106,7 +107,8 @@ public enum BizObj implements DictStr { return this == ShiGongDanWei || this == WuYe || this == ChaiQian - || this == SheQu; + || this == SheQu + || this == QiTa; } /** diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/controller/BizAuditConfigController.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/controller/BizAuditConfigController.java index c5b3e41..5cacea4 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/controller/BizAuditConfigController.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/controller/BizAuditConfigController.java @@ -67,6 +67,15 @@ public class BizAuditConfigController { } + /** + * 列表查询 + */ + @GetMapping("/list_all") + public R> listAll() { + return R.success(bizAuditConfigService.listAll()); + } + + /** * 复制文件 */ diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizAuditConfigService.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizAuditConfigService.java index 2aa2225..8a0d81f 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizAuditConfigService.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizAuditConfigService.java @@ -20,6 +20,8 @@ import com.njzscloud.common.core.ex.Exceptions; import com.njzscloud.common.core.tuple.Tuple2; import com.njzscloud.common.mp.support.PageParam; import com.njzscloud.common.mp.support.PageResult; +import com.njzscloud.common.security.support.UserDetail; +import com.njzscloud.common.security.util.SecurityUtil; import com.njzscloud.supervisory.biz.mapper.BizAuditConfigMapper; import com.njzscloud.supervisory.biz.pojo.entity.BizAuditConfigEntity; import com.njzscloud.supervisory.biz.pojo.result.SearchAuditConfigResult; @@ -35,6 +37,9 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.njzscloud.supervisory.constant.Constant.ROLE_JG; /** * 审核配置 @@ -304,4 +309,27 @@ public class BizAuditConfigService extends ServiceImpl listAll() { + UserDetail userDetail = SecurityUtil.loginUser(); + Set roles = userDetail.getRoles(); + Assert.isTrue(CollUtil.isNotEmpty(roles) && roles.contains(ROLE_JG), () -> Exceptions.clierr("非监管角色,不能查看")); + List all; + if (SecurityUtil.isAdmin()) { + all = new ArrayList<>(); + List list = list(); + for (BizAuditConfigEntity bizAuditConfigEntity : list) { + if (all.stream().anyMatch(item -> item.getArea().equals(bizAuditConfigEntity.getArea()))) continue; + all.add(bizAuditConfigEntity); + } + } else { + List areaList = list(Wrappers.lambdaQuery().in(BizAuditConfigEntity::getAreaRole, roles)); + List areas = areaList.stream().map(BizAuditConfigEntity::getArea).collect(Collectors.toList()); + List cityList = list(Wrappers.lambdaQuery().in(BizAuditConfigEntity::getCityRole, roles) + .notIn(CollUtil.isNotEmpty(areas), BizAuditConfigEntity::getArea, areas)); + all = Stream.of(areaList, cityList) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + return all; + } } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizCompanyService.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizCompanyService.java index 199e58a..6c54d88 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizCompanyService.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/biz/service/BizCompanyService.java @@ -173,7 +173,8 @@ public class BizCompanyService extends ServiceImpl it.eq("c.audit_status", AuditStatus.TongGuo).or().isNull("c.audit_status")); diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/controller/OrderInfoController.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/controller/OrderInfoController.java index b4ee617..ff3c6dc 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/controller/OrderInfoController.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/controller/OrderInfoController.java @@ -298,4 +298,20 @@ public class OrderInfoController { return R.success(orderInfoService.pushProvincial(orderSns)); } + /** + * 订单统计 + */ + @GetMapping("/statistics") + public R statistics(OrderStatisticsParam param) { + return R.success(orderInfoService.statistics(param)); + } + + /** + * 订单统计详情 + */ + @GetMapping("/statistics/detail") + public R statisticsDetail(PageParam pageParam, OrderPagingSearchParam param) { + return R.success(orderInfoService.statisticsDetail(pageParam, param)); + } + } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/mapper/OrderInfoMapper.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/mapper/OrderInfoMapper.java index 73c6b06..57d285d 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/mapper/OrderInfoMapper.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/mapper/OrderInfoMapper.java @@ -15,6 +15,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; +import java.util.Map; /** * 订单信息 @@ -49,4 +50,10 @@ public interface OrderInfoMapper extends BaseMapper { DeviceLocalizerEntity gpsLastOnlineTime(@Param("gpsId") String gpsId); List getRoute(@Param("id") Long id); + + @SuppressWarnings("MybatisXMapperMethodInspection") + List> statisticsByArea(@Param("ew") QueryWrapper ew); + + @SuppressWarnings("MybatisXMapperMethodInspection") + List> statisticsByAreaGoods(@Param("ew") QueryWrapper ew); } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/param/OrderPagingSearchParam.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/param/OrderPagingSearchParam.java index f97c0f8..105ce6b 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/param/OrderPagingSearchParam.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/param/OrderPagingSearchParam.java @@ -66,6 +66,10 @@ public class OrderPagingSearchParam { * 昵称 */ private String nickname; + /** + * 公司名称 + */ + private String companyName; /** * 手机号 @@ -77,6 +81,8 @@ public class OrderPagingSearchParam { private LocalDateTime startPayTime; private LocalDateTime endPayTime; + private LocalDateTime startOutTime; + private LocalDateTime endOutTime; private Boolean isCertificatePaging = Boolean.FALSE; diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/param/OrderStatisticsParam.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/param/OrderStatisticsParam.java new file mode 100644 index 0000000..0d0e5e0 --- /dev/null +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/param/OrderStatisticsParam.java @@ -0,0 +1,31 @@ +package com.njzscloud.supervisory.order.pojo.param; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.util.List; + +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class OrderStatisticsParam { + /** + * 区域编码 + */ + private List areaCode; + /** + * 产品 Id + */ + private Long goodsId; + /** + * 年份 + */ + private Integer year; + /** + * 月份 + */ + private Integer month; +} diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/result/OrderExportResult.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/result/OrderExportResult.java index 4d6740f..0dac8f2 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/result/OrderExportResult.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/pojo/result/OrderExportResult.java @@ -26,6 +26,11 @@ public class OrderExportResult { */ private Integer sort; + /** + * 订单号 + */ + private String sn; + /** * 清运时间(日期) */ diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java index 609479f..57eed32 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java @@ -242,6 +242,8 @@ public class OrderInfoService extends ServiceImpl ew = Wrappers.query() .eq(stationId != null && stationId > 0, "a.station_id", stationId) .like(StrUtil.isNotBlank(sn), "a.sn", sn) @@ -252,6 +254,8 @@ public class OrderInfoService extends ServiceImpl it + .in("a.order_status", OrderStatus.YiJieDan, OrderStatus.QingYunZhong, OrderStatus.YiJinChang, OrderStatus.YiChuChang, OrderStatus.YiWanCheng) + .or(it1 -> it1.eq("a.audit_status", AuditStatus.BoHui).eq("a.order_status", OrderStatus.YiQuXiao)) + ) .and(it -> it.or(CollUtil.isNotEmpty(areaList), it0 -> it0.in("b.area", areaList).eq("a.audit_status", AuditStatus.BoHui)) .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList).eq("a.audit_status", AuditStatus.BoHui)) @@ -556,7 +563,8 @@ public class OrderInfoService extends ServiceImpl待审核 if ("1".equals(auditType)) { - ew.eq("a.order_status", OrderStatus.YiJieDan) + ew + .eq("a.order_status", OrderStatus.YiJieDan) .and(it -> it.or(CollUtil.isNotEmpty(areaList), it0 -> it0.in("b.area", areaList).in("a.audit_status", AuditStatus.QuDaiShenHe, AuditStatus.ShiDaiShenHe)) .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList).eq("a.audit_status", AuditStatus.ShiDaiShenHe)) @@ -564,7 +572,9 @@ public class OrderInfoService extends ServiceImpl已审核 ew - .in("a.order_status", OrderStatus.YiJieDan, OrderStatus.QingYunZhong, OrderStatus.YiJinChang, OrderStatus.YiChuChang, OrderStatus.YiWanCheng) + .and(it -> it.in("a.order_status", OrderStatus.YiJieDan, OrderStatus.QingYunZhong, OrderStatus.YiJinChang, OrderStatus.YiChuChang, OrderStatus.YiWanCheng) + .or(it1 -> it1.eq("a.audit_status", AuditStatus.BoHui).eq("a.order_status", OrderStatus.YiQuXiao)) + ) .and(it -> it.or(CollUtil.isNotEmpty(areaList), it0 -> it0.in("b.area", areaList).in("a.audit_status", AuditStatus.ShiDaiShenHe, AuditStatus.TongGuo)) .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList).in("a.audit_status", AuditStatus.TongGuo, AuditStatus.BoHui)) @@ -572,7 +582,10 @@ public class OrderInfoService extends ServiceImpl it + .in("a.order_status", OrderStatus.YiJieDan, OrderStatus.QingYunZhong, OrderStatus.YiJinChang, OrderStatus.YiChuChang, OrderStatus.YiWanCheng) + .or(it1 -> it1.eq("a.audit_status", AuditStatus.BoHui).eq("a.order_status", OrderStatus.YiQuXiao)) + ) .and(it -> it.or(CollUtil.isNotEmpty(areaList), it0 -> it0.in("b.area", areaList)) .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList)) @@ -670,6 +683,7 @@ public class OrderInfoService extends ServiceImpl openId = new HashSet<>(); UserEntity orderUserEntity = userService.getById(orderInfo.getUserId()); param.setCfCompanyName(orderUserEntity.getNickname()); + OrderGoodsEntity entity = orderGoodsService.getById(orderInfo.getGoodsId()); + param.setGoodsName(entity.getGoodsName()); + BizCompanyEntity companyEntity = bizCompanyService.getById(orderInfo.getTransCompanyId()); + param.setCompanyName(companyEntity.getCompanyName()); + param.setCreateTime(orderInfo.getCreateTime()); + Set openId = new HashSet<>(); for (SysUserRoleEntity userRoleEntity : userIds) { UserEntity userEntity = userService.getById(userRoleEntity.getUserId()); if (null != userEntity && !Strings.isNullOrEmpty(userEntity.getOpenid())) { @@ -1766,23 +1781,42 @@ public class OrderInfoService extends ServiceImpl ew = Wrappers.query(); - ew.eq(null != searchParam.getStationId() && searchParam.getStationId() > 0, "a.station_id", searchParam.getStationId()); - ew.like(StrUtil.isNotBlank(searchParam.getSn()), "a.sn", searchParam.getSn()); - ew.like(StrUtil.isNotBlank(searchParam.getLicensePlate()), "d.license_plate", searchParam.getLicensePlate()); - ew.like(StrUtil.isNotBlank(searchParam.getPhone()), "a.phone", searchParam.getPhone()); - ew.like(StrUtil.isNotBlank(searchParam.getNickname()), "a.contacts", searchParam.getNickname()); - ew.like(StrUtil.isNotBlank(transCompanyName), "g.company_name", transCompanyName); - ew.like(StrUtil.isNotBlank(driverName), "f.driver_name", driverName); - ew.ge(startTime != null, "a.create_time", startTime); - ew.le(endTime != null, "a.create_time", endTime); - ew.eq(orderCategory != null, "a.order_category", orderCategory); - ew.eq(null != searchParam.getTransCompanyId(), "a.trans_company_id", searchParam.getTransCompanyId()); - ew.eq("a.deleted", 0); - ew.eq(StrUtil.isNotBlank(area), "h.area", area); + Long goodsId = searchParam.getGoodsId(); + String sn = searchParam.getSn(); + // 客户名称 + String nickname = searchParam.getNickname(); + // 产废单位 + String companyName = searchParam.getCompanyName(); + String licensePlate = searchParam.getLicensePlate(); + LocalDateTime startOutTime = searchParam.getStartOutTime(); + LocalDateTime endOutTime = searchParam.getEndOutTime(); + QueryWrapper ew = Wrappers.query() + .eq(null != searchParam.getStationId() && searchParam.getStationId() > 0, "a.station_id", searchParam.getStationId()) + .like(StrUtil.isNotBlank(searchParam.getSn()), "a.sn", searchParam.getSn()) + .like(StrUtil.isNotBlank(searchParam.getLicensePlate()), "d.license_plate", searchParam.getLicensePlate()) + .like(StrUtil.isNotBlank(searchParam.getPhone()), "a.phone", searchParam.getPhone()) + .like(StrUtil.isNotBlank(searchParam.getNickname()), "a.contacts", searchParam.getNickname()) + .like(StrUtil.isNotBlank(transCompanyName), "g.company_name", transCompanyName) + .like(StrUtil.isNotBlank(driverName), "f.driver_name", driverName) + .ge(startTime != null, "a.create_time", startTime) + .le(endTime != null, "a.create_time", endTime) + .eq(orderCategory != null, "a.order_category", orderCategory) + .eq(null != searchParam.getTransCompanyId(), "a.trans_company_id", searchParam.getTransCompanyId()) + .eq("a.deleted", 0) + .eq(StrUtil.isNotBlank(area), "h.area", area) + .eq(goodsId != null, "og.origin_goods_id", goodsId) + .like(StrUtil.isNotBlank(sn), "a.sn", sn) + .like(StrUtil.isNotBlank(nickname), "a.contacts", nickname) + .like(StrUtil.isNotBlank(companyName), "j.company_name", companyName) + .like(StrUtil.isNotBlank(transCompanyName), "g.company_name", transCompanyName) + .like(StrUtil.isNotBlank(licensePlate), "d.license_plate", licensePlate) + .ge(startOutTime != null, "ocio.out_time", startOutTime) + .le(endOutTime != null, "ocio.out_time", endOutTime); historyEW(searchParam, null, ew); List list = baseMapper.exportList(ew); - List detailResults = baseMapper.exportDetailList(ew); + List detailResults = baseMapper.exportDetailList(Wrappers.query() + .in("oei.order_id", list.stream().map(OrderExportResult::getId).collect(Collectors.toList())) + ); List> downList = new ArrayList<>(); List dictItems = dictItemService.list(Wrappers.lambdaQuery() .eq(DictItemEntity::getDictKey, "expense_item_category") @@ -2204,6 +2238,45 @@ public class OrderInfoService extends ServiceImpl userIds = userRoleService.list(Wrappers.lambdaQuery(SysUserRoleEntity.class) + .eq(SysUserRoleEntity::getRoleId, roleEntity.getId())); + TemplateMessageParam param = new TemplateMessageParam(); + param.setTempType(TempType.AUDIT_PENDING.getVal()); + param.setSn(order.getSn()); + UserEntity orderUserEntity = userService.getById(order.getUserId()); + param.setCfCompanyName(orderUserEntity.getNickname()); + OrderGoodsEntity entity = orderGoodsService.getById(order.getGoodsId()); + param.setGoodsName(entity.getGoodsName()); + BizCompanyEntity companyEntity = bizCompanyService.getById(order.getTransCompanyId()); + param.setCompanyName(companyEntity.getCompanyName()); + param.setCreateTime(order.getCreateTime()); + Set openId = new HashSet<>(); + for (SysUserRoleEntity userRoleEntity : userIds) { + UserEntity userEntity = userService.getById(userRoleEntity.getUserId()); + if (null != userEntity && !Strings.isNullOrEmpty(userEntity.getOpenid())) { + if (openId.add(userEntity.getOpenid())) { + param.setUserId(userRoleEntity.getUserId()); + log.info("转办发送审核通知模板消息,参数:{}", param); + wechatTemplateMessageService.sendTemplateMessage(param); + } + } else { + log.info("转办未查到用户信息:{}", userRoleEntity.getUserId()); + } + } + } + } catch (Exception e) { + log.error("通知失败", e); + } } else { throw Exceptions.clierr("未查询到装货地址"); } @@ -2235,4 +2308,77 @@ public class OrderInfoService extends ServiceImpl statistics(OrderStatisticsParam param) { + List areaCode = param.getAreaCode(); + Long goodsId = param.getGoodsId(); + Integer year = param.getYear(); + Integer month = param.getMonth(); + LocalDateTime start = null; + LocalDateTime end = null; + if (year != null) { + if (month == null) { + start = LocalDateTime.of(year, 1, 1, 0, 0, 0); + end = LocalDateTime.of(year, 12, 31, 23, 59, 59); + } else { + start = LocalDateTime.of(year, month, 1, 0, 0, 0); + end = start.plusMonths(1).minusSeconds(1); + } + } + + List> statisticsByArea = baseMapper.statisticsByArea(Wrappers.query() + .in(CollUtil.isNotEmpty(areaCode), "b.area", areaCode) + .eq("a.deleted", 0) + .in("a.order_status", OrderStatus.YiWanCheng, OrderStatus.YiChuChang) + .groupBy("b.area") + ); + List> statisticsByAreaGoods = baseMapper.statisticsByAreaGoods(Wrappers.query() + .in(CollUtil.isNotEmpty(areaCode), "b.area", areaCode) + .eq("a.deleted", 0) + .between(start != null, "a.create_time", start, end) + .eq(goodsId != null, "d.origin_goods_id", goodsId) + .in("a.order_status", OrderStatus.YiWanCheng, OrderStatus.YiChuChang) + .groupBy("b.area", "d.origin_goods_id") + ); + return MapUtil.builder() + .put("statisticsByArea", statisticsByArea) + .put("statisticsByAreaGoods", statisticsByAreaGoods) + .build(); + } + + public PageResult statisticsDetail(PageParam pageParam, OrderPagingSearchParam orderPagingSearchParam) { + String area = orderPagingSearchParam.getArea(); + Long goodsId = orderPagingSearchParam.getGoodsId(); + String sn = orderPagingSearchParam.getSn(); + // 客户名称 + String nickname = orderPagingSearchParam.getNickname(); + // 产废单位 + String companyName = orderPagingSearchParam.getCompanyName(); + String transCompanyName = orderPagingSearchParam.getTransCompanyName(); + String licensePlate = orderPagingSearchParam.getLicensePlate(); + + LocalDateTime startTime = orderPagingSearchParam.getStartTime(); + LocalDateTime endTime = orderPagingSearchParam.getEndTime(); + + LocalDateTime startOutTime = orderPagingSearchParam.getStartOutTime(); + LocalDateTime endOutTime = orderPagingSearchParam.getEndOutTime(); + + Page page = pageParam.toPage(); + + QueryWrapper ew = Wrappers.query() + .eq("a.deleted", 0) + .eq("b.area", area) + .eq("c.origin_goods_id", goodsId) + .like(StrUtil.isNotBlank(sn), "a.sn", sn) + .like(StrUtil.isNotBlank(nickname), "a.contacts", nickname) + .like(StrUtil.isNotBlank(companyName), "j.company_name", companyName) + .like(StrUtil.isNotBlank(transCompanyName), "g.company_name", transCompanyName) + .like(StrUtil.isNotBlank(licensePlate), "e.license_plate", licensePlate) + .ge(startTime != null, "a.create_time", startTime) + .le(endTime != null, "a.create_time", endTime) + .ge(startOutTime != null, "d.out_time", startOutTime) + .le(endOutTime != null, "d.out_time", endOutTime) + .eq("a.order_status", OrderStatus.YiWanCheng); + return PageResult.of(baseMapper.paging(page, ew)); + } } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/sys/user/service/UserService.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/sys/user/service/UserService.java index 8dd51f1..e88c865 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/sys/user/service/UserService.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/sys/user/service/UserService.java @@ -159,6 +159,7 @@ public class UserService extends ServiceImpl implements || bizObj == BizObj.WuYe || bizObj == BizObj.ChaiQian || bizObj == BizObj.SheQu + || bizObj == BizObj.QiTa || bizObj == BizObj.QiYe || bizObj == BizObj.GeTi ) { diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayConfiguration.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayConfiguration.java index b8f0648..c0eb1aa 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayConfiguration.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayConfiguration.java @@ -1,6 +1,7 @@ package com.njzscloud.supervisory.wxPay.config; import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.service.ProfitSharingService; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; import lombok.AllArgsConstructor; @@ -13,6 +14,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StreamUtils; +/** + * @author ljw + */ @Configuration @ConditionalOnClass(WxPayService.class) @EnableConfigurationProperties(WxPayProperties.class) @@ -50,4 +54,10 @@ public class WxPayConfiguration { wxPayService.setConfig(config); return wxPayService; } + + @Bean + @ConditionalOnMissingBean + public ProfitSharingService profitSharingService(WxPayService wxPayService) { + return wxPayService.getProfitSharingService(); + } } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayProperties.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayProperties.java index bfc1c78..ce65225 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayProperties.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/config/WxPayProperties.java @@ -5,36 +5,47 @@ import org.springframework.boot.context.properties.ConfigurationProperties; /** * 微信支付配置 + * @author ljw */ @Data @ConfigurationProperties(prefix = "wechat.pay") public class WxPayProperties { - + /** - * 商户APPID + * 服务商商户APPID */ private String appId; - + /** - * 商户号 + * 服务商商户号 */ private String mchId; - + /** - * API密钥 + * 收款方商户号 + */ + private String subMchId; + + /** + * 分账方商户号 + */ + private String receiverMchId; + + /** + * 服务商API密钥 */ private String apiKey; - + /** - * 证书序列号 + * 服务商证书序列号 */ private String certSerialNo; - + /** - * 私钥文件路径 + * 服务商私钥文件路径 */ private String privateKeyPath; - + /** * 支付回调地址 */ @@ -44,5 +55,5 @@ public class WxPayProperties { * 退款回调地址 */ private String refundNotifyUrl; - + } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/contant/TemplateID.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/contant/TemplateID.java index 514be0b..2c3e5ec 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/contant/TemplateID.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/contant/TemplateID.java @@ -6,7 +6,9 @@ public class TemplateID { public static final String DRIVER_TEMP_ID = "IwHV1nWwu8pO8Ppntyd7KzzV5S00dOEjbsgAFhz9tJM"; - public static final String AUDIT_PENDING = "0SzJv9l51KTvgXvKhQOwifR5pegR3gC1o3IAY8NpEcE"; + // public static final String AUDIT_PENDING = "0SzJv9l51KTvgXvKhQOwifR5pegR3gC1o3IAY8NpEcE"; + + public static final String AUDIT_PENDING = "KYt9UYPf104Xw_wIh6RwKCsK3x3Q6XO1yu9h-q2b3bI"; public static final String AUDIT_OK = "3peGD6joWVyZ7B29lfJH_gK0oRqharynt0sXlIHE4cg"; diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/controller/PaymentController.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/controller/PaymentController.java index e2056a3..22085e4 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/controller/PaymentController.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/controller/PaymentController.java @@ -1,5 +1,6 @@ package com.njzscloud.supervisory.wxPay.controller; +import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; @@ -8,6 +9,7 @@ import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; +import com.google.common.base.Strings; import com.njzscloud.common.core.ex.ExceptionMsg; import com.njzscloud.common.core.ex.Exceptions; import com.njzscloud.common.core.utils.R; @@ -17,7 +19,11 @@ import com.njzscloud.common.wechat.WechatUtil; import com.njzscloud.common.wechat.param.Code2SessionParam; import com.njzscloud.common.wechat.result.Code2SessionResult; import com.njzscloud.supervisory.device.service.DeviceInfoService; +import com.njzscloud.supervisory.money.contant.MoneyChangeCategory; +import com.njzscloud.supervisory.money.contant.MoneyDetailType; import com.njzscloud.supervisory.money.contant.PayStatus; +import com.njzscloud.supervisory.money.pojo.entity.MoneyChangeDetailEntity; +import com.njzscloud.supervisory.money.service.MoneyChangeDetailService; import com.njzscloud.supervisory.order.contant.MoneyWay; import com.njzscloud.supervisory.order.contant.OrderStatus; import com.njzscloud.supervisory.order.contant.PaymentStatus; @@ -30,15 +36,10 @@ import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult; import com.njzscloud.supervisory.order.service.OrderExpenseItemsService; import com.njzscloud.supervisory.order.service.OrderGoodsService; import com.njzscloud.supervisory.order.service.OrderInfoService; -import com.njzscloud.supervisory.money.service.MoneyChangeDetailService; -import com.njzscloud.supervisory.money.contant.MoneyChangeCategory; -import com.njzscloud.supervisory.money.contant.MoneyDetailType; -import com.njzscloud.supervisory.money.pojo.entity.MoneyChangeDetailEntity; import com.njzscloud.supervisory.wxPay.config.WxPayProperties; import com.njzscloud.supervisory.wxPay.dto.RefundRequestDto; import com.njzscloud.supervisory.wxPay.param.PaymentParam; import com.njzscloud.supervisory.wxPay.service.PaymentService; -import com.njzscloud.supervisory.wxPay.service.WeChatPayService; import com.njzscloud.supervisory.wxPay.utils.RequestHolder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -54,6 +55,7 @@ import java.time.LocalDateTime; /** * 支付相关接口 * 使用微信支付官方SDK的生产级实现 + * * @author ljw */ @Slf4j @@ -64,7 +66,6 @@ public class PaymentController { private final OrderExpenseItemsService orderExpenseItemsService; private final OrderInfoService orderInfoService; - private final WeChatPayService wechatPayService; private final WxPayService wxPayService; private final WxPayProperties properties; private final OrderGoodsService orderGoodsService; @@ -88,10 +89,8 @@ public class PaymentController { // 校验支付清单:id、名称、金额 boolean invalidItem = paymentParam.getItems().stream().anyMatch(it -> - it == null - || it.getId() == null - || it.getExpenseItemName() == null || it.getExpenseItemName().length() == 0 - || it.getSettleMoney() == null || it.getSettleMoney().signum() < 0 + it == null || it.getId() == null || Strings.isNullOrEmpty(it.getExpenseItemName()) || it.getSettleMoney() == null + || it.getSettleMoney().signum() < 0 ); if (invalidItem) { throw Exceptions.clierr("支付清单项不合法(id/名称/金额)"); @@ -134,18 +133,25 @@ public class PaymentController { String outTradeNo = generateOutTradeNo(ctx.getSn()); WxPayUnifiedOrderRequest wxRequest = new WxPayUnifiedOrderRequest(); wxRequest.setOutTradeNo(outTradeNo); - wxRequest.setBody("订单支付-" + ctx.getSn()); + wxRequest.setBody("订单支付-108-" + ctx.getSn()); // 转换为分 wxRequest.setTotalFee(ctx.getSettleMoney().multiply(new BigDecimal("100")).intValue()); - wxRequest.setOpenid(getCurrentUserOpenid(paymentParam.getWxCode())); wxRequest.setTradeType("JSAPI"); wxRequest.setSpbillCreateIp(RequestHolder.getClientIP()); // 需要配置实际的回调地址 wxRequest.setNotifyUrl(properties.getNotifyUrl()); + // 设置分账标识为Y,开启分账功能 + wxRequest.setProfitSharing("Y"); + // 在服务商模式下,设置收款方子商户号 + wxRequest.setSubMchId(properties.getSubMchId()); + wxRequest.setSubOpenid(getCurrentUserOpenid(paymentParam.getWxCode())); - // 调用微信支付服务 - WxPayMpOrderResult result = (WxPayMpOrderResult) wechatPayService.createJsapiOrder(wxRequest); - + // 调用微信支付服务(使用服务商WxPayService,因为需要分账权限验证) + log.info("开始创建微信支付JSAPI订单,使用服务商WxPayService,商户号:{},收款方商户号:{},分账商户号:{}", + properties.getMchId(), properties.getSubMchId(), properties.getReceiverMchId()); + log.info("开始创建微信支付JSAPI订单,入参为:{}", JSON.toJSONString(wxRequest)); + WxPayMpOrderResult result = wxPayService.createOrder(wxRequest); + log.info("创建微信支付JSAPI订单,返回结果为:{}", JSON.toJSONString(result)); // 更新订单状态为待支付 orderInfoService.lambdaUpdate() .eq(OrderInfoEntity::getId, paymentParam.getOrderId()) @@ -188,17 +194,23 @@ public class PaymentController { } /** - * 获取当前用户openid + * 获取当前用户openid(使用小程序APPID) */ public String getCurrentUserOpenid(String wxCode) { - // 调用微信API获取openId和unionId - Code2SessionResult sessionResult = WechatUtil.code2Session(new Code2SessionParam().setJs_code(wxCode)); + // 使用小程序APPID(子商户)来获取openid,因为前端wx.login()使用的是子商户APPID + Code2SessionParam param = new Code2SessionParam() + .setJs_code(wxCode); + + log.info("获取openid,使用小程序APPID,code:{}", wxCode); + + Code2SessionResult sessionResult = WechatUtil.code2Session(param); Integer errcode = sessionResult.getErrcode(); if (errcode != null && errcode != 0) { log.error("微信登录失败, errcode: {}, errmsg: {}", errcode, sessionResult.getErrmsg()); throw new UserLoginException(ExceptionMsg.CLI_ERR_MSG, "微信登录失败"); } + log.info("获取openid成功:{}", sessionResult.getOpenid()); return sessionResult.getOpenid(); } @@ -234,6 +246,55 @@ public class PaymentController { return null; } + /** + * 根据订单ID获取微信交易ID + */ + private String getTransactionIdByOrderId(Long orderId) { + log.debug("开始获取交易ID,订单ID:{}", orderId); + + // 从资金明细的扩展信息中获取交易ID + MoneyChangeDetailEntity detail = moneyChangeDetailService.getByOrderIdAndType(orderId, MoneyDetailType.WX); + + if (detail == null) { + log.warn("未找到订单的资金明细记录,订单ID:{}", orderId); + return null; + } + + log.debug("找到资金明细记录,ID:{},扩展信息:{}", detail.getId(), detail.getExtendInfo()); + + if (detail.getExtendInfo() == null) { + log.warn("资金明细扩展信息为空,订单ID:{}", orderId); + return null; + } + + // 解析JSON格式的扩展信息获取transactionId + try { + // 使用简单的字符串解析,实际项目中建议使用JSON库 + String extendInfo = detail.getExtendInfo(); + log.debug("解析扩展信息:{}", extendInfo); + + if (extendInfo.contains("transactionId")) { + int startIndex = extendInfo.indexOf("\"transactionId\":\"") + 16; + int endIndex = extendInfo.indexOf("\"", startIndex); + if (startIndex > 15 && endIndex > startIndex) { + String transactionId = extendInfo.substring(startIndex, endIndex); + log.info("成功获取交易ID:{},订单ID:{}", transactionId, orderId); + return transactionId; + } else { + log.warn("交易ID格式不正确,startIndex:{},endIndex:{},订单ID:{}", startIndex, endIndex, orderId); + } + } else { + log.warn("扩展信息中不包含transactionId,订单ID:{}", orderId); + } + } catch (Exception e) { + log.error("解析交易ID异常,订单ID:{},扩展信息:{},错误:{}", orderId, detail.getExtendInfo(), e.getMessage(), e); + } + + log.warn("获取交易ID失败,订单ID:{}", orderId); + return null; + } + + /** * 微信支付回调接口 */ @@ -268,6 +329,26 @@ public class PaymentController { log.info("支付回调:资金明细状态更新为已支付,订单ID:{}", entity.getId()); } + // 执行分账 + log.info("支付回调:开始执行分账,订单ID:{},支付金额:{}元,交易ID:{}", + entity.getId(), entity.getSettleMoney(), notifyResult.getTransactionId()); + try { + paymentService.profitSharingAsync(entity.getId(), notifyResult.getTransactionId(), entity.getSettleMoney()); + log.info("支付回调:分账成功,订单ID:{}", entity.getId()); + } catch (Exception e) { + log.error("支付回调:分账失败,订单ID:{},支付金额:{}元,交易ID:{},错误:{}", + entity.getId(), entity.getSettleMoney(), notifyResult.getTransactionId(), e.getMessage(), e); + // 分账失败不影响支付成功,但需要记录 + } + + // 保存交易ID到资金明细的扩展信息中 + if (payDetail != null) { + // 将交易ID保存到扩展信息中,格式:{"transactionId":"xxx"} + String extendInfo = String.format("{\"transactionId\":\"%s\"}", notifyResult.getTransactionId()); + payDetail.setExtendInfo(extendInfo); + moneyChangeDetailService.updateById(payDetail); + } + if (out) { DeviceInfoService.open(orderSn); } @@ -321,9 +402,64 @@ public class PaymentController { throw Exceptions.clierr("退款金额总和不能超过订单已结算金额"); } refundRequest.setOutTradeNo(orderInfo.getOutTradeNo()); + + // 如果是微信支付,需要先执行分账回退 + if (SettlementWay.CASH.getVal().equals(ctx.getOiPayWay())) { + log.info("退款处理:开始分账回退,订单ID:{},退款金额:{}元,支付方式:{}", + orderInfo.getId(), refundRequest.getRefundAmount(), ctx.getOiPayWay()); + try { + paymentService.profitSharingReturn(orderInfo.getId(), refundRequest.getRefundAmount()); + log.info("退款前分账回退成功,订单ID:{},退款金额:{}", orderInfo.getId(), refundRequest.getRefundAmount()); + } catch (Exception e) { + log.error("退款前分账回退失败,订单ID:{},退款金额:{}元,错误:{}", + orderInfo.getId(), refundRequest.getRefundAmount(), e.getMessage(), e); + throw Exceptions.clierr("分账回退失败,无法退款"); + } + } + paymentService.refund(refundRequest, ctx, Boolean.FALSE); - if (SettlementWay.MONTH.getVal().equals(ctx.getOiPayWay()) || SettlementWay.BALANCE.getVal().equals(ctx.getOiPayWay())) { + + // 微信支付的退款处理 + if (SettlementWay.CASH.getVal().equals(ctx.getOiPayWay())) { + // 计算剩余金额 = 原结算金额 - 已退款金额 - 本次退款金额 + BigDecimal remainingAmount = ctx.getSettleMoney() + .subtract(ctx.getRefundMoney() == null ? BigDecimal.ZERO : ctx.getRefundMoney()) + .subtract(refundRequest.getRefundAmount()); + + // 如果剩余金额大于0,说明是部分退款,需要重新分账 + if (remainingAmount.compareTo(BigDecimal.ZERO) > 0) { + log.info("部分退款:检测到剩余金额{}元,开始重新分账,订单ID:{}", remainingAmount, orderInfo.getId()); + try { + // 查询原始交易ID + String transactionId = getTransactionIdByOrderId(orderInfo.getId()); + if (transactionId != null) { + log.info("部分退款:获取到交易ID:{},开始重新分账", transactionId); + paymentService.reProfitSharing(orderInfo.getId(), transactionId, remainingAmount); + log.info("部分退款后重新分账成功,订单ID:{},剩余金额:{}元,交易ID:{}", + orderInfo.getId(), remainingAmount, transactionId); + } else { + log.warn("部分退款后无法获取交易ID,跳过重新分账,订单ID:{}", orderInfo.getId()); + log.warn("请检查资金明细表中是否正确存储了交易ID信息"); + } + } catch (Exception e) { + log.error("部分退款后重新分账失败,订单ID:{},剩余金额:{}元,错误:{}", + orderInfo.getId(), remainingAmount, e.getMessage(), e); + // 重新分账失败不影响退款成功,但需要记录 + } + } else { + log.info("部分退款:剩余金额为0,无需重新分账,订单ID:{}", orderInfo.getId()); + } + // 更新订单状态为已退款 + orderInfoService.lambdaUpdate() + .eq(OrderInfoEntity::getId, refundRequest.getOrderId()) + .set(OrderInfoEntity::getPaymentStatus, PaymentStatus.YiTuiKuan) + .set(OrderInfoEntity::getRefundMoney, + (ctx.getRefundMoney() == null ? BigDecimal.ZERO : ctx.getRefundMoney()).add(refundRequest.getRefundAmount())) + .set(OrderInfoEntity::getRefundTime, LocalDateTime.now()) + .update(); + } else if (SettlementWay.MONTH.getVal().equals(ctx.getOiPayWay()) || SettlementWay.BALANCE.getVal().equals(ctx.getOiPayWay())) { + // 月结和余额支付的退款处理 orderInfoService.lambdaUpdate() .eq(OrderInfoEntity::getId, refundRequest.getOrderId()) .set(OrderInfoEntity::getPaymentStatus, PaymentStatus.YiTuiKuan) @@ -380,4 +516,54 @@ public class PaymentController { return WxPayNotifyResponse.fail(e.getMessage()); } } + + /** + * 发起支付(使用生产级微信支付SDK) + */ + @PostMapping("/payTest") + @Transactional(rollbackFor = Exception.class) + public void payTest() { + // 构建微信支付请求 + WxPayUnifiedOrderRequest wxRequest = new WxPayUnifiedOrderRequest(); + wxRequest.setOutTradeNo("53456346xdfgsdfg"); + wxRequest.setBody("订单支付-108-"); + // 转换为分 + BigDecimal settleMoney = new BigDecimal("0.01"); + wxRequest.setTotalFee(settleMoney.multiply(new BigDecimal("100")).intValue()); + // wxRequest.setOpenid("oaepd1zd7umBcg0v-dnVAN4urVc0"); + wxRequest.setSubOpenid("oaepd1zd7umBcg0v-dnVAN4urVc0"); + wxRequest.setTradeType("JSAPI"); + wxRequest.setSpbillCreateIp(RequestHolder.getClientIP()); + // 需要配置实际的回调地址 + wxRequest.setNotifyUrl(properties.getNotifyUrl()); + // 设置分账标识为Y,开启分账功能 + wxRequest.setProfitSharing("Y"); + // 在服务商模式下,设置子商户号 + wxRequest.setSubMchId(properties.getSubMchId()); + + // 调用微信支付服务(使用服务商WxPayService,因为需要分账权限验证) + try { + log.info("测试方法:开始创建微信支付JSAPI订单,使用服务商WxPayService,商户号:{},收款方商户号:{},分账商户号:{}", + properties.getMchId(), properties.getSubMchId(), properties.getReceiverMchId()); + log.info("测试方法:开始创建微信支付JSAPI订单,入参为:{}", JSON.toJSONString(wxRequest)); + WxPayMpOrderResult result = wxPayService.createOrder(wxRequest); + log.info("测试方法:创建微信支付JSAPI订单,返回结果为:{}", JSON.toJSONString(result)); + } catch (WxPayException e) { + log.error("测试方法:创建订单失败", e); + throw new RuntimeException(e); + } + } + + /** + * 退款测试 + */ + @PostMapping("/refundTest") + @Transactional(rollbackFor = Exception.class) + public void refundTest(@RequestBody RefundRequestDto refundRequest) { + log.info("测试方法:开始微信退款,使用服务商WxPayService,商户号:{},收款方商户号:{},分账商户号:{}", + properties.getMchId(), properties.getSubMchId(), properties.getReceiverMchId()); + PaymentContextResult ctx = orderInfoService.paymentContext(refundRequest.getOrderId()); + paymentService.refund(refundRequest, ctx, Boolean.FALSE); + } + } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/param/TemplateMessageParam.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/param/TemplateMessageParam.java index 99be810..7672fe4 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/param/TemplateMessageParam.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/param/TemplateMessageParam.java @@ -5,6 +5,8 @@ import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import java.time.LocalDateTime; + /** * @author ljw */ @@ -59,4 +61,14 @@ public class TemplateMessageParam { */ private String licensePlate; + /** + * 清运公司 + */ + private String companyName; + + /** + * 时间 + */ + private LocalDateTime createTime; + } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/PaymentService.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/PaymentService.java index 8a5a19f..99af2b7 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/PaymentService.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/PaymentService.java @@ -1,8 +1,11 @@ package com.njzscloud.supervisory.wxPay.service; +import com.github.binarywang.wxpay.exception.WxPayException; import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult; import com.njzscloud.supervisory.wxPay.dto.RefundRequestDto; +import java.math.BigDecimal; + /** * 退款服务接口 * @author ljw @@ -14,4 +17,23 @@ public interface PaymentService { */ void refund(RefundRequestDto refundRequest, PaymentContextResult ctx, Boolean isChange); + /** + * 执行分账(支付成功后) + */ + void profitSharing(Long orderId, String transactionId, BigDecimal totalAmount) throws WxPayException; + + /** + * 分账回退 + */ + void profitSharingReturn(Long orderId, BigDecimal refundAmount) throws WxPayException; + + /** + * 重新分账剩余金额(用于部分退款后) + */ + void reProfitSharing(Long orderId, String transactionId, BigDecimal remainingAmount) throws WxPayException; + + /** + * 执行分账(支付成功后) + */ + void profitSharingAsync(Long orderId, String transactionId, BigDecimal totalAmount) throws WxPayException; } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/PaymentServiceImpl.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/PaymentServiceImpl.java index 56f1c0b..a43bc63 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/PaymentServiceImpl.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/PaymentServiceImpl.java @@ -1,6 +1,14 @@ package com.njzscloud.supervisory.wxPay.service.impl; import cn.hutool.core.util.IdUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingRequest; +import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingReturnRequest; +import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingResult; +import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingReturnResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.ProfitSharingService; import com.njzscloud.common.core.ex.Exceptions; import com.njzscloud.supervisory.money.contant.MoneyChangeCategory; import com.njzscloud.supervisory.money.contant.MoneyDetailType; @@ -17,12 +25,15 @@ import com.njzscloud.supervisory.wxPay.service.PaymentService; import com.njzscloud.supervisory.wxPay.service.WeChatPayService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.math.RoundingMode; /** * 退款服务实现类 + * * @author ljw */ @Slf4j @@ -30,11 +41,100 @@ import java.math.BigDecimal; @RequiredArgsConstructor public class PaymentServiceImpl implements PaymentService { + // 分账比例:千分之一 + private static final int PROFIT_SHARING_RATIO = 1; + private static final int PROFIT_SHARING_BASE = 1000; + private final WxPayProperties properties; private final WeChatPayService wechatPayService; + private final ProfitSharingService profitSharingService; private final MoneyAccountService moneyAccountService; private final MoneyChangeDetailService moneyChangeDetailService; + /** + * 执行分账(千分之一给特约商户) + */ + @Override + public void profitSharing(Long orderId, String transactionId, BigDecimal totalAmount) throws WxPayException { + log.info("========== 开始执行分账 =========="); + log.info("分账参数 - 订单ID:{},交易ID:{},总金额:{}元", orderId, transactionId, totalAmount); + + // 计算分账金额:总金额 * (1/1000) + BigDecimal sharingAmount = totalAmount.multiply(BigDecimal.valueOf(PROFIT_SHARING_RATIO)) + .divide(BigDecimal.valueOf(PROFIT_SHARING_BASE), 2, RoundingMode.DOWN); + + log.info("分账金额计算 - 总金额:{}元,分账比例:{}/{},分账金额:{}元", + totalAmount, PROFIT_SHARING_RATIO, PROFIT_SHARING_BASE, sharingAmount); + + if (sharingAmount.compareTo(BigDecimal.ZERO) <= 0) { + log.info("分账金额为0,跳过分账,订单ID:{}", orderId); + log.info("========== 分账跳过结束 =========="); + return; + } + + log.info("分账商户信息 - 特约商户:{},收款商户:{},分账商户:{}", properties.getMchId(), properties.getSubMchId(), + properties.getReceiverMchId()); + + // 构建分账请求 + ProfitSharingRequest request = new ProfitSharingRequest(); + request.setTransactionId(transactionId); + String outOrderNo = "PS_" + orderId + "_" + System.currentTimeMillis(); + request.setOutOrderNo(outOrderNo); + + log.info("分账请求构建 - 分账单号:{},交易ID:{}", outOrderNo, transactionId); + + // 创建分账接收方JSON字符串 + // 仅分账给 receiver-mch-id(1649452354),比例为千分之一;收款方为 subMchId(不作为接收方再次出现) + StringBuilder receiversJson = new StringBuilder("["); + int receiverMoney = sharingAmount.multiply(BigDecimal.valueOf(100)).intValue(); + receiversJson.append("{\"type\":\"MERCHANT_ID\",\"account\":\"").append(properties.getReceiverMchId()).append("\",") + .append("\"amount\":").append(receiverMoney) + .append(",\"description\":\"分账商户108\"}"); + receiversJson.append("]"); + request.setReceivers(receiversJson.toString()); + // 指定分账发生在收款子商户下 + request.setSubMchId(properties.getSubMchId()); + + // 计算子商户留存金额(用于日志) + BigDecimal subMerchantAmount = totalAmount.subtract(sharingAmount); + + log.info("分账接收方配置 - 特约商户分账:{}元({}分),子商户(留存):{}元", + sharingAmount, receiverMoney, + subMerchantAmount); + log.info("分账接收方JSON:{}", receiversJson); + + try { + // 调用分账接口 + log.info("调用微信分账接口..."); + ProfitSharingResult result = profitSharingService.profitSharing(request); + + log.info("分账成功!订单ID:{},总金额:{}元", orderId, totalAmount); + log.info("分账详情 - 特约商户分账:{}元,子商户分账:{}元", sharingAmount, subMerchantAmount); + log.info("分账结果 - 分账单号:{},微信返回状态:{}", result.getOutOrderNo(), result); + + // 将分账单号保存到资金明细的扩展信息中 + try { + MoneyChangeDetailEntity payDetail = moneyChangeDetailService.getByOrderIdAndType(orderId, MoneyDetailType.WX); + if (payDetail != null && payDetail.getExtendInfo() != null) { + // 解析现有的extendInfo,添加分账单号 + JSONObject extendJson = JSON.parseObject(payDetail.getExtendInfo()); + extendJson.put("profitSharingOrderNo", outOrderNo); + payDetail.setExtendInfo(extendJson.toJSONString()); + moneyChangeDetailService.updateById(payDetail); + log.info("分账单号已保存到资金明细,订单ID:{},分账单号:{}", orderId, outOrderNo); + } + } catch (Exception e) { + log.warn("保存分账单号到资金明细失败,订单ID:{},分账单号:{},错误:{}", orderId, outOrderNo, e.getMessage()); + } + + log.info("========== 分账执行成功 =========="); + } catch (WxPayException e) { + log.error("分账执行失败!订单ID:{},错误信息:{}", orderId, e.getMessage(), e); + log.error("失败详情 - 交易ID:{},分账单号:{},金额:{}元", transactionId, outOrderNo, totalAmount); + throw e; + } + } + /** * 申请退款 */ @@ -46,7 +146,7 @@ public class PaymentServiceImpl implements PaymentService { // 根据支付方式处理退款 if (SettlementWay.CASH.getVal().equals(ctx.getOiPayWay())) { - //微信退款 生成退款单号 + // 微信退款 生成退款单号 String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr(); // 微信退全款 int money = ctx.getSettleMoney().multiply(new BigDecimal("100")).intValue(); @@ -124,5 +224,174 @@ public class PaymentServiceImpl implements PaymentService { log.info("公司账户余额恢复成功,账户ID:{},恢复金额:{}", ctx.getCompanyAccountId(), refundAmount); } + /** + * 分账回退 + */ + @Override + public void profitSharingReturn(Long orderId, BigDecimal refundAmount) throws WxPayException { + log.info("========== 开始执行分账回退 =========="); + log.info("分账回退参数 - 订单ID:{},退款金额:{}元", orderId, refundAmount); + + // 计算需要回退的分账金额:退款金额 * (1/1000) + BigDecimal returnAmount = refundAmount.multiply(BigDecimal.valueOf(PROFIT_SHARING_RATIO)) + .divide(BigDecimal.valueOf(PROFIT_SHARING_BASE), 2, RoundingMode.DOWN); + + log.info("分账回退金额计算 - 退款金额:{}元,回退比例:{}/{},回退金额:{}元", + refundAmount, PROFIT_SHARING_RATIO, PROFIT_SHARING_BASE, returnAmount); + + if (returnAmount.compareTo(BigDecimal.ZERO) <= 0) { + log.info("分账回退金额为0,跳过分账回退,订单ID:{}", orderId); + log.info("========== 分账回退跳过结束 =========="); + return; + } + + log.info("分账回退商户信息 - 回退商户:{},回退类型:MERCHANT_ID", properties.getReceiverMchId()); + + // 构建分账回退请求 + ProfitSharingReturnRequest request = new ProfitSharingReturnRequest(); + String outReturnNo = "PSR_" + orderId + "_" + System.currentTimeMillis(); + request.setOutReturnNo(outReturnNo); + request.setReturnAccountType("MERCHANT_ID"); + // 从分账商户回退 + request.setReturnAccount(properties.getReceiverMchId()); + request.setDescription("订单退款分账回退108"); + // 设置回退金额(分) + int returnMoney = returnAmount.multiply(BigDecimal.valueOf(100)).intValue(); + request.setReturnAmount(returnMoney); + // 指定回退操作针对的子商户(收款方) + request.setSubMchId(properties.getSubMchId()); + + // 从资金明细中获取原始分账单号 + String outOrderNo = null; + try { + MoneyChangeDetailEntity payDetail = moneyChangeDetailService.getByOrderIdAndType(orderId, MoneyDetailType.WX); + if (payDetail != null && payDetail.getExtendInfo() != null) { + JSONObject extendJson = JSON.parseObject(payDetail.getExtendInfo()); + outOrderNo = extendJson.getString("profitSharingOrderNo"); + log.info("从资金明细中获取到分账单号:{},订单ID:{}", outOrderNo, orderId); + } + } catch (Exception e) { + log.warn("从资金明细中获取分账单号失败,订单ID:{},错误:{}", orderId, e.getMessage()); + } + + // 如果没有找到分账单号,使用模糊匹配(不推荐,可能会导致回退失败) + if (outOrderNo == null) { + outOrderNo = "PS_" + orderId + "_*"; + log.warn("未找到分账单号,使用模糊匹配:{},订单ID:{}", outOrderNo, orderId); + } + + request.setOutOrderNo(outOrderNo); + + log.info("分账回退请求构建 - 回退单号:{},原始分账单号:{},回退金额:{}元({}分)", + outReturnNo, outOrderNo, returnAmount, returnMoney); + + try { + // 调用分账回退接口 + log.info("调用微信分账回退接口..."); + ProfitSharingReturnResult result = profitSharingService.profitSharingReturn(request); + + log.info("分账回退成功!订单ID:{},退款金额:{}元", orderId, refundAmount); + log.info("分账回退详情 - 回退金额:{}元,从商户:{}回退", returnAmount, properties.getMchId()); + log.info("分账回退结果 - 回退单号:{},微信返回状态:{}", result.getOutReturnNo(), result); + log.info("========== 分账回退执行成功 =========="); + } catch (WxPayException e) { + log.error("分账回退执行失败!订单ID:{},错误信息:{}", orderId, e.getMessage(), e); + log.error("失败详情 - 退款金额:{}元,回退金额:{}元,回退单号:{}", refundAmount, returnAmount, outReturnNo); + throw e; + } + } + + /** + * 重新分账剩余金额(用于部分退款后) + */ + @Override + public void reProfitSharing(Long orderId, String transactionId, BigDecimal remainingAmount) throws WxPayException { + log.info("========== 开始执行重新分账 =========="); + log.info("重新分账参数 - 订单ID:{},交易ID:{},剩余金额:{}元", orderId, transactionId, remainingAmount); + + // 计算新的分账金额:剩余金额 * (1/1000) + BigDecimal sharingAmount = remainingAmount.multiply(BigDecimal.valueOf(PROFIT_SHARING_RATIO)) + .divide(BigDecimal.valueOf(PROFIT_SHARING_BASE), 2, RoundingMode.DOWN); + + log.info("重新分账金额计算 - 剩余金额:{}元,分账比例:{}/{},分账金额:{}元", + remainingAmount, PROFIT_SHARING_RATIO, PROFIT_SHARING_BASE, sharingAmount); + + if (sharingAmount.compareTo(BigDecimal.ZERO) <= 0) { + log.info("重新分账金额为0,跳过重新分账,订单ID:{}", orderId); + log.info("========== 重新分账跳过结束 =========="); + return; + } + + log.info("重新分账商户信息 - 分账商户:{},收款商户:{}", properties.getReceiverMchId(), properties.getSubMchId()); + + // 构建重新分账请求 + ProfitSharingRequest request = new ProfitSharingRequest(); + request.setTransactionId(transactionId); + String outOrderNo = "RE_PS_" + orderId + "_" + System.currentTimeMillis(); + request.setOutOrderNo(outOrderNo); + + log.info("重新分账请求构建 - 分账单号:{},交易ID:{}", outOrderNo, transactionId); + + // 创建分账接收方JSON字符串(仅给分账接收方分一部分,收款子商户保持留存) + StringBuilder receiversJson = new StringBuilder("["); + int receiverMoney = sharingAmount.multiply(BigDecimal.valueOf(100)).intValue(); + receiversJson.append("{\"type\":\"MERCHANT_ID\",\"account\":\"").append(properties.getReceiverMchId()).append("\",") + .append("\"amount\":").append(receiverMoney) + .append(",\"description\":\"部分退款后重新分账-分账商户\"}"); + receiversJson.append("]"); + request.setReceivers(receiversJson.toString()); + // 指定分账发生在收款子商户下 + request.setSubMchId(properties.getSubMchId()); + + BigDecimal subMerchantAmount = remainingAmount.subtract(sharingAmount); + log.info("重新分账接收方配置 - 特约商户分账:{}元({}分),子商户(留存):{}元", + sharingAmount, receiverMoney, + subMerchantAmount); + log.info("重新分账接收方JSON:{}", receiversJson); + + try { + // 调用分账接口 + log.info("调用微信重新分账接口..."); + ProfitSharingResult result = profitSharingService.profitSharing(request); + + log.info("重新分账成功!订单ID:{},剩余金额:{}元", orderId, remainingAmount); + log.info("重新分账详情 - 特约商户分账:{}元,子商户分账:{}元", sharingAmount, subMerchantAmount); + log.info("重新分账结果 - 分账单号:{},微信返回状态:{}", result.getOutOrderNo(), result); + log.info("========== 重新分账执行成功 =========="); + } catch (WxPayException e) { + log.error("重新分账执行失败!订单ID:{},错误信息:{}", orderId, e.getMessage(), e); + log.error("失败详情 - 交易ID:{},分账单号:{},剩余金额:{}元", transactionId, outOrderNo, remainingAmount); + throw e; + } + } + + /** + * 异步执行分账(支付成功后,支持重试) + * 延迟10秒执行,避免订单处理中导致的分账失败 + */ + @Override + @Async + public void profitSharingAsync(Long orderId, String transactionId, BigDecimal totalAmount) { + log.info("异步分账任务启动,订单ID:{},交易ID:{},总金额:{}元", orderId, transactionId, totalAmount); + + try { + // 延迟10秒执行分账,避免 ORDER_NOT_READY 错误 + Thread.sleep(10000); + log.info("延迟10秒后开始执行分账,订单ID:{}", orderId); + + // 调用同步分账方法 + profitSharing(orderId, transactionId, totalAmount); + log.info("异步分账执行成功,订单ID:{}", orderId); + + } catch (InterruptedException e) { + log.error("异步分账延迟被中断,订单ID:{},错误:{}", orderId, e.getMessage()); + Thread.currentThread().interrupt(); // 恢复中断状态 + } catch (Exception e) { + log.error("异步分账执行失败,订单ID:{},交易ID:{},总金额:{}元,错误:{}", + orderId, transactionId, totalAmount, e.getMessage(), e); + // 异步执行失败不抛出异常,避免影响主流程 + } + } + } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WeChatPayServiceImpl.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WeChatPayServiceImpl.java index c615b2e..dd2b399 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WeChatPayServiceImpl.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WeChatPayServiceImpl.java @@ -5,6 +5,7 @@ import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult; import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; +import com.njzscloud.supervisory.wxPay.config.WxPayProperties; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.njzscloud.supervisory.wxPay.service.WeChatPayService; @@ -21,6 +22,7 @@ import org.springframework.stereotype.Service; public class WeChatPayServiceImpl implements WeChatPayService { private final WxPayService wxPayService; + private final WxPayProperties wxPayProperties; @Override public Object createJsapiOrder(WxPayUnifiedOrderRequest request) throws WxPayException { @@ -96,6 +98,7 @@ public class WeChatPayServiceImpl implements WeChatPayService { .notifyUrl(notifyUrl) .build(); + refundRequest.setSubMchId(wxPayProperties.getSubMchId()); WxPayRefundResult refundResult = wxPayService.refund(refundRequest); return refundResult.getRefundId(); } catch (WxPayException e) { diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WechatTemplateMessageServiceImpl.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WechatTemplateMessageServiceImpl.java index 5ab3212..1470ab3 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WechatTemplateMessageServiceImpl.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/wxPay/service/impl/WechatTemplateMessageServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.web.client.RestTemplate; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -122,8 +123,8 @@ public class WechatTemplateMessageServiceImpl implements WechatTemplateMessageSe @Override public void sendTemplateMessage(TemplateMessageParam param) { UserEntity userEntity = userService.getById(param.getUserId()); -// UserEntity userEntity = new UserEntity(); -// userEntity.setOpenid("owC-r2Bf1axK1C2MQ6nOaCDbKuHw"); + // UserEntity userEntity = new UserEntity(); + // userEntity.setOpenid("owC-r2Bf1axK1C2MQ6nOaCDbKuHw"); if (!Strings.isNullOrEmpty(userEntity.getOpenid())) { Map data; if (TempType.TRANS_COMPANY.getVal().equals(param.getTempType())) { @@ -147,10 +148,13 @@ public class WechatTemplateMessageServiceImpl implements WechatTemplateMessageSe // 待审核 // 构建模板数据 data = new HashMap<>(); - data.put("character_string5", new TemplateData(param.getSn())); - data.put("thing11", new TemplateData(param.getGoodsName())); - data.put("thing9", new TemplateData(param.getCfCompanyName())); - data.put("car_number13", new TemplateData(param.getLicensePlate())); + data.put("character_string2", new TemplateData(param.getSn())); + data.put("thing3", new TemplateData(truncateForThingField(param.getCfCompanyName()))); + data.put("thing5", new TemplateData(param.getGoodsName())); + data.put("thing6", new TemplateData(truncateForThingField(param.getCompanyName()))); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm"); + String formattedTime = param.getCreateTime().format(formatter); + data.put("time1", new TemplateData(formattedTime)); sendMessage(userEntity.getOpenid(), TemplateID.AUDIT_PENDING, data, null, "pages/adminPage/jianGuan"); } else if (TempType.AUDIT_OK.getVal().equals(param.getTempType())) { // 审核通过 diff --git a/njzscloud-svr/src/main/resources/application-dev.yml b/njzscloud-svr/src/main/resources/application-dev.yml index 5517cc0..bb5c3bb 100644 --- a/njzscloud-svr/src/main/resources/application-dev.yml +++ b/njzscloud-svr/src/main/resources/application-dev.yml @@ -68,13 +68,21 @@ wechat: app-secret: 66c98dc487a372acb4f1931b38fee8ff base-url: https://api.weixin.qq.com pay: - app-id: wx989ea47a5ddf9bfb - mch-id: 1729703110 - # API密钥(32位字符串) - api-key: KXM36nZCXji1sQt75tGk77k7b2K5RBpf - # 证书序列号 - cert-serial-no: 1BCB1533688F349541C7B636EF67C666828BADBA - # 文件路径 + # 服务商 app-id + app-id: wxc09dc9d9bcc94edc + # 服务商 mch-id + mch-id: 1659535913 + # 收款方 sub-mch-id + sub-mch-id: 1729703110 + # 分账方 sub-mch-id + receiver-mch-id: 1649452354 + # 服务商API密钥(32位字符串) + api-key: 00ba7ceab606427071d5d755ea99e976 +# api-key: KXM36nZCXji1sQt75tGk77k7b2K5RBpf + # 服务商证书序列号 + cert-serial-no: 615FBF704AB2838EE8B387F633B340640E078F4B +# cert-serial-no: 1BCB1533688F349541C7B636EF67C666828BADBA + # 服务商文件路径 private-key-path: classpath:cert/apiclient_cert.p12 # private-key-path: D:/project/再昇云/代码/njzscloud/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 # 支付回调地址 diff --git a/njzscloud-svr/src/main/resources/application-prod.yml b/njzscloud-svr/src/main/resources/application-prod.yml index 0037142..ae83832 100644 --- a/njzscloud-svr/src/main/resources/application-prod.yml +++ b/njzscloud-svr/src/main/resources/application-prod.yml @@ -58,13 +58,21 @@ wechat: app-secret: 66c98dc487a372acb4f1931b38fee8ff base-url: https://api.weixin.qq.com pay: - app-id: wx989ea47a5ddf9bfb - mch-id: 1729703110 - # API密钥(32位字符串) - api-key: KXM36nZCXji1sQt75tGk77k7b2K5RBpf - # 证书序列号 - cert-serial-no: 1BCB1533688F349541C7B636EF67C666828BADBA - # 文件路径 + # 服务商 app-id + app-id: wxc09dc9d9bcc94edc + # 服务商 mch-id + mch-id: 1659535913 + # 收款方 sub-mch-id + sub-mch-id: 1729703110 + # 分账方 sub-mch-id + receiver-mch-id: 1649452354 + # 服务商API密钥(32位字符串) + api-key: 00ba7ceab606427071d5d755ea99e976 + # api-key: KXM36nZCXji1sQt75tGk77k7b2K5RBpf + # 服务商证书序列号 + cert-serial-no: 615FBF704AB2838EE8B387F633B340640E078F4B + # cert-serial-no: 1BCB1533688F349541C7B636EF67C666828BADBA + # 服务商文件路径 private-key-path: classpath:cert/apiclient_cert.p12 # private-key-path: D:/project/再昇云/代码/njzscloud/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 # 支付回调地址 diff --git a/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 b/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 index aa26919..6c9f077 100644 Binary files a/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 and b/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 differ diff --git a/njzscloud-svr/src/main/resources/cert/cz_apiclient_cert.p12 b/njzscloud-svr/src/main/resources/cert/cz_apiclient_cert.p12 new file mode 100644 index 0000000..aa26919 Binary files /dev/null and b/njzscloud-svr/src/main/resources/cert/cz_apiclient_cert.p12 differ diff --git a/njzscloud-svr/src/main/resources/mapper/order/OrderInfoMapper.xml b/njzscloud-svr/src/main/resources/mapper/order/OrderInfoMapper.xml index 7298602..da9a37b 100644 --- a/njzscloud-svr/src/main/resources/mapper/order/OrderInfoMapper.xml +++ b/njzscloud-svr/src/main/resources/mapper/order/OrderInfoMapper.xml @@ -267,18 +267,17 @@ f.driver_name, a.checker_memo, og.goods_name - FROM - order_info a + FROM order_info a LEFT JOIN biz_company b ON b.id = a.station_id LEFT JOIN biz_company g ON g.id = a.trans_company_id LEFT JOIN biz_truck d ON d.id = a.truck_id - LEFT JOIN sys_dict_item e ON d.truck_category = e.val - AND e.dict_key = 'vehicle_type' + LEFT JOIN sys_dict_item e ON d.truck_category = e.val AND e.dict_key = 'vehicle_type' LEFT JOIN order_car_in_out ocio ON ocio.id = a.car_in_out_id LEFT JOIN sys_user su ON a.user_id = su.id LEFT JOIN order_cargo_place h ON h.id = a.cargo_place_id LEFT JOIN biz_driver f ON f.id = a.driver_id LEFT JOIN order_goods og ON og.id = a.goods_id + LEFT JOIN biz_company j ON j.user_id = a.user_id ${ew.customSqlSegment} @@ -292,8 +291,7 @@ oei.expense_item_category, oei.expense_item_name, oei.settle_money - FROM - order_expense_items oei + FROM order_expense_items oei LEFT JOIN order_info a ON a.id = oei.order_id LEFT JOIN biz_company g ON g.id = a.trans_company_id LEFT JOIN biz_truck d ON d.id = a.truck_id @@ -375,4 +373,31 @@ AND a.deleted = 0 ORDER BY b.sort, b.create_time, b.id + +