新增restful模式

This commit is contained in:
六如
2025-02-02 15:51:47 +08:00
parent 1f04edeaff
commit ddc709ede4
97 changed files with 1487 additions and 867 deletions

View File

@@ -39,7 +39,7 @@
<!-- 具体版本对应关系见https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E --> <!-- 具体版本对应关系见https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E -->
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version> <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
<!-- dubbo版本 --> <!-- dubbo版本 -->
<dubbo.version>3.2.12</dubbo.version> <dubbo.version>3.2.16</dubbo.version>
<junit.version>4.11</junit.version> <junit.version>4.11</junit.version>
<commons-io.version>2.5</commons-io.version> <commons-io.version>2.5</commons-io.version>
@@ -207,6 +207,9 @@
<source>${java.version}</source> <source>${java.version}</source>
<target>${java.version}</target> <target>${java.version}</target>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

View File

@@ -0,0 +1,16 @@
<?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.gitee.sop</groupId>
<artifactId>payment-api</artifactId>
<version>5.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,7 @@
package com.gitee.sop.payment.api;
/**
* @author 六如
*/
public interface PaymentService {
}

View File

@@ -0,0 +1,128 @@
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop</groupId>
<artifactId>payment-service</artifactId>
<version>5.0.0-SNAPSHOT</version>
<name>payment-service</name>
<properties>
<java.version>1.8</java.version>
<!-- dubbo版本 -->
<dubbo.version>3.2.16</dubbo.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>payment-api</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>product-api</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<!-- sop接入依赖 -->
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-spring-boot-starter</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<!-- nacos注册中心 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
</dependency>
<!-- zookeeper注册中心 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-zookeeper-curator5-spring-boot-starter</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>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 打包时跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 文档推送 -->
<plugin>
<groupId>com.ly.smart-doc</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>3.0.9</version>
<configuration>
<!--指定生成文档的使用的配置文件-->
<configFile>./src/main/resources/smart-doc.json</configFile>
<!--指定项目名称-->
<projectName>${project.artifactId}</projectName>
</configuration>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-support</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>maven_central</id>
<name>Maven Central</name>
<url>https://repo.maven.apache.org/maven2/</url>
</repository>
</repositories>
</project>

View File

@@ -6,8 +6,12 @@ import com.gitee.sop.payment.open.req.PayTradeWapPayRequest;
import com.gitee.sop.payment.open.resp.PayOrderSearchResponse; import com.gitee.sop.payment.open.resp.PayOrderSearchResponse;
import com.gitee.sop.payment.open.resp.PayTradeWapPayResponse; import com.gitee.sop.payment.open.resp.PayTradeWapPayResponse;
import java.util.UUID; import java.util.UUID;
import org.apache.dubbo.config.annotation.DubboService;
import com.gitee.sop.story.api.ProductService;
import com.gitee.sop.story.api.resp.ProductResult;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Value;
/** /**
@@ -18,6 +22,13 @@ import org.apache.dubbo.config.annotation.DubboService;
@DubboService(validation = "true") @DubboService(validation = "true")
public class OpenPaymentImpl implements OpenPayment { public class OpenPaymentImpl implements OpenPayment {
@DubboReference
private ProductService storyService;
@Value("${dubbo.labels:}")
private String env;
@Override @Override
public PayTradeWapPayResponse tradeWapPay(PayTradeWapPayRequest request) { public PayTradeWapPayResponse tradeWapPay(PayTradeWapPayRequest request) {
PayTradeWapPayResponse payTradeWapPayResponse = new PayTradeWapPayResponse(); PayTradeWapPayResponse payTradeWapPayResponse = new PayTradeWapPayResponse();
@@ -32,6 +43,13 @@ public class OpenPaymentImpl implements OpenPayment {
payOrderSearchResponse.setPayNo("xxxx"); payOrderSearchResponse.setPayNo("xxxx");
payOrderSearchResponse.setPayUserId(111L); payOrderSearchResponse.setPayUserId(111L);
payOrderSearchResponse.setPayUserName("Jim"); payOrderSearchResponse.setPayUserName("Jim");
try {
ProductResult storyResult = storyService.getById(1L);
payOrderSearchResponse.setRemark(storyResult + ",env:" + env);
} catch (Exception e) {
e.printStackTrace();
}
return payOrderSearchResponse; return payOrderSearchResponse;
} }
} }

View File

@@ -10,7 +10,10 @@ import org.hibernate.validator.constraints.Length;
@Data @Data
public class PayOrderSearchRequest { public class PayOrderSearchRequest {
@ApiModelProperty(value = "订单编号", required = true, example = "xxxx") /**
* 订单编号
* @mock xxxx
*/
@Length(max = 64) // 最大长度 @Length(max = 64) // 最大长度
private String orderNo; private String orderNo;

View File

@@ -0,0 +1,35 @@
package com.gitee.sop.payment.open.resp;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class PayOrderSearchResponse {
/**
* 订单编号
*/
private String orderNo;
/**
* 支付编号
*/
private String payNo;
/**
* 支付人id
*/
private Long payUserId;
/**
* 支付人姓名
*/
private String payUserName;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,10 @@
spring.profiles.active=dev
server.port=7072
spring.application.name=payment-service
dubbo.protocol.name=dubbo
dubbo.protocol.port=-1
dubbo.application.qos-enable=false
dubbo.consumer.check=false
dubbo.registry.address=zookeeper://localhost:2181

View File

@@ -1,122 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" <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"> 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> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>example-payment</artifactId> <artifactId>example-payment</artifactId>
<version>5.0.0-SNAPSHOT</version> <version>5.0.0-SNAPSHOT</version>
<name>example-payment</name> <packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<!-- dubbo版本 -->
<dubbo.version>3.2.10</dubbo.version>
</properties>
<dependencies>
<!-- sop接入依赖 -->
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-spring-boot-starter</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<!-- nacos注册中心 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 仅在开发中使用 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 打包时跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 文档推送 -->
<plugin>
<groupId>com.ly.smart-doc</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>3.0.9</version>
<configuration>
<!--指定生成文档的使用的配置文件-->
<configFile>./src/main/resources/smart-doc.json</configFile>
<!--指定项目名称-->
<projectName>${project.artifactId}</projectName>
</configuration>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-support</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>maven_central</id>
<name>Maven Central</name>
<url>https://repo.maven.apache.org/maven2/</url>
</repository>
</repositories>
<modules>
<module>payment-api</module>
<module>payment-service</module>
</modules>
</project> </project>

View File

@@ -1,23 +0,0 @@
package com.gitee.sop.payment.open.resp;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class PayOrderSearchResponse {
@ApiModelProperty(value = "订单编号", example = "xxxx")
private String orderNo;
@ApiModelProperty(value = "支付编号", example = "xxxx")
private String payNo;
@ApiModelProperty(value = "支付人id", example = "111")
private Long payUserId;
@ApiModelProperty(value = "支付人姓名", example = "Jim")
private String payUserName;
}

View File

@@ -0,0 +1,16 @@
<?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.gitee.sop</groupId>
<artifactId>example-product</artifactId>
<version>5.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>product-api</module>
<module>product-service</module>
</modules>
</project>

View File

@@ -0,0 +1,16 @@
<?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.gitee.sop</groupId>
<artifactId>product-api</artifactId>
<version>5.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,12 @@
package com.gitee.sop.story.api;
import com.gitee.sop.story.api.resp.ProductResult;
/**
* @author 六如
*/
public interface ProductService {
ProductResult getById(Long id);
}

View File

@@ -0,0 +1,51 @@
package com.gitee.sop.story.api.resp;
import java.io.Serializable;
import java.util.Date;
/**
* @author 六如
*/
public class ProductResult implements Serializable {
private static final long serialVersionUID = -3743413007549072654L;
private Integer id;
private String name;
// 日期格式要用Date,暂不支持LocalDateTime
private Date addTime = new Date();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getAddTime() {
return addTime;
}
public void setAddTime(Date addTime) {
this.addTime = addTime;
}
@Override
public String toString() {
return "StoryResult{" +
"id=" + id +
", name='" + name + '\'' +
", addTime=" + addTime +
'}';
}
}

View File

@@ -11,9 +11,9 @@
</parent> </parent>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>example-story</artifactId> <artifactId>product-service</artifactId>
<version>5.0.0-SNAPSHOT</version> <version>5.0.0-SNAPSHOT</version>
<name>example-story</name> <name>story-service</name>
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>
@@ -22,6 +22,12 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>product-api</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.gitee.sop</groupId> <groupId>com.gitee.sop</groupId>
<artifactId>sop-spring-boot-starter</artifactId> <artifactId>sop-spring-boot-starter</artifactId>

View File

@@ -1,4 +1,4 @@
package com.gitee.sop.storyweb; package com.gitee.sop.productweb;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
@@ -6,10 +6,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
@EnableDubbo @EnableDubbo
public class ExampleStoryApplication { public class ExampleProductApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(ExampleStoryApplication.class, args); SpringApplication.run(ExampleProductApplication.class, args);
} }
} }

View File

@@ -1,4 +1,4 @@
package com.gitee.sop.storyweb.message; package com.gitee.sop.productweb.message;
import com.gitee.sop.support.message.I18nMessage; import com.gitee.sop.support.message.I18nMessage;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

View File

@@ -0,0 +1,92 @@
package com.gitee.sop.productweb.open;
import com.gitee.sop.productweb.open.req.ProductSaveRequest;
import com.gitee.sop.productweb.open.resp.ProductResponse;
import com.gitee.sop.support.annotation.Open;
import com.gitee.sop.support.context.OpenContext;
import com.gitee.sop.support.dto.FileData;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
* 产品服务
*
* @author 六如
* @dubbo
*/
@Api("产品服务")
public interface OpenProduct {
/**
* 新增故事
*
* @param request 入参
* @return 返回id
*/
@Open("product.save")
Integer save(ProductSaveRequest request);
@Open("product.update")
Integer update(Integer id, ProductSaveRequest request);
// 演示抛出异常
@Open("product.updateError")
Integer updateError(Integer id);
@ApiOperation(value = "根据id获取故事")
@Open("product.get")
ProductResponse getById(@NotNull(message = "id必填") Integer id);
// 需要授权
@Open(value = "product.get", version = "2.0", permission = true)
ProductResponse getByIdV2(Long id);
@Open(value = "product.get.context")
ProductResponse getContext(Long id, OpenContext context);
// 默认方法,注解放在这里也有效
@Open("product.find")
default ProductResponse getById(Integer id, String name) {
ProductResponse storyResponse = new ProductResponse();
storyResponse.setId(id);
storyResponse.setName(name);
return storyResponse;
}
// 默认方法,注解放在这里也有效
@Open("alipay.story.find")
default ProductResponse findByName(String name) {
ProductResponse storyResponse = new ProductResponse();
storyResponse.setName(name);
return storyResponse;
}
// 演示单文件上传
@Open("product.upload")
ProductResponse upload(ProductSaveRequest request, FileData file);
// 演示多文件上传
@Open("product.upload.more")
ProductResponse upload2(
ProductSaveRequest request,
@NotNull(message = "身份证正面必填") FileData idCardFront,
@NotNull(message = "身份证背面必填") FileData idCardBack
);
// 演示多文件上传
@Open("product.upload.list")
ProductResponse upload3(
ProductSaveRequest request,
@Size(min = 2, message = "最少上传2个文件")
List<FileData> files
);
// 下载
@Open("product.download")
FileData download(Integer id);
}

