zhumin 8 сар өмнө
commit
5886b92d7b
72 өөрчлөгдсөн 3477 нэмэгдсэн , 0 устгасан
  1. 9 0
      .idea/erpsaas_api_new.iml
  2. 6 0
      .idea/misc.xml
  3. 8 0
      .idea/modules.xml
  4. 2 0
      README.md
  5. 163 0
      pom.xml
  6. 43 0
      raycos_common/pom.xml
  7. 35 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/Result.java
  8. 43 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/Lock.java
  9. 54 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzOrder.java
  10. 25 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzOrderOriginal.java
  11. 41 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzPullOrderTask.java
  12. 50 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzToken.java
  13. 53 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbStoreInfo.java
  14. 25 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/config/LwaClientInfoNacos.java
  15. 36 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/vo/GetAmzTokenByLwaCodeVo.java
  16. 16 0
      raycos_common/src/main/java/com/raycos/raycoscommon/Internal/webapi/User.java
  17. 16 0
      raycos_common/src/main/java/com/raycos/raycoscommon/RaycosCommonApplication.java
  18. 17 0
      raycos_common/src/main/java/com/raycos/raycoscommon/json/HttpResultJson.java
  19. 30 0
      raycos_common/src/main/java/com/raycos/raycoscommon/json/TokenAmz.java
  20. 27 0
      raycos_common/src/main/java/com/raycos/raycoscommon/json/TokenRetAmzJson.java
  21. 82 0
      raycos_datacenter_api/pom.xml
  22. 31 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/RaycosDatacenterApiApplication.java
  23. 11 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderController.java
  24. 48 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderOriginalController.java
  25. 18 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderOriginalService.java
  26. 16 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderService.java
  27. 46 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzPullOrderController.java
  28. 8 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzPullOrderService.java
  29. 20 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzPullOrderTaskService.java
  30. 39 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzOrderOriginalServiceImpl.java
  31. 38 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzOrderServiceImpl.java
  32. 120 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzPullOrderServiceImpl.java
  33. 53 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzPullOrderTaskServiceImpl.java
  34. 158 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/util/AmazonUtil.java
  35. 89 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/util/CommonUtil.java
  36. 13 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/util/Constant.java
  37. 101 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/AmzTokenController.java
  38. 32 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/AmzTokenService.java
  39. 85 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/StoreInfoController.java
  40. 17 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/StoreInfoService.java
  41. 232 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/impl/AmzTokenServiceImpl.java
  42. 39 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/impl/StoreInfoServiceImpl.java
  43. 10 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/common/databaselock/LockService.java
  44. 62 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/common/databaselock/impl/LockServiceImpl.java
  45. 26 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/MybatisPlusConfig.java
  46. 34 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/RabbitMQConfig.java
  47. 28 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/RestTemplateConfig.java
  48. 32 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/attach/AmzPullOrderConsumer.java
  49. 37 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/attach/SendAmzPullOrderMsg.java
  50. 7 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/AmzOrderMapper.java
  51. 7 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/AmzOrderOriginalMapper.java
  52. 21 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/AmzPullOrderTaskMapper.java
  53. 9 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/LockMapper.java
  54. 20 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/TbAmzTokenMapper.java
  55. 7 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/TbStoreInfoMapper.java
  56. 33 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/nacostest/EchoController.java
  57. 71 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/scheduledtasks/RefreshAmazonToken.java
  58. 102 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/scheduledtasks/RefreshTask.java
  59. 113 0
      raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/scheduledtasks/SearchOrderPullTask.java
  60. 73 0
      raycos_datacenter_api/src/main/resources/application.yml
  61. 104 0
      raycos_datacenter_api/src/main/resources/logback-spring.xml
  62. 221 0
      raycos_datacenter_api/src/main/resources/static/index.html
  63. 53 0
      raycos_web_api/pom.xml
  64. 17 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/RaycosWebApiApplication.java
  65. 193 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/amazon/authorize/AmazonAuthorize.java
  66. 19 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/ConfigProperties.java
  67. 44 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/NacosController.java
  68. 43 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/TestController.java
  69. 21 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/config/NacosLoadBalancerConfig.java
  70. 21 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/config/RestTemplateConfig.java
  71. 24 0
      raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/service/EchoService.java
  72. 30 0
      raycos_web_api/src/main/resources/application.yml

+ 9 - 0
.idea/erpsaas_api_new.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/erpsaas_api_new.iml" filepath="$PROJECT_DIR$/.idea/erpsaas_api_new.iml" />
+    </modules>
+  </component>
+</project>

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+# raycos-erp
+

+ 163 - 0
pom.xml

@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.raycos</groupId>
+    <artifactId>raycos</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <modules> 
+        <module>raycos_common</module>
+        <module>raycos_web_api</module>
+        <module>raycos_datacenter_api</module>
+    </modules>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.6.13</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+
+        <!--spring cloud版本-->
+        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
+        <spring-cloud.version>2021.0.5</spring-cloud.version>
+        <!--jdk版本-->
+        <java.version>8</java.version>
+        <skipTests>true</skipTests>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+                <version>${spring-cloud-alibaba.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-boot-starter</artifactId>
+                <version>3.5.7</version>
+            </dependency>
+
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-starter</artifactId>
+                <version>1.2.19</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-jdbc</artifactId>
+                <version>3.1.5</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.mysql</groupId>
+                <artifactId>mysql-connector-j</artifactId>
+                <version>8.3.0</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba.fastjson2</groupId>
+                <artifactId>fastjson2</artifactId>
+                <version>2.0.52</version>
+            </dependency>
+
+            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-amqp</artifactId>
+                <version>3.2.0</version>
+            </dependency>
+
+
+        </dependencies>
+
+    </dependencyManagement>
+    <dependencies>
+        <!-- Nacos 服务发现 -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+        <!-- 服务发现:OpenFeign服务调用 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <!-- 负载均衡器 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <repositories>
+        <repository>
+            <id>spring-milestones</id>
+            <name>Spring Milestones</name>
+            <url>https://repo.spring.io/milestone</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+
+</project>

+ 43 - 0
raycos_common/pom.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.raycos</groupId>
+        <artifactId>raycos</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+<!--    <packaging>jar</packaging>-->
+
+    <artifactId>raycoscommon</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+<!--    <name>raycos_common</name>-->
+<!--    <description>Demo project for Spring Boot</description>-->
+
+    <properties>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <classifier>exec</classifier>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 35 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/Result.java

@@ -0,0 +1,35 @@
+package com.raycos.raycoscommon.Internal;
+
+import lombok.Data;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/26 16:15
+ */
+
+@Data
+public class Result<T> {
+    private int code;
+    private String msg;
+    private T data;
+
+    public Result<T> success(T obj){
+        this.setCode(200);
+        this.setMsg("success");
+        this.setData(obj);
+        return this;
+    }
+    public Result<T> success( ){
+        return this.success(null);
+    }
+    public Result<T> fail(T obj){
+        this.setCode(201);
+        this.setMsg("fail");
+        this.setData(obj);
+        return this;
+    }
+    public Result<T> fail(){
+        return this.fail(null);
+    }
+
+}

+ 43 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/Lock.java

@@ -0,0 +1,43 @@
+package com.raycos.raycoscommon.Internal.datacenterapi;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+
+/**
+ * 数据库锁
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("tb_lock")
+public class Lock {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 企业编号
+     */
+    private String lockKey;
+
+    /**
+     * 描述
+     */
+    @TableField("`describe`")
+    private String describe;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdAt;
+}

+ 54 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzOrder.java

@@ -0,0 +1,54 @@
+package com.raycos.raycoscommon.Internal.datacenterapi;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TbAmzOrder {
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+    private Integer companyId;
+    private Integer storeId;
+    private String amazonOrderId;
+    private LocalDateTime purchaseDate;
+    private LocalDateTime lastUpdateDate;
+    private String orderStatus;
+    private String fulfillmentChannel;
+    private String salesChannel;
+    private String shipServiceLevel;
+    private String currencyCode;
+    private BigDecimal amount;
+    private Integer numberOfItemsShipped;
+    private Integer numberOfItemsUnshipped;
+    private String paymentMethod;
+    // 注意:这里使用String,实际使用时可能需要转换为JSON
+    private String paymentMethodDetails;
+    private Boolean isReplacementOrder;
+    private String marketplaceId;
+    private String shipmentServiceLevelCategory;
+    private String orderType;
+    private LocalDateTime earliestShipDate;
+    private LocalDateTime latestShipDate;
+    private LocalDateTime earliestDeliveryDate;
+    private LocalDateTime latestDeliveryDate;
+    private Boolean isBusinessOrder;
+    private Boolean isPrime;
+    private Boolean isAccessPointOrder;
+    private Boolean isGlobalExpressEnabled;
+    private Boolean isPremiumOrder;
+    private Boolean isSoldByAb;
+    private Boolean isIba;
+    private String easyShipShipmentStatus;
+    private String electronicInvoiceStatus;
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
+}

+ 25 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzOrderOriginal.java

@@ -0,0 +1,25 @@
+package com.raycos.raycoscommon.Internal.datacenterapi;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TbAmzOrderOriginal {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long companyId;
+    private Long storeId;
+    private String amazonOrderId;
+    private String jsonData;
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
+}

+ 41 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzPullOrderTask.java

@@ -0,0 +1,41 @@
+package com.raycos.raycoscommon.Internal.datacenterapi;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 亚马逊店铺拉单任务表
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TbAmzPullOrderTask {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 企业编号
+     */
+    private Integer companyId;
+
+    /**
+     * 店铺ID
+     */
+    private Integer storeId;
+
+    /**
+     * 最后操作时间
+     */
+    private Long lastOperationTime;
+
+    /**
+     * 任务重试次数
+     */
+    private Byte retryCount;
+}

+ 50 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbAmzToken.java

@@ -0,0 +1,50 @@
+package com.raycos.raycoscommon.Internal.datacenterapi;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TbAmzToken {
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    /**
+     * 企业编号
+     */
+    private Long companyId;
+    /**
+     * 店铺ID
+     */
+    private Long storeId;
+    /**
+     * 短令牌 接口访问令牌
+     */
+    private String accessToken;
+    /**
+     * token 类型
+     */
+    private String tokenType;
+    /**
+     * 过期时间戳
+     */
+    private Long expiresIn;
+    /**
+     * 长令牌 可以用于刷新访问令牌
+     */
+    private String refreshToken;
+    /**
+     * 亚马逊区域ID
+     */
+    private String marketId;
+
+
+}

+ 53 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/TbStoreInfo.java

@@ -0,0 +1,53 @@
+package com.raycos.raycoscommon.Internal.datacenterapi;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TbStoreInfo {
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 企业编号
+     */
+    private Long companyId;
+    /**
+     * 店铺名称
+     */
+    private String storeName;
+    /**
+     * 店铺类型 跨境:1 本土:2
+     */
+    private Short storeType;
+    /**
+     * 店铺类型 FBA:1 FBM:2
+     */
+    private Short storeModel;
+    /**
+     * 平台账号
+     */
+    private String platformAccount;
+    /**
+     * 站点
+     */
+    private String stationsIdStr;
+
+    /**
+     *  授权时应用程序生成的 state (UUID) 值。
+     */
+    private String   state;
+
+
+
+}

+ 25 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/config/LwaClientInfoNacos.java

@@ -0,0 +1,25 @@
+package com.raycos.raycoscommon.Internal.datacenterapi.config;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.stereotype.Component;
+
+@Component
+//@RefreshScope
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class LwaClientInfoNacos {
+//    @Value("${grant_type}")
+    private  String grantType;
+    private  String code;
+//    @Value("${redirect_uri}")
+    private  String redirectUri;
+//    @Value("${client_id}")
+    private  String clientId;
+//    @Value("${client_secret}")
+    private  String clientSecret;
+
+
+}

+ 36 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/datacenterapi/vo/GetAmzTokenByLwaCodeVo.java

@@ -0,0 +1,36 @@
+package com.raycos.raycoscommon.Internal.datacenterapi.vo;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * url:   /erp/amztoken/getamztokenbylwacode
+ *  使用LWA code 获取亚马逊令牌
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class GetAmzTokenByLwaCodeVo {
+
+
+    /**
+     *  企业 ID
+     */
+    private Long companyId;
+    /**
+     *  亚马逊店铺信息ID
+     */
+    private Long storeId;
+    /**
+     * 用于生成 LWA 刷新令牌的 Login with Amazon (LWA) 授权码
+     */
+    private String lwaCode;
+    /**
+     * 参考文档:https://developer-docs.amazon.com/sp-api/docs/website-authorization-workflow
+     * 其中:第 4 步: 亚马逊发送授权信息 selling_partner_id  授权您的应用程序的销售合作伙伴的标识符。
+     */
+    private String sellingPartnerId;
+
+}

+ 16 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/Internal/webapi/User.java

@@ -0,0 +1,16 @@
+package com.raycos.raycoscommon.Internal.webapi;
+
+import lombok.Data;
+import lombok.ToString;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/26 16:53
+ */
+
+@Data
+@ToString
+public class User {
+    private  String name;
+    private  Integer age;
+}

+ 16 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/RaycosCommonApplication.java

@@ -0,0 +1,16 @@
+package com.raycos.raycoscommon;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/28 11:20
+ */
+
+@SpringBootApplication
+public class RaycosCommonApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(RaycosCommonApplication.class, args);
+    }
+}

+ 17 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/json/HttpResultJson.java

