# FunCodeHub
**Repository Path**: wx-wang-xin/fun-code-hub
## Basic Information
- **Project Name**: FunCodeHub
- **Description**: 收录各种工作中有意思的模块化功能代码
-支付宝SDK的二次封装
-微信SDK的二次封装
- **Primary Language**: Java
- **License**: MulanPSL-2.0
- **Default Branch**: develop
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-11-19
- **Last Updated**: 2026-03-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# FunCodeHub
## 介绍
FunCodeHub 是一个收集各种工作中有意思的模块化功能代码的项目。它旨在通过设计模式与最佳实践,帮助开发者解耦代码、提高复用性并降低修改成本。
## 软件架构
项目基于 Java 语言开发,使用以下主要依赖:
- **log4j**:用于日志记录
- **lombok**:简化 Java 代码开发
- **freemarker**:模板引擎,用于动态生成文本文件
整体架构采用模块化设计,支持多种设计模式,包括策略模式、观察者模式和模板方法模式等。
## 整理的代码功能
- ✅ **策略模板责任链模式**:通过策略与处理器的组合实现业务逻辑的解耦和高度复用。
- ✅ **观察者模式**:实现异步事件通知机制,解耦主线程与副线程之间的依赖。
- ✅ **模板文件输出**:基于 freemarker 模板引擎,动态生成文本文件(如配置文件)。
- 🌤️ **Linux/Windows 脚本自动化**
- 🌤️ **非 Spring 下的 AOP 实现**
- 🌤️ **自定义适配器模式**
- 🌤️ **Netty网络框架二次封装**
- ✅ **支付宝 SDK 便捷性二次封装**
- ✅ **微信 SDK 便捷性二次封装**
## 使用说明
### 1. 策略模板责任链模式
实现自定义处理器和策略类,继承 `BaseProcessor` 和 `BaseStrategy` 并重写相应方法。
需要自定义你的处理类,你的请求类和响应类,并自定义你的策略类来进行调用
**调用示例:**
```java
/**
* 你的自定义处理器类 需要继承 BaseProcessor 类并指定请求类和响应类
*
* YourRequest 请求类需要继承 BaseProcessorRequest 类 并且指定响应类
* YourResponse 响应类需要继承 BaseProcessorResult 类
*
* 结合spring的bean管理机制或自主实现单例模式能有效降低重复创建处理器对象造成的性能/内存损失
**/
public class YourProcessor extends BaseProcessor {
/**
* 重写父类 BaseProcessor 的handle方法处理业务逻辑
* @param request 请求参数
* @return YourResponse
*/
@Override
public YourResponse handle(YourRequest request) {
// ...你的业务逻辑代码
return next(request); // next方法会切换下一个子处理器执行对应处理的业务代码,也可以直接返回response,这样就不会切换处理器
}
}
```
```java
/**
* 你的自定义策略类 需要继承 BaseStrategy 类并指定请求类和响应类
*
* YourRequest 请求类需要继承 BaseProcessorRequest 类 并且指定响应类
* YourResponse 响应类需要继承 BaseProcessorResult 类
*
* 结合spring的bean管理机制或自主实现单例模式能有效降低重复创建策略/处理器对象造成的性能/内存损失
**/
public class YourStrategy extends BaseStrategy {
/**
* 重写父类 BaseStrategy 的初始化方法并创建生成一个总处理器
* 在总处理器中分发子处理器来进行策略处理
* 不同的策略执行不同的处理器,这样策略可以高度自定义化
* 不同的子处理器可以被不同的策略调用,实现高复用,并且不影响其他的策略
* 子处理器中的代码维护更加简单,不用像原始代码需要重新管理if else
* 能有效解耦代码之间的关联
*/
@Override
public void init() {
if (this.handle == null) {
this.handle = new BaseHandlerPro.Builder()
.addHandle(new yourBusinessProcessor())
// .... 添加更多的子处理器
.builder();
}
}
}
```
```java
/**
* 调用示例代码
*/
public class Test {
public static void main(String[] args) {
DemoStrategy strategy = new DemoStrategy();
DemoRequest request = new DemoRequest();
request.setName("张三");
request.setIp("127.0.0.1");
request.setUserId("114514");
DemoResponse response = strategy.start(request);
// 假如使用spring 将你的自定义策略注册为bean则可以用这样的方式调用
@Autowired
private YourStrategy strategy;
DemoRequest request = new DemoRequest();
request.setName("张三");
request.setIp("127.0.0.1");
request.setUserId("114514");
DemoResponse response = strategy.start(request);
}
}
```
### 2. 观察者模式
定义观察者类并继承 `ElEventObserver`,通过 `ElEventCenter` 注册和通知事件。
**调用示例:**
```java
public class Test {
public static void main(String[] args) {
DemoTestObserver obs = ObserverDepotLoader.getObserver(DemoTestObserver.class);
DemoTestObserver2 obs2 = ObserverDepotLoader.getObserver(DemoTestObserver2.class);
// 添加一个观察者到事件中心(可以使用单例模式或spring托管 避免重复创建对象)
ElEventCenter eventCenter = new ElEventCenter();
// 添加通用观察者
eventCenter.addObserver(obs);
eventCenter.addObserver(obs2);
// 添加指定ID观察者
eventCenter.addObserver("114514", obs);
// 使用异步线程通知指定ID的观察者
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(2000); // 模拟异步通知
eventCenter.execute("114514", "这里是通知114514的文本信息");
} catch (Exception e) {
e.printStackTrace();
}
});
// 使用异步线程通知全局观察者
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1500); // 模拟异步通知
eventCenter.execute("这里是通知全局的文本信息");
} catch (Exception e) {
e.printStackTrace();
}
});
// 使用异步线程通知指定类型的观察者(指定对应观察者对象)
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000); // 模拟异步通知
eventCenter.execute(DemoTestObserver2.class, "这里是通知DemoTestObserver2.class的文本信息");
} catch (Exception e) {
e.printStackTrace();
}
});
// 这里阻塞6秒,等待异步线程的的参数传递
System.out.println("主线程阻塞开始");
try {
Thread.sleep(6000);
System.out.println("主线程阻塞结束");
} catch (Exception ignored) {}
}
}
```
### 3. 模板文件生成
通过 `TemplateFileBuilder` 构建文件生成器,使用 freemarker 模板引擎动态生成文本文件。
**调用示例:**
```java
public class Test {
public static void main(String[] args) {
// 整合需要自动生成的文件(仅能生成文本类型的文件,二进制,word,PPT,表格等非纯文本内容文件不支持生成)
FileAutoGenerateParams params = new FileAutoGenerateParams();
params.setFileName("www.baidu.com");
params.setFileOutPath("D:/manager-vue/template/result");
params.setFileType("conf");
params.setTemplatePath("D:/manager-vue/template");
params.setTemplateFileName("nginx.conf-1");
// 动态替换模板内文本的参数
Map replaceParams = new HashMap<>();
replaceParams.put("host", "www.baidu.com");
params.setFilePageParams(replaceParams);
// 创建构造器
TemplateFileBuilder builder = new TemplateFileBuilder
.Builder()
.configs(Stream.of(params).collect(Collectors.toList()))
.builder();
// 调用文件生成代码
builder.generateFile();
System.out.println("文件生成成功!");
}
}
```
### 4. 支付宝SDK封装
通过 `AlipayClientBuilder` 构建支付宝客户端,使用支付宝crt证书进行客户端构建
**调用示例:**
```java
public class AlipayApiTestDemo {
public static void main(String[] args) {
// 仅支持支付宝证书模式,需要支付宝crt证书来构建客户端请求
// 创建继承 AlipayClientBaseActuator 的支付宝请求类
AlipayTradeTestService service = new AlipayTradeTestService();
// 获取支付宝客户端构建对象(AlipayClientModel 模式)
AlipayClientBuilder builder = new AlipayClientBuilder
.Builder()
.model(new AlipayClientModel())
.builder();
// 获取支付宝客户端构建对象(传参模式)
AlipayClientBuilder paramsBuilder = new AlipayClientBuilder
.Builder()
.appId("")
.privateKey("")
.publicKey("")
.alipayPubCertPath("")
.appCertPath("")
.rootCertPath("")
.dev() // 使用开发环境
/*
* 使用单例模式管理器(AlipayManage)管理 开启此选项会自动将构造器添加到管理器中
* 此时可以使用 AlipayManage.INSTANCE.getItem("your alipay app id")来重复此唯一实例
*/
.single()
.builder();
// 仅做封装,所有的支付宝请求Request对象和Response对象都可以使用官方文档中标明的代码
AlipayTradeCreateResponse response = service.createJsapiPay(builder, new AlipayTradeCreateModel());
System.out.println("支付宝接口响应结果:" + response.getBody());
// 支付宝异步通知验签
SignUtils.checkSign(builder, new HashMap<>());
// 获取支付宝ISV授权url
SignUtils.getAlipayAuthUrl("your isv app id", "http://127.0.0.1", "your state");
// 切换构造器的服务环境
builder.useDev(); // 切换到开发环境
builder.useProd(); // 切换到生产环境
}
}
```
```java
/**
* 支付宝交易API service 演示
*/
public class AlipayTradeTestService extends AlipayClientBaseActuator {
/**
* 创建支付宝JSAPI支付
* @param builder 支付宝客户端构造器
* @param model 支付宝交易创建model
* @return AlipayTradeCreateResponse
*/
public AlipayTradeCreateResponse createJsapiPay(AlipayClientBuilder builder, AlipayTradeCreateModel model) {
AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
request.setBizModel(model);
return sendApiRequest(builder, request);
}
}
```
### 4. 微信SDK封装
通过 `WechatV3ClientBuilder` 构建微信客户端,使用微信商户证书进行客户端构建。
原微信JAVA-V3-SDK官方维护不积极,大量新接口没有对应的代码实现,而根据原有的SDK结构去写自定义的Service
会十分混乱。
**因此二次封装了SDK,使自定义的Service能遵循一定规范编写的同时降低编写复杂度,让开发专注请求参数和响应参数的处理
无需关注如何去实例化client如何处理服务类的Build,简化开发难度。**
**微信官方v3-SDK仓库:https://github.com/wechatpay-apiv3/wechatpay-java**
**调用示例:**
```java
public class WechatV3SdkDemo {
public static void main(String[] args) {
// 获取微信V3客户端构造器
WechatV3ClientBuilder builder = new WechatV3ClientBuilder
.Builder()
.setConfig(new WechatV3ClientConfig()) // 客户端配置项
.singleModel() // 开启单例模式-构造器创建成功将会自动被WechatManage托管 使用 WechatManage.INSTANCE.getItem(appId, mchId) 即可获取全局唯一对象
.isv() // 开启服务商模式-仅用作Service切换服务商地址,如自定义的服务需要手动判断类型切换到对应的微信接口地址
.builder();
// 创建JSAPI服务
WechatV3JsApiPayService jsApiPayService = new WechatV3JsApiPayService();
// 请求JSAPI接口
Map response = jsApiPayService.create(builder, new WechatJsApiOrderCreateRequest());
// 查询订单信息
WechatV3PayOrderCommonQueryService commonQueryService = new WechatV3PayOrderCommonQueryService();
CommonTransaction transaction = commonQueryService.query(builder, new WechatJsApiOrderQueryByWechatOrderIdRequest());
// 订单关闭
WechatV3PayOrderCommonCloseService closeService = new WechatV3PayOrderCommonCloseService();
boolean isClose = closeService.close(builder, new WechatJsApiOrderQueryByWechatOrderIdRequest());
// 订单退款
WechatV3PayOrderRefundService refundService = new WechatV3PayOrderRefundService();
WechatPayOrderRefundResponse refundResponse = refundService.refundApplyFor(builder, new WechatPayOrderRefundRequest());
// 退款查询
WechatPayOrderRefundResponse refundQueryResponse = refundService.refundQuery(builder, "outRefundNo");
// 异常退款
WechatPayOrderRefundResponse abnormalRefundResponse = refundService.applyAbnormalRefund(builder, new WechatAbnormalRefundApplyRequest(), "refundId");
}
}
```
*由于微信生态过于庞大,接口数量众多,很多V3接口没能写入。此时需要自定义对应的Service来适配对接微信接口。
自定义主体基本为:请求类-响应类-Service*
**自定义Service示例:**
**1.创建自定义Service继承WechatV3ApiBaseActuator接口执行器**
```java
import com.mpstop.wechat.builder.WechatV3ClientBuilder;
import java.util.HashMap;
public class WechatV3SdkCustomService extends WechatV3ApiBaseActuator {
/**
* 创建订单(模拟一个微信新出的接口)
* @param builder
* @param request
* @return
*/
public WechatV3SdkCustomResponse create(WechatV3ClientBuilder builder,
WechatV3SdkCustomRequest request) {
// 请求地址(根据builder.isIsvModel()来判断是否使用ISV的接口地址)
String requestUrl = builder.isIsvModel() ? "/v3/pay/partner/new/simple/pay" : "/v3/pay/transactions/new/simple/pay";
// 获取HTTP请求对象
HttpRequest httpRequest = getSendHttpRequest(builder, requestUrl, HttpMethod.POST, request);
// 发送微信请求
return sendHttpRequest(builder, httpRequest, WechatV3SdkCustomResponse.class).getServiceResponse();
}
/**
* 在实际开发中会遇到请求微信的Get类型请求。
* 会存在例如/query/order/{orderNo}?mchId=114514这种情况。
* 此时需要使用替换模板去替换对应的参数,适用于Get和Post请求
*/
public void replaceDemo() {
// 获取微信url参数替换模板
WechatGetRequestUrlReplaceModel replaceModel = new WechatGetRequestUrlReplaceModel("{out_trade_no}", "114514");
// 获取请求地址-仅替换花括号内的参数
String requestUrl = getRequestUrl("/v3/out-trade-no/{out_trade_no}", replaceModel);
/* requestUrl 输出 "/v3/out-trade-no/114514" */
// 获取请求地址-替换花括号内的参数同时需要传入Query参数
Map params = new HashMap<>(); // 组装Query参数
params.put("mch_id", "114514");
String requestUrl = getRequestUrl("/v3/out-trade-no/{out_trade_no}", replaceModel, params);
/* requestUrl 输出 "/v3/out-trade-no/114514?mch_id=114514" */
// 如果是只有query参数直接手动拼接一下就行就不用调用方法来组装了。
// 如果有多个花括号参数需要替换
WechatGetRequestUrlReplaceModel[] replaceModelArray = new WechatGetRequestUrlReplaceModel[]
{
new WechatGetRequestUrlReplaceModel("{out_batch_no}", "aabbcc"),
new WechatGetRequestUrlReplaceModel("{query_time}", "112233")
};
String requestUrl = getRequestUrl("/v3/out-trade-no/{out_trade_no}/details/{query_time}", replaceModel, params);
/* requestUrl 输出 "/v3/out-trade-no/aabbcc/details/112233" */
/* POST请求同理,也是使用WechatGetRequestUrlReplaceModel进行参数替换 */
}
}
```
**2.创建响应对象**
```java
/**
* 推荐使用IDEA的一个插件来自动完成实体类的创建。
* GsonFormatPlus插件,它能根据你填写的JSON自动创建实体类。
* 不用手打全自动生成,复杂对象也能够生成正确的嵌套结构属性
* github地址:https://github.com/mars-men/GsonFormatPlus
*/
public class WechatV3SdkCustomResponse {
/** 需要使用com.google.gson的@SerializedName注解来对参数进行序列化 **/
@SerializedName("out_bill_no")
private String outBillNo;
}
```
**3.创建请求对象**
```java
/**
* 推荐使用IDEA的一个插件来自动完成实体类的创建。
* GsonFormatPlus插件,它能根据你填写的JSON自动创建实体类。
* 不用手打全自动生成,复杂对象也能够生成正确的嵌套结构属性
* github地址:https://github.com/mars-men/GsonFormatPlus
*/
public class WechatV3SdkCustomRequest {
/** 需要使用com.google.gson的@SerializedName注解来对参数进行序列化 **/
@SerializedName("out_bill_no")
private String outBillNo;
/** 微信会要求某些私密字段加密传输,此时在实体类中实现对应加密方法再在Service中调用方法即可 **/
@SerializedName("user_name")
private String userName;
/** 假如这个list中的每一项都存在私密字段需要加密 **/
@SerializedName("details_list")
private List list;
/**
* 加密字段
* @param s
* @return WechatConfirmTransferRequest
*/
public WechatConfirmTransferRequest cloneWithCipher(UnaryOperator s) {
if (this.getUserName() != null) {
this.setUserName(s.apply(this.getUserName()));
return this;
}
return this;
}
/**
* 加密字段(集合类型)
* @param s
* @return WechatConfirmTransferRequest
*/
public WechatConfirmTransferRequest cloneWithCipher(UnaryOperator s) {
if (this.getList().size() > 0) {
this.getList().forEach(item -> {
if (item.getUserName() != null) {
item.setUserName(s.apply(item.getUserName()));
}
});
}
return this;
}
}
```
**目前二次封装的Service支持各类常见的支付类/转账类接口:**
***1.公众号JSAPI支付***
***2.APP支付***
***3.H5支付***
***4.Native支付***
***5.小程序支付***
***5.支付订单退款***
***6.商家转账-用户端手动确认***
***6.服务商模式-微工卡***
`TIPS 一些补充:`
```java
/**
* 微信异步回调示例代码-仅供参考
*/
@Rest
public class WechatNotifyHandleController {
/**
* 微信JSAPI回调通知
*/
@PostMapping("/wechat/jsapi/{orderNo}")
public String wechatJsApiNotify(@PathVariable("orderNo") String orderNo, @RequestBody String body, HttpServletResponse res) {
JSONObject params = JSONObject.parseObject(body);
String associated_data = params.getJSONObject("resource").getString("associated_data");
String nonce = params.getJSONObject("resource").getString("nonce");
String ciphertext = params.getJSONObject("resource").getString("ciphertext");
// 解密数据-如果只是为了解密数据不验签的话可以直接使用WechatNotifySignUtils下的notifyDecrypt方法解密出通知参数
Transaction response = WechatNotifySignUtils.notifyDecrypt(associated_data,
nonce, ciphertext, getWechatApiV3Key(), Transaction.class);
return "SUCCESS";
}
}
```
****
## 参与贡献
1. Fork 本仓库
2. 新建 `Feat_xxx` 分支
3. 提交代码
4. 创建 Pull Request
## 协议
本项目遵循开源协议,请参考项目根目录下的 `LICENSE` 文件。