华为云云耀云服务器L实例评测|基于canal缓存自动更新流程 & SpringBoot项目应用案例和源码

前言

最近华为云云耀云服务器L实例上新,也搞了一台来玩,期间遇到各种问题,在解决问题的过程中学到不少和运维相关的知识。

在之前的博客中,介绍过canal的安装和配置,参考博客

本篇博客给出了canal项目应用的案例,详细介绍基于canal实现数据库和缓存同步的流程,并给出了核心diamante的源码。

其他相关的华为云云耀云服务器L实例评测文章列表如下:

文章目录

引出


1.介绍基于canal实现数据库和缓存同步的流程;

2.给出了核心diamante的源码;

基于canal缓存同步更新

整体的流程

启动spring项目时,同步启动缓存自动更新,如果数据库的相关表格数据发生变化,canal通过就会监听到,然后更新缓存redis中的相应数据

哪些数据从缓存中取?------不经常更新的数据:比如公司的部门,仓库等;

在项目启动时,启动了canal,canal用来监听数据库的变化;

业务逻辑:前端请求相关数据--> 问Redis要

(1)如果redis里面有,则返回给前端;

(2)如果redis里面没有,则从数据库查询,并且存到redis里面,返回给前端;

(3)如果数据库发生更新,canal监听到修改,同步更新到Redis里面,保证缓存和数据库一致;

相关代码和流程

1.canal通道的配置

缓存自动更新,读取配置文件中的ip和端口号

是否开启缓存自动更新,从配置文件中读取配置;

2.前端查询的业务代码

在数据库数据没有更新时,获取缓存中的数据

3.数据库数据更新

如果数据库的数据更新,canal监听到,发现是缓存对应的表,并且是对应的字段发生变化,则进行缓存的自动更新

缓存自动同步更新

存到Redis里面的最新的数据

4.缓存更新前端展示

缓存更新后,前端再次查询,获得最新的数据

核心代码源码

1.配置yml和配置类

配置yml文件

yml 复制代码
server:
  port: 10050

## 是否启用安全框架 true为开启,false为关闭
security:
  isOpen: true

## 是否开启canal管道,true为开启,false为关闭
canal:
  isOpen: true

# canal的相关配置
canalConfig:
  host: 124.70.138.34
  port: 11111

Redis存Java对象的配置类

java 复制代码
package com.tianju.fresh.config.redis;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisSerializeConfig {
    @Bean
    public RedisTemplate redisTemplateInit(RedisConnectionFactory redisConnectionFactory) {

        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置序列化Key的实例化对象
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置序列化Value的实例化对象
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        /**
         *
         * 设置Hash类型存储时,对象序列化报错解决
         */
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }
}

2.canal自动更新代码

canal自动更新的代码,用canal管道监听MySQL数据变化,自动更新redis缓存

java 复制代码
package com.tianju.fresh.config.redis;


import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;


import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.common.utils.AddressUtils;
import com.alibaba.otter.canal.protocol.Message;
import com.alibaba.otter.canal.protocol.CanalEntry.Column;
import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
import com.baomidou.mybatisplus.annotation.TableField;
import com.tianju.fresh.entity.common.GoodsTypeVo;
import com.tianju.fresh.entity.common.WarehouseVo;
import com.tianju.fresh.entity.goods.GoodsType;
import com.tianju.fresh.mapper.goods.GoodsTypeMapper;
import com.tianju.fresh.mapper.warehouse.StorehouseMapper;
import com.tianju.fresh.service.common.CommonStaticMethod;
import com.tianju.fresh.util.Constance;
import com.tianju.fresh.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

/**
 * 用canal管道监听MySQL数据变化,自动更新redis缓存
 */
@Slf4j
@Component
public class AutoUpdateRedis {

    @Value("${canalConfig.host}")
    private String host;

    @Value("${canalConfig.port}")
    private Integer port;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private GoodsTypeMapper typeMapper;

    @Autowired
    private StorehouseMapper storehouseMapper;

    @Autowired
    private RedisUtil redisUtil;


