[Xiaoxi] Synchronize Migu orders to Migufang (write interface to third parties)

foreword

Requirement: Synchronize Migu-related orders from Xiaoxizhong to Migufang.

train of thought

The idea is as follows:

  1. Define the request body and response information
  2. Define Migu related configuration information in nacos (for later verifying whether the request body is correct)
  3. write interface

accomplish

1. Define the request body and response information

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 defines Migu related configuration information

joolun-thirdparty-api-dev.yml:

#migu
migu: 
  partnerId: 
  secretkey: 
  corpId: 

3. Synchronize MIGU parameter configuration

/**
 * @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

The method needs to make the following judgments:
1. Judging whether the parameters of the request body are equal to the configuration parameters of nacos
2. Judging the idempotence of the interface (because this interface is called by Migu Fang, so it is necessary to prevent the interface call from retrying over time) 3. Conduct
testing sign

code show as below:

@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 generate parameters dictionary sort signature

@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 encryption

	 /**
	 * @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;
	}

test

The request is as follows:
insert image description here
Return result:
insert image description here
Successfully pulled brothers and sisters! ! ! ! !
My master read my code and praised me! ! ! !
insert image description here

Guess you like

Origin blog.csdn.net/someday____/article/details/129079802