diff --git a/src/main/java/com/sczx/order/config/DouyinTokenManager.java b/src/main/java/com/sczx/order/config/DouyinTokenManager.java index d314771..4d34c35 100644 --- a/src/main/java/com/sczx/order/config/DouyinTokenManager.java +++ b/src/main/java/com/sczx/order/config/DouyinTokenManager.java @@ -5,7 +5,11 @@ 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; @@ -14,31 +18,37 @@ import java.util.concurrent.atomic.AtomicReference; * 抖音 client_token 管理器 * 负责定时获取和缓存 client_token */ +@Slf4j +@Component public class DouyinTokenManager { // 应用凭证信息 - private static final String CLIENT_KEY = "awomt6nnjlfc491m"; - private static final String CLIENT_SECRET = "c678c411c7a68c6f97969f2dbd8ef8fc"; + @Value("${coupon.douyin.client_key}") + private String CLIENT_KEY; + + @Value("${coupon.douyin.client_secret}") + private String CLIENT_SECRET; // Token 缓存 - private static final AtomicReference tokenCache = + private final AtomicReference tokenCache = new AtomicReference<>(); // 定时任务执行器 - private static final ScheduledThreadPoolExecutor scheduler = + private final ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1, r -> { Thread t = new Thread(r, "DouyinTokenRefreshThread"); t.setDaemon(false); return t; }); - static { + @PostConstruct + public void init() { // 初始化时立即获取一次 token refreshClientToken(); // 每小时更新一次 token (3600秒) scheduler.scheduleAtFixedRate( - DouyinTokenManager::refreshClientToken, + this::refreshClientToken, 3600, 3600, TimeUnit.SECONDS @@ -50,7 +60,7 @@ public class DouyinTokenManager { * * @return 当前有效的 access_token */ - public static String getCurrentToken() { + public String getCurrentToken() { OauthClientTokenResponse response = tokenCache.get(); if (response != null && response.getData() != null) { return response.getData().getAccessToken(); @@ -61,7 +71,7 @@ public class DouyinTokenManager { /** * 刷新 client_token */ - private static void refreshClientToken() { + private void refreshClientToken() { try { Config config = new Config() .setClientKey(CLIENT_KEY) @@ -80,28 +90,14 @@ public class DouyinTokenManager { tokenCache.set(sdkResponse); if (sdkResponse.getData() != null) { - System.out.println("抖音 client_token 更新成功,有效期至: " + + log.info("抖音 client_token 更新成功: {},有效期至: {}", sdkResponse.getData().getAccessToken(), (System.currentTimeMillis() + sdkResponse.getData().getExpiresIn() * 1000)); } } catch (TeaException e) { - System.err.println("获取抖音 client_token 失败 (TeaException): " + e.getMessage()); + log.error("获取抖音 client_token 失败 (TeaException): " + e.getMessage()); } catch (Exception e) { - System.err.println("获取抖音 client_token 失败 (Exception): " + e.getMessage()); + log.error("获取抖音 client_token 失败 (Exception): " + e.getMessage()); } } - /** - * 关闭定时任务 - */ - public static void shutdown() { - scheduler.shutdown(); - - // 获取当前有效的 client_token - String token = DouyinTokenManager.getCurrentToken(); - - // 使用 token 调用抖音 API - if (token != null) { - // 调用订单查询等接口 - } - } } diff --git a/src/main/java/com/sczx/order/controller/VerifyController.java b/src/main/java/com/sczx/order/controller/VerifyController.java index 1a2af54..6d01af7 100644 --- a/src/main/java/com/sczx/order/controller/VerifyController.java +++ b/src/main/java/com/sczx/order/controller/VerifyController.java @@ -1,6 +1,5 @@ package com.sczx.order.controller; -import com.meituan.sdk.auth.MeituanTokenResponse; import com.sczx.order.common.Result; import com.sczx.order.service.DouyinService; import com.sczx.order.service.MeiTuanService; @@ -18,9 +17,6 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/verify") public class VerifyController { - @Autowired - private DouyinService douyinService; - @Autowired private MeiTuanService meituanService; @@ -31,41 +27,9 @@ public class VerifyController { log.info("接收美团授权数据 - code: {}, sign: {}, developerId: {}, businessId: {}, state: {}", code, sign, developerId, businessId, state); - //接收code,记录state(门店编号) - //MeituanTokenResponse response = meituanService.getAccessToken(code,state); return Result.ok(meituanService.getAccessToken(code,state)); } - @ApiOperation(value = "接收需据接口") - @GetMapping("/prepare") - public MeituanTokenResponse prepare(@RequestParam("code") String code,@RequestParam("state") String state) throws Exception { - - - MeituanTokenResponse response = meituanService.getAccessToken( code,state); - - return response; - } - - @ApiOperation(value = "接收需据接口") - @GetMapping("/mtprepare") - public MeituanTokenResponse mtprepare(@RequestParam("code") String code) throws Exception { - - - MeituanTokenResponse response = meituanService.prepare( code); - - return response; - } - - - @ApiOperation(value = "接收需据接口") - @GetMapping("/consume") - public MeituanTokenResponse consume(@RequestParam("code") String code) throws Exception { - - - MeituanTokenResponse response = meituanService.prepare( code); - - return response; - } } diff --git a/src/main/java/com/sczx/order/service/DouyinService.java b/src/main/java/com/sczx/order/service/DouyinService.java index c6a7359..aa9a8a9 100644 --- a/src/main/java/com/sczx/order/service/DouyinService.java +++ b/src/main/java/com/sczx/order/service/DouyinService.java @@ -1,17 +1,17 @@ package com.sczx.order.service; -import com.meituan.sdk.auth.MeituanTokenResponse; +import com.douyin.openapi.client.models.*; -import java.util.List; public interface DouyinService { - String resolveShortUrlToGetObjectId(String shortUrl) throws Exception; + CertificatePrepareResponse prepare(CertificatePrepareRequest req); + + CertificateVerifyResponse verify(CertificateVerifyRequest req); + + CertificateCancelResponse cancel(CertificateCancelRequest req); - void prepare(List orderIds) throws Exception; - MeituanTokenResponse getAccessToken(String code); - MeituanTokenResponse prepare(String code); } diff --git a/src/main/java/com/sczx/order/service/OrderService.java b/src/main/java/com/sczx/order/service/OrderService.java index fb9073f..e9879ef 100644 --- a/src/main/java/com/sczx/order/service/OrderService.java +++ b/src/main/java/com/sczx/order/service/OrderService.java @@ -20,7 +20,7 @@ public interface OrderService { * @param req * @return */ - OrderDetailDTO verifyGroupBuyCoupons(VerifyGroupBuyCouponsReq req); + OrderDetailDTO verifyGroupBuyCoupons(VerifyGroupBuyCouponsReq req) ; /** * 提交租车订单 diff --git a/src/main/java/com/sczx/order/service/impl/DouyinServiceImpl.java b/src/main/java/com/sczx/order/service/impl/DouyinServiceImpl.java index 388e217..3bdfe94 100644 --- a/src/main/java/com/sczx/order/service/impl/DouyinServiceImpl.java +++ b/src/main/java/com/sczx/order/service/impl/DouyinServiceImpl.java @@ -16,9 +16,13 @@ import com.meituan.sdk.model.ddzh.tuangou.tuangouReceiptPrepare.TuangouReceiptPr 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; @@ -26,36 +30,32 @@ 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("${douyin.appId}") - private String CLIENT_KEY = "awomt6nnjlfc491m"; + @Value("${coupon.douyin.client_key}") + private String CLIENT_KEY; - //@Value("${douyin.appSecret}") - private String CLIENT_SECRET = "c678c411c7a68c6f97969f2dbd8ef8fc"; + @Value("${coupon.douyin.client_secret}") + private String CLIENT_SECRET; - //@Value("${douyin.accountId}") - private String ACCOUNT_ID = "7442188302710065206"; + @Value("${coupon.douyin.account_id}") + private String ACCOUNT_ID; - //@Value("${douyin.poiId}") - private String POI_ID = "7442188302710065206"; + @Value("${coupon.douyin.poi_id}") + private String POI_ID; - private String meituanApiUrl = "https://api-open-cater.meituan.com/oauth/token"; + @Autowired + private DouyinTokenManager douyinTokenManager; - private Long DeveloperId = 116997L; - private String Signkey = "n8xlhtshk7t1luvi"; - - private String accessToken = "V2-26b8d231854d3fdf6a5c06273377a65e441e4c9f1c8cea4959c510bf0dd19ed7657e34bebe4b74aa455b80d3c757cd8a66e3ece9148e6d6838778db5f9600cf383dbf89c0cc27f07f777c98649bd94fe"; - - @Transactional(rollbackFor = Exception.class) - @Override public String resolveShortUrlToGetObjectId(String shortUrl) { try { URL url = new URL(shortUrl); @@ -66,8 +66,6 @@ public class DouyinServiceImpl implements DouyinService { String longUrl = connection.getHeaderField("Location"); connection.disconnect(); - - if (longUrl == null || longUrl.isEmpty()) { return null; } @@ -88,104 +86,106 @@ public class DouyinServiceImpl implements DouyinService { } + /** + * 抖音验券准备返回抖音验券对象 + */ @Override - public void prepare(List orderIds) throws Exception { + public CertificatePrepareResponse prepare(CertificatePrepareRequest req) { // 获取当前有效的access_token - String accessToken = DouyinTokenManager.getCurrentToken(); + 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); // 改成自己的app_id跟secret + Config config = new Config().setClientKey(CLIENT_KEY).setClientSecret(CLIENT_SECRET); Client client = new Client(config); - /* 构建请求参数,该代码示例中只给出部分参数,请用户根据需要自行构建参数值 - token: - 1.若用户自行维护token,将用户维护的token赋值给该参数即可 - 2.SDK包中有获取token的函数,请根据接口path在《OpenAPI SDK 总览》文档中查找获取token函数的名字 - 在使用过程中,请注意token互刷问题 - header: - sdk中默认填充content-type请求头,若不需要填充除content-type之外的请求头,删除该参数即可 - */ + CertificatePrepareRequest sdkRequest = new CertificatePrepareRequest(); sdkRequest.setAccessToken(accessToken); sdkRequest.setAccountId(ACCOUNT_ID); - sdkRequest.setCode(""); - sdkRequest.setEncryptedData(""); - sdkRequest.setPoiId(""); + + String encryptedData = resolveShortUrlToGetObjectId(req.getEncryptedData()); + sdkRequest.setEncryptedData(encryptedData); + + sdkRequest.setPoiId(req.getPoiId()); CertificatePrepareResponse sdkResponse = client.CertificatePrepare(sdkRequest); - processCertificatePrepareResponse(sdkResponse); - } catch (TeaException e) { - System.out.println(e.getMessage()); + System.out.println(JSONObject.toJSONString(sdkResponse)); - } catch (Exception e) { - System.out.println(e.getMessage()); - - } - } - - public void processCertificatePrepareResponse(CertificatePrepareResponse response) { - String verifyToken = response.getData().getVerifyToken(); - - response.getData().getCertificates().forEach(certificate -> { - Long certificateId = certificate.getCertificateId(); - String skuId = certificate.getSku().getSkuId(); - certificate.getReserveInfo().getOrderReserveUserInfoList().forEach(orderReserveUserInfo -> { - - }); - }); - - - } - - @Override - public MeituanTokenResponse getAccessToken(String code) { - 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)); - - return response; - - - } catch (Exception e) { - log.error("获取access_token异常", e); - return null; - } - } - - @Override - public MeituanTokenResponse prepare(String code) { - try { - MeituanClient meituanClient = DefaultMeituanClient.builder(DeveloperId, Signkey).build(); - - TuangouReceiptPrepareRequest tuangouReceiptPrepareRequest = new TuangouReceiptPrepareRequest(); - - tuangouReceiptPrepareRequest.setReceiptCode("0107945394376"); - - String appAuthToken = accessToken; - MeituanResponse response = meituanClient.invokeApi(tuangouReceiptPrepareRequest, appAuthToken); - - log.info("获取prepare响应结果: {}", JSONObject.toJSONString(response)); - if (response.isSuccess()) { - TuangouReceiptPrepareResponse resp = response.getData(); - System.out.println(resp); - } else { - System.out.println("调用失败"); + if (sdkResponse.getExtra().getErrorCode() != 0){ + throw new RuntimeException("券验证失败:" + sdkResponse.getExtra().getDescription()); } - return null; + return sdkResponse; + + } catch (TeaException e) { + throw new RuntimeException("券验证失败"); } catch (Exception e) { - log.error("获取access_token异常", e); - return null; + 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; + } + + } diff --git a/src/main/java/com/sczx/order/service/impl/MeiTuanServiceImpl.java b/src/main/java/com/sczx/order/service/impl/MeiTuanServiceImpl.java index a377d8f..6a01b24 100644 --- a/src/main/java/com/sczx/order/service/impl/MeiTuanServiceImpl.java +++ b/src/main/java/com/sczx/order/service/impl/MeiTuanServiceImpl.java @@ -42,19 +42,19 @@ public class MeiTuanServiceImpl implements MeiTuanService { // 使用美团SDK的getOAuthToken方法获取token - //MeituanTokenResponse response = client.getOAuthToken(58, code); + MeituanTokenResponse response = client.getOAuthToken(58, code); - //log.info("获取access_token响应结果: {}", JSONObject.toJSONString(response)); - //String token = response.getData().getAccessToken(); + log.info("获取access_token响应结果: {}", JSONObject.toJSONString(response)); + String token = response.getData().getAccessToken(); - storeInteg.recordToken(state, accessToken); + storeInteg.recordToken(state, token); return null; } catch (Exception e) { - throw new BizException("订单不存在"); + throw new BizException("获取access_token失败"); } } diff --git a/src/main/java/com/sczx/order/service/impl/OrderServiceImpl.java b/src/main/java/com/sczx/order/service/impl/OrderServiceImpl.java index a76be7f..a21b125 100644 --- a/src/main/java/com/sczx/order/service/impl/OrderServiceImpl.java +++ b/src/main/java/com/sczx/order/service/impl/OrderServiceImpl.java @@ -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,10 +17,7 @@ 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.GroupBuyCouponService; -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.*; @@ -34,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 @@ -85,6 +87,12 @@ public class OrderServiceImpl implements OrderService { @Autowired private GroupBuyCouponService groupBuyCouponService; + @Autowired + private DouyinService douyinService; + + @Autowired + private MeiTuanService meiTuanService; + @Override public OrderMainPO queryOrderMainPoByOrderNo(String orderNo, String delFlag) { LambdaQueryWrapper currentOrderWrapper = new LambdaQueryWrapper<>(); @@ -94,9 +102,67 @@ public class OrderServiceImpl implements OrderService { } @Override - public OrderDetailDTO verifyGroupBuyCoupons(VerifyGroupBuyCouponsReq req) { - //校验用户是否存在,是否实名认证 - GroupBuyOrderInfoDto groupBuyOrderInfoDTO = groupBuyCouponService.getGroupBuyOrderInfoDto(req.getCouponCode(), req.getCouponType(), req.getMobile()); + 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 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; } diff --git a/src/main/java/com/sczx/order/thirdpart/dto/CompanyStoreDTO.java b/src/main/java/com/sczx/order/thirdpart/dto/CompanyStoreDTO.java index 34169cf..d954622 100644 --- a/src/main/java/com/sczx/order/thirdpart/dto/CompanyStoreDTO.java +++ b/src/main/java/com/sczx/order/thirdpart/dto/CompanyStoreDTO.java @@ -65,4 +65,10 @@ public class CompanyStoreDTO { @ApiModelProperty("以租代售分成比例") private BigDecimal daishouRatio; + + @ApiModelProperty("美团token") + private String mtToken; + + @ApiModelProperty("抖音门店id") + private String dyStoreId; }