@@ -0,0 +1,17 @@
+package com.raycos.raycoscommon.json;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class HttpResultJson {
+
+    private String statusCode;
+    private String statusCodeValue;
+    private String headers;
+    private String body;
+}

+ 30 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/json/TokenAmz.java

@@ -0,0 +1,30 @@
+package com.raycos.raycoscommon.json;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+
+/**
+ *   https://api.amazon.com/auth/o2/token   接口入参实体类
+ *
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TokenAmz {
+    @JSONField(name = "grant_type")
+    private String grantType;
+    @JSONField(name = "code")
+    private String code;
+    @JSONField(name = "redirect_uri")
+    private String redirectUri;
+    @JSONField(name = "client_id")
+    private String clientId;
+    @JSONField(name = "client_secret")
+    private String clientSecret;
+
+
+
+}

+ 27 - 0
raycos_common/src/main/java/com/raycos/raycoscommon/json/TokenRetAmzJson.java

@@ -0,0 +1,27 @@
+package com.raycos.raycoscommon.json;
+
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+
+/**
+ * https://api.amazon.com/auth/o2/token   接口返回实体类 接口返回正确的情况下 (不包括 异常情况返回)
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TokenRetAmzJson {
+    @JSONField(name = "access_token")
+    private String accessToken;
+    @JSONField(name = "token_type")
+    private String tokenType;
+    @JSONField(name = "expires_in")
+    private Long expiresIn;
+    @JSONField(name = "refresh_token")
+    private String refreshToken;
+
+
+}

+ 82 - 0
raycos_datacenter_api/pom.xml

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.raycos</groupId>
+        <artifactId>raycos</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>raycos_datacenter_api</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>raycos-datacenter-api</name>
+
+    <packaging>jar</packaging>
+    <description>raycos-datacenter-api</description>
+
+
+    <properties>
+    </properties>
+    <dependencies>
+
+        <dependency>
+            <groupId>com.raycos</groupId>
+            <artifactId>raycoscommon</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+
+            <exclusions>
+            <!-- 排除默认的 HikariCP 数据源 -->
+            <exclusion>
+                <groupId>com.zaxxer</groupId>
+                <artifactId>HikariCP</artifactId>
+            </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+        </dependency>
+
+
+
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 31 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/RaycosDatacenterApiApplication.java

@@ -0,0 +1,31 @@
+package com.raycos.raycosdatacenterapi;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.amqp.rabbit.annotation.EnableRabbit;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@EnableDiscoveryClient
+@EnableFeignClients
+@SpringBootApplication
+@MapperScan("com/raycos/raycosdatacenterapi/mapper")
+
+
+// 启用MQ队列
+@EnableRabbit
+// 添加扫描范围
+@ComponentScan(basePackages = {"com.raycos.raycoscommon", "com.raycos.raycosdatacenterapi"})
+
+//扫描spring 定时任务
+@EnableScheduling
+public class RaycosDatacenterApiApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(RaycosDatacenterApiApplication.class, args);
+    }
+
+}

+ 11 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderController.java

@@ -0,0 +1,11 @@
+package com.raycos.raycosdatacenterapi.amzorder;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Slf4j
+@RestController
+@RequestMapping("/erp/amzorder")
+public class AmzOrderController {
+}

+ 48 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderOriginalController.java

@@ -0,0 +1,48 @@
+package com.raycos.raycosdatacenterapi.amzorder;
+
+
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzOrderOriginal;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/erp/amzorderoriginal")
+public class AmzOrderOriginalController {
+
+    @Autowired
+    private AmzOrderOriginalService amzOrderOriginalService;
+
+    @PostMapping("/addOrderoRiginal")
+    public Result<String> addOrderoRiginal(@RequestBody TbAmzOrderOriginal taoo) {
+        Result<String> rt = new Result<>();
+        try {
+            amzOrderOriginalService.add(taoo);
+            rt.success();
+            rt.setMsg("新增订单成功!");
+        } catch (Exception e) {
+            rt.fail();
+            log.error("  /addOrderoRiginal 异常 ", e);
+        }
+        return rt;
+    }
+
+    @GetMapping("/selAllOrderoRiginal")
+    public Result<List<TbAmzOrderOriginal>> selAllOrderoRiginal() {
+        Result<List<TbAmzOrderOriginal>> rt = new Result<>();
+        try {
+            rt.success(amzOrderOriginalService.selAll(null));
+            rt.setMsg("订单原始数据查询成功!");
+        } catch (Exception e) {
+            rt.fail();
+            log.error(" /selAllOrderoRiginal  异常 ", e);
+        }
+        return rt;
+    }
+
+
+}

+ 18 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderOriginalService.java

@@ -0,0 +1,18 @@
+package com.raycos.raycosdatacenterapi.amzorder;
+
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzOrderOriginal;
+
+import java.util.List;
+
+public interface AmzOrderOriginalService {
+
+
+    void add(TbAmzOrderOriginal params) throws Exception;
+
+    void del(TbAmzOrderOriginal params) throws Exception;
+
+    void edit(TbAmzOrderOriginal params) throws Exception;
+
+    List<TbAmzOrderOriginal> selAll(TbAmzOrderOriginal params) throws Exception;
+}

+ 16 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzOrderService.java

@@ -0,0 +1,16 @@
+package com.raycos.raycosdatacenterapi.amzorder;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzOrder;
+
+import java.util.List;
+
+public interface AmzOrderService {
+
+    void add(TbAmzOrder params) throws Exception;
+
+    void del(TbAmzOrder params) throws Exception;
+
+    void edit(TbAmzOrder params) throws Exception;
+
+    List<TbAmzOrder> selAll(TbAmzOrder params) throws Exception;
+}

+ 46 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzPullOrderController.java

@@ -0,0 +1,46 @@
+package com.raycos.raycosdatacenterapi.amzorder;
+
+
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+import com.raycos.raycosdatacenterapi.config.attach.SendAmzPullOrderMsg;
+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.RestController;
+
+/**
+ * 亚马逊拉单
+ */
+@RestController
+@Slf4j
+@RequestMapping("/erp/amzorder")
+public class AmzPullOrderController {
+
+
+    @Autowired
+    private AmzPullOrderService amzPullOrderService;
+
+
+    @Autowired
+    private SendAmzPullOrderMsg sendAmzPullOrderMsg;
+
+    @GetMapping("/getorder")
+    public Result<String> getorder() {
+        Result<String> rt = new Result<>();
+        try {
+            log.info(" 来了老铁   ");
+            TbAmzPullOrderTask task = new TbAmzPullOrderTask();
+            task.setCompanyId(100001);
+            sendAmzPullOrderMsg.sendMessage(task);
+            rt.success();
+        } catch (Exception e) {
+            rt.fail();
+            log.error("   异常 ", e);
+        }
+        return rt;
+    }
+
+
+}

+ 8 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzPullOrderService.java

@@ -0,0 +1,8 @@
+package com.raycos.raycosdatacenterapi.amzorder;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+
+public interface AmzPullOrderService {
+
+    void sendGetOrders (TbAmzPullOrderTask task )throws Exception;
+}

+ 20 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/AmzPullOrderTaskService.java

@@ -0,0 +1,20 @@
+package com.raycos.raycosdatacenterapi.amzorder;
+
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+
+import java.util.List;
+
+public interface AmzPullOrderTaskService {
+
+
+    void add( TbAmzPullOrderTask params) throws Exception;
+
+    void delById(TbAmzPullOrderTask params) throws Exception;
+
+    void editById(TbAmzPullOrderTask params) throws Exception;
+
+    List<TbAmzPullOrderTask> selAll(TbAmzPullOrderTask params) throws Exception;
+
+    List< TbAmzPullOrderTask > findPullTask ( TbAmzPullOrderTask params ) throws Exception;
+}

+ 39 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzOrderOriginalServiceImpl.java

@@ -0,0 +1,39 @@
+package com.raycos.raycosdatacenterapi.amzorder.impl;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzOrderOriginal;
+import com.raycos.raycosdatacenterapi.amzorder.AmzOrderOriginalService;
+import com.raycos.raycosdatacenterapi.mapper.AmzOrderOriginalMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@Slf4j
+public class AmzOrderOriginalServiceImpl implements AmzOrderOriginalService {
+
+    @Autowired
+    private AmzOrderOriginalMapper amzOrderOriginalMapper;
+
+    @Override
+    public void add(TbAmzOrderOriginal params) throws Exception {
+        amzOrderOriginalMapper.insert( params);
+
+    }
+
+    @Override
+    public void del(TbAmzOrderOriginal params) throws Exception {
+
+    }
+
+    @Override
+    public void edit(TbAmzOrderOriginal params) throws Exception {
+
+    }
+
+    @Override
+    public List<TbAmzOrderOriginal> selAll(TbAmzOrderOriginal params) throws Exception {
+        return amzOrderOriginalMapper.selectList(null);
+    }
+}

+ 38 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzOrderServiceImpl.java

@@ -0,0 +1,38 @@
+package com.raycos.raycosdatacenterapi.amzorder.impl;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzOrder;
+import com.raycos.raycosdatacenterapi.amzorder.AmzOrderService;
+import com.raycos.raycosdatacenterapi.mapper.AmzOrderMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+@Service
+@Slf4j
+public class AmzOrderServiceImpl implements AmzOrderService {
+
+    @Autowired
+    private AmzOrderMapper amzOrderMapper;
+
+    @Override
+    public void add(TbAmzOrder params) throws Exception {
+        amzOrderMapper.insert( params);
+
+    }
+
+    @Override
+    public void del(TbAmzOrder params) throws Exception {
+
+    }
+
+    @Override
+    public void edit(TbAmzOrder params) throws Exception {
+
+    }
+
+    @Override
+    public List<TbAmzOrder> selAll(TbAmzOrder params) throws Exception {
+        return amzOrderMapper.selectList(null);
+    }
+}

+ 120 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzPullOrderServiceImpl.java

@@ -0,0 +1,120 @@
+package com.raycos.raycosdatacenterapi.amzorder.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+import com.raycos.raycosdatacenterapi.amzorder.AmzPullOrderService;
+import com.raycos.raycosdatacenterapi.amzorder.util.AmazonUtil;
+import com.raycos.raycosdatacenterapi.amzorder.util.CommonUtil;
+import com.raycos.raycosdatacenterapi.authorize.AmzTokenService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.util.Date;
+
+
+/**
+ * 亚马逊拉单服务
+ */
+@Service
+@Slf4j
+public class AmzPullOrderServiceImpl implements AmzPullOrderService {
+
+
+    @Autowired
+    private AmzTokenService amzTokenService;
+
+    //@Resource(name = "ExternalNetwork")
+    private static RestTemplate enRrestTemplate;
+
+
+
+    @Autowired
+    public void setMyDependency(RestTemplate restTemplate) {
+        AmzPullOrderServiceImpl.enRrestTemplate = restTemplate;
+    }
+
+    public static RestTemplate getMyDependency() {
+        return enRrestTemplate;
+    }
+
+
+
+    /**
+     * 拉取亚马逊订单
+     *
+     * @throws Exception
+     */
+    @Override
+    public void sendGetOrders(TbAmzPullOrderTask task) throws Exception {
+
+        // 根据 企业编号 与站点 查询 amz接口令牌
+
+
+
+    }
+
+
+    private static final String accessToken = "Atza|IwEBIPmiS7q9d3TZFgfAbYxjTbWVEdtuYoRKgR1DTeywc1BY6EsA6ROmvoqqlwvx_08JWZLHgg5mSXMLuRVPu9vgDqhzTuVc4JH2qcRWf-wegd2JpJY5NslLho6IG5ftGSwwbiuKgrGA_AI1tnW9BkHbZZhdQbDlW4sqoKfVrT_E9iEyZYfCsOZtoLpScMWbaBi2jaHjRv27tuS2jzpeDF-NzOOAETzPD7PKWKNfLp__eq1QabglUJ6ax5sBMYPsdZm30CViI6yZXZrfkV0zctkDJ4TPy7K58ARU5okQhQu6v9WN8oRZQOfx6ziPHoLlD5u9kUAAGL4GRojFEeXi6rdX4mLUeEI8cQlXGyspsP1Uv8xbnA";
+    private static final String marketplaceId = "ATVPDKIKX0DER";
+
+    private static final String HOST = "sandbox.sellingpartnerapi-na.amazon.com";
+    private static final String region = "us-east-1";
+
+    private static final String apiMethod = "/orders/v0/orders?";
+
+
+//    Date date = new Date();
+//    String reqDateTime = CommonUtil.getTimeFormat(date, "yyyyMMdd'T'HHmmss'Z'", true);
+
+    public static void getorder1() throws Exception {
+
+        String queryString = "CreatedAfter=TEST_CASE_200&MarketplaceIds=" + marketplaceId + "&MaxResultsPerPage=2";
+        enRrestTemplate = new RestTemplate();
+        String url = "https://" + HOST + apiMethod + queryString;
+        // 设置请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setAll(AmazonUtil.calculateAwsSignatureAndReturnHeaders(
+                apiMethod,
+                "GET",
+                HOST,
+                "",
+                queryString,
+                region,
+                accessToken
+        ));
+        // 创建请求实体
+        HttpEntity<String> entity = new HttpEntity<>(headers);
+
+        // 发送请求
+        ResponseEntity<String> resp = enRrestTemplate.exchange(
+                url,
+                HttpMethod.GET,
+                entity,
+                String.class
+        );
+        log.info("   orders 接口返回 " + JSONObject.toJSONString(resp));
+
+    }
+
+
+    public static void main(String[] args) {
+        Date date = new Date();
+        String reqDateTime = CommonUtil.getTimeFormat(date, "yyyyMMdd'T'HHmmss'Z'", true);
+        System.out.println(reqDateTime);
+        try {
+            AmzPullOrderServiceImpl.getorder1();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+    }
+
+}

