多店铺需求
This commit is contained in:
parent
c424f3613f
commit
c4cdc83ec3
|
|
@ -51,9 +51,7 @@ public class ShopBranchController {
|
||||||
public CzgResult<Void> settingDataSyncMethod(@RequestParam String dataSyncMethod) {
|
public CzgResult<Void> settingDataSyncMethod(@RequestParam String dataSyncMethod) {
|
||||||
Long shopId = StpKit.USER.getShopId(0L);
|
Long shopId = StpKit.USER.getShopId(0L);
|
||||||
shopBranchService.settingDataSyncMethod(shopId, dataSyncMethod);
|
shopBranchService.settingDataSyncMethod(shopId, dataSyncMethod);
|
||||||
CzgResult<Void> ret = CzgResult.success();
|
return CzgResult.success();
|
||||||
ret.setMsg("设置成功,数据正在后台同步中...");
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -65,7 +63,9 @@ public class ShopBranchController {
|
||||||
@OperationLog("分店管理-同步启用")
|
@OperationLog("分店管理-同步启用")
|
||||||
public CzgResult<Void> dataSyncEnable(@RequestParam Long branchShopId) {
|
public CzgResult<Void> dataSyncEnable(@RequestParam Long branchShopId) {
|
||||||
shopBranchService.dataSyncEnable(branchShopId);
|
shopBranchService.dataSyncEnable(branchShopId);
|
||||||
return CzgResult.success();
|
CzgResult<Void> ret = CzgResult.success();
|
||||||
|
ret.setMsg("启用成功,数据正在后台同步中...");
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -125,4 +125,17 @@ public class ShopUser implements Serializable {
|
||||||
|
|
||||||
@Column(onInsertValue = "now()", onUpdateValue = "now()")
|
@Column(onInsertValue = "now()", onUpdateValue = "now()")
|
||||||
private LocalDateTime updateTime;
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package com.czg.product.service;
|
package com.czg.product.service;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ww
|
* @author ww
|
||||||
* @description
|
* @description
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.czg.service.account.service.impl;
|
package com.czg.service.account.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import com.czg.account.dto.ShopBranchDTO;
|
import com.czg.account.dto.ShopBranchDTO;
|
||||||
import com.czg.account.entity.ShopConfig;
|
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.enums.ShopTypeEnum;
|
||||||
import com.czg.account.param.ShopBranchParam;
|
import com.czg.account.param.ShopBranchParam;
|
||||||
import com.czg.account.service.ShopBranchService;
|
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.enums.YesNoEnum;
|
||||||
import com.czg.exception.CzgException;
|
import com.czg.exception.CzgException;
|
||||||
|
import com.czg.product.service.ShopSyncService;
|
||||||
import com.czg.sa.StpKit;
|
import com.czg.sa.StpKit;
|
||||||
import com.czg.service.account.mapper.ShopConfigMapper;
|
import com.czg.service.account.mapper.ShopConfigMapper;
|
||||||
import com.czg.service.account.mapper.ShopInfoMapper;
|
import com.czg.service.account.mapper.ShopInfoMapper;
|
||||||
|
|
@ -34,9 +38,15 @@ public class ShopBranchServiceImpl implements ShopBranchService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ShopConfigMapper shopConfigMapper;
|
private ShopConfigMapper shopConfigMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ShopInfoMapper shopInfoMapper;
|
private ShopInfoMapper shopInfoMapper;
|
||||||
|
@Resource
|
||||||
|
private ShopSyncService shopSyncService;
|
||||||
|
@Resource
|
||||||
|
private ShopUserSyncService shopUserSyncService;
|
||||||
|
@Resource
|
||||||
|
private SyncNoticeService syncNoticeService;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<ShopBranchDTO> findPage(ShopBranchParam param) {
|
public Page<ShopBranchDTO> findPage(ShopBranchParam param) {
|
||||||
|
|
@ -98,7 +108,14 @@ public class ShopBranchServiceImpl implements ShopBranchService {
|
||||||
branchConfig.setIsEnableConsSync(YesNoEnum.YES.value());
|
branchConfig.setIsEnableConsSync(YesNoEnum.YES.value());
|
||||||
branchConfig.setIsEnableVipSync(YesNoEnum.YES.value());
|
branchConfig.setIsEnableVipSync(YesNoEnum.YES.value());
|
||||||
shopConfigMapper.update(branchConfig);
|
shopConfigMapper.update(branchConfig);
|
||||||
// TODO 异步事务同步商品数据、会员数据、耗材数据
|
ThreadUtil.execAsync(() -> {
|
||||||
|
// 同步商品和耗材
|
||||||
|
shopSyncService.sync(shopId, branchShop.getId());
|
||||||
|
});
|
||||||
|
ThreadUtil.execAsync(() -> {
|
||||||
|
// 同步会员信息
|
||||||
|
shopUserSyncService.syncMergeShopUser(shopId, branchShop.getId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -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<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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.czg.account.entity.ShopInfo;
|
import com.czg.account.entity.ShopInfo;
|
||||||
import com.czg.account.service.ShopInfoService;
|
import com.czg.account.service.ShopInfoService;
|
||||||
|
import com.czg.account.service.ShopUserService;
|
||||||
import com.czg.account.service.SyncNoticeService;
|
import com.czg.account.service.SyncNoticeService;
|
||||||
import com.czg.exception.CzgException;
|
import com.czg.exception.CzgException;
|
||||||
import com.czg.product.entity.*;
|
import com.czg.product.entity.*;
|
||||||
|
|
@ -58,6 +59,8 @@ public class ShopSyncServiceImpl implements ShopSyncService {
|
||||||
@Resource
|
@Resource
|
||||||
private ProdConsRelationService prodConsRelationService;
|
private ProdConsRelationService prodConsRelationService;
|
||||||
@Resource
|
@Resource
|
||||||
|
private ShopUserService shopUserService;
|
||||||
|
@Resource
|
||||||
private SyncNoticeService syncNoticeService;
|
private SyncNoticeService syncNoticeService;
|
||||||
|
|
||||||
private void checkShopInfo(Long sourceShopId, Long targetShopId) {
|
private void checkShopInfo(Long sourceShopId, Long targetShopId) {
|
||||||
|
|
@ -73,7 +76,6 @@ public class ShopSyncServiceImpl implements ShopSyncService {
|
||||||
|| targetShop.getIsHeadShop() == null || targetShop.getIsHeadShop().equals(1)) {
|
|| targetShop.getIsHeadShop() == null || targetShop.getIsHeadShop().equals(1)) {
|
||||||
throw new CzgException("同步失败,目标店铺是主店铺或目标店铺是单店");
|
throw new CzgException("同步失败,目标店铺是主店铺或目标店铺是单店");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -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, ',');
|
||||||
Loading…
Reference in New Issue