    public void run() {
        // 创建链接
        final InetSocketAddress HOST = new InetSocketAddress(host,port);
//        final InetSocketAddress HOST = new InetSocketAddress("192.168.111.130",11111);
        CanalConnector connector = CanalConnectors.newSingleConnector(HOST, "example", "", "");
        int batchSize = 1000;
        int emptyCount = 0;
        try {
            connector.connect();
            connector.subscribe(".*\\..*");
            connector.rollback();
            int totalEmptyCount = 120;
//            while (emptyCount < totalEmptyCount) {
            while (true) {
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId == -1 || size == 0) {
                    emptyCount++;
                    if(emptyCount % 100==0 || emptyCount==1){
                        System.out.println("empty count : " + emptyCount);
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                } else {
                    emptyCount = 0;
                    // System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
                    printEntry(message.getEntries());
                }

                connector.ack(batchId); // 提交确认
                // connector.rollback(batchId); // 处理失败, 回滚数据
            }

//            System.out.println("empty too many times, exit");
        } finally {
            connector.disconnect();
        }
    }

    private void printEntry(List<Entry> entrys) {
        for (Entry entry : entrys) {
            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                continue;
            }

            RowChange rowChage = null;
            try {
                rowChage = RowChange.parseFrom(entry.getStoreValue());
            } catch (Exception e) {
                throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
                        e);
            }

            EventType eventType = rowChage.getEventType();
            System.out.println(String.format("================&gt; binlog[%s:%s] , name[%s,%s] , eventType : %s",
                    entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
                    entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
                    eventType));

            String tableName = entry.getHeader().getTableName();
            if (Constance.LISTEN_TAB_NAMES.contains(tableName)){
                for (RowData rowData : rowChage.getRowDatasList()) {
                    if (eventType == EventType.DELETE){
                        // 删除之前
                        log.debug("-------删除之前before");
                        changeBefore(rowData.getBeforeColumnsList());

                        log.debug("-------删除之后after");
                        // 传修改的表名,和常量中的map对比,从而对应的redis里的key
//                        changeAfter(rowData.getAfterColumnsList(),tableName);

                    }else if (eventType == EventType.INSERT){
                        // 插入之前
                        log.debug("-------插入之前before");
                        changeBefore(rowData.getBeforeColumnsList());

                        log.debug("-------插入之后after");
                        // 传修改的表名,和常量中的map对比,从而对应的redis里的key
//                        changeAfter(rowData.getAfterColumnsList(),tableName);

                    }else {
                        // 修改之前
                        log.debug("-------修改之前before");
                        changeBefore(rowData.getBeforeColumnsList());

                        log.debug("-------修改之后after");
                        // 传修改的表名,和常量中的map对比,从而对应的redis里的key
                        changeAfter(rowData.getAfterColumnsList(),tableName);
                    }
                }
            }
        }
    }


    /**
     * 数据库更新之前
     * @param columns
     */

    private  void changeBefore(List<Column> columns) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
        }
    }

    private  void changeAfter(List<Column> columns, String tableName) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());

            // 如果是商品类别表变化

            if ("name".equals(column.getName()) && Constance.GOODS_TYPE_TAB_NAME.equals(tableName)){ // 默认是名称那一列变化才更新缓存
                // 先删除key,再设置好新的key
                Map tabNameToRedisKey = Constance.getTabNameToRedisKey();
                String redisKey = (String) tabNameToRedisKey.get(tableName);
                redisTemplate.delete(redisKey);
                // TODO:设置新的key
                List<GoodsTypeVo> list = CommonStaticMethod.getGoodsTypeVos(typeMapper);
                redisUtil.saveObjectToRedis(Constance.GOODS_TYPE_REDIS_KEY, list);
                break;
            }

            // 如果是仓库那张表变化 storehouse
            if ("name".equals(column.getName()) && Constance.STOREHOUSE_TAB_NAME.equals(tableName)){ // 默认是名称那一列变化才更新缓存
                // 先删除key,再设置好新的key
                Map tabNameToRedisKey = Constance.getTabNameToRedisKey();
                String redisKey = (String) tabNameToRedisKey.get(tableName);
                redisTemplate.delete(redisKey);

                // 设置新的key,存到redis里面
                List<WarehouseVo> list = CommonStaticMethod.getStorehouseVos(storehouseMapper);

                redisUtil.saveObjectToRedis(Constance.STOREHOUSE_REDIS_KEY,list);

                break;
            }
        }
    }
}

redis的工具类