+ 53 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/impl/AmzPullOrderTaskServiceImpl.java

@@ -0,0 +1,53 @@
+package com.raycos.raycosdatacenterapi.amzorder.impl;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+import com.raycos.raycosdatacenterapi.amzorder.AmzPullOrderTaskService;
+import com.raycos.raycosdatacenterapi.mapper.AmzPullOrderTaskMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+
+@Slf4j
+@Service
+public class AmzPullOrderTaskServiceImpl implements AmzPullOrderTaskService {
+
+    @Autowired
+    private AmzPullOrderTaskMapper amzPullOrderTaskMapper;
+
+    @Override
+    public void add(TbAmzPullOrderTask params) throws Exception {
+        amzPullOrderTaskMapper.insert(params);
+    }
+
+    @Override
+    public void delById(TbAmzPullOrderTask params) throws Exception {
+        amzPullOrderTaskMapper.deleteById(params.getId());
+    }
+
+    @Override
+    public void editById(TbAmzPullOrderTask params) throws Exception {
+        amzPullOrderTaskMapper.updateById(params);
+    }
+
+    @Override
+    public List<TbAmzPullOrderTask> selAll(TbAmzPullOrderTask params) throws Exception {
+        return amzPullOrderTaskMapper.selectList(null);
+    }
+
+    /**
+     * 查询需要拉去订单的店铺信息
+     *
+     * @param params
+     * @return
+     * @throws Exception
+     */
+    @Override
+    public List<TbAmzPullOrderTask> findPullTask(TbAmzPullOrderTask params) throws Exception {
+        return amzPullOrderTaskMapper.findPullTask( params.getLastOperationTime() , params.getRetryCount() );
+    }
+
+
+}

+ 158 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/util/AmazonUtil.java

@@ -0,0 +1,158 @@
+package com.raycos.raycosdatacenterapi.amzorder.util;
+
+
+import com.alibaba.nacos.common.utils.StringUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+
+public class AmazonUtil {
+
+    /**
+     * 计算加密密钥及返回Header头
+     *
+     * @param methodName
+     * @param requestMethod 请求方式,例如: GET POST
+     * @param host          请求主机地址, 例如: sellingpartnerapi-na.amazon.com, 根据区域不同地址不同
+     * @param payload       传参,当为GET请求 参数留空, POST请求 拼为: abc=x1&efg=x2, 或json 格式 {"abc":"x1","efg":"x2"}
+     * @param queryString   当为GET请求,有传参则填写在这里,例如: abc=x1&efg=x2
+     * @param region        区域,例如:us-east-1
+     * @param accessToken   用户授权店铺后,短令牌token信息
+     * @return
+     * @throws UnsupportedEncodingException
+     * @throws NoSuchAlgorithmException
+     * @throws IllegalStateException
+     * @throws InvalidKeyException
+     */
+    public static Map<String, String> calculateAwsSignatureAndReturnHeaders(String methodName,
+                                                                            String requestMethod,
+                                                                            String host,
+                                                                            String payload,
+                                                                            String queryString,
+                                                                            String region,
+                                                                            String accessToken) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException, IllegalStateException {
+
+        Date date = new Date();
+        String reqDate = CommonUtil.getTimeFormat(date, "yyyyMMdd", true);
+        String reqDateTime = CommonUtil.getTimeFormat(date, "yyyyMMdd'T'HHmmss'Z'", true);
+
+        Map<String, String> canonicalHeaders = canonicalHeaders(host, accessToken, reqDateTime);
+
+        List<String> canonicalHeaderList = new ArrayList<>();
+        for (Map.Entry<String, String> entry : canonicalHeaders.entrySet()) {
+            canonicalHeaderList.add(entry.getKey() + ":" + entry.getValue());
+        }
+
+        String canonicalHeaderstr = StringUtils.join(canonicalHeaderList, "\n");
+
+        String credentialScope = credentialScopeStr(region, Constant.SERVICE, reqDate);
+        String canonicalRequestHash = canonicalRequestHashStr(requestMethod, methodName, payload, queryString,
+                canonicalHeaderstr);
+        String stringToSign = createStrToSignStr(credentialScope, canonicalRequestHash, reqDateTime);
+        byte[] kSigning = calculateKsigning(Constant.IAMUSERSECRET, region, Constant.SERVICE, reqDate);
+        byte[] sign = CommonUtil.hmacSha256(stringToSign, kSigning);
+
+        String credential = "Credential=" + Constant.IAMUSERKEY + "/" + credentialScope;
+        String signedHeaders = "SignedHeaders=" + Constant.SIGNEDHEADER;
+        String signature = "Signature=" + CommonUtil.byte2hex(sign);
+        String[] authorizationHeader = {credential, signedHeaders, signature};
+        String authorization = Constant.ALGORITHM + " " + StringUtils.join(Arrays.asList(authorizationHeader), ", ");
+        canonicalHeaders.put("authorization", authorization);
+
+        return canonicalHeaders;
+    }
+
+    /**
+     * 计算 kSigning
+     *
+     * @param iamUserSecret
+     * @param region        区域 us-east-1
+     * @param service       服务 例如:execute-api
+     * @return
+     * @throws UnsupportedEncodingException
+     * @throws IllegalStateException
+     * @throws NoSuchAlgorithmException
+     * @throws InvalidKeyException
+     */
+    private static byte[] calculateKsigning(String iamUserSecret, String region, String service, String reqDate) throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException {
+        String kSecret = "AWS4" + iamUserSecret;
+        byte[] kDate = CommonUtil.hmacSha256(reqDate, kSecret.getBytes());
+        byte[] kRegion = CommonUtil.hmacSha256(region, kDate);
+        byte[] kService = CommonUtil.hmacSha256(service, kRegion);
+        byte[] kSigning = CommonUtil.hmacSha256(Constant.TERMINATIONSTRING, kService);
+        return kSigning;
+    }
+
+
+    /**
+     * 获取请求头
+     *
+     * @param host        请求主机地址, 例如: sellingpartnerapi-na.amazon.com, 根据区域不同地址不同
+     * @param accessToken 用户授权店铺后,短令牌token信息
+     * @param reqDateTime 请求时间,格式,例如: "yyyyMMdd'T'HHmmss'Z'"
+     * @return
+     */
+    private static Map<String, String> canonicalHeaders(String host, String accessToken, String reqDateTime) {
+        Map<String, String> map = new TreeMap<String, String>();
+        map.put("content-type", Constant.CONTENT_TYPE);
+        map.put("host", host);
+        map.put("user-agent", Constant.USER_AGENT);
+        map.put("x-amz-access-token", accessToken);
+        map.put("x-amz-date", reqDateTime);
+        return map;
+    }
+
+    /**
+     * 计算请求Hash
+     *
+     * @param requestMethod        请求方式, 例如:GET, POST
+     * @param canonicalURI         请求URI 地址,例如:/orders/v0/orders
+     * @param canonicalPayload     提交参数, 当为GET请求 参数留空, POST请求 拼为: abc=x1&efg=x2, 或json 格式 {"abc":"x1","efg":"x2"}
+     * @param canonicalQueryString 提交参数,当为GET请求,有传参则填写在这里,例如: abc=x1&efg=x2
+     * @param canonicalHeaderStr
+     * @return
+     * @throws UnsupportedEncodingException
+     * @throws NoSuchAlgorithmException
+     */
+    private static String canonicalRequestHashStr(String requestMethod, String canonicalURI, String canonicalPayload,
+                                                  String canonicalQueryString, String canonicalHeaderStr) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+        String[] canonicalRequest = {requestMethod, canonicalURI, canonicalQueryString, canonicalHeaderStr + "\n",
+                Constant.SIGNEDHEADER, CommonUtil.sha256(canonicalPayload)};
+        String canonicalRequeststr = StringUtils.join(Arrays.asList(canonicalRequest), "\n");
+        return CommonUtil.sha256(canonicalRequeststr);
+    }
+
+
+    /**
+     * 计算Scope
+     *
+     * @param region  区域 us-east-1
+     * @param service 服务 例如:execute-api
+     * @param reqDate
+     * @return
+     */
+    private static String credentialScopeStr(String region, String service, String reqDate) {
+        String[] credentialScope = {reqDate, region, service, Constant.TERMINATIONSTRING};
+        return StringUtils.join(Arrays.asList(credentialScope), "/");
+    }
+
+    /**
+     * 计算 stringtoSing
+     *
+     * @param credentialScopeStr            前一个方法计算出来的字符串
+     * @param requestHashedCanonicalRequest
+     * @param reqDateTime                   请求时间,格式,例如: "yyyyMMdd'T'HHmmss'Z'"
+     * @return
+     */
+    private static String createStrToSignStr(String credentialScopeStr, String requestHashedCanonicalRequest,
+                                             String reqDateTime) {
+        String[] stringToSign = {Constant.ALGORITHM, reqDateTime, credentialScopeStr, requestHashedCanonicalRequest};
+        String stringToSignStr = StringUtils.join(Arrays.asList(stringToSign), "\n");
+        return stringToSignStr;
+    }
+
+
+}

+ 89 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/util/CommonUtil.java

