多店铺需求

This commit is contained in:
Tankaikai 2025-04-08 14:56:23 +08:00
parent c424f3613f
commit c4cdc83ec3
8 changed files with 241 additions and 9 deletions

View File

@ -51,9 +51,7 @@ public class ShopBranchController {
public CzgResult<Void> settingDataSyncMethod(@RequestParam String dataSyncMethod) {
Long shopId = StpKit.USER.getShopId(0L);
shopBranchService.settingDataSyncMethod(shopId, dataSyncMethod);
CzgResult<Void> ret = CzgResult.success();
ret.setMsg("设置成功,数据正在后台同步中...");
return ret;
return CzgResult.success();
}
/**
@ -65,7 +63,9 @@ public class ShopBranchController {
@OperationLog("分店管理-同步启用")
public CzgResult<Void> dataSyncEnable(@RequestParam Long branchShopId) {
shopBranchService.dataSyncEnable(branchShopId);
return CzgResult.success();
CzgResult<Void> ret = CzgResult.success();
ret.setMsg("启用成功,数据正在后台同步中...");
return ret;
}
/**

View File

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

View File

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

View File

@ -1,7 +1,5 @@
package com.czg.product.service;
import java.util.Map;
/**
* @author ww
* @description

View File

@ -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<ShopBranchDTO> 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

View File

@ -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当用户补充了手机号码或则修改了手机号码则需要按照手机号码进行会员信息合并把逻辑123重新执行一遍
// 查询逻辑在下单时选择会员信息时查询时用适用门店id like %,1,% and is_merged_to_head = 0
List<ShopUser> headShopUserList = shopUserMapper.selectListByQuery(QueryWrapper.create().eq(ShopUser::getShopId, headShopId).isNotNull(ShopUser::getPhone));
List<ShopUser> branchShopUserList = shopUserMapper.selectListByQuery(QueryWrapper.create().eq(ShopUser::getShopId, branchShopId).isNotNull(ShopUser::getPhone));
// 如果分店没有符合条件的会员则不进行合并
if (CollUtil.isEmpty(branchShopUserList)) {
return;
}
Map<String, ShopUser> headShopUserKv = headShopUserList.stream().collect(Collectors.toMap(ShopUser::getPhone, shopUser -> shopUser));
Map<String, ShopUser> branchShopUserKv = branchShopUserList.stream().collect(Collectors.toMap(ShopUser::getPhone, shopUser -> shopUser));
Set<String> headShopPhoneList = headShopUserList.stream().map(ShopUser::getPhone).collect(Collectors.toSet());
Set<String> branchShopPhoneList = branchShopUserList.stream().map(ShopUser::getPhone).collect(Collectors.toSet());
Set<String> 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<String> 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<String> 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<String> 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());
}
}

View File

@ -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

View File

@ -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, ',');