java 复制代码
package com.tianju.fresh.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    public void  saveObjectToRedis(String key,Object json){
        redisTemplate.opsForValue().set(key,json);
    }

    /**
     * 从redis里面获取json对象,如果没有,返回null
     * @param key
     * @return
     */
    public Object getJsonFromRedis(String key){
        return redisTemplate.opsForValue().get(key);
    }

}

监听数据库表,列名的常量类

java 复制代码
package com.tianju.fresh.util;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 专门放各种常量
 */
public interface Constance {
    // 设置哪些数据库表需要监听,比如单位unit,仓库warehouse,商品类型等
    List<String> LISTEN_TAB_NAMES = Arrays.asList("goods_type","unit_commodity","warehouse_center","warehouse_tab");

    String GOODS_TYPE_TAB_NAME = "goods_type";
    String GOODS_TYPE_REDIS_KEY = "goodsTypeVo";
    String UNIT_TAB_NAME = "unit_commodity";
    String UNIT_REDIS_KEY = "unitVo";
    String WAREHOUSE_TAB_NAME = "warehouse_center";
    String WAREHOUSE_REDIS_KEY = "warehouseCenterVo";
    String STOREHOUSE_TAB_NAME = "warehouse_tab";
    String STOREHOUSE_REDIS_KEY = "storehouseVo";

    static Map getTabNameToRedisKey(){
        Map<String,String> map = new HashMap<>();
        map.put(GOODS_TYPE_TAB_NAME,"goodsTypeVo");
        map.put("unit_commodity","unitVo");
        map.put("warehouse_center","warehouseCenterVo");
        map.put("warehouse_tab","storehouseVo");
        return map;
    }
}

3.查询的业务层service代码

java 复制代码
    @Override
    public List<WarehouseVo> findStorehouse() {
//        List<Storehouse> storehouses = storehouseMapper.selectList(null);
//        List<WarehouseVo> list = new ArrayList<>(storehouses.size());
//        storehouses.forEach(s->{
//            list.add(new WarehouseVo(s.getId()+"", s.getName()));
//        });

        // 是否有小仓库的redis的key
        Boolean hasKey = redisTemplate.hasKey(Constance.STOREHOUSE_REDIS_KEY);
        if (hasKey){ // 如果有,走缓存
            List<WarehouseVo> list = (List<WarehouseVo>) redisTemplate.opsForValue()
                    .get(Constance.STOREHOUSE_REDIS_KEY);
            log.debug("get storehouseVo from redis: "+list);
            return list;
        }
        // 如果没有从数据库查询,存到redis里面
        List<WarehouseVo> list = CommonStaticMethod.getStorehouseVos(storehouseMapper);
        log.debug("get storehouseVo from mysql: "+list);
        redisUtil.saveObjectToRedis(Constance.STOREHOUSE_REDIS_KEY, list);
        return list;
    }

4.主启动类

主启动类实现implements CommandLineRunner方法,启动canal通道,进行监听数据库的变化,实现缓存同步更新

java 复制代码
package com.woniu.fresh;


import com.woniu.fresh.config.redis.AutoUpdateRedis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableScheduling;


@SpringBootApplication
@Slf4j
@EnableAspectJAutoProxy // 让动态代理生效
@EnableScheduling // 让定时任务生效
public class FreshApp implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(FreshApp.class);
    }

    @Autowired
    private AutoUpdateRedis autoUpdateRedis;

    @Value("${canal.isOpen}")
    private Boolean isCanal;

    @Override
    public void run(String... args) throws Exception {

        if (isCanal){
            log.debug(">>>>>启动缓存自动更新");
            autoUpdateRedis.run();
        }
    }
}

5.前端vue代码

