7.1.分布式环境 7.1.分布式环境
1.EDS管理台:http://172.16.10.110/html/login.html
账号/密码:root/root
2.Mq管理台http://172.16.10.111:12581/#/topic
7.2.概念简介 7.2.概念简介
1.EDS概述:
Gillion企业分布式调度系统(Enterprice Distributed Service System,简称EDS)以吉联ESB解决方案和微服务解决方案为核心 ,面向企业级项目系统构建提供高可用分布式解决方案,是吉联IT架构解决方案的核心产品。EDS充分利用吉联多年沉淀的ESB分布式解决方案,帮助企业级客户轻松构建大型分布式应用服务系统。详细介绍,可访问地址:http://172.16.0.142/eds
2.业务系统和EDS之间的调用关系图如下:应用程序访问的时候是先通过ngnix代理到eds引擎端。通过引擎段去访问对应的zk去找到对应的服务的配置信息,从而在去调用对应的服务。
3.MQ:消息队列。主要用来异步解耦。例如:在java程序中A方法调用B,B调用C,此时需要是串行的,如果假设C方法请求时间都非常长,并发请求对极大消耗内存,对服务器产生极大压力。而采用MQ,B调用C时可以通过异步方式,B调用C方法立刻发送消息给MQ,结束B这个进程。
7.3.习题 7.3.习题
7.3.习题 7.3.习题
7.3.1.单号生成功能配置 7.3.1.单号生成功能配置
1.习题要求:在基础模块例子上,产品型号字段上。配置个单号策略【TTT-当前用户-当前日期(yyMMdd)-序号(5位的)】
2.登入到EDS管理台(http://172.16.10.110/html/login.html)-服务组管理:维护服务组,名称、编码、简介都可以自定义。
3.EDS管理台-服务治理-服务管理:维护单号规则。服务标识:自定义,协议默认是Number,单号表达式则根据业务规则自定义,如果是变量则格式为@{变量名称},时间格式为:#yyMMdd#,序号为:{SN:5}。步长指递增数量。1指每次都加1。
4.在项目中定义单号接口:SerialNoBasService,对于分布式建议是抽取到公共模块工程的api中。
package com.gillion.bas.item.service.custom;
import com.alibaba.fastjson.JSONObject;
import com.gillion.eds.sdk.annotations.EdsService;
import com.gillion.eds.sdk.annotations.ServiceMapping;
@EdsService("serialNoBasService")
public interface SerialNoBasService {
@ServiceMapping(value = "number://commondityModelNo")
String getCommondityModelNo(JSONObject jsonObject);
}
5.在扩展Service服务类中实现生成单号规则方法BasItemServiceExtImpl。
private String getCommondityModelNo() {
String allocationNo = null;
com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
String company = UserContextHolder.getCurrentUser().getSettleOfficeCode();
if(company==null){
company = "Test";
}
if (company.length() > 4) {
company = company.substring(0, 4);
}
jsonObject.put("company", company);
jsonObject.put("count", 1);
allocationNo = SerialUtils.serialOne(ModuleConstant.BAS_MOUDLE, "产品型号",
seriaNoBasService.getCommondityModelNo(jsonObject));
return allocationNo;
}
6.在保存前方法中配置扩展Service方法,去获取对应的单号配置数据BasItemServiceImpl。
public int save(BasItem basItem, String businessType)
{
int result = -1 ;
if(basItem.getRowStatus()==BaseObject.ROWSTATUS_ADDED){
basItemServiceExt.setBasItemNo(basItem);
result = super.save(basItem);
}
return result;
}
7.3.2.挑选商品 7.3.2.挑选商品
挑选商品
1.在订单明细表中新增一个自定义按钮【挑选商品】,按钮链接到商品管理界面
2.在商品管理界面配置一个自定义按钮【选择】,如下图
3.在商品界面的二次开发文件中编写代码。选择按钮逻辑:未勾选记录需要给出提示,勾选后将勾选的记录赋值到订单明细表中basItemTanchManageCtrl.js。
$scope.submitBasItem = function(){
var tempSelTable = $scope.basItemGrid.getCheckedRows();
if (!tempSelTable || tempSelTable.length == 0) {
GillionMsg.alert(null, msg || "请选择数据!");
}
var ipfUpgradeListDetails = [];
for(i=0;i< tempSelTable.length;i++)
{
var selectItem = tempSelTable[i];
var ipfUpgradeListDetail = {};
ipfUpgradeListDetail.itemCode =selectItem.itemCode;
ipfUpgradeListDetail.itemName =selectItem.itemName;
ipfUpgradeListDetail.itemType =selectItem.itemType;
ipfUpgradeListDetail.commondityModel =selectItem.commondityModel;
ipfUpgradeListDetail.rowStatus = 4;
ipfUpgradeListDetails.push(ipfUpgradeListDetail);
}
var indexScope = top.angular.element("body").scope();
var id = indexScope.activeTabId;
var iframe = window.top.$('#' + id + '_frame')[0];
var tabWin = iframe.contentWindow;
var tabScope = tabWin.$('body').scope();
var injector = tabWin.$('body').injector();
var dataSource = injector.get('$dataSourceManager').dataSources.OmsOrderDetailSource;
dataSource.records = dataSource.records.concat(ipfUpgradeListDetails);
tabScope.$broadcast("OmsOrderDetailSource", dataSource);
GillionMsg.alert("提示","添加成功",null);
$scope.queryBasItem();
};
7.3.3、更新商品
7.3.3.更新商品 7.3.3.更新商品
1.Rpc概念:
.注册中心:保存提供方和消费方的注册信息
.服务提供方:服务提供方开发完成一个服务后,需要向注册中心发起注册,标记当前提供者提供了哪些可用服务。
.服务消费方:服务消费者在启动时自动建立对注册中心信息变更的监听,若注册中心有服务发生变更,则自动更新服务消费者中的服务提供者信息
.EDS引擎端:服务的转发
2.在订单明细表上新增一个自定义按钮【更新商品】,勾选明细记录,判断商品代码值是否在基础数据中的商品表中存在。如果存在提示已经存在,如果不存在则将商品记录维护到基础数据中的商品表中,需要更新商品编码和商品名称两个字段。
3.前端代码omsOrderManageCtrl.js:
$scope.updateBasItemOmsOrderDetail= function(){
var ids = Arrays.extract($scope.omsOrderDetailGrid.getCheckedRows(), 'omsOrderDetailId');
if(ids.length == 0){
GillionMsg.alert("提示","请选择明细的记录!");
return;
}
var tempSelTable = $scope.omsOrderDetailGrid.getCheckedRows()
var ipfItems = [];
for(i=0;i< tempSelTable.length;i++)
{
var selectItem = tempSelTable[i];
var itemsDetail = {};
itemsDetail.itemCode =selectItem.itemCode;
itemsDetail.itemName =selectItem.itemName;
itemsDetail.itemPrice =selectItem.itemPrice;
ipfItems.push(itemsDetail);
}
$http.post($config.ctx + '/item/saveOrUpdateItem',ipfItems).success(function(data){
if (data.success != undefined && data.success == true){
GillionMsg.alert("提示",data.msg,null);
}
$timeout(function(){
if($scope._pageState) $scope._pageState.resetDataState();
});
}).error(function(){
});
};
3.后台代码:
3.1 在bas模块中定义一个rpc接口BasItemRpcService 。接口上要加入@Consumer注解
@Consumer
public interface BasItemRpcService extends IBaseService<Long,BasItem,BasItemExample> {
/**
* 基础数据商品
* @param BasItems
* @return
*/
public Map<String, Object> saveOrUpdateItem(BasItem[] BasItems);
}
3.2在基础模块中定义rpc实现类BasItemRpcServiceImpl,实现类中加入@Provider(BasItemRpcService.class)注解。
@Provider(BasItemRpcService.class)
public class BasItemRpcServiceImpl extends BaseServiceImplExt<Long,BasItem,BasItemExample> implements BasItemRpcService {
@Override
public Map<String, Object> saveOrUpdateItem(BasItem[] BasItems) {
Map<String, Object> result = new HashMap<String, Object>();
List<BasItem> addItems = new ArrayList<BasItem>();//新增
List<BasItem> upItems = new ArrayList<BasItem>();//修改
for (BasItem item : BasItems) {
BasItemExample basItemExample = BasItemExample.create();
basItemExample.and().andCreateCondition(BasItem.ITEMNAME, Operation.EQ,item.getItemName());
List<BasItem> basItemFinds = selectByExample(basItemExample);
if(!Collections3.isNotEmpty(basItemFinds)){
BasItem itemTempAdd = new BasItem();
itemTempAdd.setRowStatus(BaseObject.ROWSTATUS_ADDED);
itemTempAdd.setItemName(item.getItemName());
itemTempAdd.setItemCode(item.getItemCode());
addItems.add(itemTempAdd);
}
}
if(CollectionUtils.isNotEmpty(addItems)){
batchInsert(addItems);
}
result.put("msg", "SUCCESS");
return result;
}
}
3.3在oms模块中的business模块中需要引入bas-api的包,其中需要排查的包按实际项目操作。
<dependency>
<groupId>com.gillion</groupId>
<artifactId>ttt-bas-api</artifactId>
<version>1.0.0.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>
3.4服务提供方Application(启动类BasApplication)中加入@ConsumerScan注解,查看是否有把自己本身的api扫描路径排除,如果没有需要排除。
@ConsumerScan(value="com.gillion", excludePattern={
"com.gillion.platform.framework.component.*,com.gillion.bas.item.service.rpc.*"
}
)
@EnableTransactionManagement
public class BasApplication extends SpringBootServletInitializer {
//TODO EC spring boot 缺少 UserIdGetter
@Bean
@Primary
public UserIdGetter userIdGetter(){
return new DefaultUserIdGetterImpl();
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(BasApplication.class);
}
public static void main(String[] args)
{
SpringApplication.run(BasApplication.class, args);
}
}
7.3.4、MQ例子
MQ例子
场景描述:主对象为订单模块上的主对象,子对象为结算模块上实收表。点击保存按钮的时候,需要将实收表中的数据存储回结算模块对应数据库的实收表中,存储的时候实收表中要将主对象的主键存储到实收表中,最后保存成功需要加载对应主对象下的实收表中的数据。另外新增时收表数据的时候,主对象上的实收费用值为子对象上的金额总和。
4.1在订单模块订单实收里面保存(update)按钮上调用MQ方式:
4.2登入到http://172.16.10.111:12581/#/topic
发送的topic名称为为自定义名称,需要和mq消费和提供类中的topic保持一致。
4.3 登入到eds管理台(http://172.16.10.110/html/login.html)中的队列管理,点击新增一个队列
4.4 保存队列后,点击消费组按钮新增消费组数据。
其中消费组名称字段和tags字段用户自定义填写,但是需要和mq服务类中的groupName和tags保持一致。消费服务格式为:【netty://类名:方法:1.0】。
例如:netty://com.gillion.bms.freight.service.mqconsumer.BmsBcFreightMqConsumer:saveBmsBcFreight:1.0
4.5在订单模块订单实收里面重写update保存方法omsOrderForBmsEditCtrl.js。
$scope.update = function(options){
// options = options || {};
// options = _.assign({methodName:'update',url:$config.ctx +'/omsOrderForBms/update',pageType:"edit"},options);
// return $scope._commonSaveOrUpdateOmsOrderForBms(options);
$http.post($config.ctx + '/omsOrderForBms/update', $scope.omsOrderForBms).success(function(data){
if (data.success != undefined && data.success == true){
$scope.omsOrderForBms = data.omsOrderForBms;
//调用mq
$http.post($config.ctx + '/bmsFreight/sendBmsBcFreight',$scope.bmsBcFreights).success(function(data1){
if (data1.success != undefined && data1.success == true){
//
}
}).error(function(){
});
GillionMsg.alert("提示",data.msg,null);
$timeout(function(){
if($scope._pageState) {$scope._pageState.resetDataState();}
});
}
}).error(function(){
});
};
4.6 在结算模块bms新增控制器BmsMqCustomController。
@RequestMapping(value="bmsFreight/sendBmsBcFreight")
public void sendBmsBcFreight(@RequestBody List<BmsBcFreight> bmsBcFreights, HttpServletRequest request, HttpServletResponse response) throws IOException
{
List<BmsBcFreightDto> addBmsFreights = new ArrayList<BmsBcFreightDto>();
if(CollectionUtils.isNotEmpty(bmsBcFreights)){
for (BmsBcFreight bmsBcFreight:bmsBcFreights){
BmsBcFreightDto bmsBcFreightDto = new BmsBcFreightDto();
bmsBcFreightDto.setItemCode(bmsBcFreight.getItemCode());
bmsBcFreightDto.setItemName(bmsBcFreight.getItemName());
bmsBcFreightDto.setSumOfMoney(bmsBcFreight.getSumOfMoney());
bmsBcFreightDto.setOmsOrderId(bmsBcFreight.getOmsOrderId());
addBmsFreights.add(bmsBcFreightDto);
}
BmsBcFreightDto[] bmsBcFreightDtos = addBmsFreights.toArray(new BmsBcFreightDto[addBmsFreights.size()]);
bmsMqProducer.sendBmsBcFreight(bmsBcFreightDtos);
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "SUCCESS");
ResponseUtils.flushSuccess(response, "保存成功!", "omsOrders", result);
}
4.7 在结算模块bms中新增MQ消费者实现类。用来保存结算数据。
@MQConsumer(name = "rocketMQ",mode = Mode.Concurrently, topic = "bmsFreightInfo",
tags = "bmsFreightAdd", groupName = "bmsBcFreightDto_add")
@Override
public void saveBmsBcFreight(BmsBcFreightDto[] BmsBcFreightDtoArray) {
if(null == BmsBcFreightDtoArray){
//todo 抛出异常
}
if(null != BmsBcFreightDtoArray &&BmsBcFreightDtoArray.length>0){
List<BmsBcFreightDto> lists = Arrays.asList(BmsBcFreightDtoArray);
List<BmsBcFreight> listBmsAdd = new ArrayList<BmsBcFreight>();
List<BmsBcFreight> listBmsUpdate = new ArrayList<BmsBcFreight>();
for(BmsBcFreightDto dto:lists){
BmsBcFreightExample bmsBcFreightExample = BmsBcFreightExample.create();
bmsBcFreightExample.and().andCreateCondition(BmsBcFreight.ITEMNAME, Operation.EQ,dto.getItemName()).andCreateCondition(BmsBcFreight.OMSORDERID, Operation.EQ,dto.getOmsOrderId());
List<BmsBcFreight> bmsBcFreightFinds = bmsBcFreightService.selectByExample(bmsBcFreightExample);
if(!Collections3.isNotEmpty(bmsBcFreightFinds)){
BmsBcFreight bmsBcFreight = new BmsBcFreight();
bmsBcFreight.setRowStatus(BaseObject.ROWSTATUS_ADDED);
bmsBcFreight.setItemCode(dto.getItemCode());
bmsBcFreight.setItemName(dto.getItemName());
bmsBcFreight.setSumOfMoney(dto.getSumOfMoney());
bmsBcFreight.setOmsOrderId(dto.getOmsOrderId());
listBmsAdd.add(bmsBcFreight);
}else {
BmsBcFreight itemTemp = bmsBcFreightFinds.get(0);
itemTemp.setRowStatus(BaseObject.ROWSTATUS_MODIFIED);
itemTemp.setItemCode(dto.getItemCode());
itemTemp.setItemName(dto.getItemName());
itemTemp.setSumOfMoney(dto.getSumOfMoney());
itemTemp.setOmsOrderId(dto.getOmsOrderId());
listBmsUpdate.add(itemTemp);
}
}
if(Collections3.isNotEmpty(listBmsAdd)){
bmsBcFreightService.batchInsert(listBmsAdd);
}
if(Collections3.isNotEmpty(listBmsUpdate)){
bmsBcFreightService.batchUpdate(listBmsUpdate);
}
// bmsBcFreightService.saveOrUpdates(listBms);
}
}
4.8 在结算模块bms中新增结算的生产者接口BmsMqProducer。
@Consumer
public interface BmsMqProducer {
/**
* 订单新增插入费用表
* @param bmsBcFreightDtoArray
*/
@MQProducer(name = "rocketMQ", topic = "bmsFreightInfo", tags = "bmsFreightAdd",recordTable = "mq_record",keepSession = "true",
autoConsume = "true",mode = MQProducer.Mode.SINGLE)
void sendBmsBcFreight(BmsBcFreightDto[] bmsBcFreightDtoArray);
}
4.9 在oms模块中加入Mq 日志记录:mq_record表