From c4cdc83ec34241054f95d3eabf947c4bda76c8a2 Mon Sep 17 00:00:00 2001 From: Tankaikai Date: Tue, 8 Apr 2025 14:56:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E5=BA=97=E9=93=BA=E9=9C=80=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/ShopBranchController.java | 8 +- .../java/com/czg/account/entity/ShopUser.java | 13 ++ .../account/service/ShopUserSyncService.java | 17 ++ .../czg/product/service/ShopSyncService.java | 2 - .../service/impl/ShopBranchServiceImpl.java | 21 ++- .../service/impl/ShopUserSyncServiceImpl.java | 172 ++++++++++++++++++ .../service/impl/ShopSyncServiceImpl.java | 4 +- sqls/250403/tb_shop_user.sql | 13 ++ 8 files changed, 241 insertions(+), 9 deletions(-) create mode 100644 cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopUserSyncService.java create mode 100644 cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserSyncServiceImpl.java create mode 100644 sqls/250403/tb_shop_user.sql diff --git a/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopBranchController.java b/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopBranchController.java index 3433270b..15ea862d 100644 --- a/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopBranchController.java +++ b/cash-api/account-server/src/main/java/com/czg/controller/admin/ShopBranchController.java @@ -51,9 +51,7 @@ public class ShopBranchController { public CzgResult settingDataSyncMethod(@RequestParam String dataSyncMethod) { Long shopId = StpKit.USER.getShopId(0L); shopBranchService.settingDataSyncMethod(shopId, dataSyncMethod); - CzgResult ret = CzgResult.success(); - ret.setMsg("设置成功,数据正在后台同步中..."); - return ret; + return CzgResult.success(); } /** @@ -65,7 +63,9 @@ public class ShopBranchController { @OperationLog("分店管理-同步启用") public CzgResult dataSyncEnable(@RequestParam Long branchShopId) { shopBranchService.dataSyncEnable(branchShopId); - return CzgResult.success(); + CzgResult ret = CzgResult.success(); + ret.setMsg("启用成功,数据正在后台同步中..."); + return ret; } /** diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java index c74ed623..309fe017 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/entity/ShopUser.java @@ -125,4 +125,17 @@ public class ShopUser implements Serializable { @Column(onInsertValue = "now()", onUpdateValue = "now()") private LocalDateTime updateTime; + + /** + * 是否已经合并数据到主店 1-是 0-否 默认0 + */ + private Integer isMergedToHead; + /** + * 已经合并过来的用户信息,jsonArray格式,[{"id":1,"shopId":2,...},{"id":1,"shopId":2,...}] + */ + private String mergedUsers; + /** + * 适用门店id集合,逗号分隔,例如:0,1,2,3,...查询的时候 all_shop_ids like '%,1,%'; + */ + private String allShopIds; } diff --git a/cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopUserSyncService.java b/cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopUserSyncService.java new file mode 100644 index 00000000..d31330d6 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/account/service/ShopUserSyncService.java @@ -0,0 +1,17 @@ +package com.czg.account.service; + +/** + * 店铺用户同步Service + * @author tankaikai + * @since 2025-04-08 10:17 + */ +public interface ShopUserSyncService { + + /** + * 同步合并主分店会员信息 + * + * @param headShopId 主店id + * @param branchShopId 分店id + */ + void syncMergeShopUser(Long headShopId, Long branchShopId); +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java index 0cf8af1e..e283aa27 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ShopSyncService.java @@ -1,7 +1,5 @@ package com.czg.product.service; -import java.util.Map; - /** * @author ww * @description diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopBranchServiceImpl.java b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopBranchServiceImpl.java index 856f782f..ade8e03a 100644 --- a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopBranchServiceImpl.java +++ b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopBranchServiceImpl.java @@ -1,5 +1,6 @@ package com.czg.service.account.service.impl; +import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.ObjUtil; import com.czg.account.dto.ShopBranchDTO; import com.czg.account.entity.ShopConfig; @@ -8,8 +9,11 @@ import com.czg.account.enums.BranchDataSyncMethodEnum; import com.czg.account.enums.ShopTypeEnum; import com.czg.account.param.ShopBranchParam; import com.czg.account.service.ShopBranchService; +import com.czg.account.service.ShopUserSyncService; +import com.czg.account.service.SyncNoticeService; import com.czg.enums.YesNoEnum; import com.czg.exception.CzgException; +import com.czg.product.service.ShopSyncService; import com.czg.sa.StpKit; import com.czg.service.account.mapper.ShopConfigMapper; import com.czg.service.account.mapper.ShopInfoMapper; @@ -34,9 +38,15 @@ public class ShopBranchServiceImpl implements ShopBranchService { @Resource private ShopConfigMapper shopConfigMapper; - @Resource private ShopInfoMapper shopInfoMapper; + @Resource + private ShopSyncService shopSyncService; + @Resource + private ShopUserSyncService shopUserSyncService; + @Resource + private SyncNoticeService syncNoticeService; + @Override public Page findPage(ShopBranchParam param) { @@ -98,7 +108,14 @@ public class ShopBranchServiceImpl implements ShopBranchService { branchConfig.setIsEnableConsSync(YesNoEnum.YES.value()); branchConfig.setIsEnableVipSync(YesNoEnum.YES.value()); shopConfigMapper.update(branchConfig); - // TODO 异步事务同步商品数据、会员数据、耗材数据 + ThreadUtil.execAsync(() -> { + // 同步商品和耗材 + shopSyncService.sync(shopId, branchShop.getId()); + }); + ThreadUtil.execAsync(() -> { + // 同步会员信息 + shopUserSyncService.syncMergeShopUser(shopId, branchShop.getId()); + }); } @Override diff --git a/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserSyncServiceImpl.java b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserSyncServiceImpl.java new file mode 100644 index 00000000..71d3e817 --- /dev/null +++ b/cash-service/account-service/src/main/java/com/czg/service/account/service/impl/ShopUserSyncServiceImpl.java @@ -0,0 +1,172 @@ +package com.czg.service.account.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.czg.account.entity.ShopUser; +import com.czg.account.service.ShopUserSyncService; +import com.czg.enums.YesNoEnum; +import com.czg.service.account.mapper.ShopUserMapper; +import com.mybatisflex.core.query.QueryWrapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 店铺用户同步Service实现类 + * + * @author tankaikai + * @since 2025-04-08 10:18 + */ +@Service +public class ShopUserSyncServiceImpl implements ShopUserSyncService { + + @Resource + private ShopUserMapper shopUserMapper; + + /** + * 同步合并主分店会员信息 + * + * @param headShopId 主店id + * @param branchShopId 分店id + */ + @Override + public synchronized void syncMergeShopUser(Long headShopId, Long branchShopId) { + // 合并前置逻辑:查询主店、分店手机号码不为空的会员信息,分店和主店手机号码进行匹配 + // 合并逻辑1:如果手机号码一致,则把分店会员信息中的 积分、余额、消费金额、消费次数合并到主店会员信息中,把分店这些信息清零,is_merged_to_head的意思是是否已经合并数据到主店 set is_merged_to_head = 1 + // 合并逻辑2:分店手机号码在主店中不存在,则在主店创建新的会员,把分店分店会员信息中的 积分、余额、消费金额、消费次数合并到主店会员信息中,把分店这些信息清零,set is_merged_to_head = 1 + // 合并逻辑3:主店没有分店有,把分店会员信息copy到主店,更改shop_id,all_shop_ids,把分店这些信息清零, 把分店会员信息jsonArray格式化存储至merged_users字段中,便于后续查询合并前会员信息,新增字段,适用门店id(用英文逗号隔开,如:0,1,2,3, 查询时用适用门店id like %,1,% and is_merged_to_head = 0) + // 合并逻辑4:当用户补充了手机号码或则修改了手机号码,则需要按照手机号码进行会员信息合并,把逻辑1,2,3重新执行一遍 + // 查询逻辑:在下单时选择会员信息时,查询时用适用门店id like %,1,% and is_merged_to_head = 0 + List headShopUserList = shopUserMapper.selectListByQuery(QueryWrapper.create().eq(ShopUser::getShopId, headShopId).isNotNull(ShopUser::getPhone)); + List branchShopUserList = shopUserMapper.selectListByQuery(QueryWrapper.create().eq(ShopUser::getShopId, branchShopId).isNotNull(ShopUser::getPhone)); + // 如果分店没有符合条件的会员,则不进行合并 + if (CollUtil.isEmpty(branchShopUserList)) { + return; + } + Map headShopUserKv = headShopUserList.stream().collect(Collectors.toMap(ShopUser::getPhone, shopUser -> shopUser)); + Map branchShopUserKv = branchShopUserList.stream().collect(Collectors.toMap(ShopUser::getPhone, shopUser -> shopUser)); + + Set headShopPhoneList = headShopUserList.stream().map(ShopUser::getPhone).collect(Collectors.toSet()); + Set branchShopPhoneList = branchShopUserList.stream().map(ShopUser::getPhone).collect(Collectors.toSet()); + + Set phoneSet = new HashSet<>(); + phoneSet.addAll(headShopPhoneList); + phoneSet.addAll(branchShopPhoneList); + + // 执行合并逻辑 + for (String phone : phoneSet) { + ShopUser headShopUser = headShopUserKv.get(phone); + ShopUser branchShopUser = branchShopUserKv.get(phone); + // 1.如果都有 + if (ObjUtil.isNotNull(headShopUser) && ObjUtil.isNotNull(branchShopUser)) { + toMergeCase1(headShopUser, branchShopUser); + // 2.如果主店有,分店没有 + } else if (ObjUtil.isNotNull(headShopUser) && ObjUtil.isNull(branchShopUser)) { + toMergeCase2(headShopUser, branchShopId); + // 3.如果主店没有,分店有 + } else if (ObjUtil.isNull(headShopUser) && ObjUtil.isNotNull(branchShopUser)) { + toMergeCase3(headShopId, branchShopUser); + } + } + } + + /** + * 合并会员信息 1.如果都有 + * + * @param headShopUser 主店会员 + * @param branchShopUser 分店会员 + */ + public void toMergeCase1(ShopUser headShopUser, ShopUser branchShopUser) { + headShopUser.setAccountPoints(NumberUtil.nullToZero(headShopUser.getAccountPoints()) + (NumberUtil.nullToZero(branchShopUser.getAccountPoints()))); + headShopUser.setAmount(NumberUtil.nullToZero(headShopUser.getAmount()).add(NumberUtil.nullToZero(branchShopUser.getAmount()))); + headShopUser.setConsumeCount(NumberUtil.nullToZero(headShopUser.getConsumeCount()) + (NumberUtil.nullToZero(branchShopUser.getConsumeCount()))); + headShopUser.setConsumeAmount(NumberUtil.nullToZero(headShopUser.getConsumeAmount()).add(NumberUtil.nullToZero(branchShopUser.getConsumeAmount()))); + headShopUser.setUpdateTime(LocalDateTime.now()); + String mergedUsers = StrUtil.emptyToDefault(headShopUser.getMergedUsers(), "[]"); + JSONArray objects = JSON.parseArray(mergedUsers); + objects.add(JSONObject.from(branchShopUser)); + headShopUser.setMergedUsers(JSON.toJSONString(objects)); + String allShopIds = StrUtil.emptyToDefault(headShopUser.getAllShopIds(), "0,".concat(headShopUser.getShopId() + ",")); + List split = StrUtil.split(allShopIds, ",", true, true); + String branchShopIdStr = Convert.toStr(branchShopUser.getShopId()); + if (!split.contains(branchShopIdStr)) { + split.add(branchShopIdStr); + } + headShopUser.setAllShopIds(CollUtil.join(split, ",").concat(",")); + shopUserMapper.update(headShopUser); + toZero(branchShopUser); + shopUserMapper.update(branchShopUser); + } + + /** + * 合并会员信息 2.如果主店有,分店没有 + * + * @param headShopUser 主店会员 + * @param branchShopId 分店id + */ + public void toMergeCase2(ShopUser headShopUser, Long branchShopId) { + headShopUser.setUpdateTime(LocalDateTime.now()); + String allShopIds = StrUtil.emptyToDefault(headShopUser.getAllShopIds(), "0,".concat(headShopUser.getShopId() + ",")); + List split = StrUtil.split(allShopIds, ",", true, true); + String branchShopIdStr = Convert.toStr(branchShopId); + if (!split.contains(branchShopIdStr)) { + split.add(branchShopIdStr); + } + headShopUser.setAllShopIds(CollUtil.join(split, ",").concat(",")); + shopUserMapper.update(headShopUser); + } + + /** + * 合并会员信息 3.如果主店没有,分店有 + * + * @param headShopId 主店id + * @param branchShopUser 分店会员信息 + */ + public void toMergeCase3(Long headShopId, ShopUser branchShopUser) { + ShopUser headShopUser = BeanUtil.copyProperties(branchShopUser, ShopUser.class, "id", "shopId","allShopIds"); + headShopUser.setShopId(headShopId); + String mergedUsers = StrUtil.emptyToDefault(headShopUser.getMergedUsers(), "[]"); + JSONArray objects = JSON.parseArray(mergedUsers); + objects.add(JSONObject.from(branchShopUser)); + headShopUser.setMergedUsers(JSON.toJSONString(objects)); + String allShopIds = StrUtil.emptyToDefault(headShopUser.getAllShopIds(), "0,".concat(headShopUser.getShopId() + ",")); + List split = StrUtil.split(allShopIds, ",", true, true); + String branchShopIdStr = Convert.toStr(branchShopUser.getShopId()); + if (!split.contains(branchShopIdStr)) { + split.add(branchShopIdStr); + } + headShopUser.setAllShopIds(CollUtil.join(split, ",").concat(",")); + shopUserMapper.insert(headShopUser); + toZero(branchShopUser); + shopUserMapper.update(branchShopUser); + } + + /** + * 会员数据归零 + * + * @param shopUser 会员信息 + */ + public void toZero(ShopUser shopUser) { + shopUser.setAccountPoints(0); + shopUser.setAmount(BigDecimal.ZERO); + shopUser.setConsumeAmount(BigDecimal.ZERO); + shopUser.setConsumeCount(0); + shopUser.setUpdateTime(LocalDateTime.now()); + shopUser.setIsMergedToHead(YesNoEnum.YES.value()); + } + +} diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java index dfdbca7d..07adc168 100644 --- a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java +++ b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ShopSyncServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSONObject; import com.czg.account.entity.ShopInfo; import com.czg.account.service.ShopInfoService; +import com.czg.account.service.ShopUserService; import com.czg.account.service.SyncNoticeService; import com.czg.exception.CzgException; import com.czg.product.entity.*; @@ -58,6 +59,8 @@ public class ShopSyncServiceImpl implements ShopSyncService { @Resource private ProdConsRelationService prodConsRelationService; @Resource + private ShopUserService shopUserService; + @Resource private SyncNoticeService syncNoticeService; private void checkShopInfo(Long sourceShopId, Long targetShopId) { @@ -73,7 +76,6 @@ public class ShopSyncServiceImpl implements ShopSyncService { || targetShop.getIsHeadShop() == null || targetShop.getIsHeadShop().equals(1)) { throw new CzgException("同步失败,目标店铺是主店铺或目标店铺是单店"); } - } @Override diff --git a/sqls/250403/tb_shop_user.sql b/sqls/250403/tb_shop_user.sql new file mode 100644 index 00000000..0fc56209 --- /dev/null +++ b/sqls/250403/tb_shop_user.sql @@ -0,0 +1,13 @@ +-- ---------------------------- +-- tb_shop_user表扩展字段 +-- ---------------------------- +ALTER TABLE `tb_shop_user` + ADD COLUMN `is_merged_to_head` tinyint NULL DEFAULT 0 COMMENT '是否已经合并数据到主店 1-是 0-否 默认0', + ADD COLUMN `merged_users` text NULL COMMENT '已经合并过来的用户信息,jsonArray格式,[{\"id\":1,\"shopId\":2,...},{\"id\":1,\"shopId\":2,...}]', + ADD COLUMN `all_shop_ids` varchar(1200) NULL COMMENT '适用门店id集合,逗号分隔,例如:0,1,2,3,...查询的时候 all_shop_ids like \'%,1,%\';'; + +-- ---------------------------- +-- 处理历史数据 +-- ---------------------------- +update tb_shop_user set is_merged_to_head = 0; +update tb_shop_user set all_shop_ids = concat('0,', shop_id, ','); \ No newline at end of file