java 复制代码
<template>
    <div>
        <el-row>
            <el-col :span="24">
                <el-form :inline="true" label-width="80px">
                    <el-form-item label="领料单号">
                        <el-input v-model="findGoodsParams.shoppingNo"></el-input>
                    </el-form-item>

                    <el-form-item label="领料数量≥">
                        <el-input v-model="findGoodsParams.nums"></el-input>
                    </el-form-item>

                    <el-form-item label="原料名称">
                        <el-input v-model="findGoodsParams.rawName"></el-input>
                    </el-form-item>

                    <el-form-item label="领料仓库">
                        <el-select v-model="findGoodsParams.warehouseId" placeholder="请选择">
                            <el-option v-for="item in commondata.storehouse" :key="item.value" :label="item.label"
                                :value="item.value">
                            </el-option>
                        </el-select>
                    </el-form-item>

                    <el-form-item label="状态">
                        <el-select v-model="findGoodsParams.status" placeholder="请选择">
                            <el-option v-for="item in commondata.status" :key="item.value" :label="item.label"
                                :value="item.value">
                            </el-option>
                        </el-select>
                    </el-form-item>

                    <el-form-item>
                        <el-button type="primary" @click="findGoods">查询</el-button>
                        <el-button type="success" @click="reFindGoods">重置</el-button>
                    </el-form-item>
                </el-form>
            </el-col>
        </el-row>

        <el-row>
            <el-col :span="24">
                <el-button type="success" @click="addGoodsBtn">新增</el-button>
                <el-button type="warning" icon="el-icon-edit" @click="batchDelete">批量审批</el-button>
                <el-button type="primary" icon="el-icon-magic-stick" @click="batchPickDoing">批量领取中</el-button>
                <el-button type="primary" icon="el-icon-shopping-cart-full" @click="batchPickDown">批量已领完</el-button>

            </el-col>
        </el-row>

        <el-row>
            <el-col :span="24">
                <el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="55">
                    </el-table-column>
                    <el-table-column prop="pickNo" label="领料单号" width="240">
                    </el-table-column>
                    <el-table-column prop="name" label="原材料名" width="100">
                    </el-table-column>
                    <el-table-column prop="nums" label="领料数量" width="80">
                    </el-table-column>
                    <el-table-column prop="unit" label="单位" width="80">
                    </el-table-column>
                    <el-table-column prop="warehouse" label="领料仓库" width="180">
                    </el-table-column>
                    <el-table-column prop="emp" label="仓管员" width="150">
                    </el-table-column>

                    <el-table-column prop="status" label="状态" width="80">

                        <template slot-scope="scope">
                            <span v-if="scope.row.status == '0'" style="color: red;">未审批</span>
                            <span v-if="scope.row.status == '1'" style="color: rgb(9, 209, 109);">已审批</span>
                            <span v-if="scope.row.status == '2'" style="color: rgb(44, 39, 205);">已领取</span>
                            <span v-if="scope.row.status == '3'" style="color: rgb(173, 16, 157);">已领完</span>
                            <!-- {{ scope.row.status == 1 ? '已上架' : '下架' }} -->
                        </template>
                    </el-table-column>
                    <el-table-column label="操作">
                        <template slot-scope="scope">
                            <el-button type="primary" icon="el-icon-edit" circle
                                @click="loadBtn(scope.row.id)">审批</el-button>
                        </template>
                    </el-table-column>
                </el-table>

            </el-col>
        </el-row>
        <el-row>
            <el-col :span="24">
                <div class="block">
                    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
                        :current-page.sync="currentPage" :page-sizes="[3, 5, 10]" :page-size="3"
                        layout="total,sizes, prev, pager, next" :total=total>
                    </el-pagination>
                </div>
            </el-col>
        </el-row>


        <!-- 新增原材料入库弹窗 ******* -->
        <el-dialog title="添加原材料单" :visible.sync="b">
            <el-form>
                <el-row>
                    <el-col :span="12">

                        <el-form-item label="原料名称" clearable>
                            <el-select v-model="shoppingNoId" placeholder="请选择"
                                style="width: 355px;margin-right:px;margin-left:px" @change="selectBuyNo">
                                <el-option v-for="item in shoppingIdToNo" :key="item.label" :label="item.label"
                                    :value="item.label">
                                </el-option>
                            </el-select>
                        </el-form-item>

                        <el-form-item label="数量" :label-width="formLabelWidth">
                            <el-input v-model="goods.nums" autocomplete="off"></el-input>
                        </el-form-item>



                        <el-form-item label="单位" clearable>
                            <el-select v-model="goods.unit" placeholder="请选择商品单位"
                                style="width: 355px;margin-right:px;margin-left:px">
                                <el-option v-for="item in commondata.unit" :key="item.value" :label="item.label"
                                    :value="item.value">
                                </el-option>
                            </el-select>
                        </el-form-item>

                        <el-form-item label="中心仓库" clearable>
                            <el-select v-model="goods.warehouse" placeholder="请选择" disabled
                                style="width: 355px;margin-right:px;margin-left:px">
                                <el-option v-for="item in commondata.warehouse" :key="item.value" :label="item.label"
                                    :value="item.value">
                                </el-option>
                            </el-select>
                        </el-form-item>

                        <el-form-item label="领料仓库" clearable>
                            <el-select v-model="goods.storehouse" placeholder="请选择"
                                style="width: 355px;margin-right:px;margin-left:px">
                                <el-option v-for="item in commondata.storehouse" :key="item.value" :label="item.label"
                                    :value="item.value">
                                </el-option>
                            </el-select>
                        </el-form-item>


                    </el-col>
                </el-row>

            </el-form>

            <div slot="footer" class="dialog-footer">
                <el-button @click="b = false">取 消</el-button>
                <el-button type="primary" @click="addGoods()">确 定</el-button>
            </div>
        </el-dialog>



    </div>