@@ -0,0 +1,89 @@
+package com.raycos.raycosdatacenterapi.amzorder.util;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class CommonUtil {
+	
+	public static String getTimeFormat(Date d,String pattern,boolean utc) {
+		SimpleDateFormat f = new SimpleDateFormat(pattern);
+		if(utc) {
+			f.setTimeZone(TimeZone.getTimeZone("UTC"));
+		}
+		return f.format(d);
+	}
+	
+	public static String getTimeFormat(Date d,String pattern) {
+		return getTimeFormat(d, pattern,false);
+	}
+	
+	public static String getTimeFormat(String pattern) {
+		return getTimeFormat(new Date(), pattern,false);
+	}
+	
+	/**
+	 * hmac256加密
+	 * 
+	 * @param data
+	 * @param key
+	 * @return
+	 * @throws NoSuchAlgorithmException 
+	 * @throws InvalidKeyException 
+	 * @throws UnsupportedEncodingException 
+	 * @throws IllegalStateException 
+	 * @throws Exception
+	 */
+	public static byte[] hmacSha256(String data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException, UnsupportedEncodingException {
+	    String algorithm = "HmacSHA256";
+	    Mac sha256_HMAC = Mac.getInstance(algorithm);
+	    sha256_HMAC.init(new SecretKeySpec(key, algorithm));
+	    return sha256_HMAC.doFinal(data.getBytes("UTF-8"));
+	}
+	
+	/**
+	 * sha256加密
+	 * 
+	 * @param str 要加密的字符串
+	 * @return
+	 * @throws NoSuchAlgorithmException 
+	 * @throws UnsupportedEncodingException 
+	 */
+	public static String sha256(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException{
+		
+		MessageDigest md = MessageDigest.getInstance("SHA-256");
+		md.update(str.getBytes("UTF-8"));
+		return byte2hex(md.digest());
+	}
+	
+	/**
+	 * 字节byte转16进制
+	 * 
+	 * @param digest 要转换的字节
+	 * @return
+	 */
+	public static String byte2hex(byte[] bytes) {
+		StringBuffer sb = new StringBuffer();
+		String temp = null;
+		for (int i = 0; i < bytes.length; i++) {
+			temp = Integer.toHexString(bytes[i] & 0xFF);
+			if (temp.length() == 1) {
+				sb.append("0");
+			}
+			sb.append(temp);
+		}
+		return sb.toString();
+	}
+
+	public static void main(String[] args) {
+		SimpleDateFormat f = new SimpleDateFormat("yyyyMMdd");
+		f.setTimeZone(TimeZone.getTimeZone("UTC"));
+		System.out.println(f.format(new Date()));
+	}
+}

+ 13 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/amzorder/util/Constant.java

@@ -0,0 +1,13 @@
+package com.raycos.raycosdatacenterapi.amzorder.util;
+
+public class Constant {
+    public static final String IAMUSERKEY = "AKIAQEIP3EPRYQUHPOEC";
+    public static final String IAMUSERSECRET = "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";
+    public static String TERMINATIONSTRING = "aws4_request";
+    public static String ALGORITHM = "AWS4-HMAC-SHA256";
+    public static String SERVICE = "execute-api";
+    public static String SIGNEDHEADER = "content-type;host;user-agent;x-amz-access-token;x-amz-date";
+    public static String CONTENT_TYPE = "application/json;charset=utf-8";
+    public static String USER_AGENT = "Swagger-Codegen/1.0.0/java";
+
+}

+ 101 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/AmzTokenController.java

@@ -0,0 +1,101 @@
+package com.raycos.raycosdatacenterapi.authorize;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzToken;
+import com.raycos.raycoscommon.Internal.datacenterapi.vo.GetAmzTokenByLwaCodeVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.Assert;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 亚马逊令牌 服务接口
+ */
+@RestController
+@Slf4j
+@RequestMapping("/erp/amztoken")
+public class AmzTokenController {
+
+    @Autowired
+    private AmzTokenService amzTokenService;
+
+    @GetMapping
+    public Result<List<TbAmzToken>> selToken() {
+        Result<List<TbAmzToken>> tr = new Result<>();
+        try {
+            List<TbAmzToken> list = amzTokenService.selAmzToken(null);
+            tr.success(list);
+        } catch (Exception e) {
+            tr.fail();
+        }
+        return tr;
+    }
+
+    /**
+     * 新增 amz令牌
+     *
+     * @param tbAmzToken
+     * @return
+     */
+    @PostMapping
+    public Result<String> addToken(@RequestBody TbAmzToken tbAmzToken) {
+        Result<String> rt = new Result<>();
+        try {
+            log.info("  新增 token   来了老弟  " + JSONObject.toJSONString(tbAmzToken));
+            amzTokenService.addAmzToken(tbAmzToken);
+            rt.success();
+        } catch (Exception e) {
+            log.error(" 新增token 异常 ", e);
+            rt.fail();
+        }
+        return rt;
+
+    }
+
+    /**
+     * LWA code 获取亚马逊令牌
+     *
+     * @return
+     */
+    @PostMapping("/getamztokenbylwacode")
+    public Result<String> getAmzTokenByLwaCode(@RequestBody GetAmzTokenByLwaCodeVo params) {
+        Result<String> rt = new Result<>();
+        try {
+            Assert.hasLength(params.getLwaCode() , " getLwaCode is Empty 请检查提交参数 !");
+            Assert.notNull(params.getStoreId() , " getStoreId is Empty 请检查提交参数 !");
+            Assert.notNull(params.getCompanyId() , " getCompanyId is Empty 请检查提交参数 !");
+            Assert.hasLength(params.getSellingPartnerId() , " getSellingPartnerId is Empty 请检查提交参数 !");
+
+            rt = amzTokenService.getAmzTokenByLwaCode( params);
+
+        } catch (Exception e) {
+            rt.fail();
+            rt.setMsg( e.getMessage());
+            log.error(" /getamztokenbylwacode 异常 ", e);
+        }
+        return rt;
+    }
+
+    /**
+     * 刷新 LWA  令牌
+     *
+     * @return
+     */
+    @GetMapping("/lrt")
+    public Result<String> lwaRefreshToken(@RequestBody TbAmzToken tbAmzToken) {
+        Result<String> rt = new Result<>();
+        try {
+            amzTokenService.lwaRefreshToken(tbAmzToken);
+            rt.success();
+        } catch (Exception e) {
+            rt.fail();
+            log.error(" 新增token 异常 ", e);
+        }
+        return rt;
+    }
+
+
+}

+ 32 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/AmzTokenService.java

@@ -0,0 +1,32 @@
+package com.raycos.raycosdatacenterapi.authorize;
+
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzToken;
+import com.raycos.raycoscommon.Internal.datacenterapi.vo.GetAmzTokenByLwaCodeVo;
+
+import java.util.List;
+
+public interface AmzTokenService {
+
+    void  addAmzToken (TbAmzToken tbAmzToken)throws Exception;
+
+    void  delAmzToken (TbAmzToken tbAmzToken)throws Exception;
+
+    void  editAmzToken (TbAmzToken tbAmzToken)throws Exception;
+
+    List<TbAmzToken> selAmzToken (TbAmzToken tbAmzToken)throws Exception;
+
+    TbAmzToken selAmzTokenById (TbAmzToken tbAmzToken)throws Exception;
+
+
+    Result<String>  lwaRefreshToken(TbAmzToken tbAmzToken  ) throws Exception;
+
+    /**
+     * 使用LWA code 获取亚马逊令牌
+     *
+     * @return
+     */
+    Result<String> getAmzTokenByLwaCode (GetAmzTokenByLwaCodeVo  gatblcVo )throws Exception;
+
+
+}

+ 85 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/StoreInfoController.java

@@ -0,0 +1,85 @@
+package com.raycos.raycosdatacenterapi.authorize;
+
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbStoreInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ *   亚马逊店铺 服务接口
+ *
+ */
+@RestController
+@Slf4j
+@RequestMapping("/erp/amzstore")
+public class StoreInfoController {
+
+
+    @Autowired
+    private StoreInfoService storeInfoService;
+
+
+    /**
+     * 新增店铺信息
+     *
+     * @param tbStoreInfo
+     * @return
+     */
+    @PostMapping("/add")
+    public Result<Long> addStoreInfo(@RequestBody TbStoreInfo tbStoreInfo) {
+        Result<Long> rt = new Result<>();
+        try {
+            TbStoreInfo tsi = storeInfoService.addStoreInfo(tbStoreInfo);
+            log.info(" 新增店铺信息 成功 返回 ID =>  " + tsi.getId());
+            rt.success(tsi.getId());
+            rt.setMsg("保存成功!");
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            rt.fail();
+        }
+        return rt;
+    }
+
+
+    /**
+     * 获取访问令牌
+     *
+     * @return
+     */
+    public Result<String> getAccessToken() {
+        Result<String> rt = new Result<>();
+
+        try {
+
+            rt.success();
+        } catch (Exception e) {
+            rt.fail();
+            log.error("   异常 ", e);
+        }
+        return rt;
+    }
+
+    /**
+     * 查询全部店铺信息
+     *
+     * @return
+     */
+    @GetMapping("/sel")
+    public Result<List<TbStoreInfo>> selStoreInfo() {
+        Result<List<TbStoreInfo>> rt = new Result<>();
+        try {
+            List<TbStoreInfo> tbStoreInfos = storeInfoService.selStoreInfo(null);
+            rt.success(tbStoreInfos);
+            rt.setMsg("查询成功!");
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            rt.fail();
+        }
+        return rt;
+    }
+
+
+}

+ 17 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/StoreInfoService.java

@@ -0,0 +1,17 @@
+package com.raycos.raycosdatacenterapi.authorize;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbStoreInfo;
+
+import java.util.List;
+
+public interface StoreInfoService {
+    TbStoreInfo  addStoreInfo (TbStoreInfo tbStoreInfo)throws Exception;
+
+    void  delStoreInfo (TbStoreInfo tbStoreInfo)throws Exception;
+
+    void  editStoreInfo (TbStoreInfo tbStoreInfo)throws Exception;
+
+    List<TbStoreInfo> selStoreInfo (TbStoreInfo tbStoreInfo)throws Exception;
+
+
+}

+ 232 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/impl/AmzTokenServiceImpl.java

@@ -0,0 +1,232 @@
+package com.raycos.raycosdatacenterapi.authorize.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzToken;
+import com.raycos.raycoscommon.Internal.datacenterapi.vo.GetAmzTokenByLwaCodeVo;
+import com.raycos.raycoscommon.json.TokenAmz;
+import com.raycos.raycoscommon.json.TokenRetAmzJson;
+import com.raycos.raycosdatacenterapi.authorize.AmzTokenService;
+import com.raycos.raycosdatacenterapi.mapper.TbAmzTokenMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.time.Instant;
+import java.util.List;
+
+
+@Service
+@Slf4j
+public class AmzTokenServiceImpl implements AmzTokenService {
+
+    /**
+     * 参考文档:https://developer-docs.amazon.com/sp-api/docs/website-authorization-workflow
+     * 文档: 步骤 5:应用程序使用 LWA 授权码生成 LWA 刷新令牌
+     * 5.1  -> grant_type	请求的访问授权的类型。必须是 。authorization_code
+     */
+    private final String AUTHORIZATION_CODE = "authorization_code";
+    /**
+     *  参考文档: https://developer-docs.amazon.com/sp-api/docs/connecting-to-the-selling-partner-api
+     *  文档: 步骤 1.申请 Login with Amazon 访问令牌
+     *  refresh_token.将此函数用于需要销售合作伙伴授权的调用操作。所有非无授权操作都需要销售伙伴的授权。指定此值时,请包括rrefresh_token参数。
+     */
+    private final String  REFRESH_TOKEN = "refresh_token";
+    /**
+     * 参考文档:https://developer-docs.amazon.com/sp-api/docs/website-authorization-workflow
+     * 文档: 步骤 5:应用程序使用 LWA 授权码生成 LWA 刷新令牌
+     * 5.2  -> token_type	返回的 token 类型。应该是 bearer。
+     */
+    private final String BEARER = "bearer";
+    /**
+     * 参考文档:https://developer-docs.amazon.com/sp-api/docs/website-authorization-workflow
+     * 文档: 步骤 5:应用程序使用 LWA 授权码生成 LWA 刷新令牌
+     * LWA CODE 调用接口 返回令牌信息
+     */
+    private final String URL = "https://api.amazon.com/auth/o2/token";
+    /**
+     * 亚马逊授权成功 重定向浏览器页面 并且通过URL 携带
+     * LWA code 用于生成亚马逊令牌
+     */
+    private final String REDIRECTURI = "https://www.raycos.net/erp/authorize";
+    @Autowired
+    private TbAmzTokenMapper tbAmzTokenMapper;
+
+    @Resource(name = "ExternalNetwork")
+    private RestTemplate restTemplate;
+
+    /**
+     * 亚马逊刷新令牌接口
+     */
+    private String url = "https://api.amazon.com/auth/o2/token";
+
+
+    private String getRequestBody(GetAmzTokenByLwaCodeVo gatblcVo) {
+        TokenAmz amz = new TokenAmz();
+        amz.setGrantType(AUTHORIZATION_CODE);
+        amz.setCode(gatblcVo.getLwaCode());
+        amz.setRedirectUri(REDIRECTURI);
+        amz.setClientId("amzn1.application-oa2-client.2d001c4aeaaf438f98ee0b07070c1cf8");
+        amz.setClientSecret("amzn1.oa2-cs.v1.52b13f4ce04e83046b3426b620b6bfb94fd3ce61c815c6f68858af69d0ccbe54");
+        return JSONObject.toJSONString(amz);
+    }
+
+    @Override
+    public void addAmzToken(TbAmzToken tbAmzToken) throws Exception {
+        tbAmzTokenMapper.insert(tbAmzToken);
+    }
+
+    @Override
+    public void delAmzToken(TbAmzToken tbAmzToken) throws Exception {
+        Assert.notNull(tbAmzToken.getId(), "请检查提交参数!");
+        tbAmzTokenMapper.delAmzToken(tbAmzToken.getId());
+    }
+
+    @Override
+    public void editAmzToken(TbAmzToken tbAmzToken) throws Exception {
+
+    }
+
+    @Override
+    public List<TbAmzToken> selAmzToken(TbAmzToken tbAmzToken) throws Exception {
+        return tbAmzTokenMapper.selectList(null);
+    }
+
+    @Override
+    public TbAmzToken selAmzTokenById(TbAmzToken tbAmzToken) throws Exception {
+        return tbAmzTokenMapper.selectById(tbAmzToken.getId());
+    }
+
+
+    /**
+     *  刷新亚马逊 访问令牌过期时间
+     *
+     * @throws Exception
+     */
+    @Override
+    public Result<String>  lwaRefreshToken(TbAmzToken tat) throws Exception {
+        Assert.hasLength(tat.getRefreshToken(), "请检查提交参数!  getRefreshToken  is empty  ");
+        Result<String> rt = new Result<>();
+        // 设置请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+
+        JSONObject params = new JSONObject();
+        params.put("grant_type", REFRESH_TOKEN);
+        params.put("refresh_token", tat.getRefreshToken());
+        params.put("client_id", "amzn1.application-oa2-client.2d001c4aeaaf438f98ee0b07070c1cf8");
+        params.put("client_secret", "amzn1.oa2-cs.v1.52b13f4ce04e83046b3426b620b6bfb94fd3ce61c815c6f68858af69d0ccbe54");
+
+        // 设置请求体
+        String requestBody = params.toString();
+        // 创建请求实体
+        HttpEntity<String> request = new HttpEntity<>(requestBody, headers);
+        // 发送 POST 请求
+        String response = restTemplate.postForObject(URL, request, String.class);
+        log.info("企业编码=>"+tat.getCompanyId()+" 调用亚马逊" + URL + " 接口刷新令牌信息: 入参数据=> " + params);
+        log.info("企业编码=>"+tat.getCompanyId()+" 调用亚马逊" + URL + " 接口刷新令牌信息: 接口返回=> " + response);
+        try {
+            // 数据校验
+            TokenRetAmzJson tokenJson = JSONObject.parseObject(response, TokenRetAmzJson.class);
+            Assert.notNull(tokenJson, " TokenRetAmzJson is null : 接口返回数据为空  ");
+            Assert.hasLength(tokenJson.getTokenType(), "getTokenType is empty :LWA 授权码 刷新令牌失败   接口返回=>" + response);
+            Assert.isTrue(BEARER.equals(tokenJson.getTokenType()), "getTokenType 类型 不等于 bearer :LWA 授权码 刷新令牌失败   接口返回=>" + response);
+            // 获取当前时间戳
+            long second = Instant.now().getEpochSecond();
+            // 计算过期时间  *0.8 让令牌过期时间提前  留给让刷令牌操作时间
+            second = (long) (second+tokenJson.getExpiresIn() * 0.8);
+
+            // 组织更新 数据
+            TbAmzToken tbAmzToken = new TbAmzToken();
+            tbAmzToken.setId(tat.getId());
+            tbAmzToken.setCompanyId(tat.getCompanyId());
+
+            tbAmzToken.setAccessToken(tokenJson.getAccessToken());
+            tbAmzToken.setTokenType(tokenJson.getTokenType());
+            tbAmzToken.setExpiresIn(second);
+            tbAmzToken.setRefreshToken(tokenJson.getRefreshToken());
+
+            //执行更新
+            tbAmzTokenMapper.updateAccessToken( tbAmzToken );
+
+            rt.success();
+            rt.setMsg("刷新令牌成功!");
+        } catch (Exception e) {
+            log.error("刷新令牌失败! 企业编号=>" + tat.getCompanyId() + "  接口入参=>" + JSONObject.toJSONString(tat));
+            log.error("刷新令牌失败! 企业编号=>" + tat.getCompanyId() + "  接口返回=>" + JSONObject.toJSONString(response));
+            log.error("刷新令牌失败! 企业编号=>" + tat.getCompanyId() + "  异常信息=>" + e.getMessage(), e);
+            rt.fail();
+            rt.setMsg("刷新令牌失败!");
+        }
+        return rt;
+    }
+
+    /**
+     * 使用LWA code 获取亚马逊令牌
+     *
+     * @return
+     */
+    @Override
+    public Result<String> getAmzTokenByLwaCode(GetAmzTokenByLwaCodeVo gatblcVo) throws Exception {
+        Result<String> rt = new Result<>();
+        // 设置请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        //设置请求体
+        String params = this.getRequestBody(gatblcVo);
+        // 创建请求实体
+        HttpEntity<String> request = new HttpEntity<>(params, headers);
+        // 发送 POST 请求
+        String response = restTemplate.postForObject(URL, request, String.class);
+
+        log.info("企业编码=>"+gatblcVo.getCompanyId()+" 调用亚马逊" + URL + " 接口获取令牌信息: 入参数据=> " + params);
+        log.info("企业编码=>"+gatblcVo.getCompanyId()+" 调用亚马逊" + URL + " 接口获取令牌信息: 接口返回=> " + response);
+
+        try {
+            // 数据校验
+            TokenRetAmzJson tokenJson = JSONObject.parseObject(response, TokenRetAmzJson.class);
+            Assert.notNull(tokenJson, " TokenRetAmzJson is null : 接口返回数据为空  ");
+            Assert.hasLength(tokenJson.getTokenType(), "getTokenType is empty :LWA 授权码 刷新令牌失败   接口返回=>" + response);
+            Assert.isTrue(BEARER.equals(tokenJson.getTokenType()), "getTokenType 类型 不等于 bearer :LWA 授权码 刷新令牌失败   接口返回=>" + response);
+
+            // 获取当前时间戳
+            long second = Instant.now().getEpochSecond();
+            // 计算过期时间  *0.8 让令牌过期时间提前  留给让刷令牌操作时间
+            second = (long) (second+tokenJson.getExpiresIn() * 0.8);
+
+
+            // 组织保存数据
+            TbAmzToken tbAmzToken = new TbAmzToken();
+            tbAmzToken.setCompanyId(gatblcVo.getCompanyId());
+            tbAmzToken.setStoreId(gatblcVo.getStoreId());
+            tbAmzToken.setMarketId(gatblcVo.getSellingPartnerId());
+
+            tbAmzToken.setAccessToken(tokenJson.getAccessToken());
+            tbAmzToken.setTokenType(tokenJson.getTokenType());
+            tbAmzToken.setExpiresIn(second);
+            tbAmzToken.setRefreshToken(tokenJson.getRefreshToken());
+
+            //保存令牌 信息
+            this.addAmzToken(tbAmzToken);
+            rt.success();
+            rt.setMsg("LWA CODE 获取令牌成功!");
+        } catch (Exception e) {
+            log.error(" LWA CODE 获取令牌失败! 企业编号=>" + gatblcVo.getCompanyId() + "  接口入参=>" + JSONObject.toJSONString(gatblcVo));
+            log.error(" LWA CODE 获取令牌失败! 企业编号=>" + gatblcVo.getCompanyId() + "  接口返回=>" + JSONObject.toJSONString(response));
+            log.error(" LWA CODE 获取令牌失败! 企业编号=>" + gatblcVo.getCompanyId() + "  异常信息=>" + e.getMessage(), e);
+            rt.fail();
+            rt.setMsg("LWA CODE 获取令牌失败!");
+        }
+        return rt;
+    }
+
+
+
+
+}

+ 39 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/authorize/impl/StoreInfoServiceImpl.java

@@ -0,0 +1,39 @@
+package com.raycos.raycosdatacenterapi.authorize.impl;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbStoreInfo;
+import com.raycos.raycosdatacenterapi.authorize.StoreInfoService;
+import com.raycos.raycosdatacenterapi.mapper.TbStoreInfoMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@Slf4j
+public class StoreInfoServiceImpl implements StoreInfoService {
+
+    @Autowired
+    private TbStoreInfoMapper tbStoreInfoMapper;
+
+    @Override
+    public TbStoreInfo addStoreInfo(TbStoreInfo tbStoreInfo) throws Exception {
+        tbStoreInfoMapper.insert( tbStoreInfo);
+        return tbStoreInfo;
+    }
+
+    @Override
+    public void delStoreInfo(TbStoreInfo tbStoreInfo) throws Exception {
+
+    }
+
+    @Override
+    public void editStoreInfo(TbStoreInfo tbStoreInfo) throws Exception {
+
+    }
+
+    @Override
+    public List<TbStoreInfo> selStoreInfo(TbStoreInfo tbStoreInfo) throws Exception {
+        return tbStoreInfoMapper.selectList(null);
+    }
+}

+ 10 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/common/databaselock/LockService.java

@@ -0,0 +1,10 @@
+package com.raycos.raycosdatacenterapi.common.databaselock;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.Lock;
+
+public interface LockService {
+
+    boolean lock(Lock lock);
+
+    boolean unlock(Lock lock);
+}

+ 62 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/common/databaselock/impl/LockServiceImpl.java

@@ -0,0 +1,62 @@
+package com.raycos.raycosdatacenterapi.common.databaselock.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.raycos.raycoscommon.Internal.datacenterapi.Lock;
+import com.raycos.raycosdatacenterapi.common.databaselock.LockService;
+import com.raycos.raycosdatacenterapi.mapper.LockMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+
+@Slf4j
+@Service
+public class LockServiceImpl implements LockService {
+
+
+    @Autowired
+    private LockMapper lockMapper;
+
+
+    @Override
+    public boolean lock(Lock lock) {
+        try {
+            lockMapper.insert(lock);
+        } catch (Exception e) {
+            log.error(" LockService 加锁异常 lock信息=> " + JSONObject.toJSONString(lock), e);
+            return false;
+        }
+        return true;
+    }
+
+
+    public void lockInterruptibly() throws InterruptedException {
+
+    }
+
+
+    public boolean tryLock() {
+        return false;
+    }
+
+
+    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
+        return false;
+    }
+
+    @Override
+    public boolean unlock(Lock lock) {
+        try {
+            lockMapper.deleteById(lock);
+        } catch (Exception e) {
+            log.error(" LockService 解锁异常  lock信息=> " + JSONObject.toJSONString(lock), e);
+            return false;
+        }
+        return true;
+
+    }
+
+
+}

+ 26 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/MybatisPlusConfig.java

@@ -0,0 +1,26 @@
+package com.raycos.raycosdatacenterapi.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MybatisPlusConfig {
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+        return interceptor;
+    }
+
+    @Bean
+    public GlobalConfig globalConfig() {
+        GlobalConfig globalConfig = new GlobalConfig();
+        globalConfig.setDbConfig(new GlobalConfig.DbConfig().setIdType(IdType.INPUT));
+        return globalConfig;
+    }
+}

