Compare commits

...

41 Commits

Author SHA1 Message Date
4fb1d9b00c 整理代码 抖音接口 2025-10-28 17:40:32 +08:00
af71c55529 增加团购券核销接口 2025-10-28 10:28:06 +08:00
d47caef111 no message 2025-10-25 20:36:47 +08:00
09412ff8fe no message 2025-10-24 01:04:25 +08:00
d9be23b4cf no message 2025-10-24 00:57:56 +08:00
c730010937 meituan 2025-10-24 00:49:09 +08:00
540ecb522c no message 2025-10-23 23:10:23 +08:00
1b7682729c no message 2025-10-23 23:01:00 +08:00
c8e1268e87 no message 2025-10-23 22:34:10 +08:00
44508651b6 no message 2025-10-23 22:32:01 +08:00
fc29003411 no message 2025-10-23 22:09:59 +08:00
362866113a no message 2025-10-23 21:53:31 +08:00
18b9318b0d no message 2025-10-23 21:40:39 +08:00
97444a1520 no message 2025-10-23 21:28:54 +08:00
b5c4e4226a douyin meituan 2025-10-23 20:41:42 +08:00
3a0d707a5a 添加电子围栏redis 2025-10-14 16:32:55 +08:00
b3ba5ef5b1 异步提交充电订单同步 2025-10-03 12:32:43 +08:00
53f98d9174 url 2025-09-30 16:17:23 +08:00
a97513c33f 增加套餐验证接口调用 2025-09-30 15:56:12 +08:00
dab12a4b98 逾期天数有变化则更新 2025-09-30 14:50:15 +08:00
d2ce1120b0 更新逾期小时数 2025-09-30 14:30:30 +08:00
63bb831152 计算逾期天数和小时数 2025-09-30 14:10:01 +08:00
f64bb390dc 逾期小时数取整 2025-09-30 13:37:52 +08:00
151898fdac 计算时租预计还车时间 2025-09-30 11:33:02 +08:00
d627e9ed42 改回统一下单支付 2025-09-30 01:45:55 +08:00
6aa0cb4834 退款查看 2025-09-30 01:26:21 +08:00
79b0012d4e 子订单单号 2025-09-30 00:49:42 +08:00
46fa58a78a 订单号变更 2025-09-30 00:19:31 +08:00
1e95a147e6 免押支付更换子订单支付号 2025-09-30 00:10:48 +08:00
d9fe7405eb 免押支付加openId 2025-09-29 23:59:47 +08:00
19a5bde9b0 增加免押支付扣款逻辑 2025-09-29 23:43:19 +08:00
e2958b47ca 新增租电逻辑 2025-09-29 17:14:17 +08:00
9b85e3a39f 订单统计 2025-09-29 14:53:02 +08:00
dd5603c450 订单编号格式 2025-09-29 14:33:01 +08:00
7751595742 租电子订单returntime 2025-09-29 14:12:39 +08:00
60defa3bb4 门店分润入账 2025-09-29 00:21:13 +08:00
1f06e34d99 驳回解冻免押 2025-09-28 17:42:01 +08:00
70a6b578ef 增加同步租电订单的接口 2025-09-28 17:15:24 +08:00
5f56194f0c 解除免押,取消订单问题 2025-09-28 10:36:18 +08:00
b811e01da6 Merge branch 'main' of http://115.190.8.52:3000/sczx_group/sczx_order 2025-09-27 23:47:32 +08:00
164434b3da 计算重试次数 2025-09-27 23:47:28 +08:00
48 changed files with 1773 additions and 73 deletions

59
pom.xml
View File

@ -46,6 +46,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.2</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -185,6 +190,12 @@
<version>2.11.1</version> <!-- 可根据需要选择版本 -->
</dependency>
<!-- Apache Commons Codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
@ -243,8 +254,38 @@
<artifactId>shedlock-provider-redis-spring</artifactId>
<version>4.44.0</version>
</dependency>
<dependency>
<groupId>com.douyin.openapi</groupId>
<artifactId>sdk</artifactId>
<version>1.0.6</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/sdk-1.0.6.jar</systemPath>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.meituan.sdk</groupId>
<artifactId>sdk</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/MtOpJavaSDK-1.0-SNAPSHOT.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.2</version> <!-- 或更高稳定版本 -->
</dependency>
</dependencies>
<!-- Build Configuration -->
<build>
<finalName>sczx_order</finalName>
@ -262,6 +303,19 @@
<exclude>*.yml</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>lib/*.jar</include>
</includes>
<targetPath>${project.build.directory}/lib</targetPath>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>lib/**</exclude>
</excludes>
</resource>
</resources>
<plugins>
<!-- Compiler Plugin -->
@ -278,6 +332,10 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 确保包含系统作用域的依赖 -->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
@ -297,6 +355,7 @@
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>

View File

@ -7,4 +7,6 @@ public interface RedisKeyConstants {
String BIND_CAR_KEY = SERVICE_PREFIX + "bindCar:";
String E_Fence_KEY = SERVICE_PREFIX + "eFence:";
}

View File

@ -0,0 +1,20 @@
package com.sczx.order.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Author: 张黎
* @Date: 2024/03/08/17:42
* @Description: 支付方式枚举
*/
@AllArgsConstructor
@Getter
public enum CouponTypeEnum {
MT("MT", "美团"),
DY("DY", "抖音"),
;
private final String code;
private final String msg;
}

View File

@ -0,0 +1,103 @@
package com.sczx.order.config;
import com.aliyun.tea.TeaException;
import com.douyin.openapi.client.Client;
import com.douyin.openapi.client.models.OauthClientTokenRequest;
import com.douyin.openapi.client.models.OauthClientTokenResponse;
import com.douyin.openapi.credential.models.Config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 抖音 client_token 管理器
* 负责定时获取和缓存 client_token
*/
@Slf4j
@Component
public class DouyinTokenManager {
// 应用凭证信息
@Value("${coupon.douyin.client_key}")
private String CLIENT_KEY;
@Value("${coupon.douyin.client_secret}")
private String CLIENT_SECRET;
// Token 缓存
private final AtomicReference<OauthClientTokenResponse> tokenCache =
new AtomicReference<>();
// 定时任务执行器
private final ScheduledThreadPoolExecutor scheduler =
new ScheduledThreadPoolExecutor(1, r -> {
Thread t = new Thread(r, "DouyinTokenRefreshThread");
t.setDaemon(false);
return t;
});
@PostConstruct
public void init() {
// 初始化时立即获取一次 token
refreshClientToken();
// 每小时更新一次 token (3600秒)
scheduler.scheduleAtFixedRate(
this::refreshClientToken,
3600,
3600,
TimeUnit.SECONDS
);
}
/**
* 获取当前有效的 client_token
*
* @return 当前有效的 access_token
*/
public String getCurrentToken() {
OauthClientTokenResponse response = tokenCache.get();
if (response != null && response.getData() != null) {
return response.getData().getAccessToken();
}
return null;
}
/**
* 刷新 client_token
*/
private void refreshClientToken() {
try {
Config config = new Config()
.setClientKey(CLIENT_KEY)
.setClientSecret(CLIENT_SECRET);
Client client = new Client(config);
OauthClientTokenRequest sdkRequest = new OauthClientTokenRequest();
sdkRequest.setClientKey(CLIENT_KEY);
sdkRequest.setClientSecret(CLIENT_SECRET);
sdkRequest.setGrantType("client_credential");
OauthClientTokenResponse sdkResponse = client.OauthClientToken(sdkRequest);
// 更新缓存
tokenCache.set(sdkResponse);
if (sdkResponse.getData() != null) {
log.info("抖音 client_token 更新成功: {},有效期至: {}", sdkResponse.getData().getAccessToken(),
(System.currentTimeMillis() + sdkResponse.getData().getExpiresIn() * 1000));
}
} catch (TeaException e) {
log.error("获取抖音 client_token 失败 (TeaException): " + e.getMessage());
} catch (Exception e) {
log.error("获取抖音 client_token 失败 (Exception): " + e.getMessage());
}
}
}

View File

@ -65,6 +65,12 @@ public class ClientOrderController {
return Result.ok(orderService.depositFreePayRentCarOrder(rentCarOrderReq));
}
@ApiOperation(value = "第三方订单")
@PostMapping("/thirdPlatformRentCarOrder")
public Result<RentCarOrderResultDTO> thirdPlatformRentCarOrder(@Valid @RequestBody RentCarThirdPlatformOrderReq rentCarOrderReq){
return Result.ok(orderService.thirdPlatformRentCarOrder(rentCarOrderReq));
}
@ApiOperation(value = "续租车")
@PostMapping("/reRentalCarOrder")
public Result<RentCarOrderResultDTO> reRentalCarOrder(@Valid @RequestBody ReRentCarReq rentCarOrderReq){

View File

@ -2,6 +2,7 @@ package com.sczx.order.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.sczx.order.common.Result;
import com.sczx.order.dto.VerifyGroupBuyCouponsReq;
import com.sczx.order.dto.OrderDetailDTO;
import com.sczx.order.dto.OrderDistribDTO;
import com.sczx.order.dto.OrderDistribQueryReq;
@ -12,6 +13,8 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* @Author: 张黎
* @Date: 2025/07/25/16:42
@ -49,4 +52,10 @@ public class PubOrderController {
orderDistribService.saveOrderDistrib(orderNo);
return Result.ok(true);
}
@ApiOperation(value = "团购券核销")
@PostMapping("/verifyGroupBuyCoupons")
public Result<OrderDetailDTO> verifyGroupBuyCoupons(@Valid @RequestBody VerifyGroupBuyCouponsReq req){
return Result.ok(orderService.verifyGroupBuyCoupons( req));
}
}

View File

@ -60,4 +60,10 @@ public class StoreOrderController {
public Result<OrderDTO> confirmReturnCar(@RequestBody ReturnCarReq returnCarReq){
return Result.ok(orderService.confirmReturnCar(returnCarReq));
}
@ApiOperation(value = "抖音团购核销")
@PostMapping("/verifyCar")
public Result<OrderDTO> verifyCar(@RequestBody ReturnCarReq returnCarReq){
return Result.ok(orderService.confirmReturnCar(returnCarReq));
}
}

View File

@ -0,0 +1,35 @@
package com.sczx.order.controller;
import com.sczx.order.common.Result;
import com.sczx.order.service.DouyinService;
import com.sczx.order.service.MeiTuanService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/verify")
public class VerifyController {
@Autowired
private MeiTuanService meituanService;
@ApiOperation(value = "接收美团Token数据接口")
@GetMapping("/authorization")
public Result<String> authorization(@RequestParam("code") String code, @RequestParam("sign") String sign, @RequestParam("developerId") Long developerId, @RequestParam("businessId") int businessId,
@RequestParam("state") String state){
log.info("接收美团授权数据 - code: {}, sign: {}, developerId: {}, businessId: {}, state: {}",
code, sign, developerId, businessId, state);
return Result.ok(meituanService.getAccessToken(code,state));
}
}

View File

