Merge branch 'dev'
commit
6c11603af1
|
|
@ -105,7 +105,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-pay</artifactId>
|
||||
<version>4.7.0</version>
|
||||
<version>4.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- IP地址解析库 -->
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ public class BizAuditConfigController {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 列表查询
|
||||
*/
|
||||
@GetMapping("/list_all")
|
||||
public R<List<BizAuditConfigEntity>> listAll() {
|
||||
return R.success(bizAuditConfigService.listAll());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 复制文件
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<BizAuditConfigMapper, Biz
|
|||
}
|
||||
}
|
||||
|
||||
public List<BizAuditConfigEntity> listAll() {
|
||||
UserDetail userDetail = SecurityUtil.loginUser();
|
||||
Set<String> roles = userDetail.getRoles();
|
||||
Assert.isTrue(CollUtil.isNotEmpty(roles) && roles.contains(ROLE_JG), () -> Exceptions.clierr("非监管角色,不能查看"));
|
||||
List<BizAuditConfigEntity> all;
|
||||
if (SecurityUtil.isAdmin()) {
|
||||
all = new ArrayList<>();
|
||||
List<BizAuditConfigEntity> list = list();
|
||||
for (BizAuditConfigEntity bizAuditConfigEntity : list) {
|
||||
if (all.stream().anyMatch(item -> item.getArea().equals(bizAuditConfigEntity.getArea()))) continue;
|
||||
all.add(bizAuditConfigEntity);
|
||||
}
|
||||
} else {
|
||||
List<BizAuditConfigEntity> areaList = list(Wrappers.<BizAuditConfigEntity>lambdaQuery().in(BizAuditConfigEntity::getAreaRole, roles));
|
||||
List<String> areas = areaList.stream().map(BizAuditConfigEntity::getArea).collect(Collectors.toList());
|
||||
List<BizAuditConfigEntity> cityList = list(Wrappers.<BizAuditConfigEntity>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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,7 +173,8 @@ public class BizCompanyService extends ServiceImpl<BizCompanyMapper, BizCompanyE
|
|||
BizObj.WuYe.getVal(),
|
||||
BizObj.ShiGongDanWei.getVal(),
|
||||
BizObj.ChaiQian.getVal(),
|
||||
BizObj.SheQu.getVal());
|
||||
BizObj.SheQu.getVal(),
|
||||
BizObj.QiTa.getVal());
|
||||
} else if (bizObj == BizObj.QingYunGongSi) {
|
||||
bizObjList = Arrays.asList(
|
||||
BizObj.QiYe.getVal(),
|
||||
|
|
|
|||
|
|
@ -72,7 +72,8 @@ public class CustomerService {
|
|||
BizObj.WuYe,
|
||||
BizObj.ShiGongDanWei,
|
||||
BizObj.ChaiQian,
|
||||
BizObj.SheQu).map(BizObj::getVal).collect(Collectors.toList()))
|
||||
BizObj.SheQu,
|
||||
BizObj.QiTa).map(BizObj::getVal).collect(Collectors.toList()))
|
||||
.eq("c.audit_status", AuditStatus.TongGuo);
|
||||
;
|
||||
} else if (bizObj == BizObj.QingYunGongSi) {
|
||||
|
|
@ -88,6 +89,7 @@ public class CustomerService {
|
|||
BizObj.ShiGongDanWei,
|
||||
BizObj.ChaiQian,
|
||||
BizObj.SheQu,
|
||||
BizObj.QiTa,
|
||||
BizObj.QiYe,
|
||||
BizObj.GeTi).map(BizObj::getVal).collect(Collectors.toList()))
|
||||
.and(it -> it.eq("c.audit_status", AuditStatus.TongGuo).or().isNull("c.audit_status"));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<OrderInfoEntity> {
|
|||
DeviceLocalizerEntity gpsLastOnlineTime(@Param("gpsId") String gpsId);
|
||||
|
||||
List<String> getRoute(@Param("id") Long id);
|
||||
|
||||
@SuppressWarnings("MybatisXMapperMethodInspection")
|
||||
List<Map<String, Object>> statisticsByArea(@Param("ew") QueryWrapper<Object> ew);
|
||||
|
||||
@SuppressWarnings("MybatisXMapperMethodInspection")
|
||||
List<Map<String, Object>> statisticsByAreaGoods(@Param("ew") QueryWrapper<Object> ew);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String> areaCode;
|
||||
/**
|
||||
* 产品 Id
|
||||
*/
|
||||
private Long goodsId;
|
||||
/**
|
||||
* 年份
|
||||
*/
|
||||
private Integer year;
|
||||
/**
|
||||
* 月份
|
||||
*/
|
||||
private Integer month;
|
||||
}
|
||||
|
|
@ -26,6 +26,11 @@ public class OrderExportResult {
|
|||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String sn;
|
||||
|
||||
/**
|
||||
* 清运时间(日期)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -242,6 +242,8 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
String driverName = orderPagingSearchParam.getDriverName();
|
||||
LocalDateTime startPayTime = orderPagingSearchParam.getStartPayTime();
|
||||
LocalDateTime endPayTime = orderPagingSearchParam.getEndPayTime();
|
||||
LocalDateTime startOutTime = orderPagingSearchParam.getStartOutTime();
|
||||
LocalDateTime endOutTime = orderPagingSearchParam.getEndOutTime();
|
||||
QueryWrapper<OrderPagingResult> ew = Wrappers.<OrderPagingResult>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<OrderInfoMapper, OrderInfoEnti
|
|||
.like(StrUtil.isNotBlank(driverName), "f.driver_name", driverName)
|
||||
.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(orderCategory != null, "a.order_category", orderCategory)
|
||||
.eq(null != transCompanyId, "a.trans_company_id", transCompanyId)
|
||||
.eq("a.deleted", 0)
|
||||
|
|
@ -545,7 +549,10 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
);
|
||||
} else if (auditStatus == AuditStatus.BoHui) {
|
||||
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).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<OrderInfoMapper, OrderInfoEnti
|
|||
String auditType = orderPagingSearchParam.getAuditType();
|
||||
// 1-->待审核
|
||||
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<OrderInfoMapper, OrderInfoEnti
|
|||
);
|
||||
} else if ("2".equals(auditType)) { // 2-->已审核
|
||||
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<OrderInfoMapper, OrderInfoEnti
|
|||
);
|
||||
} else {
|
||||
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))
|
||||
.or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList))
|
||||
|
|
@ -670,6 +683,7 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
.setQuAuditMemo(quAuditMemo)
|
||||
.setCertificateSn(certificateSn)
|
||||
.setCheckStatus(checkStatus)
|
||||
.setOrderStatus(newAuditStatus == AuditStatus.BoHui ? OrderStatus.YiQuXiao : null)
|
||||
);
|
||||
if (newAuditStatus == AuditStatus.BoHui) {
|
||||
baseMapper.busyDriver(detail.getDriverId(), Boolean.FALSE);
|
||||
|
|
@ -694,8 +708,8 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
.eq(SysUserRoleEntity::getRoleId, roleEntity.getId()));
|
||||
for (SysUserRoleEntity userRoleEntity : userIds) {
|
||||
param.setUserId(userRoleEntity.getUserId());
|
||||
wechatTemplateMessageService.sendTemplateMessage(param);
|
||||
}
|
||||
wechatTemplateMessageService.sendTemplateMessage(param);
|
||||
} else if (AuditStatus.TongGuo.equals(auditStatus)) {
|
||||
param.setTempType(TempType.AUDIT_OK.getVal());
|
||||
param.setDriverName(detail.getDriverName());
|
||||
|
|
@ -881,13 +895,14 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
TemplateMessageParam param = new TemplateMessageParam();
|
||||
param.setTempType(TempType.AUDIT_PENDING.getVal());
|
||||
param.setSn(orderInfo.getSn());
|
||||
OrderGoodsEntity entity = orderGoodsService.getById(orderInfo.getGoodsId());
|
||||
param.setGoodsName(entity.getGoodsName());
|
||||
BizTruckEntity truckEntity = bizTruckService.getById(truckId);
|
||||
param.setLicensePlate(truckEntity.getLicensePlate());
|
||||
Set<String> 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<String> 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<OrderInfoMapper, OrderInfoEnti
|
|||
String driverName = searchParam.getDriverName();
|
||||
OrderCategory orderCategory = searchParam.getOrderCategory();
|
||||
String area = searchParam.getArea();
|
||||
QueryWrapper<OrderPagingResult> 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<OrderPagingResult> ew = Wrappers.<OrderPagingResult>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<OrderExportResult> list = baseMapper.exportList(ew);
|
||||
List<OrderExportDetailResult> detailResults = baseMapper.exportDetailList(ew);
|
||||
List<OrderExportDetailResult> detailResults = baseMapper.exportDetailList(Wrappers.<OrderPagingResult>query()
|
||||
.in("oei.order_id", list.stream().map(OrderExportResult::getId).collect(Collectors.toList()))
|
||||
);
|
||||
List<Map<String, Object>> downList = new ArrayList<>();
|
||||
List<DictItemEntity> dictItems = dictItemService.list(Wrappers.<DictItemEntity>lambdaQuery()
|
||||
.eq(DictItemEntity::getDictKey, "expense_item_category")
|
||||
|
|
@ -2204,6 +2238,45 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
placeEntity.setArea(area);
|
||||
placeEntity.setAreaName(areaName);
|
||||
orderCargoPlaceService.updateById(placeEntity);
|
||||
BizAuditConfigEntity bizAuditConfigEntity = bizAuditConfigService.getOne(Wrappers.lambdaQuery(BizAuditConfigEntity.class)
|
||||
.eq(BizAuditConfigEntity::getArea, area));
|
||||
try {
|
||||
// 通知审核人
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
if (null != bizAuditConfigEntity && !Strings.isNullOrEmpty(bizAuditConfigEntity.getAreaRole())) {
|
||||
roleEntity = roleService.getOne(Wrappers.lambdaQuery(RoleEntity.class).eq(
|
||||
RoleEntity::getRoleCode, bizAuditConfigEntity.getAreaRole()));
|
||||
}
|
||||
if (null != roleEntity && null != roleEntity.getId()) {
|
||||
List<SysUserRoleEntity> 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<String> 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<OrderInfoMapper, OrderInfoEnti
|
|||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public Map<String, Object> statistics(OrderStatisticsParam param) {
|
||||
List<String> 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<Map<String, Object>> 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<Map<String, Object>> 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.<String, Object>builder()
|
||||
.put("statisticsByArea", statisticsByArea)
|
||||
.put("statisticsByAreaGoods", statisticsByAreaGoods)
|
||||
.build();
|
||||
}
|
||||
|
||||
public PageResult<OrderPagingResult> 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<OrderPagingResult> page = pageParam.toPage();
|
||||
|
||||
QueryWrapper<OrderPagingResult> ew = Wrappers.<OrderPagingResult>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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ public class UserService extends ServiceImpl<UserMapper, UserEntity> implements
|
|||
|| bizObj == BizObj.WuYe
|
||||
|| bizObj == BizObj.ChaiQian
|
||||
|| bizObj == BizObj.SheQu
|
||||
|| bizObj == BizObj.QiTa
|
||||
|| bizObj == BizObj.QiYe
|
||||
|| bizObj == BizObj.GeTi
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
// 异步执行失败不抛出异常,避免影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<String, TemplateData> 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())) {
|
||||
// 审核通过
|
||||
|
|
|
|||
|
|
@ -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
|
||||
# 支付回调地址
|
||||
|
|
|
|||
|
|
@ -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
|
||||
# 支付回调地址
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
<if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
|
||||
${ew.customSqlSegment}
|
||||
</if>
|
||||
|
|
@ -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
|
||||
</select>
|
||||
<select id="statisticsByArea" resultType="java.util.Map">
|
||||
SELECT t3.district_name area, t1.weight
|
||||
FROM (SELECT b.area area_code, IFNULL(SUM(c.settle_weight), 0) weight
|
||||
FROM order_info a
|
||||
INNER JOIN order_cargo_place b ON b.id = a.cargo_place_id
|
||||
INNER JOIN order_car_in_out c ON c.id = a.car_in_out_id
|
||||
INNER JOIN order_goods d ON d.id = a.goods_id
|
||||
<if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
|
||||
${ew.customSqlSegment}
|
||||
</if>
|
||||
) t1
|
||||
INNER JOIN sys_district t3 ON t3.id = t1.area_code
|
||||
</select>
|
||||
<select id="statisticsByAreaGoods" resultType="java.util.Map">
|
||||
SELECT t1.goods_id goodsId,t2.goods_name goodsName, t1.area_code areaCode, t3.district_name area, t1.weight
|
||||
FROM (SELECT b.area area_code, d.origin_goods_id goods_id, IFNULL(SUM(c.settle_weight), 0) weight
|
||||
FROM order_info a
|
||||
INNER JOIN order_cargo_place b ON b.id = a.cargo_place_id
|
||||
INNER JOIN order_car_in_out c ON c.id = a.car_in_out_id
|
||||
INNER JOIN order_goods d ON d.id = a.goods_id
|
||||
<if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
|
||||
${ew.customSqlSegment}
|
||||
</if>
|
||||
) t1
|
||||
INNER JOIN goods_info t2 ON t2.id = t1.goods_id
|
||||
INNER JOIN sys_district t3 ON t3.id = t1.area_code
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
|||
Loading…
Reference in New Issue