+ 34 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/RabbitMQConfig.java

@@ -0,0 +1,34 @@
+package com.raycos.raycosdatacenterapi.config;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.TopicExchange;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+/**
+ * 创建一个 RabbitMQ 配置类来定义队列、交换机和绑定关系:
+ */
+@Configuration
+public class RabbitMQConfig {
+    public static final String AMZ_PULL_ORDER_QUEUE = "AMZ_PULL_ORDER_QUEUE";
+    public static final String AMZ_PULL_ORDER_TOPICEXCHANGE = "AMZ_PULL_ORDER_TOPICEXCHANGE";
+    public static final String AMZ_PULL_ORDER_ROUTINGKEY = "AMZ_PULL_ORDER_ROUTINGKEY";
+
+    @Bean
+    public Queue myQueue() {
+        return new Queue(AMZ_PULL_ORDER_QUEUE, false);
+    }
+
+    @Bean
+    public TopicExchange exchange() {
+        return new TopicExchange(AMZ_PULL_ORDER_TOPICEXCHANGE);
+    }
+
+    @Bean
+    public Binding binding(Queue queue, TopicExchange exchange) {
+        return BindingBuilder.bind(queue).to(exchange).with(AMZ_PULL_ORDER_ROUTINGKEY);
+    }
+}

+ 28 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/RestTemplateConfig.java

@@ -0,0 +1,28 @@
+package com.raycos.raycosdatacenterapi.config;
+
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/9/2 16:34
+ */
+
+@Configuration
+public class RestTemplateConfig {
+
+    @Bean
+    @LoadBalanced
+    public RestTemplate restTemplate() {
+        System.out.println("restTemplate nacos 服务间的调用 ");
+        return new RestTemplate();
+    }
+
+    @Bean("ExternalNetwork")
+    public RestTemplate ExternalNetwork() {
+        System.out.println("ExternalNetwork  对外发送请求 ");
+        return new RestTemplate();
+    }
+}

+ 32 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/attach/AmzPullOrderConsumer.java

@@ -0,0 +1,32 @@
+package com.raycos.raycosdatacenterapi.config.attach;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+import com.raycos.raycosdatacenterapi.amzorder.AmzPullOrderService;
+import com.raycos.raycosdatacenterapi.config.RabbitMQConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+@Slf4j
+@Component
+public class AmzPullOrderConsumer {
+
+    @Autowired
+    private AmzPullOrderService amzPullOrderService;
+
+    @RabbitListener(queues = RabbitMQConfig.AMZ_PULL_ORDER_QUEUE)
+    public void receiveMessage(String message) {
+        log.info(" 来了老弟  Received message: " + message);
+        try {
+            TbAmzPullOrderTask task = JSONObject.parseObject(message, TbAmzPullOrderTask.class);
+            amzPullOrderService.sendGetOrders( task );
+
+        } catch (Exception e) {
+             log.error( "亚马逊拉单消费 异常 " , e);
+        }
+
+    }
+}

+ 37 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/config/attach/SendAmzPullOrderMsg.java

@@ -0,0 +1,37 @@
+package com.raycos.raycosdatacenterapi.config.attach;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+import com.raycos.raycosdatacenterapi.config.RabbitMQConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.AmqpException;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * 发送亚马逊拉单  MQ任务信息
+ */
+@Service
+@Slf4j
+public class SendAmzPullOrderMsg {
+    @Autowired
+    private RabbitTemplate rabbitTemplate;
+
+    /**
+     * 消息发送
+     * 消息发送失败
+     *
+     * @param task
+     */
+    public void sendMessage(TbAmzPullOrderTask task) {
+        String message = JSONObject.toJSONString(task);
+        try {
+            rabbitTemplate.convertAndSend(RabbitMQConfig.AMZ_PULL_ORDER_TOPICEXCHANGE, RabbitMQConfig.AMZ_PULL_ORDER_ROUTINGKEY, message);
+        } catch (AmqpException e) {
+            log.info("亚马逊拉单任务发送MQ信息失败=>  " + message);
+        }
+
+    }
+}

+ 7 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/AmzOrderMapper.java

@@ -0,0 +1,7 @@
+package com.raycos.raycosdatacenterapi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzOrder;
+
+public interface AmzOrderMapper extends BaseMapper<TbAmzOrder> {
+}

+ 7 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/AmzOrderOriginalMapper.java

@@ -0,0 +1,7 @@
+package com.raycos.raycosdatacenterapi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzOrderOriginal;
+
+public interface AmzOrderOriginalMapper extends BaseMapper<TbAmzOrderOriginal> {
+}

+ 21 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/AmzPullOrderTaskMapper.java

@@ -0,0 +1,21 @@
+package com.raycos.raycosdatacenterapi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+public interface AmzPullOrderTaskMapper extends BaseMapper<TbAmzPullOrderTask> {
+
+
+    @Select("   select  id,company_id,store_id,last_operation_time,retry_count  \n" +
+            "   from  tb_amz_pull_order_task                                    \n" +
+            "   where   last_operation_time < #{lastOperationTime} and          \n" +
+            "   retry_count = #{retryCount}  ")
+    List<TbAmzPullOrderTask> findPullTask(@Param("lastOperationTime") Long lastOperationTime,
+                                          @Param("retryCount") Byte retryCount);
+
+
+}

+ 9 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/LockMapper.java

@@ -0,0 +1,9 @@
+package com.raycos.raycosdatacenterapi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.raycos.raycoscommon.Internal.datacenterapi.Lock;
+
+public interface LockMapper extends BaseMapper<Lock> {
+
+
+}

+ 20 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/TbAmzTokenMapper.java

@@ -0,0 +1,20 @@
+package com.raycos.raycosdatacenterapi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzToken;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
+
+public interface TbAmzTokenMapper extends BaseMapper<TbAmzToken> {
+    @Delete("DELETE FROM  tb_amz_token  WHERE id = #{id}")
+    int delAmzToken(@Param("id") Long id );
+
+    @Update("UPDATE tb_amz_token " +
+            "SET access_token = #{accessToken}, " +
+            "token_type = #{tokenType} ," +
+            "expires_in =#{expiresIn } , " +
+            "refresh_token =#{refreshToken} " +
+            "WHERE id = #{id}")
+    int  updateAccessToken( TbAmzToken  tbAmzToken);
+}

