修改退款申请订单详情中地址解析(含行政区划),轨迹里程计算实现

This commit is contained in:
jkcdev 2025-05-22 13:16:17 +08:00
parent 5b85cc252d
commit 31d929c99d
15 changed files with 492 additions and 45 deletions

View File

@ -37,6 +37,20 @@
<artifactId>influxdb-client-java</artifactId>
<version>6.10.0</version> <!-- 匹配InfluxDB 2.x版本 -->
</dependency>
<!-- JTS 空间计算库 -->
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</dependency>
<!-- Proj4j坐标转换库 -->
<dependency>
<groupId>org.osgeo</groupId>
<artifactId>proj4j</artifactId>
<version>0.1.0</version>
</dependency>
</dependencies>
<build>

View File

@ -1,5 +1,12 @@
package com.cdzy.common.utils;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.osgeo.proj4j.*;
import java.util.List;
/**
* 坐标转换工具类
*
@ -104,4 +111,78 @@ public class CoordinateUtil {
return GCJ02ToBD(gcj02);
}
private static final CRSFactory crsFactory = new CRSFactory();
/**
* GCJ-02 WGS84
* 输入的坐标点必须是GCJ-02坐标系的经纬度
*
* @param lon 经度
* @param lat 纬度
* @return WGS84坐标
*/
public static Double[] GCJ02ToWGS84(double lon, double lat) {
// 使用Proj4j进行坐标转换需添加Proj4j依赖
CoordinateReferenceSystem gcj02 = crsFactory.createFromName("EPSG:4490"); // 国家2000坐标系近似GCJ-02
CoordinateReferenceSystem wgs84 = crsFactory.createFromName("EPSG:4326");
CoordinateTransform transform = new CoordinateTransformFactory().createTransform(gcj02, wgs84);
ProjCoordinate src = new ProjCoordinate(lon, lat);
ProjCoordinate dst = new ProjCoordinate();
transform.transform(src, dst);
return new Double[]{dst.x, dst.y};
}
/**
* GCJ-02 WGS84
* 输入的坐标点必须是GCJ-02坐标系的经纬度
*
* @param lon 经度
* @param lat 纬度
* @return WGS84坐标
*/
public static Coordinate gcj02ToWgs84(double lon, double lat) {
// 使用Proj4j进行坐标转换需添加Proj4j依赖
CoordinateReferenceSystem gcj02 = crsFactory.createFromName("EPSG:4490"); // 国家2000坐标系近似GCJ-02
CoordinateReferenceSystem wgs84 = crsFactory.createFromName("EPSG:4326");
CoordinateTransform transform = new CoordinateTransformFactory().createTransform(gcj02, wgs84);
ProjCoordinate src = new ProjCoordinate(lon, lat);
ProjCoordinate dst = new ProjCoordinate();
transform.transform(src, dst);
return new Coordinate(dst.x, dst.y);
}
private static final GeometryFactory geometryFactory = new GeometryFactory();
/**
* 计算路径总长度单位
* 输入的坐标点必须是WGS84坐标系的经纬度
*
* @param points 坐标点列表每个点为[经度, 纬度]
* @return 总长度
*/
public static Double calculateTotalDistance(List<Double[]> points) {
// 转换坐标并构建线串
LineString lineString = convertToLineString(points);
// 使用JTS计算几何距离需确保坐标系为投影坐标系
return lineString.getLength();
}
/**
* 转换坐标点列表为LineString
*
* @param points 坐标点列表
* @return LineString对象
*/
private static LineString convertToLineString(List<Double[]> points) {
Coordinate[] coords = new Coordinate[points.size()];
for (int i = 0; i < points.size(); i++) {
Double[] p = points.get(i);
Coordinate wgs84Coord = gcj02ToWgs84(p[0], p[1]); // GCJ-02转WGS84
coords[i]= new Coordinate(wgs84Coord.x, wgs84Coord.y);
}
return geometryFactory.createLineString(coords);
}
}

View File

