【小西】同步咪咕订单给咪咕方(写接口给第三方)

news2024/11/15 23:43:52

同步咪咕订单给咪咕方

    • 前言
    • 思路
    • 实现
      • 1、定义请求体和响应信息
        • MiGuOrderSyncReq
        • MiGuOrderSyncResp
      • 2、nacos定义好咪咕相关配置信息
      • 3、同步咪咕参数配置
      • 4、MiGuOrderSyncControl
      • 5、MiGuOrderSyncService
      • 6、MiGuOrderSyncServiceImpl
        • CreateAscIISignUtil 生成参数 字典排序 签名
        • Hmacsha256Util 加密
    • 测试

前言

需求:把小西中与咪咕相关的订单同步给咪咕方。

思路

思路如下:

  1. 定义好请求体和响应信息
  2. 在nacos定义好咪咕相关配置信息(用于之后验证请求体是否正确)
  3. 写接口

实现

1、定义请求体和响应信息

MiGuOrderSyncReq

@Data
@ApiModel(description = "咪咕订单同步请求参数")
public class MiGuOrderSyncReq implements Serializable {
	private static final long serialVersionUID = 1L;


	@JsonProperty("header")
	@Valid
	private ReqHeader header;

	@JsonProperty("body")
	@Valid
	private ReqBody body;

	@Data
	public static class ReqHeader implements Serializable {

		private static final long serialVersionUID = 8807000967257080242L;
		/**
		 * 企业id
		 */
		@ApiModelProperty(value = "企业id", required = true)
		@NotEmpty(message = "企业id不能为空")
		private String corpId;
		/**
		 * 合作伙伴(合众游戏平台)提供(类似appKey)
		 */
		@ApiModelProperty(value = "合作伙伴ID", required = true)
		@NotEmpty(message = "合作伙伴ID不能为空")
		private String partnerId;
		/**
		 * 32位字母数字字符串,请求ID。用于请求去重。
		 */
		@ApiModelProperty(value = "请求流水号", required = true)
		@NotEmpty(message = "请求流水号不能为空")
		private String nonce;
		/**
		 * HMAC('SHA256')请求的签名
		 */
		@ApiModelProperty(value = "签名", required = true)
		@NotEmpty(message = "签名不能为空")
		private String signature;

	}

	@Data
	public static class ReqBody implements Serializable {
		/**
		 * 开始时间
		 */
		@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
		@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
		@NotNull(message = "开始时间不能为空")
		private Date startTime;
		/**
		 * 结束时间
		 */
		@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
		@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
		@NotNull(message = "结束时间不能为空")
		private Date endTime;
	}

}

MiGuOrderSyncResp

@Data
public class MiGuOrderSyncResp implements Serializable {
	private static final long serialVersionUID = -1383580636250379564L;

	private String resultCode;

	private String resultDesc;

	public MiGuOrderSyncResp() {
		this.setResultCode(ErrorCode.SUCCESS.getCode());
		this.setResultDesc(ErrorCode.SUCCESS.getMsg());
	}

	public  MiGuOrderSyncResp(ErrorCode errorCode) {
		this.setResultCode(errorCode.getCode());
		this.setResultDesc(errorCode.getMsg());
	}

	public MiGuOrderSyncResp(List<QueryMiGuOrderSyncRespBody> result) {
		this.setResultCode(ErrorCode.SUCCESS.getCode());
		this.setResultDesc(ErrorCode.SUCCESS.getMsg());
		this.setResult(result);
	}

	@JsonProperty("result")
	private List<QueryMiGuOrderSyncRespBody> result;

    @Data
	public static class QueryMiGuOrderSyncRespBody implements Serializable {

		private static final long serialVersionUID = 1L;

		// 订单id
		private String orderId;
		// 商品Id
		private String spuId;
		// 商品名
		private String spuName;
		// 规格信息
		private String specInfo;
		// 图片
		private String picUrl;
		// 商品数量
		private Integer quantity;
		// 咪咕奖励编码
		private String prizeCode;
		//咪咕订单号
		private String miguOrderNo;
		//昵称
		private String nickName;
		// 用户id
		private String userId;
		// 支付金额(销售金额+运费金额-积分抵扣金额-电子券抵扣金额)
		private BigDecimal paymentPrice;
		//付款时间
		private LocalDateTime paymentTime;
		// 订单状态1、待发货 2、待收货 3、确认收货/已完成 5、已关闭 10、拼团中
		private String orderStatus;
	}
}