</template>

<script>
export default {
    data() {
        return {
            findGoodsParams: {
                "pickNo": ""
                , "name": ""
                , "nums": ""
                , "storehouseId": ""
                , "status": ""
            },
            // 批量删除的id
            deleteIds: [],


            tableData: [
                {
                    "id": 1,
                    "pickNo": "PICK2023090818003927",
                    "name": "富士苹果",
                    "nums": "350.00",
                    "unit": "千克",
                    "warehouse": "南京江宁生鲜1号仓库",
                    "storehouseId": 2,
                    "emp": "领料操作员1李四",
                    "status": "0"
                }
            ],

            // 分页相关的参数
            total: 10,
            currentPage: 1,
            pageSize: 3,

            options: [
                { value: '0', label: '未审批' },
                { value: '1', label: '审批通过' },
                { value: '2', label: '已领取' },
                { value: '3', label: '已领完' },
            ],

            commondata: {
                "storehouse": [
                    {
                        "value": 1, "label": "南京中心仓库南京总统府"
                    },],
                "status": [
                    { "value": 0, "label": "未审批" },
                    { "value": 1, "label": "审批通过" }]
            },


            // 新增商品弹窗控制变量
            b: false,



            // 新增的入库信息 goods shoppingNoId
            goods: {
                "name":"富士苹果",
                "nums":"200.56",
                "unit":"1",
                // 中心仓库
                "warehouse":"1", 
                // 目标仓库
                "storehouse":"2"
            },

            formLabelWidth: '100px',

            
            // 新增原材料入库清单
            addRawTab:[{
                "name": {
                    "unit": "",
                    "warehouse": {
                        "1": 1000.20
                    }
                }},
            ],

            // 和上面进行比对
            shoppingNoId:"",

            // 选择采购清单的下拉框
            shoppingIdToNo: [
                    {"label": "BUY2023091317093927"},],
        }

    },
    methods: {
        handleSizeChange(val) {
            console.log(`每页 ${val} 条`);
            this.pageSize = val
            this.findGoods()
        },
        handleCurrentChange(val) {
            console.log(`当前页: ${val}`);
            this.pageNum = val
            this.findGoods()
        },
        findGoods() {
            let params = {}
            params.pageNum = this.currentPage
            params.pageSize = this.pageSize
            params.param = this.findGoodsParams
            console.log(params)
            this.$axios.post("/api/warehouse/findPagePickRaw", params)
                .then(response => {
                    let resp = response.data
                    console.log(resp)
                    if (resp.resultCode.code == 20000) {
                        this.tableData = resp.results.list
                        this.total = resp.results.total
                        this.currentPage = resp.results.pageNum
                        this.pageSize = resp.results.pageSize
                    }
                })
        },

        reFindGoods() {
            let params = {}

            this.findGoodsParams = {
                "pickNo": ""
                , "name": ""
                , "nums": ""
                , "storehouseId": ""
                , "status": ""
            },

            params.pageNum = this.currentPage
            params.pageSize = this.pageSize
            params.param = this.findGoodsParams

            console.log(params)
            this.$axios.post("/api/warehouse/findPagePickRaw", params)
                .then(response => {
                    let resp = response.data
                    console.log(resp)
                    if (resp.resultCode.code == 20000) {
                        this.tableData = resp.results.list
                        this.total = resp.results.total
                        this.currentPage = resp.results.pageNum
                        this.pageSize = resp.results.pageSize
                    }
                })
        },

        handleSelectionChange(val) {
            this.deleteIds = []
            console.log(val);
            val.forEach(e => this.deleteIds.push(e.id))
        },

        // 批量修改领取中
        batchPickDoing() {
            console.log(this.deleteIds)
            this.$axios.put("/api/warehouse/batchDoingPickMaterial", this.deleteIds)
                .then(response => {
                    let resp = response.data
                    console.log(resp)
                    if (resp.resultCode.code == 20000) {
                        this.findGoods()
                    } else {
                        alert(resp.results)
                    }
                })
        },

        // 批量修改已领完
        batchPickDown() {
            console.log(this.deleteIds)
            this.$axios.put("/api/warehouse/batchDownPickMaterial", this.deleteIds)
                .then(response => {
                    let resp = response.data
                    console.log(resp)
                    if (resp.resultCode.code == 20000) {
                        this.findGoods()
                    } else {
                        alert(resp.results)
                    }
                })
        },

        // 批量审批通过
        batchDelete() {
            console.log(this.deleteIds)
            this.$axios.put("/api/warehouse/batchPassPickMaterial", this.deleteIds)
                .then(response => {
                    let resp = response.data
                    console.log(resp)
                    if (resp.resultCode.code == 20000) {
                        this.findGoods()
                    } else {
                        alert(resp.results)
                    }
                })
        },

        // 逐一审批通过
        loadBtn(val) {
            console.log(val)
            const deleteIds = []
            deleteIds.push(val)
            console.log(deleteIds)
            this.$axios.put("/api/warehouse/batchPassPickMaterial", deleteIds)
                .then(response => {
                    let resp = response.data
                    console.log(resp)
                    if (resp.resultCode.code == 20000) {
                        this.findGoods()
                    } else {
                        alert(resp.results)
                    }
                })
        },

        // 获取公共数据
        getCommonData() {
            this.$axios.get("/api/common/pickCommon")
                .then(response => {
                    let resp = response.data
                    
                    if (resp.resultCode.code == 20000) {
                        this.commondata = resp.results
                        console.log("#############")
                        console.log(this.commondata)
                    }
                })
        },

        addGoodsBtn() {
            this.b = true
            this.goods = {} 
            this.shoppingIdToNo = []
            this.$axios.get('/api/warehouse/findRawNames')
                .then(res => {
                if (res.data.resultCode.code == 20000) {
                    this.addRawTab = res.data.results
                    console.log(this.addRawTab)
                    this.addRawTab.forEach(r=>{
                        this.shoppingIdToNo.push(
                            {"label": Object.keys(r)[0]}
                        )
                    })
                    console.log(this.shoppingIdToNo)
                }
            })

        },
        // 弹出的新增窗口的添加
        addGoods() {
            console.log("#############")
            console.log(this.goods)
            this.$axios.post('/api/warehouse/addPickRaw', this.goods)
                .then(res => {
                    console.log("&&&&&")
                    console.log(res.data)
                    if (res.data.resultCode.code == 20000) {
                        alert('添加成功')
                        this.findGoods()
                    }else{
                        alert('添加失败')
                    }
                }),
                this.b = false
        },

        // 绑定下拉框的选择事件
        selectBuyNo(){
            console.log("change")
            const goodsTemp = this.addRawTab.filter(r=> Object.keys(r)[0] == this.shoppingNoId)[0]
            console.log(goodsTemp)
            const nameTmp = Object.keys(goodsTemp)[0]
            const nextTmp = Object.values(goodsTemp)[0].warehouse
            const keyTmp = Object.keys(nextTmp)[0]
            console.log(nextTmp)
            this.goods={
                name:nameTmp,
                nums:nextTmp[keyTmp],
                unit:Object.values(goodsTemp)[0].unit+"",
                warehouse:Object.keys(nextTmp)[0]
            }
            console.log(this.goods)
        },
    },
    created() {
        this.findGoods()
        this.getCommonData()
    }

}

</script>

总结

1.介绍基于canal实现数据库和缓存同步的流程;

2.给出了核心diamante的源码;

相关推荐
悟空码字9 小时前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
皮皮林5512 天前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602734 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
茶杯梦轩4 天前
从零起步学习RabbitMQ || 第二章:RabbitMQ 深入理解概念 Producer、Consumer、Exchange、Queue 与企业实战案例
服务器·后端·消息队列
用户8307196840825 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解5 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解5 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记5 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者6 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840826 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp