|
@@ -6,10 +6,10 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.haha.common.exception.BusinessException;
|
|
import com.haha.common.exception.BusinessException;
|
|
|
|
|
+import com.haha.entity.Device;
|
|
|
import com.haha.entity.ErpShop;
|
|
import com.haha.entity.ErpShop;
|
|
|
-import com.haha.entity.Shop;
|
|
|
|
|
|
|
+import com.haha.mapper.DeviceMapper;
|
|
|
import com.haha.mapper.ErpShopMapper;
|
|
import com.haha.mapper.ErpShopMapper;
|
|
|
-import com.haha.mapper.ShopMapper;
|
|
|
|
|
import com.haha.service.ErpShopService;
|
|
import com.haha.service.ErpShopService;
|
|
|
import com.qdb.sdk.QdbClient;
|
|
import com.qdb.sdk.QdbClient;
|
|
|
import com.qdb.sdk.QdbException;
|
|
import com.qdb.sdk.QdbException;
|
|
@@ -20,27 +20,34 @@ import com.qdb.sdk.model.response.ShopPageResult;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
import org.springframework.scheduling.annotation.Async;
|
|
import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.HashSet;
|
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Set;
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * ERP 店铺信息服务实现
|
|
|
|
|
- */
|
|
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
@Service
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
|
public class ErpShopServiceImpl extends ServiceImpl<ErpShopMapper, ErpShop> implements ErpShopService {
|
|
public class ErpShopServiceImpl extends ServiceImpl<ErpShopMapper, ErpShop> implements ErpShopService {
|
|
|
|
|
|
|
|
- private final ShopMapper shopMapper;
|
|
|
|
|
|
|
+ private final DeviceMapper deviceMapper;
|
|
|
|
|
|
|
|
@Autowired(required = false)
|
|
@Autowired(required = false)
|
|
|
private QdbClient qdbClient;
|
|
private QdbClient qdbClient;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private JdbcTemplate jdbcTemplate;
|
|
|
|
|
+
|
|
|
@Async("taskExecutor")
|
|
@Async("taskExecutor")
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@@ -113,53 +120,140 @@ public class ErpShopServiceImpl extends ServiceImpl<ErpShopMapper, ErpShop> impl
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public void bindShop(Long shopId, Long erpShopId) {
|
|
|
|
|
- Shop shop = shopMapper.selectById(shopId);
|
|
|
|
|
- if (shop == null) {
|
|
|
|
|
- throw new BusinessException(404, "门店不存在");
|
|
|
|
|
|
|
+ public void bindDevice(Long deviceId, Long erpShopId) {
|
|
|
|
|
+ Device device = deviceMapper.selectById(deviceId);
|
|
|
|
|
+ if (device == null) {
|
|
|
|
|
+ throw new BusinessException(404, "设备不存在");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 检查 ERP 店铺是否存在
|
|
|
|
|
ErpShop erpShop = getOne(new LambdaQueryWrapper<ErpShop>()
|
|
ErpShop erpShop = getOne(new LambdaQueryWrapper<ErpShop>()
|
|
|
.eq(ErpShop::getErpShopId, erpShopId));
|
|
.eq(ErpShop::getErpShopId, erpShopId));
|
|
|
if (erpShop == null) {
|
|
if (erpShop == null) {
|
|
|
throw new BusinessException(404, "ERP店铺不存在,请先同步ERP店铺数据");
|
|
throw new BusinessException(404, "ERP店铺不存在,请先同步ERP店铺数据");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 检查是否已被其他门店绑定
|
|
|
|
|
- List<Shop> boundShops = shopMapper.selectList(new LambdaQueryWrapper<Shop>()
|
|
|
|
|
- .eq(Shop::getErpShopId, erpShopId));
|
|
|
|
|
- if (!boundShops.isEmpty() && !boundShops.get(0).getId().equals(shopId)) {
|
|
|
|
|
- throw new BusinessException(400, "该ERP店铺已绑定到门店: " + boundShops.get(0).getName());
|
|
|
|
|
|
|
+ List<Device> boundDevices = deviceMapper.selectList(new LambdaQueryWrapper<Device>()
|
|
|
|
|
+ .eq(Device::getErpShopId, erpShopId));
|
|
|
|
|
+ if (!boundDevices.isEmpty() && !boundDevices.get(0).getId().equals(deviceId)) {
|
|
|
|
|
+ throw new BusinessException(400, "该ERP店铺已绑定到设备: " + boundDevices.get(0).getDeviceId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ device.setErpShopId(erpShopId);
|
|
|
|
|
+ device.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
+ deviceMapper.updateById(device);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("设备绑定ERP店铺: deviceId={}, erpShopId={}, erpShopName={}",
|
|
|
|
|
+ device.getDeviceId(), erpShopId, erpShop.getShopName());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public void unbindDevice(Long deviceId) {
|
|
|
|
|
+ Device device = deviceMapper.selectById(deviceId);
|
|
|
|
|
+ if (device == null) {
|
|
|
|
|
+ throw new BusinessException(404, "设备不存在");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- shop.setErpShopId(erpShopId);
|
|
|
|
|
- shop.setUpdateTime(LocalDateTime.now());
|
|
|
|
|
- shopMapper.updateById(shop);
|
|
|
|
|
|
|
+ deviceMapper.update(null, new LambdaUpdateWrapper<Device>()
|
|
|
|
|
+ .set(Device::getErpShopId, null)
|
|
|
|
|
+ .set(Device::getUpdateTime, LocalDateTime.now())
|
|
|
|
|
+ .eq(Device::getId, deviceId));
|
|
|
|
|
|
|
|
- log.info("门店绑定ERP店铺: shopId={}, shopName={}, erpShopId={}, erpShopName={}",
|
|
|
|
|
- shopId, shop.getName(), erpShopId, erpShop.getShopName());
|
|
|
|
|
|
|
+ log.info("设备解绑ERP店铺: deviceId={}", device.getDeviceId());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public void unbindShop(Long shopId) {
|
|
|
|
|
- Shop shop = shopMapper.selectById(shopId);
|
|
|
|
|
- if (shop == null) {
|
|
|
|
|
- throw new BusinessException(404, "门店不存在");
|
|
|
|
|
|
|
+ public Map<String, Integer> autoBind() {
|
|
|
|
|
+ List<ErpShop> erpShopList = list(new LambdaQueryWrapper<ErpShop>()
|
|
|
|
|
+ .eq(ErpShop::getStatus, 1));
|
|
|
|
|
+ if (erpShopList.isEmpty()) {
|
|
|
|
|
+ Map<String, Integer> empty = new HashMap<>();
|
|
|
|
|
+ empty.put("bound", 0);
|
|
|
|
|
+ empty.put("skipped", 0);
|
|
|
|
|
+ return empty;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 收集所有 shopCode → erpShopId 映射 (shopCode 即设备ID)
|
|
|
|
|
+ Map<String, Long> shopCodeToErpId = new LinkedHashMap<>();
|
|
|
|
|
+ for (ErpShop erp : erpShopList) {
|
|
|
|
|
+ if (StringUtils.hasText(erp.getShopCode())) {
|
|
|
|
|
+ shopCodeToErpId.put(erp.getShopCode(), erp.getErpShopId());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- shopMapper.update(null, new LambdaUpdateWrapper<Shop>()
|
|
|
|
|
- .set(Shop::getErpShopId, null)
|
|
|
|
|
- .set(Shop::getUpdateTime, LocalDateTime.now())
|
|
|
|
|
- .eq(Shop::getId, shopId));
|
|
|
|
|
|
|
+ // 2. 批量查已绑定的设备
|
|
|
|
|
+ List<Long> allErpShopIds = erpShopList.stream().map(ErpShop::getErpShopId).toList();
|
|
|
|
|
+ Set<Long> boundErpIds = new HashSet<>(deviceMapper.selectList(new LambdaQueryWrapper<Device>()
|
|
|
|
|
+ .in(Device::getErpShopId, allErpShopIds)
|
|
|
|
|
+ .isNotNull(Device::getErpShopId))
|
|
|
|
|
+ .stream().map(Device::getErpShopId).toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 过滤掉已绑定的,剩下的按 deviceId 批量查设备
|
|
|
|
|
+ List<String> targetDeviceIds = new ArrayList<>();
|
|
|
|
|
+ for (Map.Entry<String, Long> e : shopCodeToErpId.entrySet()) {
|
|
|
|
|
+ if (!boundErpIds.contains(e.getValue())) {
|
|
|
|
|
+ targetDeviceIds.add(e.getKey());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ int alreadyBoundSkip = shopCodeToErpId.size() - targetDeviceIds.size();
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Long> deviceIdToDbId = new LinkedHashMap<>();
|
|
|
|
|
+ for (int i = 0; i < targetDeviceIds.size(); i += 100) {
|
|
|
|
|
+ int end = Math.min(i + 100, targetDeviceIds.size());
|
|
|
|
|
+ List<String> batch = targetDeviceIds.subList(i, end);
|
|
|
|
|
+ List<Device> devices = deviceMapper.selectList(new LambdaQueryWrapper<Device>()
|
|
|
|
|
+ .in(Device::getDeviceId, batch));
|
|
|
|
|
+ for (Device d : devices) {
|
|
|
|
|
+ deviceIdToDbId.put(d.getDeviceId(), d.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 唯一匹配的收集起来准备批量 UPDATE
|
|
|
|
|
+ List<Long> bindDbIds = new ArrayList<>();
|
|
|
|
|
+ List<Long> bindErpIds = new ArrayList<>();
|
|
|
|
|
+ int skipped = alreadyBoundSkip;
|
|
|
|
|
+
|
|
|
|
|
+ for (String deviceId : targetDeviceIds) {
|
|
|
|
|
+ Long dbId = deviceIdToDbId.get(deviceId);
|
|
|
|
|
+ if (dbId != null && dbId > 0) {
|
|
|
|
|
+ bindDbIds.add(dbId);
|
|
|
|
|
+ bindErpIds.add(shopCodeToErpId.get(deviceId));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ skipped++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 批量 UPDATE(CASE WHEN,100条一批)
|
|
|
|
|
+ int bound = 0;
|
|
|
|
|
+ for (int i = 0; i < bindDbIds.size(); i += 100) {
|
|
|
|
|
+ int end = Math.min(i + 100, bindDbIds.size());
|
|
|
|
|
+ List<Long> batchIds = bindDbIds.subList(i, end);
|
|
|
|
|
+ List<Long> batchErpIds = bindErpIds.subList(i, end);
|
|
|
|
|
+
|
|
|
|
|
+ StringBuilder sql = new StringBuilder(
|
|
|
|
|
+ "UPDATE t_device SET erp_shop_id = CASE id ");
|
|
|
|
|
+ for (int j = 0; j < batchIds.size(); j++) {
|
|
|
|
|
+ sql.append("WHEN ").append(batchIds.get(j))
|
|
|
|
|
+ .append(" THEN ").append(batchErpIds.get(j)).append(" ");
|
|
|
|
|
+ }
|
|
|
|
|
+ sql.append("END, update_time = NOW() WHERE id IN (");
|
|
|
|
|
+ for (int j = 0; j < batchIds.size(); j++) {
|
|
|
|
|
+ if (j > 0) sql.append(",");
|
|
|
|
|
+ sql.append(batchIds.get(j));
|
|
|
|
|
+ }
|
|
|
|
|
+ sql.append(")");
|
|
|
|
|
+
|
|
|
|
|
+ bound += jdbcTemplate.update(sql.toString());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- log.info("门店解绑ERP店铺: shopId={}, shopName={}", shopId, shop.getName());
|
|
|
|
|
|
|
+ log.info("一键自动绑定完成: 成功 {} 条, 跳过 {} 条", bound, skipped);
|
|
|
|
|
+ Map<String, Integer> result = new HashMap<>();
|
|
|
|
|
+ result.put("bound", bound);
|
|
|
|
|
+ result.put("skipped", skipped);
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 插入或更新 ERP 店铺记录(按 erp_shop_id 判断)
|
|
|
|
|
- */
|
|
|
|
|
private void upsertErpShop(ShopInfo info) {
|
|
private void upsertErpShop(ShopInfo info) {
|
|
|
ErpShop existing = getOne(new LambdaQueryWrapper<ErpShop>()
|
|
ErpShop existing = getOne(new LambdaQueryWrapper<ErpShop>()
|
|
|
.eq(ErpShop::getErpShopId, info.getShopId()));
|
|
.eq(ErpShop::getErpShopId, info.getShopId()));
|