+ 7 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/mapper/TbStoreInfoMapper.java

@@ -0,0 +1,7 @@
+package com.raycos.raycosdatacenterapi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbStoreInfo;
+
+public interface TbStoreInfoMapper  extends BaseMapper<TbStoreInfo> {
+}

+ 33 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/nacostest/EchoController.java

@@ -0,0 +1,33 @@
+package com.raycos.raycosdatacenterapi.nacostest;
+
+import com.raycos.raycoscommon.Internal.Result;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+public class EchoController {
+
+
+//    @GetMapping(value = "/echo/{string}")
+//    public Result<String> echo(@PathVariable String string) {
+//        Result<String> result = new Result<>();
+//        result.setCode(200);
+//        result.setMsg("来了老弟" + string);
+//
+//        return result;
+//    }
+    @GetMapping(value = "/echo/{string}")
+    public String echo(@PathVariable String string) {
+
+        return "来了老弟"+string;
+    }
+    @PostMapping("/sayHello")
+    public Result<String> hello(@RequestParam String name) {
+        Result<String> result = new Result<>();
+        System.out.println("  hello  " + name);
+        result.setCode(200);
+        result.setMsg("hello 来了老弟 "+ name );
+        return result;
+    }
+
+
+}

+ 71 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/scheduledtasks/RefreshAmazonToken.java

@@ -0,0 +1,71 @@
+package com.raycos.raycosdatacenterapi.scheduledtasks;
+
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzToken;
+import com.raycos.raycosdatacenterapi.authorize.AmzTokenService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/9/2 16:12
+ */
+
+@Component
+@Slf4j
+public class RefreshAmazonToken {
+
+    /**
+     * {
+     * "access_token": "Atza|IwEBIBfvDi8BGuMZaWHUiClkd6mr9gucUJlIyf4yFjxpao9cfC4jDZXoud2G3ktVcMz6bYw2aweBLUu1E3otBYSaFbY7mJjqKGanMIW0EQX2a-RHPjpx_zkdkv719DmvZquQDgRCXywCogNG81usXd-ZwFYBQ96XjcYPaTC8w4WNNdJ4JHAo02BHRzLCV7I5E-S2u6PPoeM8lv-k7QXDvQhw7S1X2ldI2CVN0QYzMTDUqQ8M7Sn-UcaUJX_CD1AgN1HbsnJZlYLcC1FVmN5rj0tNzqA0s7Usc3be7RPVtFzBa58h75H7QnETHuhn_VordCETb7g",
+     * "refresh_token": "Atzr|IwEBIITKfZoBv6fJBrBBG1tJHhgCxqePUah2uQpaUaCRgMpzYowNrpO1fQZqJuH-YxtXue5E6-0fWlQWKr9BfxuyfnCejJtaX3jxKo0TdN6l6woAv8awQrhLJuwO-KN3kDrdiB3VKyEDy-R6V0pKz7b9w0gzTN-PEL-e7uA-5CaRLOwV_299HF4ZTsz5nrfZIQMZ1S0Rgbs3TNHhw48OAlLOIXlwt95mPL7gMLvy6CJf-u5_vwh38sCQLu472ViwGGr936lGN38QSBaXaUf6mKt9LT_12C5x68nBGNsi1ZhzdMGDLJZcVHiq5CSXzL-HSa1G2VDv-0ej1YupcjBefCFBGnIS",
+     * "token_type": "bearer",
+     * "expires_in": 3600
+     * }
+     */
+    private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
+
+    @Autowired
+    private AmzTokenService amzTokenService;
+    @Scheduled(fixedRate = 5000)
+    public void reportCurrentTime() {
+
+        log.info("现在时间是:" + dateTimeFormatter.format(LocalDateTime.now()));
+
+        try{
+            // 查询 令牌
+            List<TbAmzToken> list = amzTokenService.selAmzToken(null);
+            if(CollectionUtils.isEmpty(list)){
+                log.info(" 没有查询到 需要 刷新的 amz token 稍后再试!     ");
+                return;
+            }
+            // 获取当前时间戳
+            long second = Instant.now().getEpochSecond();
+
+            // 检查是否临近过期时间
+            for (TbAmzToken tbAmzToken : list) {
+
+                Long expiresIn = tbAmzToken.getExpiresIn();
+                if(  second >=  expiresIn ){
+                    RefreshTask.queue.put( tbAmzToken );
+                }
+
+            }
+        }catch (Exception e){
+            log.error( e.getMessage() , e );
+        }
+
+
+    }
+
+
+
+
+}

+ 102 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/scheduledtasks/RefreshTask.java

@@ -0,0 +1,102 @@
+package com.raycos.raycosdatacenterapi.scheduledtasks;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzToken;
+import com.raycos.raycosdatacenterapi.authorize.AmzTokenService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedTransferQueue;
+import java.util.concurrent.locks.ReentrantLock;
+
+@Component
+@Slf4j
+public class RefreshTask {
+
+    public static LinkedTransferQueue<TbAmzToken> queue = new LinkedTransferQueue<>();
+    private final ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
+
+    @Autowired
+    private AmzTokenService amzTokenService;
+
+
+
+    public static final ReentrantLock lock = new ReentrantLock();
+
+
+    @Scheduled(fixedRate = 5000)
+    public void runTask() {
+        try {
+
+            log.info(" 调用用亚马逊 刷新令牌接口    ");
+            if (queue.isEmpty()) {
+                log.info(" 暂时没有需要刷新的LWA 令牌    ");
+                return;
+            }
+            if (lock.isLocked()) {
+                log.info(" 执行中...    ");
+                return;
+            }
+            lock.lock();
+            Runnable runnable;
+            while (!queue.isEmpty()) {
+
+                // 实现线程
+                runnable = new Runnable() {
+                    @Override
+                    public void run() {
+                        TbAmzToken poll = queue.poll();
+                        try {
+                            amzTokenService.lwaRefreshToken(  poll );
+                        } catch (Exception e) {
+                            log.error(" 亚马逊刷新令牌程序出错   异常参数=>" + JSONObject.toJSONString(poll) , e);
+                        }
+
+                    }
+                };
+                singleThreadPool.execute(runnable);
+                // 每个发出一个刷新请求 睡眠300毫秒 避免发送太快 超出亚马逊 每秒最大5个请求的限制
+                Thread.sleep(300);
+
+            }
+
+
+
+
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            try{
+                lock.unlock();
+            }catch (Exception ee){
+
+            }
+        }
+
+    }
+
+
+
+
+
+
+    /*
+    {
+    "grant_type": "refresh_token",
+    "refresh_token": "Atzr|IwEBIITKfZoBv6fJBrBBG1tJHhgCxqePUah2uQpaUaCRgMpzYowNrpO1fQZqJuH-YxtXue5E6-0fWlQWKr9BfxuyfnCejJtaX3jxKo0TdN6l6woAv8awQrhLJuwO-KN3kDrdiB3VKyEDy-R6V0pKz7b9w0gzTN-PEL-e7uA-5CaRLOwV_299HF4ZTsz5nrfZIQMZ1S0Rgbs3TNHhw48OAlLOIXlwt95mPL7gMLvy6CJf-u5_vwh38sCQLu472ViwGGr936lGN38QSBaXaUf6mKt9LT_12C5x68nBGNsi1ZhzdMGDLJZcVHiq5CSXzL-HSa1G2VDv-0ej1YupcjBefCFBGnIS",
+    "client_id": "amzn1.application-oa2-client.2d001c4aeaaf438f98ee0b07070c1cf8",
+    "client_secret": "amzn1.oa2-cs.v1.52b13f4ce04e83046b3426b620b6bfb94fd3ce61c815c6f68858af69d0ccbe54"
+}
+     */
+    /*
+    {
+    "access_token": "Atza|IwEBIBNp7J72AS5WkMjUq5LrhVMKk-dc9cbqoYnetxxTClYxYQb75c9x8R__m156V3fZj3UifdVq0VHsMINDGAnfXPKloDtx05sIl2EQ0Znetz8283UsCuObApHz-JY6wD2g_sZNcENHNZS_FLAB1nbWxI-c8teMv5W4ryBOlb4pMsQymSbqDfTtaPeq4RZ98vt3hzq6Zep6b7E2dyNXdcNLqbO8TI1q7SXy2-NPzGisq9CwxreVqeP9DZb91uSZs060Ofu5NneABx3SyUMa-yPWPJZ7Wqys-V472un5v9s_b--Bibz8mqWqm6OI4SV7aprOPaI_vtM92L55bxoLnzsfCNxp",
+    "refresh_token": "Atzr|IwEBIITKfZoBv6fJBrBBG1tJHhgCxqePUah2uQpaUaCRgMpzYowNrpO1fQZqJuH-YxtXue5E6-0fWlQWKr9BfxuyfnCejJtaX3jxKo0TdN6l6woAv8awQrhLJuwO-KN3kDrdiB3VKyEDy-R6V0pKz7b9w0gzTN-PEL-e7uA-5CaRLOwV_299HF4ZTsz5nrfZIQMZ1S0Rgbs3TNHhw48OAlLOIXlwt95mPL7gMLvy6CJf-u5_vwh38sCQLu472ViwGGr936lGN38QSBaXaUf6mKt9LT_12C5x68nBGNsi1ZhzdMGDLJZcVHiq5CSXzL-HSa1G2VDv-0ej1YupcjBefCFBGnIS",
+    "token_type": "bearer",
+    "expires_in": 3600
+     */
+}

+ 113 - 0
raycos_datacenter_api/src/main/java/com/raycos/raycosdatacenterapi/scheduledtasks/SearchOrderPullTask.java

@@ -0,0 +1,113 @@
+package com.raycos.raycosdatacenterapi.scheduledtasks;
+
+
+import com.raycos.raycoscommon.Internal.datacenterapi.Lock;
+import com.raycos.raycoscommon.Internal.datacenterapi.TbAmzPullOrderTask;
+import com.raycos.raycosdatacenterapi.amzorder.AmzPullOrderTaskService;
+import com.raycos.raycosdatacenterapi.common.databaselock.LockService;
+import com.raycos.raycosdatacenterapi.config.attach.SendAmzPullOrderMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.util.List;
+
+@Component
+@Slf4j
+public class SearchOrderPullTask {
+
+
+    @Autowired
+    private SendAmzPullOrderMsg sendAmzPullOrderMsg;
+    @Autowired
+    private LockService lockService;
+    @Autowired
+    private AmzPullOrderTaskService amzPullOrderTaskService;
+
+    /**
+     * 数据库锁key后缀
+     */
+    private final String LOCKKEYSUFFIX = "PULL_TASK";
+    /**
+     * 数据库锁 描述
+     */
+    private final String LOCKDESCRIBE = "亚马逊拉取订单任务_发送拉单任务到MQ中";
+
+
+    /**
+     * 运行中标识
+     */
+    private boolean RUNFLAG = false;
+
+    // 1 分钟拉去一次
+    @Scheduled(fixedRate = 60 * 1000)
+    public void runTask() {
+        try {
+            if (!RUNFLAG) {
+                sendOrderPullTask();
+            }
+
+        } catch (Exception e) {
+            log.error("   异常", e);
+        }
+
+    }
+
+
+    /**
+     * 发送拉单任务到 MQ 队列中
+     *
+     * @throws Exception
+     */
+    public void sendOrderPullTask() throws Exception {
+
+        // 查询 最后操作时间 小于当前时间 并且 重试次数 为零的 任务
+        // 获取当前时间戳
+        long second = Instant.now().getEpochSecond();
+        TbAmzPullOrderTask task = new TbAmzPullOrderTask();
+        task.setRetryCount((byte) 0);
+        task.setLastOperationTime(second);
+        List<TbAmzPullOrderTask> list = amzPullOrderTaskService.findPullTask(task);
+
+        for (TbAmzPullOrderTask apo : list) {
+
+            // 加锁 防止消息重复投递 lockKey =  companyId + storeId + "PULL_TASK"
+            String lockKey = apo.getCompanyId() + apo.getStoreId() + LOCKKEYSUFFIX;
+            Lock lock = new Lock();
+            lock.setLockKey(lockKey);
+            lock.setDescribe(LOCKDESCRIBE);
+
+            if (!lockService.lock(lock)) {
+                // 加锁失败证明 任务已经添加到拉单队列中了
+                continue;
+            }
+
+            // 更行当前任务的重试次数为1
+            this.updateRetryTimes(apo);
+
+            // 发送 MQ
+            sendAmzPullOrderMsg.sendMessage(apo);
+
+        }
+
+
+    }
+
+    /**
+     * 更新 亚马逊拉取订单任务  执行次数为1
+     *
+     * @param apo
+     * @throws Exception
+     */
+    private void updateRetryTimes(TbAmzPullOrderTask apo) throws Exception {
+
+        TbAmzPullOrderTask apot = new TbAmzPullOrderTask();
+        apot.setId(apo.getId());
+        apot.setRetryCount((byte) 1);
+        amzPullOrderTaskService.editById(apot);
+    }
+
+
+}

+ 73 - 0
raycos_datacenter_api/src/main/resources/application.yml

@@ -0,0 +1,73 @@
+server:
+  port: 18082
+spring:
+  application:
+    name: service-provider
+  cloud:
+    nacos:
+      serverAddr: 120.46.190.49:8848
+      discovery:
+        username: 'nacos'
+        password: 'nacos'
+        server-addr: 120.46.190.49:8848
+    #        namespace: public # 可选,‌命名空间ID,‌用于多环境隔离
+    #        group: DEFAULT_GROUP # 可选,‌默认分组
+    loadbalancer:
+      nacos:
+        enabled: true
+      ribbon:
+        enabled: false
+  config:
+    import:
+      - nacos:nacos-config-example.properties?refreshEnabled=true
+      - nacos:Lwa_client_information.yaml?refreshEnabled=true
+
+  profiles:
+    active: dev
+
+  #  数据库连接池
+
+  # 数据源类型设置为Druid
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    url: jdbc:mysql://120.46.190.49:3306/raycos_erp
+    username: root
+    password: Gd8XCWEef72GerxE
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    # 基础配置
+    druid:
+      # 初始化大小,最小,最大
+      initial-size: 5
+      min-idle: 5
+      max-active: 20
+
+      # 配置获取连接等待超时的时间
+      max-wait: 60000
+      # 配置间隔多久进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+      time-between-eviction-runs-millis: 60000
+
+      # 配置一个连接在池中最小生存的时间,单位是毫秒
+      min-evictable-idle-time-millis: 300000
+      # 用来检测连接是否有效的SQL
+      validation-query: SELECT 1 FROM DUAL
+      # 配置间隔多久进行一次检测不活动连接,单位是毫秒
+      phase-of-pool-check: 60000
+
+      # 配置监控服务器
+      stat-view-servlet:
+        login-username: admin
+        login-password: admin
+        log-slow-sql: true
+      filter:
+        stat:
+          log-slow-sql: true
+          slow-sql-millis: 1000
+
+
+  rabbitmq:
+    host: 120.46.190.49
+    port: 5672
+    username: admin
+    password: Gd8XCWEef72GerxE
+    virtual-host: /
+

+ 104 - 0
raycos_datacenter_api/src/main/resources/logback-spring.xml

@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- logback默认每60秒扫描该文件一次,如果有变动则用变动后的配置文件。 -->
+<configuration scan="false">
+
+    <!-- ==============================================开发环境=========================================== -->
+    <springProfile name="dev">
+
+        <!-- 控制台输出 -->
+        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+                <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            </encoder>
+        </appender>
+
+        <!-- 日志输出级别 -->
+        <root level="INFO">
+            <appender-ref ref="STDOUT"/>
+        </root>
+    </springProfile>
+
+    <!-- ==============================================生产环境=========================================== -->
+    <springProfile name="prod">
+        <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
+        <property name="LOG_HOME" value="/www/01_raycos_java/log"/>
+
+        <!-- 控制台输出 -->
+        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+                <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            </encoder>
+        </appender>
+
+        <!-- 按照每天生成日志文件 -->
+        <appender name="INFO_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+            <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
+            如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
+            的日志改名为今天的日期。即,<File> 的日志都是当天的。
+            -->
+            <file>${LOG_HOME}/info.log</file>
+
+            <!--滚动策略,按照大小时间滚动 SizeAndTimeBasedRollingPolicy-->
+            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+                <!--日志文件输出的文件名-->
+                <FileNamePattern>${LOG_HOME}/info.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+                <!--只保留最近30天的日志-->
+                <MaxHistory>30</MaxHistory>
+                <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
+                <totalSizeCap>1GB</totalSizeCap>
+                <MaxFileSize>10MB</MaxFileSize>
+            </rollingPolicy>
+
+            <!--日志输出编码格式化-->
+            <encoder>
+                <charset>UTF-8</charset>
+                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
+                </pattern>
+            </encoder>
+
+            <!--过滤器,只有过滤到指定级别的日志信息才会输出,如果level为ERROR,那么控制台只会输出ERROR日志-->
+            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+                <level>INFO</level>
+            </filter>
+        </appender>
+
+        <!-- 按照每天生成日志文件 -->
+        <appender name="ERROR_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+            <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则
+             如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天
+             的日志改名为今天的日期。即,<File> 的日志都是当天的。
+            -->
+            <file>${LOG_HOME}/error.log</file>
+            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+                <!--日志文件输出的文件名-->
+                <FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+                <MaxHistory>30</MaxHistory>
+                <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
+                <totalSizeCap>1GB</totalSizeCap>
+                <MaxFileSize>10MB</MaxFileSize>
+            </rollingPolicy>
+            <encoder>
+                <charset>UTF-8</charset>
+                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
+                </pattern>
+            </encoder>
+            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+                <level>ERROR</level>
+            </filter>
+        </appender>
+
+        <!--指定最基础的日志输出级别-->
+        <root level="INFO">
+            <!--appender将会添加到这个loger-->
+            <appender-ref ref="STDOUT"/>
+            <appender-ref ref="INFO_APPENDER"/>
+            <appender-ref ref="ERROR_APPENDER"/>
+        </root>
+    </springProfile>
+</configuration>

+ 221 - 0
raycos_datacenter_api/src/main/resources/static/index.html

@@ -0,0 +1,221 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Amazon 授权</title>
+    <!-- Element Plus CSS -->
+    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
+    <!-- Vue 3 -->
+    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
+    <!-- Element Plus -->
+    <script src="https://unpkg.com/element-plus"></script>
+    <!-- Axios -->
+    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+    <!-- UUID -->
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuid.min.js"></script>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 0;
+            padding: 0;
+            background-color: #f0f2f5;
+        }
+        .container {
+            max-width: 870px;
+            margin: 0 auto;
+            padding: 20px;
+        }
+        .form-container {
+            background-color: white;
+            border-radius: 8px;
+            padding: 40px;
+            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+        }
+        h1 {
+            text-align: center;
+            color: #409EFF;
+            margin-bottom: 30px;
+        }
+        .el-form-item {
+            margin-bottom: 20px;
+        }
+    </style>
+</head>
+<body>
+    <div id="app">
+        <div class="container">
+            <div class="form-container" v-if="submitShow">
+                <h1>{{ '亚马逊沙盒环境授权' }}</h1>
+                <el-form :model="AuthorizationForm" :rules="loginRules" ref="AuthorizationFormRef" label-width="120px">
+                    <el-form-item label="店铺名称:" prop="storeName">
+                        <el-input v-model="AuthorizationForm.storeName" placeholder="卖家自定义,以做区分" clearable></el-input>
+                    </el-form-item>
+                    <el-form-item label="平台账号:" prop="platformAccount">
+                        <el-input v-model="AuthorizationForm.platformAccount" placeholder="卖家平台上的账号" clearable></el-input>
+                    </el-form-item>
+                    <el-form-item label="区域:" prop="region">
+                        <el-select v-model="AuthorizationForm.region" placeholder="请选择区域" @change="handleRegionChange">
+                            <el-option v-for="(item, index) in regionList" :key="index" :label="item.label" :value="item.value"></el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item label="站点:" prop="site">
+                        <el-checkbox v-model="AuthorizationForm.checkAll" @change="handleCheckAllChange">全选</el-checkbox>
+                        <div style="margin: 15px 0"></div>
+                        <el-checkbox-group v-model="AuthorizationForm.site" @change="handleCheckedCitiesChange">
+                            <el-checkbox v-for="city in cityOptions" :label="city" :key="city">{{ city }}</el-checkbox>
+                        </el-checkbox-group>
+                    </el-form-item>
+                    <el-form-item label="店铺类型:" prop="storeType">
+                        <el-radio-group v-model="AuthorizationForm.storeType">
+                            <el-radio label="1">跨境</el-radio>
+                            <el-radio label="2">本土</el-radio>
+                        </el-radio-group>
+                    </el-form-item>
+                    <el-form-item label="店铺模式:" prop="storeModel">
+                        <el-checkbox-group v-model="AuthorizationForm.storeModel">
+                            <el-checkbox label="1">FBM</el-checkbox>
+                            <el-checkbox label="2">FBA</el-checkbox>
+                        </el-checkbox-group>
+                    </el-form-item>
+                    <el-form-item>
+                        <el-button type="primary" :loading="loading"  @click="submit()">确定</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+            <div class="form-container" v-else>
+                <h1>{{ '操作成功!' }}</h1>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        const { createApp, ref, onMounted } = Vue;
+        const { ElMessage } = ElementPlus;
+
+        const app = createApp({
+            setup() {
+                const AuthorizationForm = ref({
+                    storeName: '',
+                    platformAccount: '',
+                    region: 'https://sellercentral.amazon.com',
+                    site: ['美国', '加拿大', '墨西哥', '巴西'],
+                    storeType: '1',
+                    storeModel: ['1'],
+                    checkAll: false,
+                });
+                const submitShow = ref(true)
+                const regionList = ref([
+                    { label: '美国', value: 'https://sellercentral.amazon.com', cities: ['美国', '加拿大', '墨西哥', '巴西'] },
+                    { label: '欧洲', value: 'https://sellercentral-europe.amazon.com ', cities: ['英国', '德国', '法国', '意大利', '西班牙'] },
+                    { label: '远东', value: 'https://sellercentral.amazon.co.jp', cities: ['日本', '澳大利亚', '新加坡'] },
+                ]);
+
+                const loading = ref(false);
+                const loginRules = ref({
+                    storeName: [{ required: true, message: '请输入店铺名称', trigger: 'blur' }],
+                    platformAccount: [{ required: true, message: '请输入平台账号', trigger: 'blur' }],
+                    region: [{ required: true, message: '请选择区域', trigger: 'change' }],
+                    site: [{ type: 'array', required: true, message: '请至少选择一个站点', trigger: 'change' }],
+                    storeType: [{ required: true, message: '请选择店铺类型', trigger: 'change' }],
+                    storeModel: [{ type: 'array', required: true, message: '请至少选择一个店铺模式', trigger: 'change' }],
+                });
+                const cityOptions = ref([]);
+                const AuthorizationFormRef = ref(null);
+
+                onMounted(() => {
+                    handleRegionChange(AuthorizationForm.value.region);
+                });
+
+                const handleRegionChange = (value) => {
+                    const selectedRegion = regionList.value.find((region) => region.value === value);
+                    if (selectedRegion) {
+                        cityOptions.value = selectedRegion.cities;
+                        AuthorizationForm.value.site = [...selectedRegion.cities];
+                        AuthorizationForm.value.checkAll = true;
+                    } else {
+                        cityOptions.value = [];
+                        AuthorizationForm.value.site = [];
+                        AuthorizationForm.value.checkAll = false;
+                    }
+                };
+
+                const handleCheckAllChange = (val) => {
+                    AuthorizationForm.value.site = val ? cityOptions.value : [];
+                };
+
+                const handleCheckedCitiesChange = (value) => {
+                    const checkedCount = value.length;
+                    AuthorizationForm.value.checkAll = checkedCount === cityOptions.value.length;
+                };
+
+                const submit = async () => {
+                if (!AuthorizationFormRef.value) return;
+
+                try {
+                    await AuthorizationFormRef.value.validate();
+                    
+                    loading.value = true;
+                    const uuid = self.crypto.randomUUID();
+
+                    const formData = {
+                        ...AuthorizationForm.value,
+                        uuid: uuid,
+                        site: AuthorizationForm.value.site.join(','),
+                        storeModel: AuthorizationForm.value.storeModel.join(','),
+                    };
+                    const response = await axios.post('https://apisaas.raycos.net/erp/store/add', formData, {
+                        headers: {
+                            'Content-Type': 'application/json',
+                        },
+                    });
+
+                    if (response.data.code==200) {
+                        const authUrl = generateAuthUrl(formData.region, uuid);
+                        window.open(authUrl, '_blank');
+                        ElMessage.success('提交成功');
+                        submitShow.value=false
+                    } else {
+                        ElMessage.error(response.data.msg || '提交失败');
+                    }
+                } catch (error) {
+                    console.error('Authorization error:', error);
+                    if (error.errors) {
+                        // 表单验证错误
+                        const errorMessages = Object.values(error.errors).flat().join(', ');
+                        ElMessage.error(`请填写所有必填字段: ${errorMessages}`);
+                    } else {
+                        ElMessage.error('授权过程中发生错误');
+                    }
+                } finally {
+                    loading.value = false;
+                }
+            };
+
+                const generateAuthUrl = (region, uuid) => {
+                    const baseUrl = region;
+                    const applicationId = 'amzn1.sp.solution.b50697a2-e3d3-47e0-8de3-9ae03fbdcd9b';
+                    return `${baseUrl}/apps/authorize/consent?application_id=${applicationId}&state=${uuid}&version=beta`;
+                };
+
+                return {
+                    submitShow,
+                    AuthorizationForm,
+                    regionList,
+                    loading,
+                    loginRules,
+                    cityOptions,
+                    AuthorizationFormRef,
+                    handleRegionChange,
+                    handleCheckAllChange,
+                    handleCheckedCitiesChange,
+                    submit,
+                };
+            }
+        });
+
+        app.use(ElementPlus);
+        app.mount('#app');
+    </script>
+</body>
+</html>

+ 53 - 0
raycos_web_api/pom.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.raycos</groupId>
+        <artifactId>raycos</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+
+    <artifactId>raycos_web_api</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>raycos-web-api</name>
+
+    <packaging>jar</packaging>
+    <description>raycos-web-api</description>
+
+
+    <properties>
+    </properties>
+    <dependencies>
+        <!--配置feign-->
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.cloud</groupId>-->
+<!--            <artifactId>spring-cloud-starter-openfeign</artifactId>-->
+<!--        </dependency>-->
+        <!--web起步依赖-->
+
+
+        <dependency>
+            <groupId>com.raycos</groupId>
+            <artifactId>raycoscommon</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>com.baomidou</groupId>-->
+<!--            <artifactId>mybatis-plus-boot-starter</artifactId>-->
+<!--        </dependency>-->
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 17 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/RaycosWebApiApplication.java

@@ -0,0 +1,17 @@
+package com.raycos.raycoswebapi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@EnableDiscoveryClient
+@EnableFeignClients
+@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
+public class RaycosWebApiApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(RaycosWebApiApplication.class, args);
+    }
+}

+ 193 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/amazon/authorize/AmazonAuthorize.java

@@ -0,0 +1,193 @@
+package com.raycos.raycoswebapi.amazon.authorize;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/31 17:03
+ */
+
+public class AmazonAuthorize {
+
+
+    // 用户通过页面调用Amazon授权 接口
+    // 授权成功后 亚马逊回调  我的接口 入参:
+                /**
+                 * state	步骤 2 中的 state 值。
+                 * selling_partner_id	授权您的应用程序的销售合作伙伴的标识符。
+                 * mws_auth_token	(仅限卖家应用程序)您在为 Amazon Marketplace Web Service 调用创建查询字符串时指定的值。
+                 * spapi_oauth_code	用于生成 LWA 刷新令牌的 Login with Amazon (LWA) 授权码(请参阅第 5 步)。
+                 */
+    //  我拿到 spapi_oauth_code 去调用刷新令牌接口
+    //  发送:
+                /**
+                 * grant_type	请求的访问授权的类型。必须是 。authorization_code
+                 * code	您在步骤 4 中收到的 LWA 授权码。Amazon 会向您发送授权信息。
+                 * redirect_uri	应用程序的重定向 URI。
+                 * client_id	LWA 凭证的一部分。要获取此值,请参阅 查看您的开发者信息。
+                 * client_secret	LWA 凭证的一部分。要获取此值,请参阅 查看您的开发者信息。
+                 */
+    //  接收:
+                /**
+                 * access_token	一个令牌,用于授权您的应用程序代表销售合作伙伴执行某些操作。有关详细信息,请参阅连接到 SP-API。
+                 * token_type	返回的 token 类型。应该是 bearer。
+                 * expires_in	访问令牌失效前的秒数。
+                 * refresh_token	可用于生成新访问令牌的长寿命令牌。有关详细信息,请参阅连接到 SP-API。
+                 */
+
+
+
+
+//
+//    private  static  String  client_id;
+//
+//    public static String getAccessToken(String code) {
+//        try {
+//
+//            String url = "https://api.amazon.com/auth/o2/token";
+//            Map<String, String> map = new HashMap<>();
+//            map.put("grant_type", "authorization_code");
+//            map.put("code", code);
+//            map.put("client_id", client_id);
+//            map.put("redirect_uri", "https://api.mabangerp.com/fba/api/v1/authCode");
+//            map.put("client_secret", client_secret);
+//            Map<String, String> headerMap = new HashMap<>();
+//            logger.info(JSONObject.toJSONString(map));
+//            headerMap.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
+//            String response = HttpClientUtil.httpPostForm(url, map, headerMap, null, 5000);
+//
+//            logger.info(", get accessToken response : " + response);
+//            return response;
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//            logger.error(e.getMessage(), e);
+//        }
+//        return "报错";
+//    }
+//
+//    public static String refreshAccessToken(String refresh_token) {
+//        try {
+//            String url = "https://api.amazon.com/auth/o2/token";
+//            Map<String, String> map = new HashMap<>();
+//            map.put("grant_type", "refresh_token");
+//            map.put("refresh_token", refresh_token);
+//            map.put("client_id", client_id);
+//            map.put("client_secret", client_secret);
+//            Map<String, String> headerMap = new HashMap<>();
+//            logger.info(JSONObject.toJSONString(map));
+//            headerMap.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
+//            String response = HttpClientUtil.httpPostForm(url, map, headerMap, null, 5000);
+//            logger.info("refresh access_token response : " + response);
+//            return response;
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//            logger.error(e.getMessage(), e);
+//        }
+//        return "报错";
+//    }
+//
+//
+//    public static String httpPostForm(String requestUrl, Map<String, String> paramMap,
+//                                      Map<String, String> headerMap, String encode, int timeOut) {
+//        String response = "";
+//        if (encode == null) {
+//            encode = "utf-8";
+//        }
+//
+//        // HttpClients.createDefault() 等价于 HttpClientBuilder.create().build();
+//        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
+//        HttpPost httpPost = new HttpPost(requestUrl);
+//
+//        // 设置超时时间
+//        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeOut)
+//                .setConnectTimeout(timeOut).setSocketTimeout(timeOut).build();
+//        httpPost.setConfig(requestConfig);
+//
+//        // 设置header
+//        if (headerMap != null && headerMap.size() > 0) {
+//            for (String key : headerMap.keySet()) {
+//                httpPost.setHeader(key, headerMap.get(key));
+//            }
+//        }
+//
+//        // 设置Param
+//        if (paramMap != null && paramMap.size() > 0) {
+//
+//            List<NameValuePair> paramList = new ArrayList<NameValuePair>();
+//            for (String key : paramMap.keySet()) {
+//                paramList.add(new BasicNameValuePair(key, paramMap.get(key)));
+//            }
+//
+//            try {
+//                httpPost.setEntity(new UrlEncodedFormEntity(paramList, encode));
+//            } catch (UnsupportedEncodingException e) {
+//                e.printStackTrace();
+//            }
+//
+//        }
+//
+//        CloseableHttpResponse httpResponse = null;
+//        try {
+//            httpResponse = closeableHttpClient.execute(httpPost);
+//            HttpEntity entity = httpResponse.getEntity();
+//            byte[] bytes = (entity == null ? null : EntityUtils.toByteArray(entity));
+//            String content = (bytes == null ? null : new String(bytes, encode));
+//            response = content;
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        } finally {
+//            try {
+//                if (httpResponse != null) {
+//                    httpResponse.close();
+//                }
+//            } catch (IOException e) {
+//                e.printStackTrace();
+//            }
+//        }
+//
+//        try {
+//            // 关闭连接、释放资源
+//            if (closeableHttpClient != null) {
+//                closeableHttpClient.close();
+//            }
+//        } catch (IOException e) {
+//            e.printStackTrace();
+//        }
+//
+//        return response;
+//    }
+//
+//    /**
+//
+//     https://sellingpartnerapi-na.amazon.com/apps/authorize/consent
+//     https://sellingpartnerapi-eu.amazon.com/apps/authorize/consent
+//     https://sellingpartnerapi-fe.amazon.com/apps/authorize/consent
+//
+//     https://www.amazon.com/ap/oa?client_id=AKIAJQM2XW6X65DC5OWA
+//     &scope=profile
+//     &response_type=code
+//     &state=208257577ll0975l93l2l59l895857093449424
+//     &redirect_uri=https://client.example.com/auth_popup/token
+//     &code_challenge=Fw7s3XHRVb2m1nT7s646UrYiYLMJ54as0ZIU_injyqw
+//     &code_challenge_method=S256
+//
+//     if ($this->site == 'eu') {
+//     return 'https://sellercentral-europe.amazon.com/apps/authorize/consent';
+//     } elseif ($this->site == 'fe') {
+//     if ($this->countryCode == 'au') {
+//     return 'https://sellercentral.amazon.com.au/apps/authorize/consent';
+//     } elseif ($this->countryCode == 'jp') {
+//     return 'https://sellercentral-japan.amazon.com/apps/authorize/consent';
+//     } else {
+//     //新加坡
+//     return 'https://sellercentral.amazon.sg/apps/authorize/consent';
+//     }
+//     } else {
+//     return 'https://sellercentral.amazon.com/apps/authorize/consent';
+//     }
+//
+//     //     https://raycos.com.cn/authorize
+//
+//
+//
+//     */
+
+}

+ 19 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/ConfigProperties.java

@@ -0,0 +1,19 @@
+package com.raycos.raycoswebapi.nacoscase;
+
+
+import org.springframework.stereotype.Component;
+
+@Component
+//@RefreshScope
+public class ConfigProperties {
+//    @Value("${spring.cloud.nacos.config.serveraddr}")
+    private String someConfig;
+
+    public String getSomeConfig() {
+        return someConfig;
+    }
+
+    public void setSomeConfig(String someConfig) {
+        this.someConfig = someConfig;
+    }
+}

+ 44 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/NacosController.java

@@ -0,0 +1,44 @@
+package com.raycos.raycoswebapi.nacoscase;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+
+@RestController()
+@RequestMapping("/nacos")
+public class NacosController {
+
+    @Autowired
+    private ConfigProperties configProperties;
+
+
+    @GetMapping( )
+    public String bean(){
+        String someConfig = configProperties.getSomeConfig();
+        String  str = "查询了老弟" + someConfig;
+
+        return str;
+    }
+
+    @PostMapping( )
+    public String bean1(){
+        String  str = "修改了老弟";
+
+        return str;
+    }
+
+    @PutMapping( )
+    public String bean2(){
+        String  str = "新增了老弟";
+
+        return str;
+    }
+
+    @DeleteMapping( )
+    public String bean3(){
+        String  str = "删除了老弟!!";
+
+        return str;
+    }
+}

+ 43 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/TestController.java

@@ -0,0 +1,43 @@
+package com.raycos.raycoswebapi.nacoscase;
+
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.webapi.User;
+import com.raycos.raycoswebapi.nacoscase.service.EchoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/26 13:23
+ */
+@RestController
+public class TestController {
+    @Autowired
+    private RestTemplate restTemplate;
+    @Autowired
+    private EchoService echoService;
+
+    @GetMapping(value = "/echo-rest/{str}")
+    public Result<String> rest(@PathVariable String str) {
+        System.out.println("来了来了");
+//        return restTemplate.getForObject("http://service-provider/echo/" + str, Result.class);
+        String forObject = restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
+        Result<String> result = new Result<>();
+        result.setCode(200);
+        result.setMsg(forObject);
+        return result;
+    }
+    @GetMapping(value = "/echo-feign/{str}")
+    public Result<String> feign(@PathVariable String str) {
+        return echoService.echo(str);
+    }
+
+    @PostMapping("/hello")
+    public Result<String> helloConsumer (@RequestBody User user ){
+        System.out.println("9999999999999"+user.toString());
+        return echoService.sayHello( user );
+//        return restTemplate.getForObject("http://service-provider/sayHello/" , Result.class, user);
+    }
+
+}

+ 21 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/config/NacosLoadBalancerConfig.java

@@ -0,0 +1,21 @@
+package com.raycos.raycoswebapi.nacoscase.config;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/27 9:39
+ */
+//@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerConfig.class)
+public class NacosLoadBalancerConfig {
+//    @Resource
+//    private NacosDiscoveryProperties nacosDiscoveryProperties;
+//    @Bean
+//    public ReactorLoadBalancer<ServiceInstance> nacosLoadBalancer(
+//            Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
+//        String name = environment.getProperty("loadbalancer.client.name");
+//        return new NacosLoadBalancer(
+//                loadBalancerClientFactory.getLazyProvider(name,
+//                        ServiceInstanceListSupplier.class), name
+//                ,nacosDiscoveryProperties);
+//    }
+
+}

+ 21 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/config/RestTemplateConfig.java

@@ -0,0 +1,21 @@
+package com.raycos.raycoswebapi.nacoscase.config;
+
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/26 13:27
+ */
+
+@Configuration
+public class RestTemplateConfig {
+    @Bean
+    @LoadBalanced
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+
+}

+ 24 - 0
raycos_web_api/src/main/java/com/raycos/raycoswebapi/nacoscase/service/EchoService.java

@@ -0,0 +1,24 @@
+package com.raycos.raycoswebapi.nacoscase.service;
+
+import com.raycos.raycoscommon.Internal.Result;
+import com.raycos.raycoscommon.Internal.webapi.User;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.cloud.openfeign.SpringQueryMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+
+/**
+ * @Author: xiaojiabin
+ * @Date: 2024/8/26 13:25
+ */
+
+@FeignClient(name = "service-provider")
+public interface EchoService {
+
+    @GetMapping(value = "/echo/{str}")
+    Result<String> echo(@PathVariable("str") String str);
+
+    @PostMapping(value = "/sayHello")
+    Result<String> sayHello (@SpringQueryMap User user );
+}

+ 30 - 0
raycos_web_api/src/main/resources/application.yml

@@ -0,0 +1,30 @@
+server:
+  port: 18084
+spring:
+  application:
+    #    #项目名称必填,在注册中心唯一
+    #    #最好和之前域名规范、kubernetes service名等保持一致(会作为调用)
+    name: service-consumer
+  cloud:
+    nacos:
+      serverAddr: 120.46.190.49:8848
+      discovery:
+        server-addr: 120.46.190.49:8848
+#        namespace: public # 可选,‌命名空间ID,‌用于多环境隔离
+#        group: DEFAULT_GROUP # 可选,‌默认分组
+
+
+
+    loadbalancer:
+      nacos:
+        enabled: true
+      ribbon:
+        enabled: false
+  config:
+    import:
+      - nacos:nacos-config-example.properties?refreshEnabled=true
+
+
+
+
+