View File

@@ -1,15 +1,16 @@
package com.gitee.sop.storyweb.impl; package com.gitee.sop.productweb.open.impl;
import com.gitee.sop.storyweb.message.StoryMessageEnum; import com.gitee.sop.productweb.message.StoryMessageEnum;
import com.gitee.sop.storyweb.open.OpenStory; import com.gitee.sop.productweb.open.OpenProduct;
import com.gitee.sop.storyweb.open.req.StorySaveRequest; import com.gitee.sop.productweb.open.req.ProductSaveRequest;
import com.gitee.sop.storyweb.open.resp.StoryResponse; import com.gitee.sop.productweb.open.resp.ProductResponse;
import com.gitee.sop.support.context.OpenContext; import com.gitee.sop.support.context.OpenContext;
import com.gitee.sop.support.dto.CommonFileData; import com.gitee.sop.support.dto.CommonFileData;
import com.gitee.sop.support.dto.FileData; import com.gitee.sop.support.dto.FileData;
import com.gitee.sop.support.exception.OpenException; import com.gitee.sop.support.exception.OpenException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@@ -17,6 +18,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
@@ -25,18 +27,21 @@ import java.util.List;
* @author 六如 * @author 六如
*/ */
@DubboService(validation = "true") @DubboService(validation = "true")
public class OpenStoryImpl implements OpenStory { public class OpenProductImpl implements OpenProduct {
@Value("${dubbo.labels:}")
private String env;
@Override @Override
public Integer save(StorySaveRequest storySaveDTO) { public Integer save(ProductSaveRequest request) {
return 1; return 1;
} }
@Override @Override
public Integer update(Integer id, StorySaveRequest storySaveDTO) { public Integer update(Integer id, ProductSaveRequest request) {
System.out.println("update, id:" + id + ", storySaveDTO=" + storySaveDTO); System.out.println("update, id:" + id + ", storySaveDTO=" + request);
return 1; return 1;
} }
@@ -54,42 +59,46 @@ public class OpenStoryImpl implements OpenStory {
} }
@Override @Override
public StoryResponse getById(Integer id) { public ProductResponse getById(Integer id) {
StoryResponse storyResponse = new StoryResponse(); ProductResponse storyResponse = new ProductResponse();
storyResponse.setId(id); storyResponse.setId(id);
storyResponse.setName("乌鸦喝水"); storyResponse.setName("冰箱-" + env);
return storyResponse; return storyResponse;
} }
@Override @Override
public StoryResponse getByIdV2(Long id) { public ProductResponse getByIdV2(Long id) {
StoryResponse storyResponse = new StoryResponse(); ProductResponse storyResponse = new ProductResponse();
storyResponse.setId(2); storyResponse.setId(2);
storyResponse.setName("乌鸦喝水2.0"); storyResponse.setName("冰箱2.0");
return storyResponse; return storyResponse;
} }
// 演示获取上下文 // 演示获取上下文
@Override @Override
public StoryResponse getContext(Long id, OpenContext context) { public ProductResponse getContext(Long id, OpenContext context) {
StoryResponse storyResponse = new StoryResponse(); ProductResponse storyResponse = new ProductResponse();
storyResponse.setId(3); storyResponse.setId(3);
storyResponse.setName(context.toString()); storyResponse.setName(context.toString());
// 获取回调参数 // 获取回调参数
String notifyUrl = context.getNotifyUrl(); String notifyUrl = context.getNotifyUrl();
System.out.println(notifyUrl); System.out.println(notifyUrl);
// 方式2使用OpenContext.current()
String notifyUrl1 = OpenContext.current().getNotifyUrl();
System.out.println(Objects.equals(notifyUrl1, notifyUrl));
return storyResponse; return storyResponse;
} }
@Override @Override
public StoryResponse upload(StorySaveRequest storySaveDTO, FileData file) { public ProductResponse upload(ProductSaveRequest storySaveDTO, FileData file) {
System.out.println("getName:" + file.getName()); System.out.println("getName:" + file.getName());
System.out.println("getOriginalFilename:" + file.getOriginalFilename()); System.out.println("getOriginalFilename:" + file.getOriginalFilename());
checkFile(Arrays.asList(file)); checkFile(Arrays.asList(file));
StoryResponse storyResponse = new StoryResponse(); ProductResponse storyResponse = new ProductResponse();
storyResponse.setId(1); storyResponse.setId(1);
storyResponse.setName(file.getOriginalFilename()); storyResponse.setName(file.getOriginalFilename());
return storyResponse; return storyResponse;
@@ -97,25 +106,25 @@ public class OpenStoryImpl implements OpenStory {
@Override @Override
public StoryResponse upload2(StorySaveRequest storySaveDTO, FileData idCardFront, FileData idCardBack) { public ProductResponse upload2(ProductSaveRequest storySaveDTO, FileData idCardFront, FileData idCardBack) {
System.out.println("upload:" + storySaveDTO); System.out.println("upload:" + storySaveDTO);
checkFile(Arrays.asList(idCardFront, idCardBack)); checkFile(Arrays.asList(idCardFront, idCardBack));
StoryResponse storyResponse = new StoryResponse(); ProductResponse storyResponse = new ProductResponse();
storyResponse.setId(1); storyResponse.setId(1);
storyResponse.setName(storySaveDTO.getStoryName()); storyResponse.setName(storySaveDTO.getProductName());
return storyResponse; return storyResponse;
} }
@Override @Override
public StoryResponse upload3(StorySaveRequest storySaveDTO, List<FileData> files) { public ProductResponse upload3(ProductSaveRequest storySaveDTO, List<FileData> files) {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
list.add("upload:" + storySaveDTO); list.add("upload:" + storySaveDTO);
checkFile(files); checkFile(files);
StoryResponse storyResponse = new StoryResponse(); ProductResponse storyResponse = new ProductResponse();
storyResponse.setId(1); storyResponse.setId(1);
storyResponse.setName(storySaveDTO.getStoryName()); storyResponse.setName(storySaveDTO.getProductName());
return storyResponse; return storyResponse;
} }

View File

@@ -1,8 +1,7 @@
package com.gitee.sop.storyweb.open.req; package com.gitee.sop.productweb.open.req;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
import org.springframework.context.annotation.Lazy;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -13,7 +12,7 @@ import java.util.Date;
* @author 六如 * @author 六如
*/ */
@Data @Data
public class StorySaveRequest implements Serializable { public class ProductSaveRequest implements Serializable {
private static final long serialVersionUID = -1214422742659231037L; private static final long serialVersionUID = -1214422742659231037L;
/** /**
@@ -21,7 +20,7 @@ public class StorySaveRequest implements Serializable {
*/ */
@NotBlank(message = "故事名称必填") @NotBlank(message = "故事名称必填")
@Length(max = 64) @Length(max = 64)
private String storyName; private String productName;
/** /**
* 添加时间 * 添加时间

View File

@@ -1,4 +1,4 @@
package com.gitee.sop.storyweb.open.resp; package com.gitee.sop.productweb.open.resp;
import lombok.Data; import lombok.Data;
@@ -9,7 +9,7 @@ import java.util.Date;
* @author 六如 * @author 六如
*/ */
@Data @Data
public class StoryResponse implements Serializable { public class ProductResponse implements Serializable {
private static final long serialVersionUID = -3743413007549072654L; private static final long serialVersionUID = -3743413007549072654L;
private Integer id; private Integer id;

View File

@@ -0,0 +1,28 @@
package com.gitee.sop.productweb.rpc;
import com.gitee.sop.story.api.ProductService;
import com.gitee.sop.story.api.resp.ProductResult;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Value;
import java.util.Date;
/**
* @author 六如
*/
@DubboService
public class ProductServiceImpl implements ProductService {
@Value("${dubbo.labels:}")
private String env;
@Override
public ProductResult getById(Long id) {
System.out.println("StoryService.getById, env=" + env);
ProductResult storyResult = new ProductResult();
storyResult.setName("彩电-" + env);
storyResult.setId(id.intValue());
storyResult.setAddTime(new Date());
return storyResult;
}
}

View File

@@ -1,7 +1,7 @@
spring.profiles.active=dev spring.profiles.active=dev
server.port=7071 server.port=7071
spring.application.name=story-service spring.application.name=product-service
dubbo.protocol.name=dubbo dubbo.protocol.name=dubbo
dubbo.protocol.port=-1 dubbo.protocol.port=-1

View File

@@ -1,4 +1,4 @@
package com.gitee.sop.storyweb; package com.gitee.sop.productweb;
import cn.torna.swaggerplugin.SwaggerPlugin; import cn.torna.swaggerplugin.SwaggerPlugin;

33
sop-example/example-rest/.gitignore vendored Normal file
View File

@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

View File

@@ -0,0 +1,133 @@
<?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>
<groupId>com.sop.example.rest</groupId>
<artifactId>example-rest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>example-rest</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
<!-- dubbo版本 -->
<dubbo.version>3.2.16</dubbo.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-spring-boot-starter</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos注册中心 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
</dependency>
<!-- zookeeper注册中心 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-zookeeper-curator5-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>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<!-- 文档推送 -->
<plugin>
<groupId>com.ly.smart-doc</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>3.0.9</version>
<configuration>
<!--指定生成文档的使用的配置文件-->
<configFile>./src/main/resources/smart-doc.json</configFile>
<!--指定项目名称-->
<projectName>${project.artifactId}</projectName>
</configuration>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-support</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.sop.example.rest.examplerest.ExampleRestApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,15 @@
package com.sop.example.rest.examplerest;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class ExampleRestApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleRestApplication.class, args);
}
}

View File

@@ -0,0 +1,16 @@
package com.sop.example.rest.examplerest.rest;
import com.gitee.sop.support.annotation.Open;
import com.sop.example.rest.examplerest.rest.vo.GoodsVO;
import javax.validation.constraints.NotNull;
/**
* @author 六如
*/
public interface GoodsController {
@Open("/getGoodsById")
GoodsVO getById(@NotNull(message = "id必填") Integer id);
}

View File

@@ -0,0 +1,20 @@
package com.sop.example.rest.examplerest.rest.impl;
import com.sop.example.rest.examplerest.rest.GoodsController;
import com.sop.example.rest.examplerest.rest.vo.GoodsVO;
import org.apache.dubbo.config.annotation.DubboService;
/**
* @author 六如
*/
@DubboService(validation = "true")
public class GoodsControllerImpl implements GoodsController {
@Override
public GoodsVO getById(Integer id) {
GoodsVO goodsVO = new GoodsVO();
goodsVO.setId(id);
goodsVO.setName("冰箱");
return goodsVO;
}
}

View File

@@ -0,0 +1,15 @@
package com.sop.example.rest.examplerest.rest.vo;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class GoodsVO {
private Integer id;
private String name;
}

View File

@@ -0,0 +1 @@
dubbo.registry.address=zookeeper://localhost:2181

View File

@@ -1,7 +1,7 @@
spring.profiles.active=dev spring.profiles.active=dev
server.port=7072 server.port=7073
spring.application.name=example-payment spring.application.name=rest-service
dubbo.protocol.name=dubbo dubbo.protocol.name=dubbo
dubbo.protocol.port=-1 dubbo.protocol.port=-1

View File

@@ -0,0 +1,6 @@
<html>
<body>
<h1>hello word!!!</h1>
<p>this is a html page</p>
</body>
</html>

View File

@@ -0,0 +1,13 @@
package com.sop.example.rest.examplerest;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ExampleRestApplicationTests {
@Test
void contextLoads() {
}
}

View File

@@ -1,92 +0,0 @@
package com.gitee.sop.storyweb.open;
import com.gitee.sop.storyweb.open.req.StorySaveRequest;
import com.gitee.sop.storyweb.open.resp.StoryResponse;
import com.gitee.sop.support.annotation.Open;
import com.gitee.sop.support.context.OpenContext;
import com.gitee.sop.support.dto.FileData;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
* 故事服务
*
* @author 六如
* @dubbo
*/
@Api("故事服务")
public interface OpenStory {
/**
* 新增故事
*
* @param storySaveRequest 入参
* @return 返回id
*/
@Open("story.save")
Integer save(StorySaveRequest storySaveRequest);
@Open("story.update")
Integer update(Integer id, StorySaveRequest storySaveRequest);
// 演示抛出异常
@Open("story.updateError")
Integer updateError(Integer id);
@ApiOperation(value = "根据id获取故事")
@Open("story.get")
StoryResponse getById(@NotNull(message = "id必填") Integer id);
// 需要授权
@Open(value = "story.get", version = "2.0", permission = true)
StoryResponse getByIdV2(Long id);
@Open(value = "story.get.context")
StoryResponse getContext(Long id, OpenContext context);
// 默认方法,注解放在这里也有效
@Open("story.find")
default StoryResponse getById(Integer id, String name) {
StoryResponse storyResponse = new StoryResponse();
storyResponse.setId(id);
storyResponse.setName(name);
return storyResponse;
}
// 默认方法,注解放在这里也有效
@Open("alipay.story.find")
default StoryResponse findByName(String name) {
StoryResponse storyResponse = new StoryResponse();
storyResponse.setName(name);
return storyResponse;
}
// 演示单文件上传
@Open("story.upload")
StoryResponse upload(StorySaveRequest storySaveRequest, FileData file);
// 演示多文件上传
@Open("story.upload.more")
StoryResponse upload2(
StorySaveRequest storySaveRequest,
@NotNull(message = "身份证正面必填") FileData idCardFront,
@NotNull(message = "身份证背面必填") FileData idCardBack
);
// 演示多文件上传
@Open("story.upload.list")
StoryResponse upload3(
StorySaveRequest storySaveRequest,
@Size(min = 2, message = "最少上传2个文件")
List<FileData> files
);
// 下载
@Open("story.download")
FileData download(Integer id);
}

View File

@@ -10,7 +10,8 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>example-story</module>
<module>example-payment</module> <module>example-payment</module>
<module>example-product</module>
<module>example-rest</module>
</modules> </modules>
</project> </project>

View File

@@ -1,3 +1,5 @@
# 服务示例 # 服务示例
- sop-story:微服务示例 - example-payment:微服务示例
- example-product微服务示例
- example-rest微服务示例,rest模式

View File

@@ -14,5 +14,8 @@ public final class SopConstants {
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
public static final String UTF8 = "UTF-8"; public static final String UTF8 = "UTF-8";
public static final String NULL = "null"; public static final String NULL = "null";
public static final String DUBBO_TAG = "dubbo.tag";
public static final String OPEN_CONTEXT = "open.context";
public static final String DEFAULT_VERSION = "1.0";
} }

View File

@@ -91,4 +91,6 @@ public class ApiConfig {
* 字段下划线小写形式 * 字段下划线小写形式
*/ */
private Boolean fieldSnakeCase = false; private Boolean fieldSnakeCase = false;
private String headerKeyTag = "open-tag";
} }

View File

@@ -4,6 +4,7 @@ import com.gitee.sop.gateway.service.ParamExecutor;
import com.gitee.sop.gateway.service.RouteService; import com.gitee.sop.gateway.service.RouteService;
import com.gitee.sop.gateway.service.Serde; import com.gitee.sop.gateway.service.Serde;
import com.gitee.sop.gateway.service.impl.ParamExecutorImpl; import com.gitee.sop.gateway.service.impl.ParamExecutorImpl;
import com.gitee.sop.gateway.service.impl.RestRouteServiceImpl;
import com.gitee.sop.gateway.service.impl.RouteServiceImpl; import com.gitee.sop.gateway.service.impl.RouteServiceImpl;
import com.gitee.sop.gateway.service.impl.SerdeGsonImpl; import com.gitee.sop.gateway.service.impl.SerdeGsonImpl;
import com.gitee.sop.gateway.service.impl.SerdeImpl; import com.gitee.sop.gateway.service.impl.SerdeImpl;
@@ -21,8 +22,8 @@ import com.gitee.sop.gateway.service.manager.impl.RedisIsvManagerImpl;
import com.gitee.sop.gateway.service.manager.impl.RedisSecretManager; import com.gitee.sop.gateway.service.manager.impl.RedisSecretManager;
import com.gitee.sop.support.message.OpenMessageFactory; import com.gitee.sop.support.message.OpenMessageFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -35,81 +36,60 @@ import javax.annotation.PostConstruct;
@Slf4j @Slf4j
public class GatewayConfig { public class GatewayConfig {
private static final String REDIS = "redis";
@Bean @Bean
@ConditionalOnProperty(value = "gateway.manager.api", havingValue = "local", matchIfMissing = true) public ApiManager localApiManager(@Value("${gateway.manager.api:local}") String gatewayManagerApi) {
public ApiManager localApiManager() { if (REDIS.equalsIgnoreCase(gatewayManagerApi)) {
return new RedisApiManagerImpl();
}
return new LocalApiManagerImpl(); return new LocalApiManagerImpl();
} }
@Bean
@ConditionalOnProperty(value = "gateway.manager.api", havingValue = "redis")
public ApiManager redisApiManager() {
return new RedisApiManagerImpl();
}
@Bean @Bean
@ConditionalOnProperty(value = "gateway.manager.isv", havingValue = "local", matchIfMissing = true) public IsvManager localIsvManager(@Value("${gateway.manager.isv:local}") String gatewayManagerIsv) {
public IsvManager localIsvManager() { if (REDIS.equalsIgnoreCase(gatewayManagerIsv)) {
return new RedisIsvManagerImpl();
}
return new LocalIsvManagerImpl(); return new LocalIsvManagerImpl();
} }
@Bean @Bean
@ConditionalOnProperty(value = "gateway.manager.isv", havingValue = "redis") public SecretManager localSecretManager(@Value("${gateway.manager.secret:local}") String gatewayManagerSecret) {
public IsvManager redisIsvManager() { if (REDIS.equalsIgnoreCase(gatewayManagerSecret)) {
return new RedisIsvManagerImpl(); return new RedisSecretManager();
} }
@Bean
@ConditionalOnProperty(value = "gateway.manager.secret", havingValue = "local", matchIfMissing = true)
public SecretManager localSecretManager() {
return new LocalSecretManagerImpl(); return new LocalSecretManagerImpl();
} }
@Bean @Bean
@ConditionalOnProperty(value = "gateway.manager.secret", havingValue = "redis") public IsvApiPermissionManager localIsvApiPermissionManager(
public SecretManager redisSecretManager() { @Value("${gateway.manager.isv-api-perm:local}") String gatewayManagerIsvApiPrm
return new RedisSecretManager(); ) {
} if (REDIS.equalsIgnoreCase(gatewayManagerIsvApiPrm)) {
return new RedisIsvApiPermissionManagerImpl();
@Bean }
@ConditionalOnProperty(value = "gateway.manager.isv-api-perm", havingValue = "local", matchIfMissing = true)
public IsvApiPermissionManager localIsvApiPermissionManager() {
return new LocalIsvApiPermissionManagerImpl(); return new LocalIsvApiPermissionManagerImpl();
} }
@Bean
@ConditionalOnProperty(value = "gateway.manager.isv-api-perm", havingValue = "redis")
public IsvApiPermissionManager redisIsvApiPermissionManager() {
return new RedisIsvApiPermissionManagerImpl();
}
// 默认使用fastjson2序列化 // 默认使用fastjson2序列化
@Bean @Bean
@ConditionalOnProperty(value = "gateway.serialize.json-formatter", havingValue = "fastjson2", matchIfMissing = true) public Serde serdeFastjson2(@Value("${gateway.serialize.json-formatter:fastjson2}") String gatewaySerializeJsonFormatter) {
public Serde serdeFastjson2() { log.info("[init]使用{}序列化", gatewaySerializeJsonFormatter);
log.info("[init]使用fastjson2序列化"); if ("gson".equalsIgnoreCase(gatewaySerializeJsonFormatter)) {
return new SerdeGsonImpl();
}
return new SerdeImpl(); return new SerdeImpl();
} }
@Bean
@ConditionalOnProperty(value = "gateway.serialize.json-formatter", havingValue = "gson")
public Serde serdeGson() {
log.info("[init]使用gson序列化");
return new SerdeGsonImpl();
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ParamExecutor paramExecutor() { public ParamExecutor paramExecutor() {
return new ParamExecutorImpl(); return new ParamExecutorImpl();
} }
@Bean
@ConditionalOnMissingBean
public RouteService routeService() {
return new RouteServiceImpl();
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public Serde serde() { public Serde serde() {

View File

@@ -1,12 +1,16 @@
package com.gitee.sop.gateway.controller; package com.gitee.sop.gateway.controller;
import com.gitee.sop.gateway.common.SopConstants;
import com.gitee.sop.gateway.request.ApiRequest;
import com.gitee.sop.gateway.request.ApiRequestContext; import com.gitee.sop.gateway.request.ApiRequestContext;
import com.gitee.sop.gateway.response.Response; import com.gitee.sop.gateway.response.Response;
import com.gitee.sop.gateway.service.ParamExecutor; import com.gitee.sop.gateway.service.ParamExecutor;
import com.gitee.sop.gateway.service.RouteService; import com.gitee.sop.gateway.service.RouteService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@@ -23,8 +27,13 @@ import java.io.IOException;
public class IndexController { public class IndexController {
@Autowired @Autowired
@Qualifier("routeService")
private RouteService routeService; private RouteService routeService;
@Autowired
@Qualifier("restRouteService")
private RouteService restRouteService;
@Autowired @Autowired
private ParamExecutor<HttpServletRequest, HttpServletResponse> paramExecutor; private ParamExecutor<HttpServletRequest, HttpServletResponse> paramExecutor;
@@ -39,19 +48,19 @@ public class IndexController {
* 请求入口 * 请求入口
* *
* @apiNote 参数描述 * @apiNote 参数描述
<pre> * <pre>
参数 类型 是否必填 最大长度 描述 示例值 * 参数 类型 是否必填 最大长度 描述 示例值
app_id String 是 32 平台分配给开发者的应用ID 2014072300007148 * app_id String 是 32 平台分配给开发者的应用ID 2014072300007148
method String 是 128 接口名称 alipay.trade.fastpay.refund.query * method String 是 128 接口名称 alipay.trade.fastpay.refund.query
format String 否 40 仅支持JSON JSON * format String 否 40 仅支持JSON JSON
charset String 是 10 请求使用的编码格式如utf-8,gbk,gb2312等 utf-8 * charset String 是 10 请求使用的编码格式如utf-8,gbk,gb2312等 utf-8
sign_type String 是 10 商户生成签名字符串所使用的签名算法类型目前支持RSA2和RSA推荐使用RSA2 RSA2 * sign_type String 是 10 商户生成签名字符串所使用的签名算法类型目前支持RSA2和RSA推荐使用RSA2 RSA2
sign String 是 344 商户请求参数的签名串,详见签名 详见示例 * sign String 是 344 商户请求参数的签名串,详见签名 详见示例
timestamp String 是 19 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50 * timestamp String 是 19 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50
version String 是 3 调用的接口版本固定为1.0 1.0 * version String 是 3 调用的接口版本固定为1.0 1.0
app_auth_token String 否 40 详见应用授权概述 * app_auth_token String 否 40 详见应用授权概述
biz_content String 是 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 * biz_content String 是 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档
</pre> * </pre>
*/ */
@RequestMapping(value = "${gateway.path:/api}", method = {RequestMethod.GET, RequestMethod.POST}) @RequestMapping(value = "${gateway.path:/api}", method = {RequestMethod.GET, RequestMethod.POST})
public void index(HttpServletRequest request, HttpServletResponse response) throws IOException { public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {
@@ -60,4 +69,46 @@ public class IndexController {
paramExecutor.write(apiRequestContext, apiResponse, response); paramExecutor.write(apiRequestContext, apiResponse, response);
} }
/**
* restful请求
*
* @param request
* @param response
* @param path 路径,对应@Open.value
* @param version 版本号
* @throws IOException
*/
@RequestMapping(value = "${gateway.rest}/{path}/{version}")
public void rest0(
HttpServletRequest request,
HttpServletResponse response,
@PathVariable String path,
@PathVariable String version
) throws IOException {
ApiRequestContext apiRequestContext = paramExecutor.build(request);
ApiRequest apiRequest = apiRequestContext.getApiRequest();
apiRequest.setMethod(path);
apiRequest.setVersion(version);
apiRequestContext.setIsRest(true);
Response apiResponse = restRouteService.route(apiRequestContext);
paramExecutor.write(apiRequestContext, apiResponse, response);
}
/**
* restful请求
*
* @param request
* @param response
* @param path 路径,对应@Open.value
* @throws IOException
*/
@RequestMapping(value = "${gateway.rest}/{path}")
public void rest(
HttpServletRequest request,
HttpServletResponse response,
@PathVariable String path
) throws IOException {
this.rest0(request, response, path, SopConstants.DEFAULT_VERSION);
}
} }

View File

@@ -32,7 +32,7 @@ public class ExceptionExecutorImpl implements ExceptionExecutor {
String exceptionClass = genericException.getExceptionClass(); String exceptionClass = genericException.getExceptionClass();
if (exceptionClass.contains(CONSTRAINT_VIOLATION_EXCEPTION)) { if (exceptionClass.contains(CONSTRAINT_VIOLATION_EXCEPTION)) {
String exceptionMessage = genericException.getExceptionMessage(); String exceptionMessage = genericException.getExceptionMessage();
// 参数校验:Failed to validate service: com.gitee.sop.storyweb.open.StoryService, method: save, cause: [ConstraintViolationImpl{interpolatedMessage='故事名称必填', propertyPath=storyName, rootBeanClass=class com.gitee.sop.storyweb.open.req.StorySaveDTO, messageTemplate='故事名称必填'}] // 参数校验:Failed to validate service: com.gitee.sop.storyweb.open.StoryService, method: save, cause: [ConstraintViolationImpl{interpolatedMessage='故事名称必填', propertyPath=productName, rootBeanClass=class com.gitee.sop.storyweb.open.req.StorySaveDTO, messageTemplate='故事名称必填'}]
Set<String> msgs = findErrorMsg(exceptionMessage); Set<String> msgs = findErrorMsg(exceptionMessage);
return ApiResponse.error(ErrorEnum.ISV_ERROR_PARAMETER, apiRequestContext.getLocale(), String.join(",", msgs)); return ApiResponse.error(ErrorEnum.ISV_ERROR_PARAMETER, apiRequestContext.getLocale(), String.join(",", msgs));
} }

View File

@@ -1,5 +1,6 @@
package com.gitee.sop.gateway.request; package com.gitee.sop.gateway.request;
import com.alibaba.fastjson2.JSONObject;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -33,4 +34,20 @@ public class ApiRequestContext {
* 上传文件 * 上传文件
*/ */
private UploadContext uploadContext; private UploadContext uploadContext;
/**
* 标签
*/
private String tag;
/**
* 是否需要签名校验
*/
@Builder.Default
private Boolean isRest = false;
/**
* 原始参数
*/
private JSONObject rawParams;
} }

View File

@@ -1,74 +0,0 @@
package com.gitee.sop.gateway.request;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.gitee.sop.gateway.common.SopConstants;
import com.gitee.sop.gateway.config.ApiConfig;
import com.gitee.sop.gateway.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
/**
* @author 六如
*/
@Slf4j
public class ApiRequestContextFactory {
private static final String CONTENT_TYPE = "content-type";
private static final String JSON_NAME = "json";
private static final String TEXT_NAME = "text";
private static final String MULTIPART = "multipart";
public static final String FORM = "form";
public static ApiRequestContext build(HttpServletRequest request, ApiConfig apiConfig) {
// get请求可能返回null
String contentType = request.getHeader(CONTENT_TYPE);
if (contentType == null) {
contentType = "";
}
ApiRequest apiRequest = new ApiRequest();
String ip = RequestUtil.getIP(request);
byte[] body;
try {
body = IOUtils.toByteArray(request.getInputStream());
} catch (IOException e) {
log.error("获取请求体失败", e);
body = new byte[0];
}
JSONObject params = null;
UploadContext uploadContext = null;
String contentTypeStr = contentType.toLowerCase();
// 如果是json方式提交
if (StringUtils.containsAny(contentTypeStr, JSON_NAME, TEXT_NAME)) {
params = JSON.parseObject(body);
} else if (StringUtils.containsIgnoreCase(contentTypeStr, MULTIPART)) {
// 如果是文件上传请求
RequestUtil.UploadInfo uploadInfo = RequestUtil.getUploadInfo(request);
params = uploadInfo.getApiParam();
uploadContext = uploadInfo.getUploadContext();
} else if (StringUtils.containsIgnoreCase(contentTypeStr, FORM)) {
// APPLICATION_FORM_URLENCODED请求
params = RequestUtil.parseQuerystring(new String(body, SopConstants.CHARSET_UTF8));
} else {
// get请求,参数跟在url后面
params = RequestUtil.parseParameterMap(request.getParameterMap());
}
if (params != null) {
apiRequest = params.toJavaObject(ApiRequest.class);
}
return ApiRequestContext.builder()
.apiRequest(apiRequest)
.locale(request.getLocale())
.ip(ip)
.uploadContext(uploadContext)
.traceId(UUID.randomUUID().toString().replace("-", ""))
.build();
}
}

View File

@@ -18,6 +18,9 @@ public interface Serde {
Map<String, Object> parseJson(String json); Map<String, Object> parseJson(String json);
default JSONObject parseObject(String json) { default JSONObject parseObject(String json) {
if (json == null) {
return null;
}
Map<String, Object> jsonObj = parseJson(json); Map<String, Object> jsonObj = parseJson(json);
return jsonObj instanceof JSONObject ? (JSONObject) jsonObj : new JSONObject(jsonObj); return jsonObj instanceof JSONObject ? (JSONObject) jsonObj : new JSONObject(jsonObj);
} }

View File

@@ -82,31 +82,38 @@ public class ParamExecutorImpl implements ParamExecutor<HttpServletRequest, Http
params = RequestUtil.parseParameterMap(request.getParameterMap()); params = RequestUtil.parseParameterMap(request.getParameterMap());
} }
if (params != null) { if (params != null) {
apiRequest = convertApiRequest(params); apiRequest = convertApiRequest(request, params);
} }
return ApiRequestContext.builder() return ApiRequestContext.builder()
.apiRequest(apiRequest) .apiRequest(apiRequest)
.locale(request.getLocale()) .locale(request.getLocale())
.ip(ip) .ip(ip)
.tag(getTag(request))
.uploadContext(uploadContext) .uploadContext(uploadContext)
.traceId(UUID.randomUUID().toString().replace("-", "")) .traceId(UUID.randomUUID().toString().replace("-", ""))
.rawParams(params)
.build(); .build();
} }
protected ApiRequest convertApiRequest(JSONObject jsonObject) { protected String getTag(HttpServletRequest request) {
return request.getHeader(apiConfig.getHeaderKeyTag());
}
protected ApiRequest convertApiRequest(HttpServletRequest request, JSONObject jsonObject) {
ApiRequest apiRequest = new ApiRequest(); ApiRequest apiRequest = new ApiRequest();
apiRequest.setAppId(jsonObject.getString(apiConfig.getAppIdName())); apiRequest.setAppId(jsonObject.getString(apiConfig.getAppIdName()));
apiRequest.setMethod(jsonObject.getString(apiConfig.getApiName())); apiRequest.setMethod(jsonObject.getString(apiConfig.getApiName()));
apiRequest.setFormat(jsonObject.getString(apiConfig.getFormatName())); apiRequest.setFormat(jsonObject.getString(apiConfig.getFormatName()));
apiRequest.setCharset(jsonObject.getString(apiConfig.getCharsetName())); apiRequest.setCharset(jsonObject.getOrDefault(apiConfig.getCharsetName(), SopConstants.UTF8).toString());
apiRequest.setSignType(jsonObject.getString(apiConfig.getSignTypeName())); apiRequest.setSignType(jsonObject.getString(apiConfig.getSignTypeName()));
apiRequest.setSign(jsonObject.getString(apiConfig.getSignName())); apiRequest.setSign(jsonObject.getString(apiConfig.getSignName()));
apiRequest.setTimestamp(jsonObject.getString(apiConfig.getTimestampName())); apiRequest.setTimestamp(jsonObject.getString(apiConfig.getTimestampName()));
apiRequest.setVersion(jsonObject.getString(apiConfig.getVersionName())); apiRequest.setVersion(jsonObject.getString(apiConfig.getVersionName()));
apiRequest.setNotifyUrl(jsonObject.getString(apiConfig.getNotifyUrlName())); apiRequest.setNotifyUrl(jsonObject.getString(apiConfig.getNotifyUrlName()));
apiRequest.setAppAuthToken(jsonObject.getString(apiConfig.getAppAuthTokenName())); apiRequest.setAppAuthToken(jsonObject.getString(apiConfig.getAppAuthTokenName()));
apiRequest.setBizContent(jsonObject.getString(apiConfig.getBizContentName())); String bizContent = jsonObject.getString(apiConfig.getBizContentName());
apiRequest.setBizContent(bizContent);
return apiRequest; return apiRequest;
} }

View File

@@ -0,0 +1,23 @@
package com.gitee.sop.gateway.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.gitee.sop.gateway.common.ApiInfoDTO;
import com.gitee.sop.gateway.request.ApiRequestContext;
import org.springframework.stereotype.Service;
/**
* @author 六如
*/
@Service("restRouteService")
public class RestRouteServiceImpl extends RouteServiceImpl {
@Override
protected ApiInfoDTO validate(ApiRequestContext apiRequestContext) {
return validator.validateRest(apiRequestContext);
}
@Override
protected JSONObject getParamObject(ApiRequestContext apiRequestContext) {
return apiRequestContext.getRawParams();
}
}

View File

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.gitee.sop.gateway.common.ApiInfoDTO; import com.gitee.sop.gateway.common.ApiInfoDTO;
import com.gitee.sop.gateway.common.ParamInfoDTO; import com.gitee.sop.gateway.common.ParamInfoDTO;
import com.gitee.sop.gateway.common.SopConstants;
import com.gitee.sop.gateway.exception.ApiException; import com.gitee.sop.gateway.exception.ApiException;
import com.gitee.sop.gateway.exception.ExceptionExecutor; import com.gitee.sop.gateway.exception.ExceptionExecutor;
import com.gitee.sop.gateway.interceptor.RouteInterceptor; import com.gitee.sop.gateway.interceptor.RouteInterceptor;
@@ -25,8 +26,11 @@ import com.gitee.sop.support.dto.CommonFileData;
import com.gitee.sop.support.dto.FileData; import com.gitee.sop.support.dto.FileData;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.ClassUtils; import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@@ -45,8 +49,10 @@ import java.util.Optional;
* @author 六如 * @author 六如
*/ */
@Slf4j @Slf4j
@Service("routeService")
public class RouteServiceImpl implements RouteService { public class RouteServiceImpl implements RouteService {
@Autowired @Autowired
protected Validator validator; protected Validator validator;
@@ -71,10 +77,10 @@ public class RouteServiceImpl implements RouteService {
log.info("收到客户端请求, ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest); log.info("收到客户端请求, ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest);
try { try {
// 接口校验 // 接口校验
ApiInfoDTO apiInfoDTO = validator.validate(apiRequestContext); ApiInfoDTO apiInfoDTO = validate(apiRequestContext);
// 执行拦截器前置动作 // 执行拦截器前置动作
this.doPreRoute(apiRequestContext, apiInfoDTO); this.doPreRoute(apiRequestContext, apiInfoDTO);
// 微服务结果 // 执行路由,返回微服务结果
Object result = doRoute(apiRequestContext, apiInfoDTO); Object result = doRoute(apiRequestContext, apiInfoDTO);
// 执行拦截器后置动作 // 执行拦截器后置动作
result = this.doAfterRoute(apiRequestContext, apiInfoDTO, result); result = this.doAfterRoute(apiRequestContext, apiInfoDTO, result);
@@ -87,14 +93,25 @@ public class RouteServiceImpl implements RouteService {
} }
} }
protected ApiInfoDTO validate(ApiRequestContext apiRequestContext) {
return validator.validate(apiRequestContext);
}
protected Object doRoute(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfo) { protected Object doRoute(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfo) {
String tag = apiRequestContext.getTag();
// 设置隔离环境
if (StringUtils.hasText(tag)) {
RpcContext.getClientAttachment().setAttachment(SopConstants.DUBBO_TAG, tag);
}
String paramInfo = apiInfo.getParamInfo(); String paramInfo = apiInfo.getParamInfo();
List<ParamInfoDTO> paramInfoList = JSON.parseArray(paramInfo, ParamInfoDTO.class); List<ParamInfoDTO> paramInfoList = JSON.parseArray(paramInfo, ParamInfoDTO.class);
OpenContext openRequest = buildOpenContext(apiRequestContext);
RpcContext.getClientAttachment().setAttachment(SopConstants.OPEN_CONTEXT, JSON.toJSONString(openRequest));
return genericServiceInvoker.invoke( return genericServiceInvoker.invoke(
apiInfo.getInterfaceClassName(), apiInfo.getInterfaceClassName(),
apiInfo.getMethodName(), apiInfo.getMethodName(),
buildParamType(paramInfoList), buildParamType(paramInfoList),
buildInvokeParam(apiRequestContext, paramInfoList) buildInvokeParam(apiRequestContext, paramInfoList, openRequest)
); );
} }
@@ -121,13 +138,14 @@ public class RouteServiceImpl implements RouteService {
.toArray(String[]::new); .toArray(String[]::new);
} }
protected Object[] buildInvokeParam(ApiRequestContext apiRequestContext, List<ParamInfoDTO> paramInfoList) { protected Object[] buildInvokeParam(
ApiRequestContext apiRequestContext,
List<ParamInfoDTO> paramInfoList,
OpenContext openRequest) {
if (ObjectUtils.isEmpty(paramInfoList)) { if (ObjectUtils.isEmpty(paramInfoList)) {
return new Object[0]; return new Object[0];
} }
ApiRequest apiRequest = apiRequestContext.getApiRequest(); JSONObject jsonObject = getParamObject(apiRequestContext);
String bizContent = apiRequest.getBizContent();
JSONObject jsonObject = serde.parseObject(bizContent);
List<Object> params = new ArrayList<>(); List<Object> params = new ArrayList<>();
for (ParamInfoDTO paramInfoDTO : paramInfoList) { for (ParamInfoDTO paramInfoDTO : paramInfoList) {
String type = paramInfoDTO.getType(); String type = paramInfoDTO.getType();
@@ -135,7 +153,6 @@ public class RouteServiceImpl implements RouteService {
// 上下文 // 上下文
if (Objects.equals(type, OpenContext.class.getName())) { if (Objects.equals(type, OpenContext.class.getName())) {
OpenContext openRequest = buildOpenContext(apiRequestContext);
params.add(openRequest); params.add(openRequest);
} else if (Objects.equals(type, FileData.class.getName()) || Objects.equals(actualType, FileData.class.getName())) { } else if (Objects.equals(type, FileData.class.getName()) || Objects.equals(actualType, FileData.class.getName())) {
// 处理文件上传 // 处理文件上传
@@ -156,7 +173,7 @@ public class RouteServiceImpl implements RouteService {
} }
params.add(value); params.add(value);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
log.error("找不到参数class, paramInfoDTO={}, apiRequest={}", paramInfoDTO, apiRequest, e); log.error("找不到参数class, paramInfoDTO={}, apiRequest={}", paramInfoDTO, apiRequestContext.getApiRequest(), e);
throw new RuntimeException("找不到class:" + type, e); throw new RuntimeException("找不到class:" + type, e);
} }
} else { } else {
@@ -167,6 +184,12 @@ public class RouteServiceImpl implements RouteService {
return params.toArray(new Object[0]); return params.toArray(new Object[0]);
} }
protected JSONObject getParamObject(ApiRequestContext apiRequestContext) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
String bizContent = apiRequest.getBizContent();
return serde.parseObject(bizContent);
}
protected OpenContext buildOpenContext(ApiRequestContext apiRequestContext) { protected OpenContext buildOpenContext(ApiRequestContext apiRequestContext) {
ApiRequest apiRequest = apiRequestContext.getApiRequest(); ApiRequest apiRequest = apiRequestContext.getApiRequest();
DefaultOpenContext defaultOpenRequest = new DefaultOpenContext(); DefaultOpenContext defaultOpenRequest = new DefaultOpenContext();
@@ -178,6 +201,8 @@ public class RouteServiceImpl implements RouteService {
defaultOpenRequest.setNotifyUrl(apiRequest.getNotifyUrl()); defaultOpenRequest.setNotifyUrl(apiRequest.getNotifyUrl());
defaultOpenRequest.setTraceId(apiRequestContext.getTraceId()); defaultOpenRequest.setTraceId(apiRequestContext.getTraceId());
defaultOpenRequest.setLocale(apiRequestContext.getLocale()); defaultOpenRequest.setLocale(apiRequestContext.getLocale());
defaultOpenRequest.initContext();
return defaultOpenRequest; return defaultOpenRequest;
} }

View File

@@ -80,10 +80,16 @@ public class ApiValidator implements Validator {
@Override @Override
public ApiInfoDTO validate(ApiRequestContext apiRequestContext) { public ApiInfoDTO validate(ApiRequestContext apiRequestContext) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
ApiInfoDTO apiInfo = apiManager.get(apiRequest.getMethod(), apiRequest.getVersion());
// 检查接口信息
checkApiInfo(apiRequestContext, apiInfo);
// 校验字段完整性 // 校验字段完整性
checkField(apiRequestContext); checkField(apiRequestContext);
// 检查isv // 检查isv
IsvDTO isvDTO = checkIsv(apiRequestContext); IsvDTO isvDTO = checkIsv(apiRequestContext);
// 检查isv接口授权
checkPermission(apiRequestContext, apiInfo, isvDTO);
// 校验签名 // 校验签名
checkSign(apiRequestContext, isvDTO); checkSign(apiRequestContext, isvDTO);
// 检查是否超时 // 检查是否超时
@@ -92,12 +98,6 @@ public class ApiValidator implements Validator {
checkFormat(apiRequestContext); checkFormat(apiRequestContext);
// IP能否访问 // IP能否访问
checkIP(apiRequestContext); checkIP(apiRequestContext);
ApiRequest apiRequest = apiRequestContext.getApiRequest();
ApiInfoDTO apiInfo = apiManager.get(apiRequest.getMethod(), apiRequest.getVersion());
// 检查接口信息
checkApiInfo(apiRequestContext, apiInfo, isvDTO);
// 检查上传文件 // 检查上传文件
checkUploadFile(apiRequestContext); checkUploadFile(apiRequestContext);
// 检查token // 检查token
@@ -105,7 +105,22 @@ public class ApiValidator implements Validator {
return apiInfo; return apiInfo;
} }
public void checkApiInfo(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfoDTO, IsvDTO isvDTO) { @Override
public ApiInfoDTO validateRest(ApiRequestContext apiRequestContext) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
ApiInfoDTO apiInfo = apiManager.get(apiRequest.getMethod(), apiRequest.getVersion());
// 检查接口信息
checkApiInfo(apiRequestContext, apiInfo);
// IP能否访问
checkIP(apiRequestContext);
// 检查上传文件
checkUploadFile(apiRequestContext);
// 检查token
checkToken(apiRequestContext);
return apiInfo;
}
public void checkApiInfo(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfoDTO) {
// 检查路由是否存在 // 检查路由是否存在
if (apiInfoDTO == null) { if (apiInfoDTO == null) {
throw new ApiException(ErrorEnum.ISV_INVALID_METHOD, apiRequestContext.getLocale()); throw new ApiException(ErrorEnum.ISV_INVALID_METHOD, apiRequestContext.getLocale());
@@ -114,11 +129,12 @@ public class ApiValidator implements Validator {
if (StatusEnum.of(apiInfoDTO.getStatus()) != StatusEnum.ENABLE) { if (StatusEnum.of(apiInfoDTO.getStatus()) != StatusEnum.ENABLE) {
throw new ApiException(ErrorEnum.ISP_API_DISABLED, apiRequestContext.getLocale()); throw new ApiException(ErrorEnum.ISP_API_DISABLED, apiRequestContext.getLocale());
} }
}
public void checkPermission(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfoDTO, IsvDTO isvDTO) {
// 校验是否需要授权访问 // 校验是否需要授权访问
boolean needCheckPermission = BooleanUtils.toBoolean(apiInfoDTO.getIsPermission()); boolean needCheckPermission = BooleanUtils.toBoolean(apiInfoDTO.getIsPermission());
if (needCheckPermission) { if (needCheckPermission) {
ApiRequest apiRequest = apiRequestContext.getApiRequest();
String appKey = apiRequest.getAppId();
boolean hasPermission = isvApiPermissionManager.hasPermission(isvDTO.getId(), apiInfoDTO); boolean hasPermission = isvApiPermissionManager.hasPermission(isvDTO.getId(), apiInfoDTO);
if (!hasPermission) { if (!hasPermission) {
throw new ApiException(ErrorEnum.ISV_ROUTE_NO_PERMISSIONS, apiRequestContext.getLocale()); throw new ApiException(ErrorEnum.ISV_ROUTE_NO_PERMISSIONS, apiRequestContext.getLocale());

View File

@@ -17,4 +17,12 @@ public interface Validator {
*/ */
ApiInfoDTO validate(ApiRequestContext apiRequestContext); ApiInfoDTO validate(ApiRequestContext apiRequestContext);
/**
* 校验rest
*
* @param apiRequestContext 请求内容
* @return 校验通过返回路由信息
*/
ApiInfoDTO validateRest(ApiRequestContext apiRequestContext);
} }

View File

@@ -5,6 +5,8 @@ server.port=8081
####### gateway config ####### ####### gateway config #######
# request entry path # request entry path
gateway.path=/api gateway.path=/api
# restfull entry path
gateway.rest=/rest
# manager cache type, local/redis # manager cache type, local/redis
gateway.manager.cache-type=local gateway.manager.cache-type=local
# api manager,local/redis # api manager,local/redis

View File

@@ -16,7 +16,7 @@
<maven.compiler.target>8</maven.compiler.target> <maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- dubbo版本 --> <!-- dubbo版本 -->
<dubbo.version>3.2.10</dubbo.version> <dubbo.version>3.2.16</dubbo.version>
</properties> </properties>
<dependencies> <dependencies>

11
sop-sdk/sdk-csharp/SDKCSharp/Model/GetStoryModel.cs Executable file → Normal file
View File

@@ -4,11 +4,12 @@ namespace SDKCSharp.Model
{ {
public class GetStoryModel public class GetStoryModel
{ {
/// <summary>
/// 故事名称 /// <summary>
/// id
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The id.</value>
[JsonProperty("name")] [JsonProperty("id")]
public string Name { get; set; } public int Id { get; set; }
} }
} }

8
sop-sdk/sdk-csharp/SDKCSharp/Program.cs Executable file → Normal file
View File

@@ -42,15 +42,15 @@ namespace SDKTest
private static void TestGet() private static void TestGet()
{ {
// 创建请求对象 // 创建请求对象
GetStoryRequest request = new GetStoryRequest(); GetProductRequest request = new GetProductRequest();
// 请求参数 // 请求参数
GetStoryModel model = new GetStoryModel(); GetStoryModel model = new GetStoryModel();
model.Name = "白雪公主"; model.Id = 1;
request.BizModel = model; request.BizModel = model;
// 发送请求 // 发送请求
Result<GetStoryResponse> result = client.Execute(request); Result<GetProductResponse> result = client.Execute(request);
if (result.IsSuccess()) if (result.IsSuccess())
{ {
// 返回结果 // 返回结果

View File

@@ -1,15 +0,0 @@
using System;
using SDKCSharp.Common;
using SDKCSharp.Response;
namespace SDKCSharp.Request
{
public class GetStoryRequest : BaseRequest<GetStoryResponse>
{
public override string GetMethod()
{
return "alipay.story.find";
}
}
}

View File

@@ -1,18 +0,0 @@
using System;
using Newtonsoft.Json;
namespace SDKCSharp.Response
{
public class GetStoryResponse
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("gmt_create")]
public string GmtCreate { get; set; }
}
}

View File

@@ -7,6 +7,6 @@ import lombok.Data;
*/ */
@Data @Data
public class DemoFileUploadModel { public class DemoFileUploadModel {
private String storyName; private String productName;
private String addTime; private String addTime;
} }

View File

@@ -1,13 +1,13 @@
package com.gitee.sop.sdk.request; package com.gitee.sop.sdk.request;
import com.gitee.sop.sdk.response.GetStoryResponse; import com.gitee.sop.sdk.response.GetProductResponse;
/** /**
* @author 六如 * @author 六如
*/ */
public class DemoFileUploadRequest extends BaseRequest<GetStoryResponse> { public class DemoFileUploadRequest extends BaseRequest<GetProductResponse> {
@Override @Override
protected String method() { protected String method() {
return "story.upload.more"; return "product.upload.more";
} }
} }

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.sdk.request;
import com.gitee.sop.sdk.response.GetProductResponse;
public class GetProductRequest extends BaseRequest<GetProductResponse> {
@Override
protected String method() {
return "product.get";
}
}

View File

@@ -1,11 +0,0 @@
package com.gitee.sop.sdk.request;
import com.gitee.sop.sdk.response.GetStoryResponse;
public class GetStoryRequest extends BaseRequest<GetStoryResponse> {
@Override
protected String method() {
return "story.get";
}
}

View File

@@ -5,7 +5,7 @@ import lombok.Data;
import java.util.Date; import java.util.Date;
@Data @Data
public class GetStoryResponse { public class GetProductResponse {
private Long id; private Long id;
private String name; private String name;
private Date addTime; private Date addTime;

View File

@@ -7,17 +7,15 @@ import com.gitee.sop.sdk.common.UploadFile;
import com.gitee.sop.sdk.model.DemoFileUploadModel; import com.gitee.sop.sdk.model.DemoFileUploadModel;
import com.gitee.sop.sdk.model.GetStoryModel; import com.gitee.sop.sdk.model.GetStoryModel;
import com.gitee.sop.sdk.request.DemoFileUploadRequest; import com.gitee.sop.sdk.request.DemoFileUploadRequest;
import com.gitee.sop.sdk.request.GetStoryRequest; import com.gitee.sop.sdk.request.GetProductRequest;
import com.gitee.sop.sdk.request.PayTradeWapPayModel; import com.gitee.sop.sdk.request.PayTradeWapPayModel;
import com.gitee.sop.sdk.request.PayTradeWapPayRequest; import com.gitee.sop.sdk.request.PayTradeWapPayRequest;
import com.gitee.sop.sdk.response.DemoFileUploadResponse; import com.gitee.sop.sdk.response.GetProductResponse;
import com.gitee.sop.sdk.response.GetStoryResponse;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List;
import com.gitee.sop.sdk.response.PayTradeWapPayResponse; import com.gitee.sop.sdk.response.PayTradeWapPayResponse;
import junit.framework.TestCase; import junit.framework.TestCase;
@@ -62,17 +60,17 @@ public class SdkTest extends TestCase {
@Test @Test
public void testGet() { public void testGet() {
// 创建请求对象 // 创建请求对象
GetStoryRequest request = new GetStoryRequest(); GetProductRequest request = new GetProductRequest();
// 请求参数 // 请求参数
GetStoryModel model = new GetStoryModel(); GetStoryModel model = new GetStoryModel();
model.setId(1); model.setId(1);
request.setBizModel(model); request.setBizModel(model);
// 发送请求 // 发送请求
Result<GetStoryResponse> result = client.execute(request); Result<GetProductResponse> result = client.execute(request);
if (result.isSuccess()) { if (result.isSuccess()) {
GetStoryResponse response = result.getData(); GetProductResponse response = result.getData();
// 返回结果 // 返回结果
System.out.println(String.format("response:%s", System.out.println(String.format("response:%s",
JSON.toJSONString(response))); JSON.toJSONString(response)));
@@ -88,7 +86,7 @@ public class SdkTest extends TestCase {
DemoFileUploadRequest request = new DemoFileUploadRequest(); DemoFileUploadRequest request = new DemoFileUploadRequest();
DemoFileUploadModel model = new DemoFileUploadModel(); DemoFileUploadModel model = new DemoFileUploadModel();
model.setStoryName("上传文件参数"); model.setProductName("上传文件参数");
model.setAddTime( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); model.setAddTime( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
request.setBizModel(model); request.setBizModel(model);
@@ -98,11 +96,11 @@ public class SdkTest extends TestCase {
request.addFile(new UploadFile("idCardFront", new File(root + "/src/main/resources/file1.txt"))); request.addFile(new UploadFile("idCardFront", new File(root + "/src/main/resources/file1.txt")));
request.addFile(new UploadFile("idCardBack", new File(root + "/src/main/resources/file2.txt"))); request.addFile(new UploadFile("idCardBack", new File(root + "/src/main/resources/file2.txt")));
Result<GetStoryResponse> result = client.execute(request); Result<GetProductResponse> result = client.execute(request);
System.out.println("--------------------"); System.out.println("--------------------");
if (result.isSuccess()) { if (result.isSuccess()) {
GetStoryResponse response = result.getData(); GetProductResponse response = result.getData();
System.out.println("您上传的文件信息:" + response); System.out.println("您上传的文件信息:" + response);
} else { } else {
System.out.println(JSON.toJSONString(result)); System.out.println(JSON.toJSONString(result));

View File

@@ -28,7 +28,7 @@
<dependency> <dependency>
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId> <artifactId>dubbo</artifactId>
<version>3.2.10</version> <version>3.2.16</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -1,6 +1,7 @@
package com.gitee.sop.support.context; package com.gitee.sop.support.context;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable; import java.io.Serializable;
import java.util.Locale; import java.util.Locale;
@@ -8,8 +9,9 @@ import java.util.Locale;
/** /**
* @author 六如 * @author 六如
*/ */
@EqualsAndHashCode(callSuper = true)
@Data @Data
public class DefaultOpenContext implements OpenContext, Serializable { public class DefaultOpenContext extends OpenContext implements Serializable {
private static final long serialVersionUID = -3218354527911979685L; private static final long serialVersionUID = -3218354527911979685L;
/** /**
@@ -51,4 +53,8 @@ public class DefaultOpenContext implements OpenContext, Serializable {
* locale * locale
*/ */
private Locale locale; private Locale locale;
public void initContext() {
this.setContext(this);
}
} }

View File

@@ -1,50 +1,61 @@
package com.gitee.sop.support.context; package com.gitee.sop.support.context;
import com.alibaba.ttl.TransmittableThreadLocal;
import java.util.Locale; import java.util.Locale;
/** /**
* @author 六如 * @author 六如
*/ */
public interface OpenContext { public abstract class OpenContext {
private static final ThreadLocal<OpenContext> THREAD_LOCAL = new TransmittableThreadLocal<>();
/** /**
* 获取appId * 获取appId
*/ */
String getAppId(); public abstract String getAppId();
/** /**
* 获取apiName * 获取apiName
*/ */
String getApiName(); public abstract String getApiName();
/** /**
* 获取version * 获取version
*/ */
String getVersion(); public abstract String getVersion();
/** /**
* 获取token,没有返回null * 获取token,没有返回null
*/ */
String getAppAuthToken(); public abstract String getAppAuthToken();
/** /**
* 获取客户端ip * 获取客户端ip
*/ */
String getClientIp(); public abstract String getClientIp();
/** /**
* 获取回调地址 * 获取回调地址
*/ */
String getNotifyUrl(); public abstract String getNotifyUrl();
/** /**
* 获取唯一请求id * 获取唯一请求id
*/ */
String getTraceId(); public abstract String getTraceId();
/** /**
* 获取locale * 获取locale
*/ */
Locale getLocale(); public abstract Locale getLocale();
protected void setContext(OpenContext openContext) {
THREAD_LOCAL.set(openContext);
}
public static OpenContext current() {
return THREAD_LOCAL.get();
}
} }

View File

@@ -0,0 +1,70 @@
package com.gitee.sop.support.dubbo;
import com.alibaba.fastjson2.JSON;
import com.gitee.sop.support.context.DefaultOpenContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcContextAttachment;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcServiceContext;
/**
* @author 六如
*/
@Slf4j
@Activate(group = {CommonConstants.PROVIDER})
public class DubboProviderTraceFilter implements Filter {
private static final String OPEN_CONTEXT = "open.context";
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
initOpenContext();
long startTime = System.currentTimeMillis();
long endTime = 0;
try {
Result result = invoker.invoke(invocation);
endTime = System.currentTimeMillis();
return result;
} finally {
trace(invocation, startTime, endTime);
}
}
private void initOpenContext() {
// 从 ServerAttachment 中读取的参数是从 Client 中传递过来的
RpcContextAttachment serverAttachment = RpcContext.getServerAttachment();
String attachment = serverAttachment.getAttachment(OPEN_CONTEXT);
if (attachment != null) {
DefaultOpenContext defaultOpenContext = JSON.parseObject(attachment, DefaultOpenContext.class);
defaultOpenContext.initContext();
}
}
private void trace(Invocation invocation, long startTime, long endTime) {
// 如果是服务提供端
RpcServiceContext serviceContext = RpcContext.getServiceContext();
// 获取客户端IP
String fromIP = serviceContext.getRemoteHost();
// 获取客户端应用名称
String fromApp = serviceContext.getRemoteApplicationName();
// 获取调用的接口
String interfaceName = invocation.getInvoker().getInterface().getName();
// 获取调用的方法
String methodName = serviceContext.getMethodName();
// 调用参数值
String param = JSON.toJSONString(serviceContext.getArguments());
// 超过500毫秒告警
long spend = (endTime == 0 ? System.currentTimeMillis() : endTime) - startTime;
if (log.isWarnEnabled() && spend > 500) {
log.warn("[sop_trace][dubbo_server][time_warn] Dubbo 耗时告警({}ms), from={}({}), methodName={}.{}, param={}",
spend, fromApp, fromIP, interfaceName, methodName, param);
}
}
}

View File

@@ -11,6 +11,8 @@ import lombok.Data;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.util.StringUtils;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -34,6 +36,12 @@ public class ApiRegister {
private static final Log LOG = LogFactory.getLog(ApiRegister.class); private static final Log LOG = LogFactory.getLog(ApiRegister.class);
/**
* Spring自带的参数提取工具类
*/
private static final DefaultParameterNameDiscoverer DISCOVERER = new DefaultParameterNameDiscoverer();
private final ApiRegisterService apiRegisterService; private final ApiRegisterService apiRegisterService;
public ApiRegister(ApiRegisterService apiRegisterService) { public ApiRegister(ApiRegisterService apiRegisterService) {
@@ -106,7 +114,7 @@ public class ApiRegister {
List<ParamInfo> paramInfos = buildParamInfo(method); List<ParamInfo> paramInfos = buildParamInfo(method);
RegisterDTO registerDTO = new RegisterDTO(); RegisterDTO registerDTO = new RegisterDTO();
registerDTO.setApplication(appName); registerDTO.setApplication(appName);
registerDTO.setApiName(open.value()); registerDTO.setApiName(getApiName(open));
registerDTO.setApiVersion(open.version()); registerDTO.setApiVersion(open.version());
registerDTO.setInterfaceClassName(interfaceClass.getName()); registerDTO.setInterfaceClassName(interfaceClass.getName());
registerDTO.setMethodName(method.getName()); registerDTO.setMethodName(method.getName());
@@ -125,6 +133,10 @@ public class ApiRegister {
} }
} }
protected String getApiName(Open open) {
return StringUtils.trimLeadingCharacter(open.value(), '/');
}
private List<ParamInfo> buildParamInfo(Method method) { private List<ParamInfo> buildParamInfo(Method method) {
Parameter[] parameters = method.getParameters(); Parameter[] parameters = method.getParameters();
if (parameters.length == 0) { if (parameters.length == 0) {

View File

@@ -0,0 +1 @@
dubboProviderTraceFilter=com.gitee.sop.support.dubbo.DubboProviderTraceFilter

View File

@@ -14,7 +14,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version> <spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<!-- dubbo版本 --> <!-- dubbo版本 -->
<dubbo.version>3.2.10</dubbo.version> <dubbo.version>3.2.16</dubbo.version>
</properties> </properties>
<dependencies> <dependencies>

View File

@@ -12,6 +12,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -268,6 +269,21 @@ public class Client {
return this; return this;
} }
/**
* 设置请求头
*
* @param key 请求头key
* @param value 请求头value
* @return 返回RequestBuilder
*/
public RequestBuilder header(String key, String value) {
if (this.header == null) {
this.header = new LinkedHashMap<>(8);
}
header.put(key, value);
return this;
}
/** /**
* 是否忽略签名验证 * 是否忽略签名验证
* *

View File

@@ -2,6 +2,7 @@ package com.gitee.sop.test;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.gitee.sop.test.sign.AlipaySignature; import com.gitee.sop.test.sign.AlipaySignature;
import okhttp3.internal.http2.Header;
import org.junit.Test; import org.junit.Test;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -77,8 +78,8 @@ public class AlipayClientPostTest extends TestBase {
// 公共请求参数 // 公共请求参数
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId); params.put("app_id", appId);
params.put("method", "story.get"); params.put("method", "pay.trade.wap.pay");
params.put("format", "xml"); // xml params.put("format", "xml");
params.put("charset", "utf-8"); params.put("charset", "utf-8");
params.put("sign_type", "RSA2"); params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
@@ -86,44 +87,10 @@ public class AlipayClientPostTest extends TestBase {
// 业务参数 // 业务参数
Map<String, Object> bizContent = new HashMap<>(); Map<String, Object> bizContent = new HashMap<>();
bizContent.put("id", "1"); bizContent.put("outTradeNo", "70501111111S001111119");
bizContent.put("name", "葫芦娃"); bizContent.put("totalAmount", "9.00");
bizContent.put("subject", "衣服");
params.put("biz_content", JSON.toJSONString(bizContent)); bizContent.put("productCode", "QUICK_WAP_WAY");
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
System.out.println("待签名内容:" + content);
System.out.println("签名(sign)" + sign);
System.out.println("URL参数" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");
String responseData = postJson(url, params);// 发送请求
// <ApiResponse><code>0</code><msg>success</msg><sub_code></sub_code><sub_msg></sub_msg><data><name>乌鸦喝水</name><id>1</id></data></ApiResponse>
System.out.println(responseData);
}
@Test
public void testFind() throws Exception {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "story.find");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("id", "122");
bizContent.put("name", "葫芦娃");
params.put("biz_content", JSON.toJSONString(bizContent)); params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params); String content = AlipaySignature.getSignContent(params);
@@ -143,12 +110,12 @@ public class AlipayClientPostTest extends TestBase {
} }
@Test @Test
public void testSave() throws Exception { public void testSearch() throws Exception {
// 公共请求参数 // 公共请求参数
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId); params.put("app_id", appId);
params.put("method", "story.save"); params.put("method", "pay.order.search");
params.put("format", "json"); params.put("format", "json");
params.put("charset", "utf-8"); params.put("charset", "utf-8");
params.put("sign_type", "RSA2"); params.put("sign_type", "RSA2");
@@ -157,8 +124,7 @@ public class AlipayClientPostTest extends TestBase {
// 业务参数 // 业务参数
Map<String, Object> bizContent = new HashMap<>(); Map<String, Object> bizContent = new HashMap<>();
bizContent.put("storyName", "小猫钓鱼"); bizContent.put("orderNo", "70501111111S001111119");
bizContent.put("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("biz_content", JSON.toJSONString(bizContent)); params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params); String content = AlipaySignature.getSignContent(params);
@@ -173,45 +139,10 @@ public class AlipayClientPostTest extends TestBase {
System.out.println("URL参数" + buildUrlQuery(params)); System.out.println("URL参数" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------"); System.out.println("----------- 返回结果 -----------");
String responseData = postJson(url, params);// 发送请求 Header[] header = {new Header("open-env", "gray")};
// Header[] header = new Header[0];
String responseData = postJson(url, params, header);// 发送请求
System.out.println(responseData); System.out.println(responseData);
} }
@Test
public void testUpdate() throws Exception {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "story.update");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("id", "1");
bizContent.put("storyName", "花仙子");
bizContent.put("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
System.out.println("待签名内容:" + content);
System.out.println("签名(sign)" + sign);
System.out.println("URL参数" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");
String responseData = postJson(url, params);// 发送请求
System.out.println(responseData);
}
} }

View File

@@ -34,7 +34,21 @@ public class AllInOneTest extends TestBase {
*/ */
public void testGet() { public void testGet() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get") .method("product.get")
.version("1.0")
.bizContent(new BizContent().add("id", "1"))
.httpMethod(HttpTool.HTTPMethod.GET);
client.execute(requestBuilder);
}
/**
* 以get方式提交
*/
public void testGetGray() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("product.get")
.header("open-env", "gray")
.version("1.0") .version("1.0")
.bizContent(new BizContent().add("id", "1")) .bizContent(new BizContent().add("id", "1"))
.httpMethod(HttpTool.HTTPMethod.GET); .httpMethod(HttpTool.HTTPMethod.GET);
@@ -47,9 +61,9 @@ public class AllInOneTest extends TestBase {
*/ */
public void testPostForm() { public void testPostForm() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get") .method("product.get")
.version("1.0") .version("1.0")
.bizContent(new BizContent().add("id", "1").add("name", "葫芦娃")) .bizContent(new BizContent().add("id", "1").add("name", "彩电"))
.httpMethod(HttpTool.HTTPMethod.POST); .httpMethod(HttpTool.HTTPMethod.POST);
client.execute(requestBuilder); client.execute(requestBuilder);
@@ -60,11 +74,11 @@ public class AllInOneTest extends TestBase {
*/ */
public void testPostJSON() { public void testPostJSON() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get") .method("product.get")
.version("1.0") .version("1.0")
// 以json方式提交 // 以json方式提交
.postJson(true) .postJson(true)
.bizContent(new BizContent().add("id", "1").add("name", "葫芦娃")); .bizContent(new BizContent().add("id", "1").add("name", "彩电"));
client.execute(requestBuilder); client.execute(requestBuilder);
} }
@@ -79,12 +93,12 @@ public class AllInOneTest extends TestBase {
header.put("Accept-Language", "en-US"); header.put("Accept-Language", "en-US");
// header.put("Accept-Language", "zh-CN"); // header.put("Accept-Language", "zh-CN");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.updateError") .method("product.updateError")
.version("1.0") .version("1.0")
.header(header) .header(header)
// 以json方式提交 // 以json方式提交
.postJson(true) .postJson(true)
.bizContent(new BizContent().add("id", "0").add("name", "葫芦娃")) .bizContent(new BizContent().add("id", "0").add("name", "彩电"))
.callback((requestInfo, responseData) -> { .callback((requestInfo, responseData) -> {
System.out.println(responseData); System.out.println(responseData);
JSONObject jsonObject = JSON.parseObject(responseData); JSONObject jsonObject = JSON.parseObject(responseData);
@@ -100,9 +114,9 @@ public class AllInOneTest extends TestBase {
*/ */
public void testGet_v2() { public void testGet_v2() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get") .method("product.get")
.version("2.0") .version("2.0")
.bizContent(new BizContent().add("id", "2").add("name", "葫芦娃2")) .bizContent(new BizContent().add("id", "2").add("name", "电脑"))
.httpMethod(HttpTool.HTTPMethod.GET) .httpMethod(HttpTool.HTTPMethod.GET)
.callback((requestInfo, responseData) -> { .callback((requestInfo, responseData) -> {
System.out.println(responseData); System.out.println(responseData);
@@ -118,10 +132,10 @@ public class AllInOneTest extends TestBase {
*/ */
public void testGetContext() { public void testGetContext() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get.context") .method("product.get.context")
.version("1.0") .version("1.0")
.notifyUrl("http://www.baidu.com") .notifyUrl("http://www.baidu.com")
.bizContent(new BizContent().add("id", "2").add("name", "葫芦娃2")) .bizContent(new BizContent().add("id", "2").add("name", "电脑"))
.httpMethod(HttpTool.HTTPMethod.GET) .httpMethod(HttpTool.HTTPMethod.GET)
.callback((requestInfo, responseData) -> { .callback((requestInfo, responseData) -> {
System.out.println(responseData); System.out.println(responseData);
@@ -138,7 +152,7 @@ public class AllInOneTest extends TestBase {
*/ */
public void testPermission() { public void testPermission() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get.permission") .method("product.get.permission")
.version("1.0") .version("1.0")
.bizContent(new BizContent()) .bizContent(new BizContent())
.httpMethod(HttpTool.HTTPMethod.GET) .httpMethod(HttpTool.HTTPMethod.GET)
@@ -159,10 +173,10 @@ public class AllInOneTest extends TestBase {
Client client = new Client(url, appId, privateKey); Client client = new Client(url, appId, privateKey);
String root = System.getProperty("user.dir"); String root = System.getProperty("user.dir");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.upload") .method("product.upload")
.version("1.0") .version("1.0")
.bizContent( .bizContent(
new BizContent().add("storyName", "白雪公主") new BizContent().add("productName", "冰箱")
.add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) .add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
) )
// 添加文件 // 添加文件
@@ -181,10 +195,10 @@ public class AllInOneTest extends TestBase {
Client client = new Client(url, appId, privateKey); Client client = new Client(url, appId, privateKey);
String root = System.getProperty("user.dir"); String root = System.getProperty("user.dir");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.upload.more") .method("product.upload.more")
.version("1.0") .version("1.0")
.bizContent( .bizContent(
new BizContent().add("storyName", "白雪公主") new BizContent().add("productName", "冰箱")
.add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) .add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
) )
// 添加文件 // 添加文件
@@ -203,10 +217,10 @@ public class AllInOneTest extends TestBase {
public void testFileList() { public void testFileList() {
String root = System.getProperty("user.dir"); String root = System.getProperty("user.dir");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.upload.list") .method("product.upload.list")
.version("1.0") .version("1.0")
.bizContent( .bizContent(
new BizContent().add("storyName", "白雪公主") new BizContent().add("productName", "冰箱")
.add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) .add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
) )
// 添加文件 // 添加文件
@@ -223,7 +237,7 @@ public class AllInOneTest extends TestBase {
Client client = new Client(url, appId, privateKey); Client client = new Client(url, appId, privateKey);
String root = System.getProperty("user.dir"); String root = System.getProperty("user.dir");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.upload") .method("product.upload")
.version("1.0") .version("1.0")
.bizContent(new BizContent().add("remark", "test file upload")) .bizContent(new BizContent().add("remark", "test file upload"))
// 添加文件, 这个文件1.5M,上传会报错 // 添加文件, 这个文件1.5M,上传会报错
@@ -243,10 +257,10 @@ public class AllInOneTest extends TestBase {
public void testFileMaxSize() { public void testFileMaxSize() {
String root = System.getProperty("user.dir"); String root = System.getProperty("user.dir");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.upload.list") .method("product.upload.list")
.version("1.0") .version("1.0")
.bizContent( .bizContent(
new BizContent().add("storyName", "白雪公主") new BizContent().add("productName", "冰箱")
.add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) .add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
) )
.callback((requestInfo, responseData) -> { .callback((requestInfo, responseData) -> {
@@ -274,7 +288,7 @@ public class AllInOneTest extends TestBase {
File file = new File(root + "/src/main/resources/large_data.txt"); File file = new File(root + "/src/main/resources/large_data.txt");
String fileContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8); String fileContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.find") .method("product.find")
.version("1.0") .version("1.0")
.bizContent(new BizContent().add("id", 111)) .bizContent(new BizContent().add("id", 111))
.bizContent(new BizContent().add("name", fileContent)) .bizContent(new BizContent().add("name", fileContent))
@@ -288,7 +302,7 @@ public class AllInOneTest extends TestBase {
*/ */
public void testDownloadFile() throws IOException { public void testDownloadFile() throws IOException {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.download") .method("product.download")
.version("1.0") .version("1.0")
.bizContent(new BizContent().add("id", 1)) .bizContent(new BizContent().add("id", 1))
.httpMethod(HttpTool.HTTPMethod.GET); .httpMethod(HttpTool.HTTPMethod.GET);
@@ -302,10 +316,10 @@ public class AllInOneTest extends TestBase {
public void testToken() { public void testToken() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.save") .method("product.save")
.version("1.0") .version("1.0")
.bizContent( .bizContent(
new BizContent().add("storyName", "白雪公主") new BizContent().add("productName", "冰箱")
.add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) .add("addTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
) )
.appAuthToken("asdfasdfadsf") .appAuthToken("asdfasdfadsf")
@@ -322,10 +336,10 @@ public class AllInOneTest extends TestBase {
Map<String, String> header = new HashMap<>(4); Map<String, String> header = new HashMap<>(4);
header.put("Accept-Language", "en-US"); header.put("Accept-Language", "en-US");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get==") // 制造错误的接口名 .method("product.get==") // 制造错误的接口名
.version("1.0") .version("1.0")
.header(header) .header(header)
.bizContent(new BizContent().add("id", "1").add("name", "葫芦娃")) .bizContent(new BizContent().add("id", "1").add("name", "彩电"))
.httpMethod(HttpTool.HTTPMethod.GET) .httpMethod(HttpTool.HTTPMethod.GET)
.callback((requestInfo, responseData) -> { .callback((requestInfo, responseData) -> {
System.out.println(responseData); System.out.println(responseData);

View File

@@ -2,12 +2,14 @@ package com.gitee.sop.test;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import junit.framework.TestCase; import junit.framework.TestCase;
import okhttp3.internal.http2.Header;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
/** /**
@@ -31,9 +33,13 @@ public class TestBase extends TestCase {
} }
} }
public static String postJson(String url, Map<String, String> params) { public static String postJson(String url, Map<String, String> params, Header... headers) {
try { try {
return httpTool.requestJson(url, JSON.toJSONString(params), Collections.emptyMap()); Map<String, String> headerMap = new LinkedHashMap<>();
for (Header header : headers) {
headerMap.put(header.name.utf8(), header.value.utf8());
}
return httpTool.requestJson(url, JSON.toJSONString(params), headerMap);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("网络请求异常", e); throw new RuntimeException("网络请求异常", e);
} }

527
sop.sql

File diff suppressed because one or more lines are too long