@ -1,9 +1,6 @@
package com.sczx.order.convert;
import com.sczx.order.dto.OrderDTO;
import com.sczx.order.dto.OrderDetailDTO;
import com.sczx.order.dto.RentCarOrderReq;
import com.sczx.order.dto.SimpleUserInfoDTO;
import com.sczx.order.dto.*;
import com.sczx.order.po.OrderMainPO;
import com.sczx.order.po.OrderSubPO;
import com.sczx.order.thirdpart.dto.*;
@ -46,6 +43,31 @@ public interface OrderConvert {
})
OrderMainPO subOrderToPo(RentCarOrderReq rentCarOrderReq, SimpleUserInfoDTO userInfoDTO, RentCarRuleDTO rentCarRuleDTO);
@Mappings({
@Mapping(target = "orderId", ignore = true),
@Mapping(target = "createTime", ignore = true),
@Mapping(target = "updateTime", ignore = true),
@Mapping(target = "delFlag", ignore = true),
@Mapping(source = "rentCarOrderReq.storeId", target = "storeId"),
@Mapping(source = "rentCarOrderReq.operatorId", target = "operatorId"),
@Mapping(source = "rentCarOrderReq.carModelId", target = "carModelId"),
@Mapping(source = "rentCarOrderReq.rentCarRuleId", target = "rentCarRuleId"),
@Mapping(source = "rentCarOrderReq.rentBatteyRuleId", target = "rentBatteyRuleId"),
@Mapping(source = "rentCarOrderReq.isDepositFree", target = "isDepositFree"),
@Mapping(source = "rentCarOrderReq.batteryType", target = "batteryType"),
@Mapping(source = "userInfoDTO.userId", target = "customerId"),
@Mapping(source = "userInfoDTO.userName", target = "customerName"),
@Mapping(source = "userInfoDTO.phoneNumber", target = "customerPhone"),
@Mapping(source = "rentCarRuleDTO.rentalType", target = "rentalType"),
@Mapping(source = "rentCarRuleDTO.rentalDays", target = "rentalDays"),
@Mapping(source = "rentCarRuleDTO.rentalPrice", target = "rentalPrice"),
@Mapping(source = "rentCarRuleDTO.depositPrice", target = "depositPrice"),
@Mapping(source = "rentCarRuleDTO.overdueFee", target = "overdueFee"),
@Mapping(source = "rentCarRuleDTO.overdueType", target = "overdueType")
})
OrderMainPO subOrderToPo(RentCarThirdPlatformOrderReq rentCarOrderReq, SimpleUserInfoDTO userInfoDTO, RentCarRuleDTO rentCarRuleDTO);
@Mappings({
@Mapping(source = "orderMainPO.orderId", target = "orderId"),

View File

@ -0,0 +1,38 @@
package com.sczx.order.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 团购订单必须信息
* @Author: 张黎
* @Date: 2025/10/27/20:42
* @Description:
*/
@Data
public class GroupBuyOrderInfoDto {
@ApiModelProperty(value = "门店id")
@NotNull(message = "门店id不能为空")
private Long storeId;
@ApiModelProperty(value = "客户id")
private Long customerId;
@ApiModelProperty("车型ID")
@NotNull(message = "车型ID不能为空")
private Long carModelId;
@ApiModelProperty(value = "租车套餐id")
@NotNull(message = "租车套餐id不能为空")
private Long rentCarRuleId;
@ApiModelProperty(value = "租电套餐id")
private Long rentBatteyRuleId;
@ApiModelProperty(value = "团购券类型")
private String couponType;
}

View File

@ -0,0 +1,62 @@
package com.sczx.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* @Author: 张黎
* @Date: 2025/07/25/16:58
* @Description:
*/
@Data
@ApiModel(value = "租车订单请求参数")
public class RentCarThirdPlatformOrderReq {
@ApiModelProperty(value = "订单编号,租车不需要传,续租和逾期处理需要传")
private String orderNo;
@ApiModelProperty(value = "运营商id")
private Long operatorId;
@ApiModelProperty(value = "门店id")
@NotNull(message = "门店id不能为空")
private Long storeId;
@ApiModelProperty(value = "客户id")
private Long customerId;
@ApiModelProperty(value = "客户姓名")
private String customerName;
@ApiModelProperty(value = "客户电话")
private String customerPhone;
@ApiModelProperty(value = "第三方订单号")
private String thirdOrderNo;
@ApiModelProperty(value = "订单来源")
private String orderSource;
@ApiModelProperty("车型ID")
@NotNull(message = "车型ID不能为空")
private Long carModelId;
@ApiModelProperty(value = "租车套餐id")
@NotNull(message = "租车套餐id不能为空")
private Long rentCarRuleId;
@ApiModelProperty(value = "租电套餐id")
private Long rentBatteyRuleId;
@ApiModelProperty("选择的电池类型")
private String batteryType;
@ApiModelProperty("是否开通免押")
private Boolean isDepositFree = false;
}

View File

@ -0,0 +1,34 @@
package com.sczx.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* @Author: 张黎
* @Date: 2025/10/27/19:13
* @Description:
*/
@ApiModel(value = "团购券核销请求参数")
@Data
public class VerifyGroupBuyCouponsReq {
@NotEmpty(message = "券码类型不能为空")
@ApiModelProperty(value = "券码类型:美团MT,抖音DY")
private String couponType;
@NotEmpty(message = "券码不能为空")
@ApiModelProperty(value = "券码")
private String couponCode;
@NotNull(message = "门店id不能为空")
@ApiModelProperty(value = "门店id")
private Long storeId;
@NotEmpty(message = "用户手机号不能为空")
@ApiModelProperty(value = "用户完整手机号")
private String mobile;
}

View File

@ -0,0 +1,17 @@
package com.sczx.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sczx.order.po.ElectronicFenceRulePO;
import com.sczx.order.po.OrderCarChangePO;
/**
* <p>
* 订单车辆变更表 Mapper 接口
* </p>
*
* @author zhangli
* @since 2025-09-03 23:12:55
*/
public interface ElectronicFenceRuleMapper extends BaseMapper<ElectronicFenceRulePO> {
}

View File

@ -0,0 +1,66 @@
package com.sczx.order.po;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 套餐规则与电子围栏关联表
* </p>
*
* @author lingma
* @since 2025-07-25 17:17:28
*/
@Getter
@Setter
@TableName("zc_electronic_fence_rule")
@ApiModel(value = "ElectronicFenceRulePO对象", description = "套餐规则与电子围栏关联表")
public class ElectronicFenceRulePO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("规则ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty("车辆规则ID")
private Long carRuleId;
@ApiModelProperty("电子围栏ID")
private Long electronicFenceId;
@ApiModelProperty("排序")
private Integer sortOrder;
@ApiModelProperty("删除标志0代表存在 2代表删除")
private String delFlag;
@ApiModelProperty("创建者")
private String createBy;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty("创建时间")
private LocalDateTime createTime;
@ApiModelProperty("更新者")
private String updateBy;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty("更新时间")
private LocalDateTime updateTime;
@ApiModelProperty("备注")
private String remark;
}

View File

@ -0,0 +1,16 @@
package com.sczx.order.repository;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sczx.order.po.ElectronicFenceRulePO;
/**
* <p>
* 套餐规则与电子围栏关联表 服务类
* </p>
*
* @author lingma
* @since 2025-07-25 17:17:28
*/
public interface ElectronicFenceRuleRepo extends IService<ElectronicFenceRulePO> {
}

View File

@ -0,0 +1,21 @@
package com.sczx.order.repository.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sczx.order.po.ElectronicFenceRulePO;
import com.sczx.order.repository.ElectronicFenceRuleRepo;
import com.sczx.order.mapper.ElectronicFenceRuleMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 套餐规则与电子围栏关联表 服务实现类
* </p>
*
* @author lingma
* @since 2025-07-25 17:17:28
*/
@Service
public class ElectronicFenceRuleRepoImpl extends ServiceImpl<ElectronicFenceRuleMapper, ElectronicFenceRulePO> implements ElectronicFenceRuleRepo {
}

View File

@ -0,0 +1,17 @@
package com.sczx.order.service;
import com.douyin.openapi.client.models.*;
public interface DouyinService {
CertificatePrepareResponse prepare(CertificatePrepareRequest req);
CertificateVerifyResponse verify(CertificateVerifyRequest req);
CertificateCancelResponse cancel(CertificateCancelRequest req);
}

View File

@ -0,0 +1,27 @@
package com.sczx.order.service;
import com.sczx.order.dto.GroupBuyOrderInfoDto;
/** 团购券服务
* @Author: 张黎
* @Date: 2025/10/27/20:30
* @Description:
*/
public interface GroupBuyCouponService {
/**
* 获取团购券下单信息
* @param couponCode 团购券码
* @param couponType 团购券类型
* @param mobile 手机号
* @return
*/
GroupBuyOrderInfoDto getGroupBuyOrderInfoDto(String couponCode, String couponType, String mobile);
/**
* 校验团购券码
* @param couponCode 团购券码
* @param couponType 团购券类型
* @return
*/
boolean checkCouponCode(String couponCode, String couponType);
}

View File

@ -0,0 +1,17 @@
package com.sczx.order.service;
import com.meituan.sdk.MeituanResponse;
import com.meituan.sdk.auth.MeituanTokenResponse;
import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptConsume.TuangouReceiptConsumeResponse;
public interface MeiTuanService {
MeituanTokenResponse getAccessToken(String code , String state);
MeituanTokenResponse prepare(String code);
MeituanResponse<TuangouReceiptConsumeResponse> consume(String code);
MeituanTokenResponse refreshAccessToken(String refreshToken);
}

View File

@ -15,6 +15,13 @@ public interface OrderService {
*/
OrderMainPO queryOrderMainPoByOrderNo(String orderNo, String delFlag);
/**
* 团购券核销
* @param req
* @return
*/
OrderDetailDTO verifyGroupBuyCoupons(VerifyGroupBuyCouponsReq req) ;
/**
* 提交租车订单
* @param rentCarOrderReq
@ -36,6 +43,14 @@ public interface OrderService {
*/
RentCarOrderResultDTO depositFreePayRentCarOrder(RentCarOrderReq rentCarOrderReq);
/**
* 第三方订单
* @param rentCarOrderReq
* @return
*/
RentCarOrderResultDTO thirdPlatformRentCarOrder(RentCarThirdPlatformOrderReq rentCarOrderReq);
/**
* 续租车
* @param rentCarOrderReq

View File

@ -7,6 +7,8 @@ import java.math.BigDecimal;
public interface PayService {
/**
* 预支付下单
* @param payType
@ -30,6 +32,19 @@ public interface PayService {
String refundOrder(String payType,Long companyId,String outTradeNo, BigDecimal totalFee, BigDecimal refundFee);
/**
* 支付宝免押扣款
* @param body
* @param companyId
* @param outTradeNo
* @param authNo
* @param aliPayOpenId
* @param totalFee
* @return
*/
UnifiedPaymentInfoDTO zhimaPayOrder(String body, Long companyId, String outTradeNo, String authNo, String aliPayOpenId,BigDecimal totalFee);
/**
* 冻结
* @param companyId
@ -46,7 +61,7 @@ public interface PayService {
* @param authCode
* @return
*/
boolean finishFreezeOrder(Long companyId,String outOrderNo, String authCode);
boolean finishFreezeOrder(Long companyId,String outOrderNo, String authCode, BigDecimal freezeFee);
/**
* 取消冻结

View File

@ -0,0 +1,191 @@
package com.sczx.order.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.tea.TeaException;
import com.douyin.openapi.client.Client;
import com.douyin.openapi.client.models.*;
import com.douyin.openapi.credential.models.Config;
import com.meituan.sdk.DefaultMeituanClient;
import com.meituan.sdk.MeituanClient;
import com.meituan.sdk.MeituanResponse;
import com.meituan.sdk.auth.MeituanTokenResponse;
import com.meituan.sdk.internal.utils.SignerUtil;
import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptPrepare.TuangouReceiptPrepareRequest;
import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptPrepare.TuangouReceiptPrepareResponse;
import com.sczx.order.common.Result;
import com.sczx.order.config.DouyinTokenManager;
import com.sczx.order.dto.VerifyGroupBuyCouponsReq;
import com.sczx.order.exception.InnerException;
import com.sczx.order.service.DouyinService;
import com.sczx.order.thirdpart.dto.CompanyStoreDTO;
import com.sczx.order.thirdpart.integration.StoreInteg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
@Service
public class DouyinServiceImpl implements DouyinService {
@Value("${coupon.douyin.client_key}")
private String CLIENT_KEY;
@Value("${coupon.douyin.client_secret}")
private String CLIENT_SECRET;
@Value("${coupon.douyin.account_id}")
private String ACCOUNT_ID;
@Value("${coupon.douyin.poi_id}")
private String POI_ID;
@Autowired
private DouyinTokenManager douyinTokenManager;
public String resolveShortUrlToGetObjectId(String shortUrl) {
try {
URL url = new URL(shortUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(false); // 禁止自动重定向
connection.connect();
String longUrl = connection.getHeaderField("Location");
connection.disconnect();
if (longUrl == null || longUrl.isEmpty()) {
return null;
}
// 使用正则表达式匹配object_id参数
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("object_id=([^&]*)");
java.util.regex.Matcher matcher = pattern.matcher(longUrl);
if (matcher.find()) {
return matcher.group(1);
}
return null;
} catch (Exception e) {
log.error("二维码不正确");
throw new InnerException("扫码核销失败,二维码不正确");
}
}
/**
* 抖音验券准备返回抖音验券对象
*/
@Override
public CertificatePrepareResponse prepare(CertificatePrepareRequest req) {
// 获取当前有效的access_token
String accessToken = douyinTokenManager.getCurrentToken();
if (accessToken == null || accessToken.isEmpty()) {
throw new RuntimeException("无法获取有效的access_token");
}
//抖音验券请求
try {
Config config = new Config().setClientKey(CLIENT_KEY).setClientSecret(CLIENT_SECRET);
Client client = new Client(config);
CertificatePrepareRequest sdkRequest = new CertificatePrepareRequest();
sdkRequest.setAccessToken(accessToken);
sdkRequest.setAccountId(ACCOUNT_ID);
String encryptedData = resolveShortUrlToGetObjectId(req.getEncryptedData());
sdkRequest.setEncryptedData(encryptedData);
sdkRequest.setPoiId(req.getPoiId());
CertificatePrepareResponse sdkResponse = client.CertificatePrepare(sdkRequest);
System.out.println(JSONObject.toJSONString(sdkResponse));
if (sdkResponse.getExtra().getErrorCode() != 0){
throw new RuntimeException("券验证失败:" + sdkResponse.getExtra().getDescription());
}
return sdkResponse;
} catch (TeaException e) {
throw new RuntimeException("券验证失败");
} catch (Exception e) {
throw new RuntimeException("券验证失败");
}
}
@Override
public CertificateVerifyResponse verify(CertificateVerifyRequest req) {
// 获取当前有效的access_token
String accessToken = douyinTokenManager.getCurrentToken();
if (accessToken == null || accessToken.isEmpty()) {
throw new RuntimeException("无法获取有效的access_token");
}
try{
Config config = new Config().setClientKey(CLIENT_KEY).setClientSecret(CLIENT_SECRET);
Client client = new Client(config);
CertificateVerifyRequest sdkRequest = new CertificateVerifyRequest();
sdkRequest.setAccessToken(accessToken);
sdkRequest.setAccountId(ACCOUNT_ID);
sdkRequest.setPoiId(req.getPoiId());
sdkRequest.setVerifyToken(req.getVerifyToken());
CertificateVerifyResponse sdkResponse = client.CertificateVerify(sdkRequest);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public CertificateCancelResponse cancel(CertificateCancelRequest req) {
// 获取当前有效的access_token
String accessToken = douyinTokenManager.getCurrentToken();
if (accessToken == null || accessToken.isEmpty()) {
throw new RuntimeException("无法获取有效的access_token");
}
try{
Config config = new Config().setClientKey(CLIENT_KEY).setClientSecret(CLIENT_SECRET);
Client client = new Client(config);
CertificateCancelRequest sdkRequest = new CertificateCancelRequest();
sdkRequest.setAccessToken(accessToken);
sdkRequest.setVerifyId(req.getVerifyId());
sdkRequest.setCertificateId(req.getCertificateId());
CertificateCancelResponse sdkResponse = client.CertificateCancel(sdkRequest);
System.out.println(JSONObject.toJSONString(sdkResponse));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,49 @@
package com.sczx.order.service.impl;
import com.sczx.order.common.enums.CouponTypeEnum;
import com.sczx.order.dto.GroupBuyOrderInfoDto;
import com.sczx.order.dto.SimpleUserInfoDTO;
import com.sczx.order.exception.BizException;
import com.sczx.order.service.GroupBuyCouponService;
import com.sczx.order.service.MeiTuanService;
import com.sczx.order.thirdpart.integration.UserInteg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: 张黎
* @Date: 2025/10/27/20:48
* @Description:
*/
@Slf4j
@Service
public class GroupBuyCouponServiceImpl implements GroupBuyCouponService {
@Autowired
private UserInteg userInteg;
@Autowired
private MeiTuanService meiTuanService;
@Override
public GroupBuyOrderInfoDto getGroupBuyOrderInfoDto(String couponCode, String couponType, String mobile) {
SimpleUserInfoDTO userInfoDTO = userInteg.getUInfoByMobile(mobile);
if(userInfoDTO==null){
throw new BizException("用户不存在");
}
if(userInfoDTO.getAuthed()==0){
throw new BizException("用户未实名认证");
}
return null;
}
@Override
public boolean checkCouponCode(String couponCode, String couponType) {
if(CouponTypeEnum.MT.getCode().equals(couponType)){
}
return false;
}
}

View File

@ -0,0 +1,133 @@
package com.sczx.order.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.meituan.sdk.DefaultMeituanClient;
import com.meituan.sdk.MeituanClient;
import com.meituan.sdk.MeituanResponse;
import com.meituan.sdk.auth.MeituanTokenResponse;
import com.meituan.sdk.internal.exceptions.MtSdkException;
import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptConsume.TuangouReceiptConsumeRequest;
import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptConsume.TuangouReceiptConsumeResponse;
import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptPrepare.TuangouReceiptPrepareRequest;
import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptPrepare.TuangouReceiptPrepareResponse;
import com.sczx.order.exception.BizException;
import com.sczx.order.service.MeiTuanService;
import com.sczx.order.thirdpart.integration.StoreInteg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class MeiTuanServiceImpl implements MeiTuanService {
private Long DeveloperId = 116997L;
private String Signkey = "n8xlhtshk7t1luvi";
//private String accessToken = "V2-26b8d231854d3fdf6a5c06273377a65e441e4c9f1c8cea4959c510bf0dd19ed7657e34bebe4b74aa455b80d3c757cd8a66e3ece9148e6d6838778db5f9600cf383dbf89c0cc27f07f777c98649bd94fe";
private String accessToken = "V2-549da132ffebe5ce5731a17ae166cb899e13c579900d6cbc4163b60e4d40440ccbe8e56d25c3341cd1003c5529c407085c06af2eb41d2d091353b4d2be6f887582d9d4bf247cf9a511581ad46ade47e8";
@Autowired
private StoreInteg storeInteg;
@Override
public MeituanTokenResponse getAccessToken(String code, String state) {
try {
// 使用美团SDK构建客户端
MeituanClient client = DefaultMeituanClient.builder(DeveloperId, Signkey).build();
// 使用美团SDK的getOAuthToken方法获取token
MeituanTokenResponse response = client.getOAuthToken(58, code);
log.info("获取access_token响应结果: {}", JSONObject.toJSONString(response));
String token = response.getData().getAccessToken();
storeInteg.recordToken(state, token);
return null;
} catch (Exception e) {
throw new BizException("获取access_token失败");
}
}
@Override
public MeituanTokenResponse prepare(String code) {
try {
MeituanClient meituanClient = DefaultMeituanClient.builder(DeveloperId, Signkey).build();
TuangouReceiptPrepareRequest tuangouReceiptPrepareRequest = new TuangouReceiptPrepareRequest();
tuangouReceiptPrepareRequest.setReceiptCode("0106972239359");
String appAuthToken = accessToken;
MeituanResponse<TuangouReceiptPrepareResponse> response = meituanClient.invokeApi(tuangouReceiptPrepareRequest, appAuthToken);
log.info("获取access_token响应结果: {}", JSONObject.toJSONString(response));
if (response.isSuccess()) {
TuangouReceiptPrepareResponse resp = response.getData();
System.out.println(resp);
} else {
System.out.println("调用失败");
}
return null;
} catch (Exception e) {
log.error("获取access_token异常", e);
return null;
}
}
@Override
public MeituanTokenResponse refreshAccessToken(String refreshToken) {
try {
MeituanClient meituanClient = DefaultMeituanClient.builder(DeveloperId, Signkey).build();
MeituanTokenResponse response = meituanClient.refreshToken(58, refreshToken);
return response;
} catch (Exception e) {
log.error("获取access_token异常", e);
return null;
}
}
@Override
public MeituanResponse<TuangouReceiptConsumeResponse> consume (String code) {
try {
MeituanClient meituanClient = DefaultMeituanClient.builder(DeveloperId, Signkey).build();
TuangouReceiptConsumeRequest tuangouReceiptConsumeRequest = new TuangouReceiptConsumeRequest();
tuangouReceiptConsumeRequest.setReceiptCode("0106972239359");
tuangouReceiptConsumeRequest.setCount(1);
tuangouReceiptConsumeRequest.setRequestId("fdae143414");
tuangouReceiptConsumeRequest.setAppShopAccountName("customer");
tuangouReceiptConsumeRequest.setAppShopAccount("customer");
String appAuthToken = accessToken;
MeituanResponse<TuangouReceiptConsumeResponse> response = meituanClient.invokeApi(tuangouReceiptConsumeRequest, appAuthToken);
log.info("获取access_token响应结果: {}", JSONObject.toJSONString(response));
return response;
} catch (MtSdkException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -5,16 +5,18 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import com.sczx.order.common.enums.DistribTypeEnum;
import com.sczx.order.common.enums.WalletChangeTypeEnum;
import com.sczx.order.dto.OrderDistribDTO;
import com.sczx.order.dto.OrderDistribQueryReq;
import com.sczx.order.exception.BizException;
import com.sczx.order.po.*;
import com.sczx.order.po.BaseUserReferralPO;
import com.sczx.order.po.OrderDistribPO;
import com.sczx.order.po.OrderMainPO;
import com.sczx.order.repository.*;
import com.sczx.order.service.OrderDistribService;
import com.sczx.order.thirdpart.dto.CompanyDTO;
import com.sczx.order.thirdpart.dto.CompanyStoreDTO;
import com.sczx.order.thirdpart.dto.SysConfigDTO;
import com.sczx.order.thirdpart.dto.req.DistibIncomeReq;
import com.sczx.order.thirdpart.integration.StoreInteg;
import com.sczx.order.thirdpart.integration.UserInteg;
import lombok.extern.slf4j.Slf4j;
@ -24,7 +26,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
@ -115,30 +116,39 @@ public class OrderDistribServiceImpl implements OrderDistribService {
.set(OrderMainPO::getDistribed, 1);
orderMainRepo.update(updateMainWrapper);
//门店钱包入账
LambdaQueryWrapper<BaseWalletPO> queryWalletWrapper = new LambdaQueryWrapper<>();
queryWalletWrapper.eq(BaseWalletPO::getUserId, orderMainPO.getOperatorId()).last(" limit 1");
BaseWalletPO baseWalletPO = baseWalletRepo.getOne(queryWalletWrapper);
if(baseWalletPO != null){
LambdaUpdateWrapper<BaseWalletPO> updateWalletWrapper = new LambdaUpdateWrapper<>();
updateWalletWrapper.eq(BaseWalletPO::getUserId, orderMainPO.getOperatorId())
.set(BaseWalletPO::getBalance, baseWalletPO.getBalance().add(storeDistribPO.getDistribAmount()));
baseWalletRepo.update(updateWalletWrapper);
BaseWalletChangePO baseWalletChangePO = new BaseWalletChangePO();
baseWalletChangePO.setUserId(orderMainPO.getOperatorId());
baseWalletChangePO.setChangeType(WalletChangeTypeEnum.REFERRAL.getCode());
baseWalletChangePO.setChangeTime(LocalDateTime.now());
baseWalletChangePO.setChangeAmount(storeDistribPO.getDistribAmount());
baseWalletChangePO.setReferralOrderNo(orderMainPO.getOrderNo());
baseWalletChangeRepo.save(baseWalletChangePO);
//更新推荐信息,防止重复分润
LambdaUpdateWrapper<BaseUserReferralPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(BaseUserReferralPO::getUserId, orderMainPO.getCustomerId())
.set(BaseUserReferralPO::getReferralOrderNo, storeDistribPO.getOrderNo());
baseUserReferralRepo.update(updateWrapper);
}
//门店钱包入账
DistibIncomeReq distibIncomeReq = new DistibIncomeReq();
distibIncomeReq.setStoreId(orderMainPO.getStoreId());
distibIncomeReq.setAmount(storeDistribPO.getDistribAmount());
storeInteg.distibIncome(distibIncomeReq);
// LambdaQueryWrapper<BaseWalletPO> queryWalletWrapper = new LambdaQueryWrapper<>();
// queryWalletWrapper.eq(BaseWalletPO::getUserId, orderMainPO.getStoreId()).last(" limit 1");
// BaseWalletPO baseWalletPO = baseWalletRepo.getOne(queryWalletWrapper);
// if(baseWalletPO != null){
// LambdaUpdateWrapper<BaseWalletPO> updateWalletWrapper = new LambdaUpdateWrapper<>();
// updateWalletWrapper.eq(BaseWalletPO::getUserId, orderMainPO.getOperatorId())
// .set(BaseWalletPO::getBalance, baseWalletPO.getBalance().add(storeDistribPO.getDistribAmount()));
// baseWalletRepo.update(updateWalletWrapper);
// }else {
// baseWalletPO = new BaseWalletPO();
// baseWalletPO.setUserId(Long.valueOf(companyStoreDTO.getId()));
// baseWalletPO.setUserName(companyStoreDTO.getName());
// }
// BaseWalletChangePO baseWalletChangePO = new BaseWalletChangePO();
// baseWalletChangePO.setUserId(orderMainPO.getOperatorId());
// baseWalletChangePO.setChangeType(WalletChangeTypeEnum.REFERRAL.getCode());
// baseWalletChangePO.setChangeTime(LocalDateTime.now());
// baseWalletChangePO.setChangeAmount(storeDistribPO.getDistribAmount());
// baseWalletChangePO.setReferralOrderNo(orderMainPO.getOrderNo());
// baseWalletChangeRepo.save(baseWalletChangePO);

View File

@ -3,6 +3,10 @@ package com.sczx.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.douyin.openapi.client.models.CertificatePrepareRequest;
import com.douyin.openapi.client.models.CertificatePrepareResponse;
import com.douyin.openapi.client.models.CertificateVerifyRequest;
import com.douyin.openapi.client.models.CertificateVerifyResponse;
import com.sczx.order.common.constant.RedisKeyConstants;
import com.sczx.order.common.enums.*;
import com.sczx.order.convert.OrderCarImgConvert;
@ -13,15 +17,10 @@ import com.sczx.order.exception.BizException;
import com.sczx.order.exception.InnerException;
import com.sczx.order.po.*;
import com.sczx.order.repository.*;
import com.sczx.order.service.OrderDistribService;
import com.sczx.order.service.OrderService;
import com.sczx.order.service.PayService;
import com.sczx.order.service.*;
import com.sczx.order.thirdpart.dto.*;
import com.sczx.order.thirdpart.dto.req.*;
import com.sczx.order.thirdpart.integration.CarInteg;
import com.sczx.order.thirdpart.integration.PayInteg;
import com.sczx.order.thirdpart.integration.StoreInteg;
import com.sczx.order.thirdpart.integration.SyncInteg;
import com.sczx.order.thirdpart.integration.*;
import com.sczx.order.utils.JwtUtil;
import com.sczx.order.utils.OrderUtil;
import com.sczx.order.utils.RedisUtil;
@ -36,6 +35,7 @@ import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@Slf4j
@ -60,6 +60,9 @@ public class OrderServiceImpl implements OrderService {
@Autowired
private OrderCarChangeRepo orderCarChangeRepo;
@Autowired
private ElectronicFenceRuleRepo electronicFenceRuleRepo;
@Autowired
private JwtUtil jwtUtil;
@ -81,6 +84,15 @@ public class OrderServiceImpl implements OrderService {
@Autowired
private SyncInteg syncInteg;
@Autowired
private GroupBuyCouponService groupBuyCouponService;
@Autowired
private DouyinService douyinService;
@Autowired
private MeiTuanService meiTuanService;
@Override
public OrderMainPO queryOrderMainPoByOrderNo(String orderNo, String delFlag) {
LambdaQueryWrapper<OrderMainPO> currentOrderWrapper = new LambdaQueryWrapper<>();
@ -89,6 +101,72 @@ public class OrderServiceImpl implements OrderService {
return orderMainRepo.getOne(currentOrderWrapper);
}
@Override
public OrderDetailDTO verifyGroupBuyCoupons(VerifyGroupBuyCouponsReq req){
if (req.getCouponType() == CouponTypeEnum.MT.getCode()){
//TODO 调用美团验券接口
}else if (req.getCouponType() == CouponTypeEnum.DY.getCode()){
//根据门店ID获取抖音门店ID
CompanyStoreDTO store = storeInteg.getStoreById(req.getStoreId().intValue());
String poiId = store.getDyStoreId();
//调用抖音验券接口
CertificatePrepareRequest prepareRequest = new CertificatePrepareRequest();
prepareRequest.setPoiId(poiId);
prepareRequest.setEncryptedData(req.getCouponCode());
CertificatePrepareResponse response =douyinService.prepare(prepareRequest);
//获取核销所需信息
String verifyToken = response.getData().getVerifyToken();
AtomicReference<String> phone = new AtomicReference<>("");
response.getData().getCertificates().forEach(certificate -> {
Long certificateId = certificate.getCertificateId();
String skuId = certificate.getSku().getSkuId();
certificate.getReserveInfo().getOrderReserveUserInfoList().forEach(orderReserveUserInfo -> {
phone.set(orderReserveUserInfo.getPhone());
System.out.println(orderReserveUserInfo.getPhone());
});
});
String finalPhone = phone.get();
//校验用户是否存在,是否实名认证
GroupBuyOrderInfoDto groupBuyOrderInfoDTO = groupBuyCouponService.getGroupBuyOrderInfoDto(req.getCouponCode(), req.getCouponType(), req.getMobile());
if (finalPhone != null && !finalPhone.isEmpty()) {
// 如果校验券没有返回手机号码,则使用前端所传手机号码
log.info("您所团购的订单未填写手机号,将使用您提交给门店的手机号生成租车订单");
}else{
// 如果校验券有返回手机号码,则使用前端所传手机号码与该脱敏手机号码比对
}
//核销抖音团购券
CertificateVerifyRequest verifyRequest = new CertificateVerifyRequest();
verifyRequest.setVerifyToken(verifyToken);
verifyRequest.setPoiId(poiId);
CertificateVerifyResponse verifyResponse =douyinService.verify(verifyRequest);
verifyResponse.getData().getVerifyResults().forEach(verifyResult -> {
//撤销验券需要下面两个值
verifyResult.getCertificateId();
verifyResult.getVerifyId();
});
//TODO 生成订单
}
return null;
}
@Transactional(rollbackFor = Exception.class)
@Override
public RentCarOrderResultDTO submitRentCarOrder(RentCarOrderReq rentCarOrderReq) {
@ -182,6 +260,7 @@ public class OrderServiceImpl implements OrderService {
depositOrder.setAmount(rentBatteyRuleDTO.getRentPrice());
depositOrder.setCreatedAt(LocalDateTime.now());
depositOrder.setPaymentMethod(paymentType);
depositOrder.setReturnTime(endRentTime);
orderSubPOList.add(depositOrder);
}
//生成租车订单
@ -239,6 +318,7 @@ public class OrderServiceImpl implements OrderService {
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public RentCarOrderResultDTO depositFreeSubmitRentCarOrder(RentCarOrderReq rentCarOrderReq) {
log.info("开始发起免押 : {}", rentCarOrderReq);
@ -307,10 +387,11 @@ public class OrderServiceImpl implements OrderService {
String paymentType = PaymentTypeEnum.ZFB_PAY.getCode();
List<OrderSubPO> orderSubPOList = new ArrayList<>();
//生成免押订单
OrderSubPO noDepositOrder = new OrderSubPO();
noDepositOrder.setSuborderNo(OrderUtil.generateSubOrderNo(OrderUtil.MY_PREFIX));
noDepositOrder.setPaymentId(OrderUtil.generateSubOrderNo(OrderUtil.FZ_PREFIX));
noDepositOrder.setPaymentId(OrderUtil.generateSubOrderNo(OrderUtil.ZF_PREFIX));
noDepositOrder.setSuborderType(SubOrderTypeEnum.NO_DEPOSIT.getCode());
noDepositOrder.setAmount(orderMainPO.getDepositPrice());
noDepositOrder.setCreatedAt(LocalDateTime.now());
@ -319,6 +400,7 @@ public class OrderServiceImpl implements OrderService {
orderSubPOList.add(noDepositOrder);
String paymentId = OrderUtil.generateSubOrderNo(OrderUtil.ZF_PREFIX);
//如果选择了租电套餐,则还需要生成租电子订单
if(rentBatteyRuleDTO!=null){
rentBatteyRuleDTO = carInteg.getRentBatteyRuleByBatteyRuleId(rentCarOrderReq.getRentBatteyRuleId());
@ -330,6 +412,7 @@ public class OrderServiceImpl implements OrderService {
rentBatteyRuleOrder.setPaymentMethod(paymentType);
rentBatteyRuleOrder.setPayStatus(PayStatusEnum.INIT.getCode());
rentBatteyRuleOrder.setPaymentId(paymentId);
rentBatteyRuleOrder.setReturnTime(endRentTime);
orderSubPOList.add(rentBatteyRuleOrder);
}
//生成租车订单
@ -384,6 +467,7 @@ public class OrderServiceImpl implements OrderService {
}
@Transactional(rollbackFor = Exception.class)
@Override
public RentCarOrderResultDTO depositFreePayRentCarOrder(RentCarOrderReq rentCarOrderReq) {
SimpleUserInfoDTO userInfoDTO = jwtUtil.getUserInfoFromToken();
@ -421,6 +505,10 @@ public class OrderServiceImpl implements OrderService {
//开始发起支付
UnifiedPaymentInfoDTO unifiedPaymentInfoDTO = payService.prepayOrder(rentCarOrder.getPaymentMethod(),orderMainPO.getRentCarRuleId().toString(),orderMainPO.getOperatorId(),rentCarOrder.getPaymentId(),userInfoDTO
,orderMainPO.getOrderAmount());
//免押代扣支付-过审验证用
// UnifiedPaymentInfoDTO unifiedPaymentInfoDTO = payService.zhimaPayOrder("闪充智行租车免押支付",orderMainPO.getOperatorId(),rentCarOrder.getPaymentId()
// ,noDepositOrder.getTransactionId(),userInfoDTO.getAlipayOpenid(),orderMainPO.getOrderAmount());
LambdaUpdateWrapper<OrderSubPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(OrderSubPO::getSuborderId, noDepositOrder.getSuborderId())
.eq(OrderSubPO::getDelFlag, "0")
@ -449,6 +537,161 @@ public class OrderServiceImpl implements OrderService {
}
@Transactional(rollbackFor = Exception.class)
@Override
public RentCarOrderResultDTO thirdPlatformRentCarOrder(RentCarThirdPlatformOrderReq rentCarOrderReq) {
LambdaQueryWrapper<OrderMainPO> currentOrderWrapper = new LambdaQueryWrapper<>();
currentOrderWrapper.eq(OrderMainPO::getCustomerId, rentCarOrderReq.getCustomerId())
.notIn(OrderMainPO::getOrderStatus, Arrays.asList(OrderStatusEnum.AUTO_END.getCode(), OrderStatusEnum.MANUAL_END.getCode()))
.eq(OrderMainPO::getDelFlag, "0");
List<OrderMainPO> currentOrderList = orderMainRepo.list(currentOrderWrapper);
OrderMainPO waitPayOrder = currentOrderList.stream().filter(order -> order.getOrderStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())).findFirst().orElse(null);
if(!currentOrderList.isEmpty() && waitPayOrder==null){
throw new BizException("您有未完成的订单,请先完成订单");
}
if(waitPayOrder!=null){
log.info("存在待支付的订单,取消订单");
PayOrderReq payOrderReq = new PayOrderReq();
payOrderReq.setOrderNo(waitPayOrder.getOrderNo());
cancelOrder(payOrderReq,null);
}
//判断是否存有空闲车辆可用
LambdaQueryWrapper<CarPO> carWrapper = new LambdaQueryWrapper<>();
carWrapper.eq(CarPO::getModelId, rentCarOrderReq.getCarModelId())
.eq(CarPO::getDelFlag, "0")
.eq(CarPO::getBrsStatus, "0")
.eq(CarPO::getStoreId, rentCarOrderReq.getStoreId());
List<CarPO> carPOList = carRepo.list(carWrapper);
if(CollectionUtils.isEmpty(carPOList)){
throw new BizException("门店没有该车型的车辆可租");
}
//获取门店信息
// CompanyStoreDTO companyStoreDTO = storeInteg.getStoreById(Integer.valueOf(rentCarOrderReq.getStoreId().toString()));
// if(Objects.isNull(companyStoreDTO)){
// throw new BizException("运营商或门店已下架");
// }
CarModelSimpleDTO carModelSimpleDTO = carInteg.getCarModelByModelId(rentCarOrderReq.getCarModelId());
RentCarRuleDTO rentCarRuleDTO = carInteg.getRentCarRuleByCarRuleId(rentCarOrderReq.getRentCarRuleId());
RentBatteyRuleDTO rentBatteyRuleDTO = null;
if(rentCarOrderReq.getRentBatteyRuleId()!=null){
rentBatteyRuleDTO = carInteg.getRentBatteyRuleByBatteyRuleId(rentCarOrderReq.getRentBatteyRuleId());
}
String redisLockKey = RedisKeyConstants.ORDER_SUB_KEY + rentCarOrderReq.getCustomerId();
SimpleUserInfoDTO userInfoDTO = new SimpleUserInfoDTO();
userInfoDTO.setUserId(Math.toIntExact(rentCarOrderReq.getCustomerId()));
userInfoDTO.setUserName(rentCarOrderReq.getCustomerName());
userInfoDTO.setPhoneNumber(rentCarOrderReq.getCustomerPhone());
if(redisUtil.getRedisLock(redisLockKey, "租车下单")) {
try{
//TODO 这里保存订单要做事物处理
//生成订单主表
OrderMainPO orderMainPO = OrderConvert.INSTANCE.subOrderToPo(rentCarOrderReq, userInfoDTO, rentCarRuleDTO);
//orderMainPO.setOperatorId(Long.valueOf(companyStoreDTO.getOperatingCompanyId()));
orderMainPO.setOrderNo(OrderUtil.generateOrderNo());
orderMainPO.setOrderStatus(OrderStatusEnum.WAIT_PICK.getCode());
orderMainPO.setFirstOrderTime(LocalDateTime.now());
//设置预计还车时间
LocalDateTime endRentTime = OrderUtil.getEndRentTime(orderMainPO.getFirstOrderTime(),1,rentCarRuleDTO.getRentalDays(), rentCarRuleDTO.getRentalType());
orderMainPO.setEndRentTime(endRentTime);
//生成子表订单
String paymentType = rentCarOrderReq.getOrderSource();
List<OrderSubPO> orderSubPOList = new ArrayList<>();
//如果未开通免押则要生成押金订单
if(!rentCarOrderReq.getIsDepositFree()){
OrderSubPO depositOrder = new OrderSubPO();
depositOrder.setSuborderNo(OrderUtil.generateSubOrderNo(OrderUtil.YJ_PREFIX));
depositOrder.setSuborderType(SubOrderTypeEnum.DEPOSIT.getCode());
depositOrder.setAmount(orderMainPO.getDepositPrice());
depositOrder.setCreatedAt(LocalDateTime.now());
depositOrder.setPaymentMethod(paymentType);
depositOrder.setTransactionId(rentCarOrderReq.getThirdOrderNo());
orderSubPOList.add(depositOrder);
}
//如果选择了租电套餐,则还需要生成租电子订单
if(rentBatteyRuleDTO!=null){
rentBatteyRuleDTO = carInteg.getRentBatteyRuleByBatteyRuleId(rentCarOrderReq.getRentBatteyRuleId());
OrderSubPO depositOrder = new OrderSubPO();
depositOrder.setSuborderNo(OrderUtil.generateSubOrderNo(OrderUtil.ZD_PREFIX));
depositOrder.setSuborderType(SubOrderTypeEnum.RENTBATTEY.getCode());
depositOrder.setAmount(rentBatteyRuleDTO.getRentPrice());
depositOrder.setCreatedAt(LocalDateTime.now());
depositOrder.setPaymentMethod(paymentType);
depositOrder.setReturnTime(endRentTime);
depositOrder.setTransactionId(rentCarOrderReq.getThirdOrderNo());
orderSubPOList.add(depositOrder);
}
//生成租车订单
BigDecimal rentCarOrderAmount = OrderUtil.getRentCarAmount(rentCarRuleDTO.getRentalType(), rentCarRuleDTO.getRentalPrice(), rentCarRuleDTO.getRentalDays());
OrderSubPO rentOrder = new OrderSubPO();
rentOrder.setSuborderNo(OrderUtil.generateSubOrderNo(OrderUtil.ZC_PREFIX));
rentOrder.setSuborderType(SubOrderTypeEnum.RENTCAR.getCode());
rentOrder.setAmount(rentCarOrderAmount);
rentOrder.setCreatedAt(LocalDateTime.now());
rentOrder.setPaymentMethod(paymentType);
rentOrder.setReturnTime(endRentTime);
rentOrder.setTransactionId(rentCarOrderReq.getThirdOrderNo());
orderSubPOList.add(rentOrder);
BigDecimal orderAmount = orderSubPOList.stream().map(OrderSubPO::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
orderMainPO.setOrderAmount(orderAmount);
//发起支付返回预支付信息
String paymentId = OrderUtil.generateSubOrderNo(OrderUtil.ZF_PREFIX);
for(OrderSubPO orderSubPO : orderSubPOList){
orderSubPO.setPaymentId(paymentId);
orderSubPO.setPayStatus(PayStatusEnum.USERPAYING.getCode());
}
orderMainRepo.save(orderMainPO);
orderSubPOList.forEach(orderSubPO -> {
orderSubPO.setOrderId(orderMainPO.getOrderId());
});
orderSubRepo.saveBatch(orderSubPOList);
//返回订单信息
OrderDTO orderDTO = OrderConvert.INSTANCE.poToDto(orderMainPO);
//orderDTO.setCompanyStoreDTO(companyStoreDTO);
orderDTO.setCarModelSimpleDTO(carModelSimpleDTO);
RentCarOrderResultDTO rentCarOrderResultDTO = new RentCarOrderResultDTO();
rentCarOrderResultDTO.setOrderMainInfo(orderDTO);
//rentCarOrderResultDTO.setUnifiedPaymentInfo(unifiedPaymentInfoDTO);
return rentCarOrderResultDTO;
}catch (Exception e){
log.warn("下单失败", e);
throw e;
} finally {
redisUtil.deleteRedisLock(redisLockKey);
}
} else {
log.warn("下单失败,锁已被占用");
throw new InnerException("服务器正在处理,请稍后再试");
}
}
@Transactional(rollbackFor = Exception.class)
@Override
@ -521,13 +764,14 @@ public class OrderServiceImpl implements OrderService {
//生成租电子订单
OrderSubPO rentBatraayOrder = new OrderSubPO();
rentBatraayOrder.setOrderId(orderMainPO.getOrderId());
rentBatraayOrder.setSuborderNo(OrderUtil.generateSubOrderNo(OrderUtil.ZC_PREFIX));
rentBatraayOrder.setSuborderNo(OrderUtil.generateSubOrderNo(OrderUtil.ZD_PREFIX));
rentBatraayOrder.setPaymentId(paymentId);
rentBatraayOrder.setSuborderType(SubOrderTypeEnum.RENTBATTEY.getCode());
rentBatraayOrder.setAmount(rentBatteyOrderAmount);
rentBatraayOrder.setCreatedAt(LocalDateTime.now());
rentBatraayOrder.setPaymentMethod(paymentType);
rentBatraayOrder.setPayStatus(PayStatusEnum.USERPAYING.getCode());
rentBatraayOrder.setReturnTime(newEndRentTime);
orderSubRepo.save(rentBatraayOrder);
}
@ -593,6 +837,7 @@ public class OrderServiceImpl implements OrderService {
,orderAmount);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void cancelOrder(PayOrderReq payOrderReq,OrderMainPO orderMainPO) {
log.info("开始取消订单");
@ -606,7 +851,7 @@ public class OrderServiceImpl implements OrderService {
LambdaQueryWrapper<OrderSubPO> orderSubQueryWrapper = new LambdaQueryWrapper<>();
orderSubQueryWrapper.eq(OrderSubPO::getOrderId, orderMainPO.getOrderId());
orderSubQueryWrapper.in(OrderSubPO::getPayStatus, Arrays.asList(PayStatusEnum.USERPAYING.getCode(), PayStatusEnum.INIT.getCode()));
orderSubQueryWrapper.in(OrderSubPO::getPayStatus, Arrays.asList(PayStatusEnum.USERPAYING.getCode(), PayStatusEnum.INIT.getCode(), PayStatusEnum.SUCCESS.getCode()));
orderSubQueryWrapper.orderByDesc(OrderSubPO::getCreatedAt);
List<OrderSubPO> orderSubPOList = orderSubRepo.list(orderSubQueryWrapper);
boolean closePayOrder = false;
@ -634,11 +879,11 @@ public class OrderServiceImpl implements OrderService {
//不管取消是否成功,都要删除订单
closePayOrder = true;
}else {
closePayOrder = payService.finishFreezeOrder(Long.valueOf(companyStoreDTO.getOperatingCompanyId()),noDepositOrder.getPaymentId(),noDepositOrder.getTransactionId());
closePayOrder = payService.finishFreezeOrder(Long.valueOf(companyStoreDTO.getOperatingCompanyId()),noDepositOrder.getPaymentId(),noDepositOrder.getTransactionId(),noDepositOrder.getAmount());
}
} else if(StringUtils.equalsIgnoreCase(noDepositOrder.getPayStatus(), PayStatusEnum.SUCCESS.getCode())){
closePayOrder = payService.finishFreezeOrder(Long.valueOf(companyStoreDTO.getOperatingCompanyId()),noDepositOrder.getPaymentId(),noDepositOrder.getTransactionId());
closePayOrder = payService.finishFreezeOrder(Long.valueOf(companyStoreDTO.getOperatingCompanyId()),noDepositOrder.getPaymentId(),noDepositOrder.getTransactionId(),noDepositOrder.getAmount());
}
if (closePayOrder) {
//解冻成功更新状态为解冻成功
@ -932,6 +1177,7 @@ public class OrderServiceImpl implements OrderService {
orderDetailDTO.setOrderStatus(OrderStatusEnum.RENT_OVERDUE.getCode());
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(OrderMainPO::getOrderStatus, OrderStatusEnum.RENT_OVERDUE.getCode());
updateWrapper.set(OrderMainPO::getOverdueDays, overdueHours);
updateWrapper.eq(OrderMainPO::getOrderId, orderMainPO.getOrderId());
orderMainRepo.update(updateWrapper);
}
@ -971,6 +1217,13 @@ public class OrderServiceImpl implements OrderService {
if(StringUtils.equalsIgnoreCase(RentCarTypeEnum.HOUR_RENTAL.getCode(), orderMainPO.getRentalType())){
overdueDaysOrHours = OrderUtil.getOrderOverdueHours(orderMainPO.getEndRentTime());
}
//逾期天数有变化,则更新
if(!overdueDaysOrHours.equals(orderMainPO.getOverdueDays())){
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(OrderMainPO::getOverdueDays, overdueDaysOrHours);
updateWrapper.eq(OrderMainPO::getOrderId, orderMainPO.getOrderId());
orderMainRepo.update(updateWrapper);
}
orderDetailDTO.setOverdueAmount(OrderUtil.getOrderOverdueAmount(overdueDaysOrHours, orderMainPO.getOverdueFee()));
//计算续租周期
int rerentInterval = OrderUtil.calculateRerentRoundsToCoverOverdue(overdueDaysOrHours, orderMainPO.getRentalDays());
@ -1047,7 +1300,6 @@ public class OrderServiceImpl implements OrderService {
if(!orderMainPO.getOrderStatus().equals(OrderStatusEnum.WAIT_PICK.getCode()) && !orderMainPO.getOrderStatus().equals(OrderStatusEnum.WAIT_RETURN.getCode())){
throw new BizException("订单状态异常,非待取车或待还车状态,无法驳回");
}
SimpleUserInfoDTO userInfoDTO = jwtUtil.getUserInfoFromToken();
if(orderMainPO.getOrderStatus().equals(OrderStatusEnum.WAIT_PICK.getCode())){
//获取租车子订单
@ -1062,6 +1314,7 @@ public class OrderServiceImpl implements OrderService {
//变更订单状态,记录结束订单时间,记录订单备注
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(OrderMainPO::getOrderStatus, OrderStatusEnum.MANUAL_END.getCode());
updateWrapper.set(OrderMainPO::getEndOrderTime, LocalDateTime.now());
updateWrapper.set(OrderMainPO::getActEndRentTime, LocalDateTime.now());
updateWrapper.eq(OrderMainPO::getOrderNo, rejectOrderReq.getOrderNo());
orderMainRepo.update(updateWrapper);
@ -1083,6 +1336,32 @@ public class OrderServiceImpl implements OrderService {
orderSubRepo.save(fdSubOrder);
}
}
LambdaQueryWrapper<OrderSubPO> noDipoSubWrapper = new LambdaQueryWrapper<>();
noDipoSubWrapper.eq(OrderSubPO::getOrderId, orderMainPO.getOrderId())
.eq(OrderSubPO::getDelFlag, "0")
.eq(OrderSubPO::getSuborderType, SubOrderTypeEnum.NO_DEPOSIT.getCode())
.eq(OrderSubPO::getPayStatus, PayStatusEnum.SUCCESS).last(" limit 1");
OrderSubPO noDepositOrder = orderSubRepo.getOne(noDipoSubWrapper);
if(Objects.nonNull(noDepositOrder)){
log.info("查询支付宝冻结订单结果");
AlipayQueryFreezeRequest alipayQueryFreezeRequest = new AlipayQueryFreezeRequest();
alipayQueryFreezeRequest.setOutOrderNo(noDepositOrder.getPaymentId());
alipayQueryFreezeRequest.setOperationType("FREEZE");
alipayQueryFreezeRequest.setCompanyId(orderMainPO.getOperatorId().toString());
AlipayQueryFreezeResponse queryFreezeResponse = payInteg.queryFundFreeze(alipayQueryFreezeRequest);
if(queryFreezeResponse!=null){
boolean closePayOrder = payService.finishFreezeOrder(orderMainPO.getOperatorId(),noDepositOrder.getPaymentId(),noDepositOrder.getTransactionId(),noDepositOrder.getAmount());
if(closePayOrder){
//解冻成功更新状态为解冻成功
LambdaUpdateWrapper<OrderSubPO> updatenoDepositWrapper = new LambdaUpdateWrapper<>();
updatenoDepositWrapper.set(OrderSubPO::getPayStatus, PayStatusEnum.UNFREEZE_SUCCESS.getCode())
.eq(OrderSubPO::getSuborderId, noDepositOrder.getSuborderId());
orderSubRepo.update(updatenoDepositWrapper);
}
}
}
});
} else {
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
@ -1123,6 +1402,37 @@ public class OrderServiceImpl implements OrderService {
throw new BizException("订单状态异常,非待取车状态");
}
//同步租电订单
LambdaQueryWrapper<OrderSubPO> querySubBatteryWrapper = new LambdaQueryWrapper<>();
querySubBatteryWrapper.eq(OrderSubPO::getOrderId, orderMainPO.getOrderId())
.eq(OrderSubPO::getSuborderType, SubOrderTypeEnum.RENTBATTEY.getCode())
.orderByDesc(OrderSubPO::getCreatedAt)
.last(" limit 1");
OrderSubPO renBatteryOrderSubPO = orderSubRepo.getOne(querySubBatteryWrapper);
if(renBatteryOrderSubPO!=null){
Map<String, String> result = syncInteg.sendUserMeal(renBatteryOrderSubPO.getSuborderId());
String status = result.get("code");
if (status.equals("40001") || status.equals("40002") || status.equals("40003") || status.equals("40005") || status.equals("40006")){
throw new BizException("此用户在租电APP还有未完成的订单请联系用户完成租电订单或是驳回此租车订单");
}
}
//绑定电子围栏
LambdaQueryWrapper<ElectronicFenceRulePO> electqueryWrapper = new LambdaQueryWrapper<>();
electqueryWrapper.eq(ElectronicFenceRulePO::getCarRuleId, orderMainPO.getRentCarRuleId())
.eq(ElectronicFenceRulePO::getDelFlag, "0")
.orderByAsc(ElectronicFenceRulePO::getSortOrder);
ElectronicFenceRulePO electronicFenceRulePO = electronicFenceRuleRepo.getOne(electqueryWrapper);
if (electronicFenceRulePO != null) {
String redisEfenceKey = RedisKeyConstants.E_Fence_KEY + carDTO.getLotNumber();
redisUtil.set(redisEfenceKey, electronicFenceRulePO.getElectronicFenceId().toString());
log.info("电子围栏ID已存入Rediskey: {}, 电子围栏ID: {}", redisEfenceKey, electronicFenceRulePO.getElectronicFenceId());
} else {
log.warn("未找到租车规则ID为 {} 的电子围栏规则", orderMainPO.getRentCarRuleId());
}
LambdaQueryWrapper<OrderSubPO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderSubPO::getOrderId, orderMainPO.getOrderId())
.eq(OrderSubPO::getSuborderType, SubOrderTypeEnum.RENTCAR.getCode())
@ -1173,17 +1483,22 @@ public class OrderServiceImpl implements OrderService {
.set(CarPO::getBrsStatus, CarStatusEnum.RENT_ING.getCode());
carRepo.update(carPOLambdaUpdateWrapper);
//同步租电订单
LambdaQueryWrapper<OrderSubPO> querySubBatteryWrapper = new LambdaQueryWrapper<>();
querySubBatteryWrapper.eq(OrderSubPO::getOrderId, orderMainPO.getOrderId())
.eq(OrderSubPO::getSuborderType, SubOrderTypeEnum.RENTBATTEY.getCode())
.orderByDesc(OrderSubPO::getCreatedAt)
.last(" limit 1");
OrderSubPO renBatteryOrderSubPO = orderSubRepo.getOne(querySubBatteryWrapper);
if(renBatteryOrderSubPO!=null){
Long batterySubOrderId = renBatteryOrderSubPO.getSuborderId();
syncInteg.sendSubOrderId(batterySubOrderId);
ThreadPoolUtils.getThreadPool().execute(() -> {
try {
Map<String, String> result = syncInteg.sendSubOrderId(renBatteryOrderSubPO.getSuborderId());
String status = result.get("code");
if (status.equals("40001") || status.equals("40002") || status.equals("40003") || status.equals("40005") || status.equals("40006")){
log.warn("租电订单同步失败用户在租电APP还有未完成的订单subOrderId: {}", renBatteryOrderSubPO.getSuborderId());
}
} catch (Exception e) {
log.error("异步同步租电订单异常subOrderId: {}", renBatteryOrderSubPO.getSuborderId(), e);
}
});
}
return getOrderInfoByOrderNo(bindCarToOrderReq.getOrderNo());
} catch (Exception e){
log.warn("绑定车辆失败",e);
@ -1245,6 +1560,8 @@ public class OrderServiceImpl implements OrderService {
updateWrapper.eq(OrderMainPO::getOrderNo, bindCarToOrderReq.getOrderNo());
orderMainRepo.update(updateWrapper);
if(!CollectionUtils.isEmpty(bindCarToOrderReq.getImageList())){
//添加车辆图片
List<OrderCarImgPO> carImagePOList = new ArrayList<>();
@ -1271,7 +1588,18 @@ public class OrderServiceImpl implements OrderService {
.set(CarPO::getBrsStatus, CarStatusEnum.NORMAL.getCode());
carRepo.update(oldCarPOLambdaUpdateWrapper);
//更新电子围栏
LambdaUpdateWrapper<CarPO> oldCarIotPOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
oldCarIotPOLambdaUpdateWrapper.eq(CarPO::getId, orderMainPO.getVehicleId());
CarPO oldCarPO = carRepo.getOne(oldCarIotPOLambdaUpdateWrapper);
String redisEfenceKey = RedisKeyConstants.E_Fence_KEY + oldCarPO.getLotNumber();
if(redisUtil.get(redisEfenceKey) != null){
String efence = redisUtil.get(redisEfenceKey);
String newKey = RedisKeyConstants.E_Fence_KEY + carDTO.getLotNumber();
redisUtil.delete(redisEfenceKey);
redisUtil.set(newKey, efence);
}
//记录变更记录
OrderCarChangePO orderCarChangePO = new OrderCarChangePO();
@ -1332,6 +1660,7 @@ public class OrderServiceImpl implements OrderService {
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(OrderMainPO::getOrderStatus, OrderStatusEnum.AUTO_END.getCode());
updateWrapper.set(OrderMainPO::getActEndRentTime, LocalDateTime.now());
updateWrapper.set(OrderMainPO::getEndOrderTime, LocalDateTime.now());
updateWrapper.set(Objects.nonNull(returnCarReq.getDamageAmount()),OrderMainPO::getDamageAmount, returnCarReq.getDamageAmount());
updateWrapper.set(StringUtils.isNotBlank(returnCarReq.getDamageDesc()),OrderMainPO::getDamageDesc, returnCarReq.getDamageDesc());
updateWrapper.set(rentalDuration != null,OrderMainPO::getRentalDuration, rentalDuration);
@ -1361,6 +1690,13 @@ public class OrderServiceImpl implements OrderService {
carPOLambdaUpdateWrapper.eq(CarPO::getId, orderDTO.getVehicleId())
.set(CarPO::getBrsStatus, CarStatusEnum.NORMAL.getCode());
carRepo.update(carPOLambdaUpdateWrapper);
//删除车辆电子围栏缓存
String redisEfenceKey = RedisKeyConstants.E_Fence_KEY + carPO.getLotNumber();
if (redisUtil.get(redisEfenceKey) != null){
redisUtil.deleteRedisLock(redisEfenceKey);
}
//异步执行分润
ThreadPoolUtils.getThreadPool().execute(() -> {
orderDistribService.saveOrderDistrib(returnCarReq.getOrderNo());
@ -1398,7 +1734,7 @@ public class OrderServiceImpl implements OrderService {
.eq(OrderSubPO::getPayStatus, PayStatusEnum.SUCCESS).last(" limit 1");
OrderSubPO noDepositOrder = orderSubRepo.getOne(orderSubWrapper);
if (Objects.nonNull(noDepositOrder)) {
boolean result = payService.finishFreezeOrder(orderDTO.getOperatorId(),noDepositOrder.getPaymentId(),noDepositOrder.getTransactionId());
boolean result = payService.finishFreezeOrder(orderDTO.getOperatorId(),noDepositOrder.getPaymentId(),noDepositOrder.getTransactionId(),noDepositOrder.getAmount());
if (result) {
//解冻成功更新状态为解冻成功
LambdaUpdateWrapper<OrderSubPO> updatenoDepositWrapper = new LambdaUpdateWrapper<>();
@ -1486,8 +1822,7 @@ public class OrderServiceImpl implements OrderService {
updateWrapper.eq(OrderSubPO::getPaymentId, orderSubList.get(0).getPaymentId());
orderSubRepo.update(updateWrapper);
//租车订单
OrderSubPO rentSubPO = orderSubList.stream().filter(orderSubPO -> SubOrderTypeEnum.RENTCAR.getCode().equalsIgnoreCase(orderSubPO.getSuborderType())).findFirst().orElse( null);
LambdaUpdateWrapper<OrderMainPO> updateWrapper2 = new LambdaUpdateWrapper<>();
updateWrapper2.eq(OrderMainPO::getOrderId, orderMainPO.getOrderId());
@ -1495,6 +1830,8 @@ public class OrderServiceImpl implements OrderService {
updateWrapper2.set(OrderMainPO::getOrderStatus, OrderStatusEnum.WAIT_PICK.getCode());
orderStatus = OrderStatusEnum.WAIT_PICK.getCode();
} else if(OrderStatusEnum.RERENT_WAIT_PAY.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())){
//租车订单
OrderSubPO rentSubPO = orderSubList.stream().filter(orderSubPO -> SubOrderTypeEnum.RENTCAR.getCode().equalsIgnoreCase(orderSubPO.getSuborderType())).findFirst().orElse( null);
BigDecimal rentCarOrderAmount = orderSubList.stream().map(OrderSubPO::getAmount).reduce(BigDecimal.ZERO,BigDecimal::add);
updateWrapper2.set(OrderMainPO::getOrderAmount, orderMainPO.getOrderAmount().add(rentCarOrderAmount));
if(rentSubPO!=null){
@ -1505,11 +1842,18 @@ public class OrderServiceImpl implements OrderService {
updateWrapper2.set(OrderMainPO::getOverdueDays, 0);
}
orderStatus = OrderStatusEnum.RENT_ING.getCode();
}
//如果是续租订单,增加续租次数
if(rentSubPO!=null){
updateWrapper2.set(OrderMainPO::getRenewalTimes, orderMainPO.getRenewalTimes()+1);
}
//异步同步租电订单
orderSubList.stream().filter(orderSubPO -> SubOrderTypeEnum.RENTBATTEY.getCode().equalsIgnoreCase(orderSubPO.getSuborderType())).findFirst()
.ifPresent(rentBatteySubPO -> ThreadPoolUtils.getThreadPool().execute(() -> {
Long batterySubOrderId = rentBatteySubPO.getSuborderId();
syncInteg.sendSubOrderId(batterySubOrderId);
}));
}
orderMainRepo.update(updateWrapper2);
return orderStatus;
}

View File

@ -89,6 +89,20 @@ public class PayServiceImpl implements PayService {
return null;
}
@Override
public UnifiedPaymentInfoDTO zhimaPayOrder(String body, Long companyId, String outTradeNo, String authNo, String aliPayOpenId,BigDecimal totalFee) {
AlipayCreateRequest alipayCreateRequest = new AlipayCreateRequest();
alipayCreateRequest.setCompanyId(companyId);
alipayCreateRequest.setSubject(body);
alipayCreateRequest.setBody(body);
alipayCreateRequest.setOutTradeNo(outTradeNo);
alipayCreateRequest.setAuthNo(authNo);
alipayCreateRequest.setOpenId(aliPayOpenId);
alipayCreateRequest.setTotalAmount(totalFee.toString());
return payInteg.zhiMaOrder(alipayCreateRequest);
}
@Override
public UnifiedPaymentInfoDTO freezeOrder(Long companyId, String outOrderNo, BigDecimal freezeFee) {
AlipayFundFreezeRequest request = new AlipayFundFreezeRequest();
@ -103,12 +117,13 @@ public class PayServiceImpl implements PayService {
}
@Override
public boolean finishFreezeOrder(Long companyId, String outOrderNo, String authCode) {
public boolean finishFreezeOrder(Long companyId, String outOrderNo, String authCode, BigDecimal freezeFee) {
log.info("开始完结支付宝押金冻结支付单");
AlipayFinishFreezeRequest finishFreezeRequest = new AlipayFinishFreezeRequest();
finishFreezeRequest.setCompanyId(companyId.toString());
finishFreezeRequest.setAuthNo(authCode);
finishFreezeRequest.setOutRequestNo(OrderUtil.generateSubOrderNo(OrderUtil.FFZ_PREFIX));
finishFreezeRequest.setAmount(freezeFee.toString());
AlipayResponse alipayResponse = payInteg.finishFreeze(finishFreezeRequest);
return alipayResponse.isSuccess();
}

View File

@ -118,9 +118,10 @@ public class NoPayOrderProcessTask {
orderProcessPO.setRetryNum(1);
orderProcessRepo.save(orderProcessPO);
}else {
orderProcessPO.setRetryNum(orderProcessPO.getRetryNum()+1);
LambdaUpdateWrapper<OrderProcessPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(OrderProcessPO::getId, orderProcessPO.getId())
.set(OrderProcessPO::getRetryNum, orderProcessPO.getRetryNum()+1);
.set(OrderProcessPO::getRetryNum, orderProcessPO.getRetryNum());
orderProcessRepo.update(updateWrapper);
}
@ -128,9 +129,16 @@ public class NoPayOrderProcessTask {
log.info("订单{}已超过最大重试次数,强行删除订单", orderMainPO.getOrderNo());
orderService.forceRemoveRentOrder(orderMainPO);
} else {
if(orderMainPO.getOrderStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())){
PayOrderReq payOrderReq = new PayOrderReq();
payOrderReq.setOrderNo(orderMainPO.getOrderNo());
orderService.cancelOrder(payOrderReq,orderMainPO);
} else if(orderMainPO.getOrderStatus().equals(OrderStatusEnum.RERENT_WAIT_PAY.getCode())){
PayOrderReq payOrderReq = new PayOrderReq();
payOrderReq.setOrderNo(orderMainPO.getOrderNo());
orderService.cancelRerentOrOverDueOrder(orderMainPO);
}
}

View File

@ -118,9 +118,10 @@ public class RefundSubOrderProcessTask {
orderProcessPO.setRetryNum(1);
orderProcessRepo.save(orderProcessPO);
}else {
orderProcessPO.setRetryNum(orderProcessPO.getRetryNum()+1);
LambdaUpdateWrapper<OrderProcessPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(OrderProcessPO::getId, orderProcessPO.getId())
.set(OrderProcessPO::getRetryNum, orderProcessPO.getRetryNum()+1);
.set(OrderProcessPO::getRetryNum, orderProcessPO.getRetryNum());
orderProcessRepo.update(updateWrapper);
}
if(orderProcessPO.getRetryNum()>5){
@ -142,6 +143,7 @@ public class RefundSubOrderProcessTask {
AlipayRefundRequest request = new AlipayRefundRequest();
request.setOutTradeNo(subPO.getPaymentId());
request.setCompanyId(orderMainPO.getOperatorId());
request.setOutRequestNo(subPO.getRefundId());
AlipayResponse alipayResponse = payInteg.alipayRefundQuery(request);
fundResult = "SUCCESS".equals(alipayResponse.getCode());
}

View File

@ -65,4 +65,10 @@ public class CompanyStoreDTO {
@ApiModelProperty("以租代售分成比例")
private BigDecimal daishouRatio;
@ApiModelProperty("美团token")
private String mtToken;
@ApiModelProperty("抖音门店id")
private String dyStoreId;
}

View File

@ -11,4 +11,5 @@ public class AlipayCreateRequest {
private String totalAmount; // 订单总金额
private String body; // 订单描述
private String openId; // 用户ID
private String authNo; //支付宝资金授权操作处理号
}

View File

@ -0,0 +1,18 @@
package com.sczx.order.thirdpart.dto.req;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@ApiModel(value = "分润收入查询参数")
@Data
public class DistibIncomeReq {
@ApiModelProperty("门店id")
private Long storeId;
@ApiModelProperty("金额")
private BigDecimal amount ;
}

View File

@ -60,6 +60,12 @@ public interface PayFacade {
@PostMapping("/api/alipay/unifiedOrder")
AlipayCreateResponse alipayUnifiedOrder(@RequestBody AlipayCreateRequest request);
/**
* 支付宝统一下单接口
*/
@PostMapping("/api/alipay/zhiMaOrder")
AlipayCreateResponse zhiMaOrder(@RequestBody AlipayCreateRequest request);
/**
* 查询订单接口
*/

View File

@ -5,6 +5,7 @@ import com.sczx.order.thirdpart.dto.CompanyDTO;
import com.sczx.order.thirdpart.dto.CompanyStoreDTO;
import com.sczx.order.thirdpart.dto.SysConfigDTO;
import com.sczx.order.thirdpart.dto.SysDictDataDTO;
import com.sczx.order.thirdpart.dto.req.DistibIncomeReq;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -32,4 +33,10 @@ public interface StoreFacade {
@GetMapping("/sys/getConfigByConfigKey")
Result<SysConfigDTO> getConfigByConfigKey(@RequestParam(name = "configKey") String configKey);
@PostMapping("/wallet/distibIncome")
Result<Boolean> distibIncome(@RequestBody DistibIncomeReq distibIncomeReq);
@PostMapping("/store/recordToken")
Result<Boolean> recordToken(@RequestParam(name = "storeNumber") String storeNumber, @RequestParam(name = "token") String token);
}

View File

@ -12,4 +12,7 @@ public interface SyncFacade {
@GetMapping("/send/subOrder/{subOrderId}")
Map<String, String> sendSubOrderId(@PathVariable Long subOrderId);
@GetMapping("/send/OrderMeal/{subOrderId}")
Map<String, String> sendUserMeal(@PathVariable Long subOrderId);
}

View File

@ -1,6 +1,7 @@
package com.sczx.order.thirdpart.facade;
import com.sczx.order.common.Result;
import com.sczx.order.dto.SimpleUserInfoDTO;
import com.sczx.order.thirdpart.dto.BaseUserReferralDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@ -11,4 +12,7 @@ public interface UserFacade {
@GetMapping("/referral/getUserReferralByUserId")
Result<BaseUserReferralDTO> getUserReferralByUserId(@RequestParam("userId") Long userId);
@GetMapping("/auth/getUInfoByMobile")
Result<SimpleUserInfoDTO> getUInfoByMobile(@RequestParam("mobile") String mobile);
}

View File

@ -152,6 +152,26 @@ public class PayInteg {
}
}
/**
* 支付宝免押扣款接口
*/
public UnifiedPaymentInfoDTO zhiMaOrder(AlipayCreateRequest request){
try {
AlipayCreateResponse result = payFacade.zhiMaOrder(request);
if(StringUtils.isNotBlank(result.getCode()) && "SUCCESS".equals(result.getCode())){
UnifiedPaymentInfoDTO unifiedPaymentInfo = new UnifiedPaymentInfoDTO();
unifiedPaymentInfo.setOutTradeNo(result.getOutTradeNo());
unifiedPaymentInfo.setTradeNo(result.getTradeNo());
return unifiedPaymentInfo;
}else {
throw new InnerException("支付宝免押扣款失败");
}
} catch (Exception e){
log.error("支付宝免押扣款失败",e);
throw new InnerException("支付宝免押扣款失败");
}
}
/**
* 查询订单接口
*/

View File

@ -6,6 +6,7 @@ import com.sczx.order.thirdpart.dto.CompanyDTO;
import com.sczx.order.thirdpart.dto.CompanyStoreDTO;
import com.sczx.order.thirdpart.dto.SysConfigDTO;
import com.sczx.order.thirdpart.dto.SysDictDataDTO;
import com.sczx.order.thirdpart.dto.req.DistibIncomeReq;
import com.sczx.order.thirdpart.facade.StoreFacade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -86,4 +87,36 @@ public class StoreInteg {
}
return null;
}
/**
* 分润收入
* @param distibIncomeReq
* @return
*/
public boolean distibIncome(DistibIncomeReq distibIncomeReq){
try{
Result<Boolean> result = storeFacade.distibIncome(distibIncomeReq);
if(result.isSuccess()){
return result.getData();
}
} catch (Exception e){
log.error("分润收入失败",e);
throw new InnerException("分润收入失败");
}
return false;
}
public boolean recordToken(String storeNumber, String token){
try{
Result<Boolean> result = storeFacade.recordToken(storeNumber, token);
if(result.isSuccess()){
return result.getData();
}
} catch (Exception e){
log.error("记录门店token失败",e);
throw new InnerException("记录门店token失败");
}
return false;
}
}

View File

@ -18,16 +18,29 @@ public class SyncInteg {
public Map<String, String> sendSubOrderId(Long subOrderId){
try{
Map<String, String> result = syncFacade.sendSubOrderId(subOrderId);
if(result.get("code") == "200"){
if( "200".equals(result.get("code"))){
return result;
}else{
return result;
}
} catch (Exception e){
log.error("发送租电订单同步失败",e);
throw new InnerException("发送租电订单同步失败");
}
return null;
}
public Map<String, String> sendUserMeal(Long subOrderId){
try{
Map<String, String> result = syncFacade.sendUserMeal(subOrderId);
if( "200".equals(result.get("code"))){
return result;
}else{
return result;
}
} catch (Exception e){
log.error("发送租电订单查询失败",e);
}
return null;
}
}

View File

@ -1,12 +1,14 @@
package com.sczx.order.thirdpart.integration;
import com.sczx.order.common.Result;
import com.sczx.order.dto.SimpleUserInfoDTO;
import com.sczx.order.exception.InnerException;
import com.sczx.order.thirdpart.dto.BaseUserReferralDTO;
import com.sczx.order.thirdpart.facade.UserFacade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
@Slf4j
@Component
@ -27,4 +29,17 @@ public class UserInteg {
}
return null;
}
public SimpleUserInfoDTO getUInfoByMobile(String mobile){
try{
Result<SimpleUserInfoDTO> result = userFacade.getUInfoByMobile(mobile);
if(result.isSuccess()){
return result.getData();
}
} catch (Exception e){
log.error("获取用户信息失败",e);
throw new InnerException("获取用户信息失败");
}
return null;
}
}

View File

@ -0,0 +1,107 @@
package com.sczx.order.utils;
import com.aliyun.tea.TeaException;
import com.douyin.openapi.client.Client;
import com.douyin.openapi.client.models.OauthClientTokenRequest;
import com.douyin.openapi.client.models.OauthClientTokenResponse;
import com.douyin.openapi.credential.models.Config;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 抖音 client_token 管理器
* 负责定时获取和缓存 client_token
*/
public class DouyinTokenManager {
// 应用凭证信息
private static final String CLIENT_KEY = "awomt6nnjlfc491m";
private static final String CLIENT_SECRET = "c678c411c7a68c6f97969f2dbd8ef8fc";
// Token 缓存
private static final AtomicReference<OauthClientTokenResponse> tokenCache =
new AtomicReference<>();
// 定时任务执行器
private static final ScheduledThreadPoolExecutor scheduler =
new ScheduledThreadPoolExecutor(1, r -> {
Thread t = new Thread(r, "DouyinTokenRefreshThread");
t.setDaemon(false);
return t;
});
static {
// 初始化时立即获取一次 token
refreshClientToken();
// 每小时更新一次 token (3600秒)
scheduler.scheduleAtFixedRate(
DouyinTokenManager::refreshClientToken,
3600,
3600,
TimeUnit.SECONDS
);
}
/**
* 获取当前有效的 client_token
*
* @return 当前有效的 access_token
*/
public static String getCurrentToken() {
OauthClientTokenResponse response = tokenCache.get();
if (response != null && response.getData() != null) {
return response.getData().getAccessToken();
}
return null;
}
/**
* 刷新 client_token
*/
private static void refreshClientToken() {
try {
Config config = new Config()
.setClientKey(CLIENT_KEY)
.setClientSecret(CLIENT_SECRET);
Client client = new Client(config);
OauthClientTokenRequest sdkRequest = new OauthClientTokenRequest();
sdkRequest.setClientKey(CLIENT_KEY);
sdkRequest.setClientSecret(CLIENT_SECRET);
sdkRequest.setGrantType("client_credential");
OauthClientTokenResponse sdkResponse = client.OauthClientToken(sdkRequest);
// 更新缓存
tokenCache.set(sdkResponse);
if (sdkResponse.getData() != null) {
System.out.println("抖音 client_token 更新成功,有效期至: " +
(System.currentTimeMillis() + sdkResponse.getData().getExpiresIn() * 1000));
}
} catch (TeaException e) {
System.err.println("获取抖音 client_token 失败 (TeaException): " + e.getMessage());
} catch (Exception e) {
System.err.println("获取抖音 client_token 失败 (Exception): " + e.getMessage());
}
}
/**
* 关闭定时任务
*/
public static void shutdown() {
scheduler.shutdown();
// 获取当前有效的 client_token
String token = DouyinTokenManager.getCurrentToken();
// 使用 token 调用抖音 API
if (token != null) {
// 调用订单查询等接口
}
}
}

View File

@ -0,0 +1,86 @@
package com.sczx.order.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@Slf4j
public class MeiTuanSignUtils {
private MeiTuanSignUtils() {}
public static String getSign(String signKey, Map<String, String> params) {
try {
String sortedStr = getSortedParamStr(params);
String paraStr = signKey + sortedStr;
return createSign(paraStr);
} catch (UnsupportedEncodingException e) {
log.warn("getSign UnsupportedEncodingException ", e);
}
return StringUtils.EMPTY;
}
/**
* 构造自然排序请求参数
*
* @param params 请求
* @return 字符串
*/
private static String getSortedParamStr(Map<String, String> params) throws UnsupportedEncodingException {
Set<String> sortedParams = new TreeSet<>(params.keySet());
StringBuilder strB = new StringBuilder();
// 排除sign和空值参数
for (String key : sortedParams) {
if ("sign".equalsIgnoreCase(key)) {
continue;
}
String value = params.get(key);
if (StringUtils.isNotEmpty(value)) {
strB.append(key).append(value);
}
}
return strB.toString();
}
/**
* 生成新sign
*
* @param str 字符串
* @return String
*/
private static String createSign(String str) {
if (str == null || str.length() == 0) {
return null;
}
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char[] buf = new char[j * 2];
int k = 0;
int i = 0;
while (i < j) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
i++;
}
return new String(buf);
} catch (Exception e) {
log.warn("create sign was failed", e);
return null;
}
}
}

View File

@ -62,8 +62,10 @@ public class OrderUtil {
public static Integer getOrderOverdueHours(LocalDateTime endRentTime) {
if(endRentTime!=null){
LocalDateTime now = LocalDateTime.now();
log.info("计算间隔小时数,预计还车时间:{}, 当前时间 : {}", endRentTime, now);
if(now.isAfter(endRentTime)){
return (int) ChronoUnit.HOURS.between(endRentTime, now);
int hours = (int) ChronoUnit.HOURS.between(endRentTime, now);
return hours +1;
}
}
return 0;
@ -77,8 +79,14 @@ public class OrderUtil {
public static Integer getOrderOverdueDays(LocalDateTime endRentTime) {
if(endRentTime!=null){
LocalDateTime now = LocalDateTime.now();
log.info("计算间隔天数,预计还车时间:{}, 当前时间 : {}", endRentTime, now);
if(now.isAfter(endRentTime)){
return (int) ChronoUnit.DAYS.between(endRentTime, now);
long days = ChronoUnit.DAYS.between(endRentTime, now);
// 检查是否有不足一天的余数
if (!now.truncatedTo(ChronoUnit.DAYS).equals(endRentTime.plusDays(days).truncatedTo(ChronoUnit.DAYS))) {
return (int) days + 1;
}
return (int) days;
}
}
return 0;
@ -111,9 +119,14 @@ public class OrderUtil {
if(startTime!=null&&endTime!=null){
if(startTime.isBefore(endTime)){
if(StringUtils.equalsIgnoreCase(rentalType, RentCarTypeEnum.HOUR_RENTAL.getCode())){
return (int) ChronoUnit.HOURS.between(startTime, endTime);
return (int) ChronoUnit.HOURS.between(startTime, endTime) +1;
} else {
return (int) ChronoUnit.DAYS.between(startTime, endTime);
long days = ChronoUnit.DAYS.between(startTime, endTime) +1;
// 检查是否有不足一天的余数
if (!endTime.truncatedTo(ChronoUnit.DAYS).equals(endTime.plusDays(days).truncatedTo(ChronoUnit.DAYS))) {
return (int) days + 1;
}
return (int) days;
}
}
}

View File

@ -24,6 +24,10 @@ public class RedisUtil {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
public void set(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sczx.order.mapper.ElectronicFenceRuleMapper">
</mapper>

View File

@ -6,8 +6,8 @@
SELECT
store_id,
COUNT(*) as total_orders,
SUM(CASE WHEN (order_status = 'AUTO_END' OR order_status = 'MANUAL_END') AND DATE_FORMAT(end_order_time, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m') THEN 1 ELSE 0 END) as monthly_completed_orders,
SUM(CASE WHEN (order_status = 'AUTO_END' OR order_status = 'MANUAL_END') AND DATE_FORMAT(end_order_time, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m') THEN order_amount ELSE 0 END) as monthly_order_amount,
SUM(CASE WHEN (order_status = 'AUTO_END' OR order_status = 'MANUAL_END') AND DATE_FORMAT(ifnull(end_order_time,act_end_rent_time), '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m') THEN 1 ELSE 0 END) as monthly_completed_orders,
SUM(CASE WHEN (order_status = 'AUTO_END' OR order_status = 'MANUAL_END') AND DATE_FORMAT(ifnull(end_order_time,act_end_rent_time), '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m') THEN order_amount ELSE 0 END) as monthly_order_amount,
COUNT(CASE WHEN order_status = 'WAIT_PICK' THEN 1 END) as pending_pickup_count,
COUNT(CASE WHEN order_status = 'WAIT_RETURN' THEN 1 END) as pending_return_count,
COUNT(CASE WHEN order_status = 'RENT_OVERDUE' THEN 1 END) as overdue_count