@ -4,6 +4,7 @@ import com.cdzy.common.model.EbikeTracking;
import com.cdzy.common.model.JsonResult;
import com.cdzy.common.model.ReqBatchRegionDto;
import com.ebike.feign.model.res.ReqEbikeSiteQuery;
import com.ebike.feign.model.res.ReqEbikeTrackingDto;
import com.ebike.feign.model.res.ReqUserOperateDto;
import com.ebike.feign.model.res.ResFeignEbikeSysRcostsetDto;
import com.ebike.feign.model.rsp.FeignEbikeRegionDto;
@ -69,6 +70,15 @@ public interface OperateFeignClient {
@PostMapping("ebikeTracking/save")
JsonResult<?> saveEbikeTracking(@RequestBody EbikeTracking ebikeTracking);
/**
* 车辆轨迹查询
*
* @param reqEbikeTrackingDto 查询参数
* @return List<EbikeTrackingDto>
*/
@PostMapping("ebikeTracking/query")
JsonResult<?> queryEbikeTracking(@RequestBody ReqEbikeTrackingDto reqEbikeTrackingDto);
/**
* 获取运营区详情
*

View File

@ -1,4 +1,4 @@
package com.cdzy.ebikeoperate.model.dto.request;
package com.ebike.feign.model.res;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;

View File

@ -1,11 +1,15 @@
package com.cdzy.ebikeoperate.model.dto.response;
package com.ebike.feign.model.rsp;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EbikeTrackingDto implements Serializable {
/**

View File

@ -52,6 +52,12 @@ spring:
- Path=/operate/**
filters:
- StripPrefix=1
- id: ebike-payment
uri: lb://ebike-payment
predicates:
- Path=/payment/**
filters:
- StripPrefix=1
data:
# redis配置
redis:

View File

@ -6,8 +6,8 @@ import com.cdzy.ebikeoperate.model.pojo.EbikeSiteRegion;
import com.cdzy.ebikeoperate.service.EbikeSiteRegionService;
import com.cdzy.ebikeoperate.utils.RedisUtil;
import com.ebike.feign.model.res.ReqEbikeSiteQuery;
import com.cdzy.ebikeoperate.model.dto.request.ReqEbikeTrackingDto;
import com.cdzy.ebikeoperate.model.dto.response.EbikeTrackingDto;
import com.ebike.feign.model.res.ReqEbikeTrackingDto;
import com.ebike.feign.model.rsp.EbikeTrackingDto;
import com.cdzy.ebikeoperate.service.EbikeTrackingService;
import com.ebike.feign.model.rsp.EbikeSiteInfo;
import lombok.extern.slf4j.Slf4j;

View File

@ -1,8 +1,8 @@
package com.cdzy.ebikeoperate.service;
import com.cdzy.common.model.EbikeTracking;
import com.cdzy.ebikeoperate.model.dto.request.ReqEbikeTrackingDto;
import com.cdzy.ebikeoperate.model.dto.response.EbikeTrackingDto;
import com.ebike.feign.model.res.ReqEbikeTrackingDto;
import com.ebike.feign.model.rsp.EbikeTrackingDto;
import java.util.List;

View File

@ -4,10 +4,9 @@ import com.cdzy.common.model.EbikeTracking;
import com.cdzy.common.model.JsonResult;
import com.cdzy.common.utils.ConvertUtil;
import com.cdzy.ebikeoperate.model.dto.EbikeTrackingConfg;
import com.cdzy.ebikeoperate.model.dto.request.ReqEbikeTrackingDto;
import com.cdzy.ebikeoperate.model.dto.response.EbikeTrackingDto;
import com.ebike.feign.model.res.ReqEbikeTrackingDto;
import com.ebike.feign.model.rsp.EbikeTrackingDto;
import com.cdzy.ebikeoperate.service.EbikeTrackingService;
import com.cdzy.common.utils.CoordinateUtil;
import com.ebike.feign.clients.MaintenanceFeignClient;
import com.influxdb.annotations.Measurement;
import com.influxdb.client.InfluxDBClient;
@ -21,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.StringJoiner;

View File

@ -0,0 +1,22 @@
package com.cdzy.payment.config;
import com.cdzy.payment.utils.GeoCodingUtil;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Setter
@Getter
@Configuration
@ConfigurationProperties(prefix = "geo-coding")
public class GeoCodingConfig {
private String apiUrl;
private String accessKey;
@Bean
public GeoCodingUtil geoCodingUtil() {
return new GeoCodingUtil(apiUrl, accessKey);
}
}

View File

@ -2,11 +2,13 @@ package com.cdzy.payment.service;
import com.cdzy.payment.model.dto.OrderDetailInfo;
import com.cdzy.payment.model.dto.ResOrderInfoDto;
import com.cdzy.payment.model.dto.ResRefundOrderInfo;
import com.mybatisflex.core.service.IService;
import com.cdzy.payment.model.entity.EbikePayment;
import com.wechat.pay.java.service.payments.model.Transaction;
import java.util.List;
import java.util.Map;
/**
* 用户订单支付记录 服务层
@ -53,5 +55,5 @@ public interface EbikePaymentService extends IService<EbikePayment> {
* @param orderId 退款id
* @return 订单详情
*/
OrderDetailInfo getOrderDetail(String orderId);
Map getOrderDetail(String orderId);
}

View File

@ -1,11 +1,10 @@
package com.cdzy.payment.service.impl;
import com.cdzy.payment.model.dto.OrderDetailInfo;
import com.cdzy.payment.model.dto.ResOrderInfoDto;
import com.cdzy.payment.model.enums.PayMethod;
import com.cdzy.payment.utils.StringUtils;
import com.ebike.feign.clients.OrdersFeignClient;
import com.ebike.feign.model.res.ResFeignOrderPaymentDto;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cdzy.payment.model.entity.EbikePayment;
@ -13,11 +12,15 @@ import com.cdzy.payment.mapper.EbikePaymentMapper;
import com.cdzy.payment.service.EbikePaymentService;
import com.wechat.pay.java.service.payments.model.Transaction;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import static com.cdzy.payment.model.entity.table.EbikePaymentTableDef.EBIKE_PAYMENT;
import static com.cdzy.payment.model.entity.table.EbikeUserOrdersTableDef.EBIKE_USER_ORDERS;
import static com.cdzy.payment.model.entity.table.EbikeUserTableDef.EBIKE_USER;
import static com.mybatisflex.core.constant.FuncName.*;
/**
@ -26,6 +29,7 @@ import static com.mybatisflex.core.constant.FuncName.*;
* @author dingchao
* @since 2025-04-24
*/
@Slf4j
@Service
public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, EbikePayment> implements EbikePaymentService{
@ -84,10 +88,23 @@ public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, Ebi
}
@Override
public OrderDetailInfo getOrderDetail(String orderId) {
//QueryWrapper query = QueryWrapper.create()
// .select(EBIKE_PAYMENT., EBIKE_PAYMENT.PAYMENT_ID, EBIKE_PAYMENT.TOTAL, EBIKE_PAYMENT.COST_PRICE, EBIKE_PAYMENT.CREATE_TIME, EBIKE_PAYMENT.TRADE_STATE)
return null;
public Map getOrderDetail(String orderId) {
QueryWrapper query = QueryWrapper.create()
.select(EBIKE_PAYMENT.COST_PRICE.as("totalAmount"), EBIKE_PAYMENT.TOTAL.as("actualAmount"),EBIKE_USER_ORDERS.BIKE_CODE.as("bikeCode"),
EBIKE_USER_ORDERS.ORDER_ID.as("orderId"), EBIKE_USER_ORDERS.BIKE_CODE, EBIKE_USER_ORDERS.CREATED_AT.as("unLockTime"),
EBIKE_USER_ORDERS.END_TIME.as("lockTime"), EBIKE_USER_ORDERS.CREATED_AT.as("createTime"),
EBIKE_USER_ORDERS.END_TIME.as("endTime"), EBIKE_USER.NICKNAME.as("userName"), EBIKE_USER.MOBILE.as("phone"),
EBIKE_USER_ORDERS.RIDE_POINT.as("borrowCarCoordinate"), EBIKE_USER_ORDERS.RETURN_POINT.as("returnCarCoordinate"),
EBIKE_PAYMENT.PAYMENT_TIME.as("payTime"), QueryMethods.case_(EBIKE_PAYMENT.PAYMENT_METHOD)
.when(PayMethod.wechat.name()).then("微信支付")
.when(PayMethod.alipay.name()).then("支付宝")
.when(PayMethod.balance.name()).then("余额").end().as("payMethod")
)
.leftJoin(EBIKE_USER_ORDERS).on(EBIKE_USER_ORDERS.ORDER_ID.eq(EBIKE_PAYMENT.ORDER_ID))
.leftJoin(EBIKE_USER).on(EBIKE_USER.USER_ID.eq(EBIKE_USER_ORDERS.USER_ID))
.where(EBIKE_PAYMENT.ORDER_ID.eq(orderId));
return getOneAs(query, Map.class);
}
/**

View File

@ -2,7 +2,9 @@ package com.cdzy.payment.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.cdzy.common.enums.Code;
import com.cdzy.common.model.JsonResult;
import com.cdzy.common.utils.CoordinateUtil;
import com.cdzy.payment.config.WxPayConfig;
import com.cdzy.payment.model.dto.*;
import com.cdzy.payment.model.entity.EbikePayment;
@ -13,15 +15,16 @@ import com.cdzy.payment.model.enums.RefundProcessState;
import com.cdzy.payment.service.EbikePaymentService;
import com.cdzy.payment.service.EbikeRefundService;
import com.cdzy.payment.service.WxPayService;
import com.cdzy.payment.utils.GeoCodingUtil;
import com.cdzy.payment.utils.HttpServletUtils;
import com.cdzy.payment.utils.StringUtils;
import com.ebike.feign.clients.MaintenanceFeignClient;
import com.ebike.feign.clients.OperateFeignClient;
import com.ebike.feign.clients.OrdersFeignClient;
import com.ebike.feign.model.rsp.AmountDto;
import com.ebike.feign.model.rsp.DetailDto;
import com.ebike.feign.model.rsp.EbikePaymentDto;
import com.ebike.feign.model.rsp.PayDetailDto;
import com.ebike.feign.model.res.ReqEbikeSiteQuery;
import com.ebike.feign.model.res.ReqEbikeTrackingDto;
import com.ebike.feign.model.rsp.*;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.core.exception.HttpException;
@ -41,6 +44,7 @@ import com.wechat.pay.java.service.refund.model.*;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
@ -48,9 +52,8 @@ import java.math.BigDecimal;
import java.time.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static com.cdzy.payment.model.entity.table.EbikePaymentTableDef.EBIKE_PAYMENT;
import static com.cdzy.payment.model.entity.table.EbikeRefundTableDef.EBIKE_REFUND;
import static com.wechat.pay.java.core.http.Constant.*;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SERIAL;
@ -81,6 +84,13 @@ public class WxPayServiceImpl implements WxPayService {
private EbikeRefundService ebikeRefundService;
@Resource
private OrdersFeignClient ordersFeignClient;
@Resource
private MaintenanceFeignClient maintenanceFeignClient;
@Resource
private OperateFeignClient operateFeignClient;
@Resource
private GeoCodingUtil geoCodingUtil;
@Override
public boolean closeOrder(String outTradeNo) {
@ -571,7 +581,7 @@ public class WxPayServiceImpl implements WxPayService {
public ResOrderInfoDto queryRefundOrderById(String refundId) {
ResOrderInfoDto orderDto = ebikeRefundService.getRefundOrderDetail(refundId);
if (orderDto == null) {
log.error("{}退款订单不存在", refundId);
log.error("{} 退款订单不存在", refundId);
return null;
}
//查询订单, orderFeingClient.getOrderById(orderId)
@ -585,10 +595,8 @@ public class WxPayServiceImpl implements WxPayService {
//1-骑行时长费 2-运营区调度费用 3-停车区调度费用 4-高峰时段出行费用 5-高峰日出行费用 6-起步费用
switch (detailDto.getItemType()) {
case 1, 4, 5 -> orderDto.setDurationCost(orderDto.getDurationCost() + detailDto.getUnitPrice());
case 2 ->
orderDto.setDispatchFeeOutOperateArea(orderDto.getParkingAreaOutDispatchFee() + detailDto.getUnitPrice());
case 3 ->
orderDto.setParkingAreaOutDispatchFee(orderDto.getParkingAreaOutDispatchFee() + detailDto.getUnitPrice());
case 2 -> orderDto.setDispatchFeeOutOperateArea(orderDto.getParkingAreaOutDispatchFee() + detailDto.getUnitPrice());
case 3 -> orderDto.setParkingAreaOutDispatchFee(orderDto.getParkingAreaOutDispatchFee() + detailDto.getUnitPrice());
case 6 -> orderDto.setStartupCost(orderDto.getStartupCost() + detailDto.getUnitPrice());
}
}
@ -600,7 +608,152 @@ public class WxPayServiceImpl implements WxPayService {
@Override
public ResRefundOrderInfo queryRefundApplyOrderById(String orderId) {
return null;
Map recDetail = ebikePaymentService.getOrderDetail(orderId);
if (recDetail == null) {
log.error("{} 订单不存在", orderId);
return null;
}
ResRefundOrderInfo orderInfo = new ResRefundOrderInfo();
// 订单信息
OrderInfo order = new OrderInfo();
BeanUtils.copyProperties(recDetail, order);
// 用户信息
UserInfo userInfo = new UserInfo();
BeanUtils.copyProperties(recDetail, orderInfo);
orderInfo.setUserInfo(userInfo);
// 车辆基本信息
EbikeBikeBaseInfo bike = null;
JsonResult<?> bikeInfo = maintenanceFeignClient.getBikeBaseInfoByCode(order.getBikeCode());
if (bikeInfo.getCode() == Code.SUCCESS) {
bike = JSON.parseObject(JSONObject.toJSONString(bikeInfo.getData()), EbikeBikeBaseInfo.class);
}else {
log.error("获取车辆基本信息失败,车辆编码: {}", order.getBikeCode());
}
// 借用信息
BorrowingInfo borrowingInfo = new BorrowingInfo();
BeanUtils.copyProperties(recDetail, borrowingInfo);
Double[] borrowLocation = getBikeLocation(borrowingInfo.getBorrowCarCoordinate());
if (bike != null) {
if (borrowLocation != null) {
ReqEbikeSiteQuery siteQuery = new ReqEbikeSiteQuery();
siteQuery.setLatitude(borrowLocation[0]);
siteQuery.setLongitude(borrowLocation[1]);
siteQuery.setAreaId(Long.parseLong(bike.getReginId()));
JsonResult<?> siteInfo = operateFeignClient.querySite(siteQuery);
if (siteInfo.getCode() == Code.SUCCESS) {
EbikeSiteInfo site = JSON.parseObject(JSONObject.toJSONString(siteInfo.getData()), EbikeSiteInfo.class);
borrowingInfo.setBorrowSite(site.getSiteName());
}else {
log.error("获取车辆骑行站点信息失败,订单编号: {}", orderId);
}
}
}
// 借车地址
if(borrowLocation != null){
JSONObject location = new JSONObject();
location.put("lng", borrowLocation[0]);
location.put("lat", borrowLocation[1]);
JSONObject address = geoCodingUtil.getLocationToAddress(location);
if(address!= null){
borrowingInfo.setBorrowAddress(address.getString("detail"));
order.setStartRegion(address.getString("district"));
}
}
orderInfo.setBorrowingInfo(borrowingInfo);
// 还车信息
ReturnInfo returnInfo = new ReturnInfo();
BeanUtils.copyProperties(recDetail, returnInfo);
Double[] returnLocation = getBikeLocation(returnInfo.getReturnCarCoordinate());
if (bike != null) {
if (returnLocation != null) {
ReqEbikeSiteQuery siteQuery = new ReqEbikeSiteQuery();
siteQuery.setLatitude(returnLocation[0]);
siteQuery.setLongitude(returnLocation[1]);
siteQuery.setAreaId(Long.parseLong(bike.getReginId()));
JsonResult<?> siteInfo = operateFeignClient.querySite(siteQuery);
if (siteInfo.getCode() == Code.SUCCESS) {
EbikeSiteInfo site = JSON.parseObject(JSONObject.toJSONString(siteInfo.getData()), EbikeSiteInfo.class);
returnInfo.setReturnSite(site.getSiteName());
}else {
log.error("获取车辆还车站点信息失败,订单编号: {}", orderId);
}
}
}
// 还车地址
if(returnLocation != null){
JSONObject location = new JSONObject();
location.put("lng", returnLocation[0]);
location.put("lat", returnLocation[1]);
JSONObject address = geoCodingUtil.getLocationToAddress(location);
if(address!= null){
returnInfo.setReturnAddress(address.getString("detail"));
order.setEndRegion(address.getString("district"));
}
}
// 骑行时长
if (order.getUnLockTime() != null && order.getLockTime() != null) {
order.setCyclingDuration(String.valueOf(Duration.between(order.getUnLockTime(), order.getLockTime()).toMinutes()));
}
// 轨迹里程
if (order.getUnLockTime() != null && order.getLockTime() != null&& order.getBikeCode()!=null) {
ReqEbikeTrackingDto trackingDto = new ReqEbikeTrackingDto();
trackingDto.setEbikeCode(order.getBikeCode());
trackingDto.setStartTime(order.getUnLockTime());
trackingDto.setEndTime(order.getLockTime());
trackingDto.setInterval("30s");
JsonResult<?> tracking = operateFeignClient.queryEbikeTracking(trackingDto);
if (tracking.getCode() == Code.SUCCESS) {
List<EbikeTrackingDto> trackingList = JSON.parseArray(JSONObject.toJSONString(tracking.getData()), EbikeTrackingDto.class);
List<Double[]> trackingPoints = new ArrayList<>(trackingList.stream().map(t -> new Double[]{t.getLatitude(), t.getLongitude()}).toList());
if (borrowLocation!=null){
trackingPoints.add(0, borrowLocation);
}
if (returnLocation!=null){
trackingPoints.add(returnLocation);
}
Double distance = CoordinateUtil.calculateTotalDistance(trackingPoints);
if (distance > 1000) {
order.setTrajectoryMileage(String.format("%.3f公里", distance));
}else {
order.setTrajectoryMileage(String.format("%d米", Math.round(distance)));
}
}else {
log.error("获取车辆轨迹信息失败,订单编号: {}", orderId);
}
}
orderInfo.setOrderInfo(order);
orderInfo.setReturnInfo(returnInfo);
// 支付信息
PayInfo payInfo = new PayInfo();
BeanUtils.copyProperties(recDetail, payInfo);
orderInfo.setPayInfo(payInfo);
// 支付详情
OrderDetailInfo detailInfo = new OrderDetailInfo();
BeanUtils.copyProperties(recDetail, detailInfo);
detailInfo.setDiscountAmount(detailInfo.getTotalAmount() - detailInfo.getActualAmount());
//查询订单, orderFeingClient.getOrderById(orderId)
JsonResult<?> result = ordersFeignClient.getPaymentDetails(Long.valueOf(orderId));
if (result.getCode() != 200) {
EbikePaymentDto paymentDto = JSON.parseObject(JSONObject.toJSONString(result.getData()), EbikePaymentDto.class);
for (PayDetailDto detailDto : paymentDto.getDetail().getGoodsDetail()) {
//1-骑行时长费 2-运营区调度费用 3-停车区调度费用 4-高峰时段出行费用 5-高峰日出行费用 6-起步费用
switch (detailDto.getItemType()) {
case 1, 4, 5 -> detailInfo.setDurationCost(detailInfo.getDurationCost() + detailDto.getUnitPrice());
case 2 -> detailInfo.setDispatchFeeOutOperateArea(detailInfo.getParkingAreaOutDispatchFee() + detailDto.getUnitPrice());
case 3 -> detailInfo.setParkingAreaOutDispatchFee(detailInfo.getParkingAreaOutDispatchFee() + detailDto.getUnitPrice());
case 6 -> detailInfo.setStartupCost(detailInfo.getStartupCost() + detailDto.getUnitPrice());
}
}
}
orderInfo.setOrderDetailInfo(detailInfo);
return orderInfo;
}
@Override
@ -609,6 +762,21 @@ public class WxPayServiceImpl implements WxPayService {
return null;
}
/**
* 获取车辆经纬度
*
* @param coordinate 坐标
* @return 经纬度
*/
private Double[] getBikeLocation(String coordinate){
String[] split = coordinate.split(",");
if (split.length == 2) {
return new Double[]{Double.valueOf(split[0]), Double.valueOf(split[1])};
}
return null;
}
/**
* 打印日志
*

View File

@ -0,0 +1,121 @@
package com.cdzy.payment.utils;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Service;
import java.util.Arrays;
/**
* 地址解析反解析工具类
*
* @author dingchao
* @date 2025/4/3
* @modified by:
*/
@Slf4j
@Service
public class GeoCodingUtil {
private final static String LOCATION_TO_ADDRESS = "location";
private final static String ADDRESS_TO_LOCATION = "address";
private final String url;
private final String accessKey;
private final OkHttpClient client;
/**
* 地理编码工具类构造函数
* 目前实现的腾讯地图webservice
*
* @param apiUrl
* @param accessKey
*/
public GeoCodingUtil(String apiUrl, String accessKey) {
this.url = apiUrl;
this.accessKey = accessKey;
this.client = new OkHttpClient();
}
/**
* 输入经纬度返回地址
*
* @param location 经纬度
* @return 地址
*/
public JSONObject getLocationToAddress(JSONObject location) {
Request request = new Request.Builder()
.url(url + "/?"+LOCATION_TO_ADDRESS+"=" + String.format("%f,%f", location.getDouble("lat"), location.getDouble("lng")) + "&key=" + accessKey)
.build();
try(Response response = client.newCall(request).execute()) {
if(response.isSuccessful()) {
if (response.body()!= null) {
String result = response.body().string();
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.getInteger("status") == 0) {
JSONObject address = new JSONObject();
String detail = jsonObject.getJSONObject("result").getJSONObject("formatted_addresses").getString("standard_address");
address.put("detail", detail);
String district = jsonObject.getJSONObject("result").getJSONObject("ad_info").getString("district");
address.put("district", district);
String adcode = jsonObject.getJSONObject("result").getJSONObject("ad_info").getString("adcode");
address.put("adcode", adcode);
return address;
}
logError("地址解析失败==>{}", jsonObject.getString("message"));
return null;
}
logError("地址解析失败==>{}", response.message());
return null;
}
logError("地址解析失败==>{}", response.message());
return null;
} catch (Exception e) {
logError("地址解析失败==>{}", e.getMessage() + Arrays.toString(e.getStackTrace()));
return null;
}
}
/**
* 输入地址返回经纬度(GCJ02)
*
* @param address 地址
* @return 经纬度
*/
public JSONObject getAddressToLocation(String address) {
Request request = new Request.Builder()
.url(url + "/?"+ADDRESS_TO_LOCATION+"=" + address + "&key=" + accessKey)
.build();
try(Response response = client.newCall(request).execute()) {
if(response.isSuccessful()) {
if (response.body() != null) {
String result = response.body().string();
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.getInteger("status") == 0) {
return jsonObject.getJSONObject("result").getJSONObject("location");
}else{
logError("位置解析失败==>{}", jsonObject.getString("message"));
return null;
}
}else{
logError("位置解析失败==>{}", response.message());
return null;
}
}else{
logError("位置解析失败==>{}", response.message());
return null;
}
} catch (Exception e) {
logError("位置解析失败==>{}", e.getMessage() + Arrays.toString(e.getStackTrace()));
return null;
}
}
private void logError(String errDesc, String errorMessage) {
log.error(errDesc, errorMessage);
}
}

View File

@ -31,6 +31,22 @@ spring:
max-lifetime: 1800000
mybatis-flex:
mapper-locations: classpath:mapper/*.xml
sa-token:
# token 名称(同时也是 cookie 名称)
token-name: Authorization
# token 有效期(单位:秒) 默认30天-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token
is-share: true
# token 风格默认可取值uuid、simple-uuid、random-32、random-64、random-128、tik
token-style: random-32
# 是否输出操作日志
is-log: true
payment:
wx-pay:
app-id: wx327d788d7bd6eddf
@ -50,18 +66,6 @@ task-scheduler-pool:
threadNamePrefix: task-scheduled-
waitForTasksToCompleteOnShutdown: true
awaitTerminationSeconds: 30
sa-token:
# token 名称(同时也是 cookie 名称)
token-name: Authorization
# token 有效期(单位:秒) 默认30天-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token
is-share: true
# token 风格默认可取值uuid、simple-uuid、random-32、random-64、random-128、tik
token-style: random-32
# 是否输出操作日志
is-log: true
geo-coding:
api-url: https://apis.map.qq.com/ws/geocoder/v1
access-key: BECBZ-EJIEQ-LUU5N-B5ISQ-3TLMZ-BXFLG