2、nacos定义好咪咕相关配置信息

joolun-thirdparty-api-dev.yml:

#migu
migu: 
  partnerId: 
  secretkey: 
  corpId: 

3、同步咪咕参数配置

/**
 * @Description:  同步咪咕参数
 */
@Data
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "migu")
public class MiGuConfigProperties {
	/**
	 * 合作伙伴ID-tsp平台提供(类似appKey)
	 */
	private String partnerId;
	/**
	 * 企业id
	 */
	private String corpId;
	/**
	 * secretkey
	 */
	private String secretkey;
}

4、MiGuOrderSyncControl

@RestController
@AllArgsConstructor
@RequestMapping("sv")
@Slf4j
@Api(value = "MiGu_Order_Sync", tags = "咪咕订单同步模块API")
public class MiGuOrderSyncControl {
	@Autowired
	private MiGuOrderSyncService miGuOrderSyncService;

    /**
     * @Description: 咪咕同步订单对接
     */
	@ApiOperation(value = "咪咕订单同步任务")
	@PostMapping(value = "/app/miGuOrderSync")
	public MiGuOrderSyncResp miGuOrderSync(@Valid @RequestBody MiGuOrderSyncReq req) {
		log.info("MiGuOrderSyncDTO param:[{}]", JSON.toJSONString(req));
		MiGuOrderSyncResp resp = miGuOrderSyncService.miGuOrderSync(req);
		log.info("MiGuOrderSyncDTO resp:[{}]", JSON.toJSONString(resp));
		return resp;
	}
}

5、MiGuOrderSyncService

/**
 1. @Description: 咪咕同步订单对接
 */
public interface MiGuOrderSyncService {

	MiGuOrderSyncResp miGuOrderSync(MiGuOrderSyncReq miGuOrderSyncReq);
}

6、MiGuOrderSyncServiceImpl

方法需进行以下判断:
1.判断请求体的参数是否和nacos配置参数相等
2. 判断接口幂等性(因为这接口是给咪咕方调用,因此要防止接口调用超时重试)
3. 进行验签

代码如下:

@Service
@Slf4j
@AllArgsConstructor
public class MiGuOrderSyncServiceImpl implements MiGuOrderSyncService {
	private final MiGuConfigProperties miGuConfigProperties;
	private final RedisTemplate<String, String> redisTemplate;
	@Autowired
	private MiGuOrderSyncMapper miGuOrderSyncMapper;

	@Override
	public MiGuOrderSyncResp miGuOrderSync(MiGuOrderSyncReq req) {
		log.info("miGuOrderSync param:{}", JSON.toJSONString(req));
		MiGuOrderSyncReq.ReqHeader header = req.getHeader();
		MiGuOrderSyncReq.ReqBody body = req.getBody();
		if (!StrUtil.equals(miGuConfigProperties.getCorpId(), header.getCorpId())) {
			log.error("miGuOrderSync fail! corpId is error!");
			return new MiGuOrderSyncResp(ErrorCode.MIGU_ORDER_SYNC_ERROR1);
		}
		if (!StrUtil.equals(miGuConfigProperties.getPartnerId(), header.getPartnerId())) {
			log.error("miGuOrderSync fail! partnerId is error!");
			return new MiGuOrderSyncResp(ErrorCode.MIGU_ORDER_SYNC_ERROR2);
		}
		if (!validateApi(body, header)) {
			log.error("miGuOrderSync fail! request repeat!");
			return new MiGuOrderSyncResp(ErrorCode.IO_POINTS_ISSUE_ERROR5);
		}
		boolean signFlag = validateSign(header, body);
		if (!signFlag) {
			log.error("miGuOrderSync fail! sign is error!");
			return new MiGuOrderSyncResp(ErrorCode.MIGU_ORDER_SYNC_ERROR3);
		}
		List<MiGuOrderSyncDTO> miGuOrderSyncList = miGuOrderSyncMapper.queryMiGuOrderSync(body.getStartTime(),body.getEndTime());
		if (CollUtil.isEmpty(miGuOrderSyncList)) {
			log.info("miGuOrderSyncList is Empty!");
			return new MiGuOrderSyncResp(new ArrayList<>());
		}
		List<MiGuOrderSyncResp.QueryMiGuOrderSyncRespBody> result = BeanConvertUtils.convert(miGuOrderSyncList, MiGuOrderSyncResp.QueryMiGuOrderSyncRespBody.class);
		return new MiGuOrderSyncResp(result);
	}

