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

news2024/11/18 23:24:43

在这里插入图片描述

前言

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

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

  • 拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步

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

在这里插入图片描述

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

  • 初始化配置SSH连接 & 安装MySQL的docker镜像 & 安装redis以及主从搭建 & 7.2版本redis.conf配置文件

  • 安装Java8环境 & 配置环境变量 & spring项目部署 &【!】存在问题未解决

  • 部署spring项目端口开放问题的解决 & 服务器项目环境搭建MySQL,Redis,Minio…指南

  • 由于自己原因导致MySQL数据库被攻击 & MySQL的binlog日志文件的理解

  • 认识redis未授权访问漏洞 & 漏洞的部分复现 & 设置连接密码 & redis其他命令学习

  • 拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步

  • Docker版的Minio安装 & Springboot项目中的使用 & 结合vue进行图片的存取

  • 在Redis的Docker容器中安装BloomFilter & 在Spring中使用Redis插件版的布隆过滤器

在这里插入图片描述

  • Elasticsearch的Docker版本的安装和参数设置 & 端口开放和浏览器访问

  • Elasticsearch的可视化Kibana工具安装 & IK分词器的安装和使用

  • Elasticsearch的springboot整合 & Kibana进行全查询和模糊查询

文章目录

  • 前言
  • 引出
  • 基于canal缓存同步更新
    • 整体的流程
  • 相关代码和流程
    • 1.canal通道的配置
    • 2.前端查询的业务代码
    • 3.数据库数据更新
    • 4.缓存更新前端展示
  • 核心代码源码
    • 1.配置yml和配置类
    • 2.canal自动更新代码
    • 3.查询的业务层service代码
    • 4.主启动类
    • 5.前端vue代码
  • 总结

引出


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文件

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对象的配置类

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缓存

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的工具类

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);
    }

}

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

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代码

在这里插入图片描述

    @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通道,进行监听数据库的变化,实现缓存同步更新

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代码

<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的源码;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1062715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据库——MySQL】(14)过程式对象程序设计——游标、触发器

目录 1. 游标1.1 声明游标1.2 打开游标1.3 读取游标1.4 关闭游标1.5 游标示例 2. 触发器2.1 创建触发器2.2 修改触发器2.3 删除触发器2.4 触发器类型2.5 触发器示例 参考书籍 1. 游标 游标一般和存储过程一起配合使用。 1.1 声明游标 要使用游标&#xff0c;需要用到 DECLAR…

<C++> String

目录 一、标准库中的string类 1. string类 2. string类的常用接口说明 2.1 string类对象的常见构造 2.2 string类对象的容量操作 2.3 string类对象的访问及遍历操作 2.4 string类对象的修改操作 2.5 string类非成员函数 总结 前言 C语言中&#xff0c;字符串是以 \0 结尾的一些…

用JMeter对HTTP接口进行压测(一)压测脚本的书写、调试思路

文章目录 安装JMeter和Groovy为什么选择Groovy&#xff1f; 压测需求以及思路准备JMeter脚本以及脚本正确性验证使用Test Script Recorder来获取整条业务线上涉及的接口为什么使用Test Script Recorder&#xff1f; 配置Test Script Recorder对接口进行动态化处理处理全局变量以…

2. 资源管理

2. 资源管理 文章目录 2. 资源管理2.1 资源管理介绍2.2 YAML语言介绍2.3 资源管理方式2.2.1 命令式对象管理2.2.2 命令式对象配置2.2.3 声明式对象配置 2.4. 模拟使用普通用户来操作 2.1 资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通…

二十九、【进阶】MySQL索引的概述和索引查询

1、索引概述 2、 普通查询和索引查询 &#xff08;1&#xff09;基础演示 无索引查询&#xff1a;在查询信息时&#xff0c;比如查询年龄age45的员工&#xff0c;系统会遍历字段为age的列&#xff0c;在找到age45的员工后&#xff0c;依旧会向下扫描&#xff0c;直到表末&…

如何使用 Dijkstra 算法找到从源到所有顶点的最短路径--附C++/Java源码

给定一个图和图中的源顶点,找到从源到给定图中所有顶点的最短路径。 例子: 输入: src = 0,图形如下图所示。 输出: 0 4 12 19 21 11 9 8 14解释:从 0 到 1 的距离 = 4。 从 0 到 2 的最小距离 = 12。0->1->2 从 0 到 3 的最小距离 = 19。0 ->1-

Python基础语法(3)

目录 一、函数 1.1 函数是什么 1.2 函数语法格式 1.3 函数参数 1.4 函数返回值 a. 一个函数中可以有多个 return 语句 b. 执行到 return 语句&#xff0c;函数就会立即执行结束&#xff0c;回到调用位置 c. 一个函数是可以一次返回多个返回值的。使用 , 来分割多个返回值…

Jmeter基础篇

