Biz-SIP中间件之xbank项目实战(5)——支付域服务的开发

xbank项目版本库:https://gitee.com/szhengye/xbank.git

项目实践:支付域服务的开发

1. Payment领域服务的封装

payment领域服务是对接第三方缴费平台的,第三方缴费平台的接口是XML报文格式。
payment领域服务是属于对接第三方的领域服务,前面提到的customer领域服务和account领域服务,主要是内部交易处理的领域服务。
这二类领域服务在开发时有比较大的不同:

  • 对接第三方的领域服务,一般涉及到复杂的通讯接口对接和报文格式转换;
  • 内部交易处理领域服务,应用层和领域层之间,建议采用约定的Interface接口类的调用约定(底层Biz-SIP平台自动转换成RESTful协议和BizMessage标准消息进行交互)。

在开发对接第三方的领域服务时,建议采用实现SinkBeanInterface接口的JavaBean或SpringBean:

public interface SinkBeanInterface {
    /**
     * Java服务调用接口
     * @param beforeJsonObject 打包前的JsonObject
     * @param packMessage 传入的消息
     * @return 返回值
     * @throws BizException
     */
    public byte[] process(JSONObject beforeJsonObject, byte[] packMessage) throws BizException;
}

在process()方法中传入的beforeJsonObject参数,是传入sink但还没经过打包的JSONObject对象,packMessage参数已经根据sink的消息格式配置,完成了对于目标消息格式的转换。

Payment领域服务的封装,依次有以下步骤:

第1步

领域服务的实现:创建xbank-payment1-sink子模块,根据SinkBeanInterface接口,实现Payment1SinkService微服务,并封装成能独立运行的微服务应用:

@Service
public class Payment1SinkService implements SinkBeanInterface {
    @Override
    public byte[] process(JSONObject beforeJsonObject, byte[] inMessage) throws BizException {
        log.info("传入消息:\n{}", BizUtils.buildHexLog(inMessage));
        return inMessage;
    }
}

第2步

在sink.yml文件中,把Payment1SinkService微服务作为Sink连接到Biz-SIP平台:

- id: payment1-sink
  type: rest
  url: http://payment1-sink/sink
  converter:
    type: simple-xml
  connector:
    type: sink-bean
    class-name: com.xbank.sink.payment.service.Payment1SinkService
    spring-bean: true

2. Payment服务的快速发布

配置service.yml文件,把payment-sink直接作为sink-service暴露给Biz-SIP开放平台接口:

- bizServiceId: sink/payment1
  type: sink-service
  sinkId: payment1-sink

接口测试如下:

$ curl -H "Content-Type:application/json" -H "Biz-Service-Id:sink/payment1" -X POST --data '{"message":"Hello world!"}' http://localhost:8888/api|jq

{
  "code": 0,
  "message": "success",
  "extMessage": null,
  "traceId": "af37bbbb495744d79f2f1811178436f5",
  "parentTraceId": null,
  "timestamp": 1631153112943,
  "data": {
    "message": "Hello world!"
  }
}

Payment1SinkApplication会打印出日志,收到了完成消息转换后的XML报文:

2021-09-09 10:05:13.265  INFO 10756 --- [nio-8003-exec-1] c.x.d.p.service.Payment1DomainService    : 传入消息:
====+ 01-02-03-04-05-06-07-08-09-10-11-12-13-14-15-16-17-18-19-20 + ====== ASCII  ====== +
0000: 3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D 22 31 2E 30 22 20 | <?xml version="1.0"  |
0020: 65 6E 63 6F 64 69 6E 67 3D 22 55 54 46 2D 38 22 20 73 74 61 | encoding="UTF-8" sta |
0040: 6E 64 61 6C 6F 6E 65 3D 22 6E 6F 22 3F 3E 3C 72 6F 6F 74 3E | ndalone="no"?><root> |
0060: 3C 6D 65 73 73 61 67 65 3E 48 65 6C 6C 6F 20 77 6F 72 6C 64 | <message>Hello world |
0080: 21 3C 2F 6D 65 73 73 61 67 65 3E 3C 2F 72 6F 6F 74 3E       | !</message></root>.. |

3. payment领域服务在应用层和适配层的定制

通过在service.yml中配置sink-service聚合服务,能实现已经挂接到Sink的领域服务的快速发布。
但是,客户针对应用层和适配层,还是有个性化定制要求,这就涉及到应用层和适配层的定制。
例如我们要实现一个把消息发送给缴费平台的业务,输入的消息包括:交易码、传递的消息。
首先,需要在xbank-personal-app-client子模块中的PersonalAppInterface接口,增加一个消息发送接口send2payment1(),这个接口是共享给应用层和适配层的:

public interface PersonalAppInterface {
    ...
    public BizMessage send2Payment1(Object message) throws BizException;
    ...
}

然后在xbank-app模块中基于此接口方法。在PersonalAppService类实现接口:

public class PersonalAppService implements PersonalAppInterface {
	...
    private BizMessageInterface payment1DomainInterface = IntegratorClientFactory
            .getSinkClient(BizMessageInterface.class,"payment1-sink");
	...
    @Override
    public BizMessage<JSONObject> send2Payment1(Object message) throws BizException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("message",message);
        return this.payment1DomainInterface.call(jsonObject);
    }
    ...
}

由于payment领域服务没有另外约定接口,是采用BizMessage标准消息接口,所以是采用BizMessageInterface接口来构建sink调用接口的,并统一通过call()方法来调用领域服务。
由于前面已经在service.yml中把PersonalAppService应用层服务类,配置成了“bean-service”的聚合服务,所以可以直接进行接口调用:

$ curl -H "Content-Type:application/json" -H "Biz-Service-Id:app/personal" -X POST --data '{"methodName":"send2Payment1","params":["Hello world!"]}' http://localhost:8888/api|jq

{
  "code": 0,
  "message": "success",
  "extMessage": null,
  "traceId": "19620fe5843d4d53a2bf1f599b516006",
  "parentTraceId": null,
  "timestamp": 1631153624870,
  "data": {
    "result": {
      "traceId": "19620fe5843d4d53a2bf1f599b516006",
      "code": 0,
      "data": {
        "message": "Hello world!"
      },
      "message": "success",
      "timestamp": 1631153624870
    }
  }
}

Payment1SinkApplication应用会收到消息:

2021-09-09 10:13:44.911  INFO 10756 --- [nio-8003-exec-2] c.x.d.p.service.Payment1DomainService    : 传入消息:
====+ 01-02-03-04-05-06-07-08-09-10-11-12-13-14-15-16-17-18-19-20 + ====== ASCII  ====== +
0000: 3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D 22 31 2E 30 22 20 | <?xml version="1.0"  |
0020: 65 6E 63 6F 64 69 6E 67 3D 22 55 54 46 2D 38 22 20 73 74 61 | encoding="UTF-8" sta |
0040: 6E 64 61 6C 6F 6E 65 3D 22 6E 6F 22 3F 3E 3C 72 6F 6F 74 3E | ndalone="no"?><root> |
0060: 3C 6D 65 73 73 61 67 65 3E 48 65 6C 6C 6F 20 77 6F 72 6C 64 | <message>Hello world |
0080: 21 3C 2F 6D 65 73 73 61 67 65 3E 3C 2F 72 6F 6F 74 3E       | !</message></root>.. |

在适配层中,在原有的PersonalController类中,增加对“/send2Payment1"请求的实现:

    @GetMapping(value ="/send2Payment1")
    public BizMessage<JSONObject> send2Payment1(String message) throws BizException {
        return this.personalAppInterface.send2Payment1(message);
    }

开发者可以在适配层的PersonalController类中,通过personalAppInterface,把消息发送给缴费平台:

$ curl http://localhost:9001/personal/send2Payment1\?message=hello|jq 

{
  "code": 0,
  "message": "success",
  "extMessage": null,
  "traceId": "3c9d800b3c76480c9ae605d543709ea3",
  "parentTraceId": null,
  "timestamp": 1631154087879,
  "data": {
    "message": "hello"
  }
}

在适配层中,还可以通过配置sink-service类型的聚合服务,直接调用领域层的Sink服务。例如可以在原有的PersonalController类中,直接向应用层发起JSONObject类型的平台报文:

@RestController
@RequestMapping("/personal")
public class PersonalController  {
	...
    private BizMessageInterface payment1SinkInterface = SourceClientFactory
            .getBizServiceClient(BizMessageInterface.class,"sink/payment1");
	...
	@GetMapping(value ="/send2Payment")
    public BizMessage<JSONObject> send2Payment(String message) throws BizException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("message",message);
        return this.payment1SinkInterface.call(jsonObject);
    }
}

开发者可以在适配层的PersonalController类中,通过personalAppInterface,把消息发送给缴费平台:

$ curl http://localhost:9001/personal/send2Payment1\?message=hello|jq 

{
  "code": 0,
  "message": "success",
  "extMessage": null,
  "traceId": "3c9d800b3c76480c9ae605d543709ea3",
  "parentTraceId": null,
  "timestamp": 1631154087879,
  "data": {
    "message": "hello"
  }
}

Biz-SIP官方网站:http://bizsip.bizmda.com
Gitee:https://gitee.com/szhengye/biz-sip

posted @ 2021-10-10 16:30  抽游烟鸡  阅读(69)  评论(0)    收藏  举报