Prefácio
Recentemente, a instância Huawei Cloud Yaoyun Server L foi lançada e eu também construí uma para brincar. Durante o processo, encontrei vários problemas e aprendi muito sobre operação e manutenção no processo de solução dos problemas.
No blog anterior apresentei a instalação e configuração do canal, consulte o blog.
Este blog apresenta um caso de aplicação do projeto canal, detalha o processo de banco de dados e sincronização de cache baseado no canal e fornece o código-fonte do núcleo diamante.
A lista de outros artigos relacionados à avaliação de instância do Huawei Cloud Yaoyun Server L é a seguinte:
Diretório de artigos
liderar
1. Apresentar o processo de sincronização de banco de dados e cache baseado no canal;
2. Fornecer o código fonte do núcleo diamante;
Atualizações síncronas baseadas no cache do canal
processo geral
Ao iniciar o projeto Spring, a atualização automática do cache é iniciada de forma síncrona.Se os dados relevantes da tabela do banco de dados forem alterados, o canal irá ouvi-los e então atualizar os dados correspondentes no cache redis.
Quais dados são retirados do cache? ——Dados que não são atualizados com frequência: como departamentos da empresa, armazéns, etc.;
Quando o projeto começou, foi iniciado o canal, que serve para monitorar alterações no banco de dados;
Lógica de negócios: Solicite dados relevantes do front-end -> Peça ao Redis
(1) Se houver no redis, será devolvido ao front end;
(2) Se não estiver no redis, consulte-o no banco de dados, salve-o no redis e retorne-o ao front-end;
(3) Se o banco de dados for atualizado, o canal monitora a modificação e o atualiza no Redis de forma síncrona para garantir que o cache e o banco de dados sejam consistentes;
Código e processos relacionados
1.Configuração do canal do canal
O cache é atualizado automaticamente e o IP e o número da porta no arquivo de configuração são lidos.
Se deve ativar a atualização automática do cache e ler a configuração do arquivo de configuração;
2. Código comercial para consulta front-end
Obtenha os dados no cache quando os dados do banco de dados não forem atualizados
3. Atualização de dados do banco de dados
Se os dados do banco de dados forem atualizados, o canal os monitora e descobre que a tabela correspondente está armazenada em cache e os campos correspondentes mudam, e o cache é atualizado automaticamente.
Atualização de sincronização automática de cache
Os dados mais recentes armazenados no Redis
4. Exibição front-end de atualização de cache
Após a atualização do cache, o front-end consulta novamente para obter os dados mais recentes.
Código fonte do código principal
1. Configure classes yml e de configuração
Arquivo yml de configuração
server:
port: 10050
## 是否启用安全框架 true为开启,false为关闭
security:
isOpen: true
## 是否开启canal管道,true为开启,false为关闭
canal:
isOpen: true
# canal的相关配置
canalConfig:
host: 124.70.138.34
port: 11111
Classe de configuração para armazenar objetos Java no Redis
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 atualiza automaticamente o código
O Canal atualiza automaticamente o código, usa pipeline de canal para monitorar alterações de dados MySQL e atualiza automaticamente o cache 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("================> 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;
}
}
}
}
classe de ferramenta 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);
}
}
Classe constante para monitorar tabelas de banco de dados e nomes de colunas
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. Consulte o código de serviço da camada de negócios
@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. Aula principal de startups
A classe de inicialização principal implementa o método CommandLineRunner, inicia o canal do canal, monitora alterações no banco de dados e implementa atualizações de sincronização de cache.
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. Código vue de front-end
<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>
Resumir
1. Apresentar o processo de sincronização de banco de dados e cache baseado no canal;
2. Fornecer o código fonte do núcleo diamante;