1.性能测试指标 【虚拟用户数】&#xff1a;线程用户 【并发数】&#xff1a;指在某一时间&#xff0c;一定数量的虚拟用户同时对系统的某个功能进行交互&#xff0c;一般通过集合点实现 【事务】:事务代表一个完整的功能&#xff0c;一个接口可以是事务&#xff0c;多个接口…

MyBatisPlus(十一)包含查询:in

说明 包含查询&#xff0c;对应SQL语句中的 in 语句&#xff0c;查询参数包含在入参列表之内的数据。 in Testvoid inNonEmptyList() {// 非空列表&#xff0c;作为参数List<Integer> ages Stream.of(18, 20, 22).collect(Collectors.toList());in(ages);}Testvoid in…

【C语言】转圈报数问题(三种方法--指针,数组)

题目&#xff1a;有n个人围成一圈&#xff0c;顺序排号。从第一个人开始报数&#xff08;从1到3报数&#xff09;&#xff0c;凡报到3的人退出圈子&#xff0c;问最后留下的是原来第几号的那位。 方法一&#xff1a; #include <stdio.h> #define N 10int main() {int …

systemverilog function的一点小case

关于function的应用无论是在systemverilog还是verilog中都有很广泛的应用&#xff0c;但是一直有一个模糊的概念困扰着我&#xff0c;今天刚好有时间来搞清楚并记录下来。 关于fucntion的返回值的问题&#xff1a; function integer clog2( input logic[255:0] value);for(cl…

实验三十五、LM117 稳压电源的设计

一、题目 利用 LM117 设计一个稳压电路&#xff0c;要求输出电压的调节范围为 5 ∼ 20 V 5\sim20\,\textrm V 5∼20V&#xff0c;最大负载电流为 400 mA 400\,\textrm{mA} 400mA。利用 Multisim 对所设计电路进行仿真&#xff0c;并测试所有性能指标。 二、仿真电路 仿真电…

安装NodeJS并使用yarn下载前端依赖

文章目录 1、安装NodeJS1.1 下载NodeJS安装包1.2 解压并配置NodeJS1.3 验证是否安装成功2、使用yarn下载前端依赖2.1 安装yarn2.2 使用yarn下载前端依赖参考目标:在Windows下安装新版NodeJS,并使用yarn下载前端依赖,实现运行前端项目。 1、安装NodeJS 1.1 下载NodeJS安装包…

Vue中实现自定义编辑邮件发送到指定邮箱(纯前端实现)

formspree里面注册账号 注册完成后进入后台新建项目并且新建表单 这一步完成之后你将得到一个地址 最后就是在项目中请求这个地址 关键代码如下&#xff1a; submitForm() {this.fullscreenLoading true;this.$axios({method: "post",url: "https://xxxxxxx…

【python海洋专题十一】colormap调色

【python海洋专题十一】colormap调色 上期内容 本期内容 图像的函数包调用&#xff01; Part01. 自带颜色条Colormap 调用方法&#xff1a; cmap3plt.get_cmap(ocean)查询方法&#xff01; Part02. seaborn函数包 01&#xff1a;sns.cubehelix_palette cmap5 sns.cu…

【MySQL】错误1166 Incorrect column name

错误1166 Incorrect column name 是指字段名里有空格 Incorrect column name ‘name’ 图中报错的原因是name字段中有空格 删除空格即可

基于SpringBoot的视频网站系统

目录 前言 一、技术栈 二、系统功能介绍 用户信息管理 视频分享管理 视频排名管理 交流论坛管理 留言板管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 使用旧方法对视频信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运…

ParagonNTFSforMac_15.5.102中文版最受欢迎的NTFS硬盘格式读取工具

Paragon NTFS for Mac是一款可以为您轻松解决Mac平台上不能识别Windows通用的NTFS文件难题&#xff0c;这是一款强大的Mac读写工具&#xff0c;相信在很多时候&#xff0c;Mac用户需要对NTFS文件的移动硬盘进行写入&#xff0c;但是macOS系统默认是不让写入的&#xff0c;使用小…

妙不可言的Python之旅----(二)

Python基础语法 什么是字面量 字面量&#xff1a;在代码中&#xff0c;被写下来的的固定的值&#xff0c;称之为字面量 常用的值类型 类型 描述 说明 数字&#xff08;Number&#xff09; 支持 • 整数&#xff08;int&#xff09; • 浮点数&#xff08;float&#xff…

C++基础知识(二) -- 函数重载

自然语言中&#xff0c;一个词可以有多重含义&#xff0c;人们可以通过上下文来判断该词真实的含义&#xff0c;即该词被重载了。 比如&#xff1a;以前有一个笑话&#xff0c;国有两个体育项目大家根本不用看&#xff0c;也不用担心。一个是乒乓球&#xff0c;一个是男足。前者…