	/**
	 * @Description: 接口幂等性
	 */
	private boolean validateApi(MiGuOrderSyncReq.ReqBody body, MiGuOrderSyncReq.ReqHeader header) {
		String key = body.getStartTime() + "_" + header.getNonce() + "_" + header.getSignature();
		if (incr(key, 2L) > 1) {
			return false;
		}
		return true;
	}

	/**
	 * @Description: Redis原子性自增
	 */
	private long incr(String key, long expireTime) {
		long next = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()).incrementAndGet();
		redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
		return next;
	}

	/**
	 * @Description: 验签
	 */
	private boolean validateSign(MiGuOrderSyncReq.ReqHeader header, MiGuOrderSyncReq.ReqBody body) {
		Map<String, Object> params = BeanUtil.beanToMap(header);
		params.put("startTime", body.getStartTime());
		params.put("endTime", body.getEndTime());
		Map<String, Object> validateParams = new HashMap<>();
		validateParams.putAll(params);
		String signVal = MapUtil.getStr(validateParams, SyncDeptAndEmpConst.SIGNATURE);
		validateParams.remove(SyncDeptAndEmpConst.SIGNATURE);
		String val = CreateAscIISignUtil.getSignToken(validateParams);
		String sign = Hmacsha256Util.hmacMD5(val, miGuConfigProperties.getSecretkey());
		log.info("miGuOrderSync validateSign param:{}, sign:{},signVal:{}", val, sign, signVal);
		if (StrUtil.isNotBlank(signVal) && signVal.equals(sign)) {
			log.info("验签成功 param:{}, sign:{},signVal:{}", val, sign, signVal);
			return true;
		}
		log.error("验签失败 param:{}, sign:{},signVal:{}", val, sign, signVal);
		return false;
	}

}

CreateAscIISignUtil 生成参数 字典排序 签名

@Slf4j
public class CreateAscIISignUtil {
    /**
     * @MethodName: getSignToken
     * @Description: 生成签名
     */
    public static String getSignToken(Map<String, Object> map) {
        String result = "";
        try {
            List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(map.entrySet());
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
            Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
                @Override
                public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            // 构造签名键值对的格式
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Object> item : infoIds) {
                if (StrUtil.isNotEmpty(item.getKey())) {
                    String key = item.getKey();
                    String val = StrUtil.toString(item.getValue());
                    if (StrUtil.isNotEmpty(val)) {
                        sb.append(key + "=" + val + "&");
                    }
                }
            }
            result = StrUtil.sub(sb, 0, sb.length()-1);
        } catch (Exception e) {
            log.error("CreateAscIISignUtil error = [{}]", e.getMessage(), e);
            return null;
        }
        return result;
    }
}

Hmacsha256Util 加密

	 /**
	 * @MethodName: hmacMD5
	 * @Description: HmacMD5加密
	 * @Param: [message加密原文, secret秘钥]
	 * @Return: java.lang.String加密后字符串
	 */
	public static String hmacMD5(String message, String secret) {

		String hash = "";
		try {
			Mac sha256_HMAC = Mac.getInstance("HmacMD5");
			SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(Charset.forName("UTF-8")), "HmacMD5");
			sha256_HMAC.init(secret_key);
			byte[] bytes = sha256_HMAC.doFinal(message.getBytes(Charset.forName("UTF-8")));
			hash = byteArrayToHexString(bytes);
		} catch (Exception e) {
			log.error("Hmacsha256Util hmacMD5 error = [{}]", e);
		}
		return hash;
	}

测试

请求如下:
在这里插入图片描述
返回结果:
在这里插入图片描述
成功拉兄弟姐妹们!!!!!
我师父看了我的代码表扬我了!!!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析:消费者数据分析

数据分析&#xff1a;消费者数据分析 作者&#xff1a;AOAIYI 创作不易&#xff0c;如果觉得文章不错或能帮助到你学习&#xff0c;记得点赞收藏评论一下哦 文章目录数据分析&#xff1a;消费者数据分析一、前言二、数据准备三、数据预处理四、个体消费者分析五、用户消费行为总…

【CMake】CMake构建C++代码(一)

在Linux开发过程中&#xff0c;难免会用到CMake来构建你的代码。本文将说明如何构建自己的代码&#xff0c;将自己的代码变为共享库&#xff0c;共其他代码使用。 文章目录在Linux开发过程中&#xff0c;难免会用到CMake来构建你的代码。本文将说明如何构建自己的代码&#xff…

R语言Ternary包绘制三元图、RGB三色空间分布图的方法

本文介绍基于R语言中的Ternary包&#xff0c;绘制三元图&#xff08;Ternary Plot&#xff09;的详细方法&#xff1b;其中&#xff0c;我们就以RGB三色分布图为例来具体介绍。 三元图可以从三个不同的角度反应数据的特征&#xff0c;因此在很多领域都得以广泛应用&#xff1b;…

2023美赛F题思路数据代码分享

文章目录赛题思路2023年美国大学生数学建模竞赛选题&论文一、关于选题二、关于论文格式三、关于论文提交四、论文提交流程注意不要手滑美赛F题思路数据代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片&#xff0c;加入获取一手资源 202…

MySQL 索引 (只要能看完)(一篇就够了)

文章目录前言一、MySQL索引介绍1.1 索引的类别1.2 索引的创建原则二、索引的管理和使用2.1 制造实验数据2.2 explain 使用说明2.3 创建索引2.3.1 基于创建表时建立索引2.3.2 基于已创建好的表创建索引2.4 删除索引2.5 聚集索引和二级索引2.5.1 聚集索引2.5.2 二级索引&#xff…

【python知识】win10下如何用python将网页转成pdf文件

一、说明 本篇记录一个自己享用的简单工具。在大量阅读网上文章中&#xff0c;常常遇到一个专题对应多篇文章&#xff0c;用浏览器的收藏根本不够。能否见到一篇文章具有搜藏价值&#xff0c;就转到线下&#xff0c;以备日后慢慢消化吸收。这里终于找到一个办法&#xff0c;将在…

【IIC子系统之读取温湿度】

IIC子系统之读取温湿度IIC总线协议主机读取一个字节主机发送一个字节设备树编写IIC设备驱动层API编写程序读取温湿度应用层驱动读取温湿度函数解析头文件IIC总线协议 1.I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线&#xff0c;主要用于连接整体电路。 1&a…

面试准备知识点与总结——(基础篇)

目录Java基础Java面向对象有哪些特征ArrayList和LinkedList有什么区别高并发的集合有哪些问题迭代器的fail-fast和fail-safeArrayList底层扩容机制HashMap面试合集解答设计模式单例设计模式哪些地方体现了单例模式Java基础 Java面向对象有哪些特征 Java面向对象有三大特征&am…

Win10显示dds及tga缩略图

整理之前做游戏MOD时收集的模型资源,3D游戏模型的贴图文件格式基本都是dds或tga的,毕竟无损压缩、支持嵌入MipMap、带透明通道、可以被GPU硬解balabala...道理我都懂但这俩玩意系统根本直接查看不了,就算装上专门的看图软件或插件,文件夹视图下也没有缩略图预览,只能一个个点开…

SQL查询的优化:如何调整SQL查询的性能

查询优化是在合理利用系统资源和性能指标的基础上&#xff0c;定义最有效、最优化的方式和技术&#xff0c;以提高查询性能的过程。查询调整的目的是找到一种方法来减少查询的响应时间&#xff0c;防止资源的过度消耗&#xff0c;并识别不良的查询性能。 在查询优化的背景下&a…

CF1795D Triangle Coloring (组合数学)

链接 题意&#xff1a; 给定一个 n 个点 n 条边的图 &#xff0c;n 为 6 的倍数&#xff0c;每条边都有边权。 这个图是由 n/3 个三元环构成的 [1,2,3],[4,5,6],[7,8,9],[10,11,12]...... 一个 n12 的图长成这个&#xff08;唯一&#xff09;&#xff1a; 现在你需要给图染上红…

厂商推送限制说明及极光的适配方案

背景 自2023年起&#xff0c;各个厂商逐步对营销类消息做数量管控&#xff0c;具体如下&#xff1a; 华为 自2023年1月5日起&#xff0c;华为PUSH通道将根据应用类型对资讯营销类消息的每日推送数量进行上限管理&#xff0c;服务与通讯类消息每日推送数量不受限制。详情请参考推…

使用MMDetection进行目标检测、实例和全景分割

MMDetection 是一个基于 PyTorch 的目标检测开源工具箱&#xff0c;它是 OpenMMLab 项目的一部分。包含以下主要特性&#xff1a; 支持三个任务 目标检测&#xff08;Object Detection&#xff09;是指分类并定位图片中物体的任务实例分割&#xff08;Instance Segmentation&a…

服务间调用方式 OpenFeign 的介绍和使用

文章目录前言1、 RestTemplate1.1、通用方法 exchange1.2、RestTemplate 和 OpenFeign 的区别2、RPC 和 HTTP2.1、RPC 是什么2.2、RPC 调用过程示意图2.3、HTTP 是什么2.4、HTTP 调用过程示意图2.5、对比3 、OpenFeign3.1 、OpenFeign 常用注解3.2 、案例前言 Feign 停更了&am…

空间复杂度与时间复杂度

1、时间复杂度和空间复杂度 &#xff08;1&#xff09;时间复杂度、空间复杂度是什么&#xff1f; 算法效率分析分为两种&#xff1a;第一种是时间效率&#xff0c;第二种是空间效率。时间效率被称为时间复杂度&#xff0c;空间效率被称作空间复杂度时间复杂度主要衡量的是一…

Python空间分析| 02 利用Python计算空间局部自相关(LISA)

局部空间自相关 import esda import numpy as np import pandas as pd import libpysal as lps import geopandas as gpd import contextily as ctx import matplotlib.pyplot as plt from geopandas import GeoDataFrame from shapely.geometry import Point from pylab im…

LeetCode 1139. 最大的以 1 为边界的正方形

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 2023/2/17 每日一题 题目描述 给你一个由若干 000 和 111 组成的二维网格 gridgridgrid&#xff0c;请你找出边界全部由 111 组成的最大 正方形 子网格&#xff0c;并返回该子网格中的元素数量。如果不存在&am…

Transformer论文阅读:ViT算法笔记

标题&#xff1a;An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 会议&#xff1a;ICLR2021 论文地址&#xff1a;https://openreview.net/forum?idYicbFdNTTy 文章目录Abstract1 Introduction2 Related Work3 Method3.1 Vision Transformer3.2…

【已解决】关于 luckysheet 设置纯文本,解决日期格式回显错误的办法

目录 一、现象 二、分析 三、思考过程 五、解决 六、参考链接 一、现象 在excel里面输入内容&#xff0c;如 2023-2-17 12:00 保存后&#xff0c;传回后端的数据被转化成了 数值类型&#xff0c;这显然是一种困扰。 如图所示 二、分析 查阅了文档和一些博客发现 Lucky…

2023美赛ICM F题 详细思路

对于本次美赛F题来说&#xff0c;核心之处就是综合评价模型&#xff0c;但考察我们这个模型的角度较以往常规的制定指标&#xff0c;计算得分给出排名然后分析结果这一套常规流程不同&#xff0c;这次美赛F题出题更贴合实际&#xff0c;整体聚焦在“制定完一个新指标后会带来的…