第七天培训内容

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去找到对应的服务的配置信息,从而在去调用对应的服务。
分布式调用图1
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)-服务组管理:维护服务组,名称、编码、简介都可以自定义。
单号规则1

3.EDS管理台-服务治理-服务管理:维护单号规则。服务标识:自定义,协议默认是Number,单号表达式则根据业务规则自定义,如果是变量则格式为@{变量名称},时间格式为:#yyMMdd#,序号为:{SN:5}。步长指递增数量。1指每次都加1。
单号规则2

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.在商品管理界面配置一个自定义按钮【选择】,如下图
挑选商品1

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.在订单明细表上新增一个自定义按钮【更新商品】,勾选明细记录,判断商品代码值是否在基础数据中的商品表中存在。如果存在提示已经存在,如果不存在则将商品记录维护到基础数据中的商品表中,需要更新商品编码和商品名称两个字段。
更新商品1

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方式:
mq例子1

4.2登入到http://172.16.10.111:12581/#/topic
发送的topic名称为为自定义名称,需要和mq消费和提供类中的topic保持一致。
mq例子2

4.3 登入到eds管理台(http://172.16.10.110/html/login.html)中的队列管理,点击新增一个队列
mq例子3

4.4 保存队列后,点击消费组按钮新增消费组数据。
mq例子4

其中消费组名称字段和tags字段用户自定义填写,但是需要和mq服务类中的groupName和tags保持一致。消费服务格式为:【netty://类名:方法:1.0】。
例如:netty://com.gillion.bms.freight.service.mqconsumer.BmsBcFreightMqConsumer:saveBmsBcFreight:1.0
mq例子4-2

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表