doum
3 天以前 c51de64d5ee334d914c90f5e4f82a13f159492ca
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -14,21 +14,32 @@
import com.doumee.core.utils.Utils;
import com.doumee.biz.system.AreasBiz;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.core.utils.aliyun.AliSmsService;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.MultifileMapper;
import com.doumee.dao.business.OrdersMapper;
import com.doumee.dao.business.PricingRuleMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.model.Areas;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.Multifile;
import com.doumee.dao.business.model.Orders;
import com.doumee.dao.business.model.PricingRule;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.dto.*;
import com.doumee.dao.system.SystemUserMapper;
import com.doumee.dao.system.model.SystemUser;
import com.doumee.dao.vo.ShopDetailVO;
import com.doumee.dao.vo.ShopCenterVO;
import com.doumee.dao.vo.ShopLoginVO;
import com.doumee.dao.vo.ShopNearbyVO;
import com.doumee.dao.vo.ShopSalesStatsVO;
import com.doumee.dao.vo.ShopWebDetailVO;
import com.doumee.service.business.AreasService;
import com.doumee.service.business.ShopInfoService;
import com.doumee.dao.business.SmsrecordMapper;
import com.doumee.dao.business.model.Smsrecord;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
@@ -45,6 +56,7 @@
 * @author rk
 * @date 2026/04/08
 */
@Slf4j
@Service
public class ShopInfoServiceImpl implements ShopInfoService {
@@ -68,6 +80,18 @@
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private PricingRuleMapper pricingRuleMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private AreasService areasService;
    @Autowired
    private SmsrecordMapper smsrecordMapper;
    @Override
    public Integer create(ShopInfo shopInfo) {
        shopInfoMapper.insert(shopInfo);
@@ -182,6 +206,9 @@
        if (pageWrap.getModel().getAreaId() != null) {
            queryWrapper.lambda().eq(ShopInfo::getAreaId, pageWrap.getModel().getAreaId());
        }
        if (pageWrap.getModel().getAddress() != null) {
            queryWrapper.lambda().like(ShopInfo::getAddress, pageWrap.getModel().getAddress());
        }
        if (pageWrap.getModel().getAuditStatus() != null) {
            queryWrapper.lambda().eq(ShopInfo::getAuditStatus, pageWrap.getModel().getAuditStatus());
        }
@@ -221,11 +248,9 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyShop(ShopApplyDTO request, Member member) {
        Integer memberId = member.getId();
    public void applyShop(ShopApplyDTO request) {
        Member member = memberMapper.selectById(request.getMemberId());
        // 1. 校验门店手机号唯一性(shop_info.telephone)
        checkTelephoneUnique(request.getTelephone(), null);
        // 2. 根据类型校验附件
        if (Constants.equalsInteger(request.getCompanyType(), Constants.ZERO)) {
@@ -260,7 +285,7 @@
        // 3. 查询该会员是否已有门店记录
        QueryWrapper<ShopInfo> existQw = new QueryWrapper<>();
        existQw.lambda()
                .eq(ShopInfo::getRegionMemberId, memberId)
                .eq(ShopInfo::getRegionMemberId, member.getId())
                .eq(ShopInfo::getDeleted, Constants.ZERO)
                .last("limit 1");
        ShopInfo existing = shopInfoMapper.selectOne(existQw);
@@ -299,15 +324,20 @@
            existing.setLegalPersonCard(request.getLegalPersonCard());
            existing.setPassword(encryptedPassword);
            existing.setSalt(salt);
            existing.setAuditStatus(Constants.ZERO);
            existing.setAliAccount(request.getAliAccount());
            existing.setAliName(request.getAliName());
            existing.setUpdateTime(now);
            existing.setUpdateUser(memberId);
            existing.setAuditRemark(null);
            existing.setAuditTime(null);
            existing.setAuditUserId(null);
            existing.setAuditStatus(Constants.ZERO);
            // 读取押金金额
            setDepositAmountFromPricingRule(existing);
            shopInfoMapper.updateById(existing);
            shopId = existing.getId();
        } else {
            // 1. 校验门店手机号唯一性(shop_info.telephone)
            checkTelephoneUnique(request.getTelephone(), null);
            // 新建
            ShopInfo shopInfo = new ShopInfo();
            shopInfo.setCompanyType(request.getCompanyType());
@@ -328,14 +358,15 @@
            shopInfo.setLegalPersonCard(request.getLegalPersonCard());
            shopInfo.setPassword(encryptedPassword);
            shopInfo.setSalt(salt);
            shopInfo.setOpenid(member.getOpenid());
            shopInfo.setAuditStatus(Constants.ZERO);
            shopInfo.setAliAccount(request.getAliAccount());
            shopInfo.setAliName(request.getAliName());
            shopInfo.setStatus(Constants.ZERO);
            shopInfo.setDeleted(Constants.ZERO);
            shopInfo.setCreateTime(now);
            shopInfo.setUpdateTime(now);
            shopInfo.setCreateUser(memberId);
            shopInfo.setRegionMemberId(memberId);
            shopInfo.setRegionMemberId(member.getId());
            // 读取押金金额
            setDepositAmountFromPricingRule(shopInfo);
            shopInfoMapper.insert(shopInfo);
            shopId = shopInfo.getId();
        }
@@ -407,7 +438,49 @@
        shopInfo.setAuditRemark(auditDTO.getAuditRemark());
        shopInfo.setAuditUserId(auditDTO.getAuditUser());
        shopInfo.setUpdateTime(now);
        // 审批通过时,校验城市pricing_rule配置,读取押金金额
        if (Constants.equalsInteger(newAuditStatus, Constants.ONE)) {
            // 1. 解析门店所在城市
            Areas area = areasBiz.resolveArea(shopInfo.getAreaId());
            if (area == null || area.getParentId() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "门店区划信息异常,无法确定所在城市");
            }
            Integer cityId = area.getParentId();
            // 2. 校验 pricing_rule 配置(城市开通在押金支付完成后处理)
            Areas cityArea = areasService.findById(cityId, Constants.ONE);
            if (cityArea == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "城市信息不存在");
            }
            if (!Constants.equalsInteger(cityArea.getStatus(), Constants.ONE)) {
                List<String> errors = validateCityPricingRules(cityId);
                if (!errors.isEmpty()) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),
                            "城市[" + cityArea.getName() + "]尚未开通,定价规则未配置完整:" + String.join(";", errors));
                }
            }
            // 3. 从PricingRule读取押金金额(审批时更新)
            setDepositAmountFromPricingRule(shopInfo);
        }
        shopInfoMapper.updateById(shopInfo);
        // 短信通知
        if (Constants.equalsInteger(newAuditStatus, Constants.ONE)) {
            // 审核通过 → 通知缴纳押金
            String depositMoney = shopInfo.getDepositAmount() != null
                    ? String.valueOf(shopInfo.getDepositAmount() / 100.0) : "0";
            sendSmsNotify(shopInfo.getTelephone(),
                    Constants.SmsNotify.SHOP_AUTH_APPROVED_DEPOSIT,
                    "storeName", shopInfo.getName(),
                    "money", depositMoney);
        } else if (Constants.equalsInteger(newAuditStatus, Constants.TWO)) {
            // 审核驳回
            sendSmsNotify(shopInfo.getTelephone(),
                    Constants.SmsNotify.SHOP_AUTH_REJECTED,
                    "storeName", shopInfo.getName(),
                    "reason", auditDTO.getAuditRemark() != null ? auditDTO.getAuditRemark() : "");
        }
    }
    @Override
@@ -505,6 +578,8 @@
        shopInfo.setLegalPersonName(request.getLegalPersonName());
        shopInfo.setLegalPersonPhone(request.getLegalPersonPhone());
        shopInfo.setLegalPersonCard(request.getLegalPersonCard());
        shopInfo.setAliAccount(request.getAliAccount());
        shopInfo.setAliName(request.getAliName());
        shopInfo.setUpdateTime(now);
        shopInfoMapper.updateById(shopInfo);
@@ -608,12 +683,15 @@
        vo.setPayStatus(shopInfo.getPayStatus());
        vo.setScore(shopInfo.getScore());
        vo.setCreateTime(shopInfo.getCreateTime());
        vo.setAliAccount(shopInfo.getAliAccount());
        vo.setAliName(shopInfo.getAliName());
        vo.setDepositAmount(shopInfo.getDepositAmount());
        // 拼接图片前缀
        String imgPrefix = "";
        try {
            imgPrefix = systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.SHOP_FILES).getCode();
            imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.SHOP_FILES).getCode();
        } catch (Exception e) {
            // 未配置时忽略
        }
@@ -670,8 +748,8 @@
            if (payMember != null && StringUtils.isNotBlank(payMember.getCoverImage())) {
                String memberPrefix = "";
                try {
                    memberPrefix = systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
                            + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.MEMBER_FILES).getCode();
                    memberPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                            + systemDictDataBiz.queryByCode(Constants.OSS, Constants.MEMBER_FILES).getCode();
                } catch (Exception e) {
                    // 未配置时忽略
                }
@@ -712,6 +790,12 @@
            qw.lambda().like(ShopInfo::getName, dto.getName());
        }
        // 城市筛选(areaId是区县,需匹配其parentId等于城市ID)
        if (dto.getCityId() != null) {
            qw.inSql("AREA_ID",
                    "SELECT id FROM areas WHERE parent_id = " + dto.getCityId() + " AND isdeleted = 0");
        }
        // 距离筛选(单位:米 → 转换为km比较)
        if (distanceMeter != null && distanceMeter > 0) {
            double maxKm = distanceMeter / 1000.0;
@@ -744,6 +828,8 @@
            vo.setShopHours(shop.getShopHours());
            vo.setAddress(shop.getAddress());
            vo.setScore(shop.getScore());
            vo.setLatitude(shop.getLatitude());
            vo.setLongitude(shop.getLongitude());
            // 门头照第一张
            vo.setCoverImg(getFirstImage(shop.getId(), Constants.FileType.STORE_FRONT.getKey(), imgPrefix));
            // 距离
@@ -774,6 +860,10 @@
        vo.setName(shop.getName());
        vo.setAddress(shop.getAddress());
        vo.setContent(shop.getContent());
        vo.setDepositTypes(shop.getDepositTypes());
        vo.setFeeStandard(shop.getFeeStandard());
        vo.setLatitude(shop.getLatitude());
        vo.setLongitude(shop.getLongitude());
        // 门头照 + 内部照 全路径集合
        String imgPrefix = getShopPrefix();
@@ -861,8 +951,8 @@
     */
    private String getShopPrefix() {
        try {
            return systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.SHOP_FILES).getCode();
            return systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.SHOP_FILES).getCode();
        } catch (Exception e) {
            return "";
        }
@@ -903,7 +993,137 @@
        return urls;
    }
    @Override
    public ShopCenterVO getShopCenterInfo(Integer shopId) {
        ShopInfo shop = shopInfoMapper.selectById(shopId);
        if (shop == null || Constants.equalsInteger(shop.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        ShopCenterVO vo = new ShopCenterVO();
        vo.setShopName(shop.getName());
        vo.setLinkName(shop.getLinkName());
        vo.setCompanyType(shop.getCompanyType());
        vo.setCoverImg(shop.getCoverImg());
        if (StringUtils.isNotBlank(shop.getCoverImg())) {
            String path = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.SHOP_FILES).getCode();
            vo.setFullCoverImg(path + shop.getCoverImg());
        }
        vo.setHasMessage(false);
        // 待核验订单数量(存件门店,status=1)
        Long waitDepositCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDepositShopId, shopId)
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getStatus, Constants.OrderStatus.waitDeposit.getStatus()));
        vo.setWaitDepositCount(waitDepositCount.intValue());
        // 待收货订单数量(取件门店,status IN 4,5)
        Long waitReceiveCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getTakeShopId, shopId)
                .eq(Orders::getDeleted, Constants.ZERO)
                .in(Orders::getStatus, Constants.OrderStatus.delivering.getStatus(), Constants.OrderStatus.arrived.getStatus()));
        vo.setWaitReceiveCount(waitReceiveCount.intValue());
        // 支付宝提现账号
        vo.setAliAccount(shop.getAliAccount());
        vo.setAliName(shop.getAliName());
        return vo;
    }
    @Override
    public ShopSalesStatsVO getShopSalesStats(Integer shopId, Integer period) {
        // 计算时间范围 0=今日 1=本月 2=上月
        Calendar cal = Calendar.getInstance();
        Date startTime;
        Date endTime;
        if (Constants.equalsInteger(period, 2)) {
            // 上月:上月1号00:00:00 ~ 本月1号00:00:00
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            endTime = cal.getTime();
            cal.add(Calendar.MONTH, -1);
            startTime = cal.getTime();
        } else if (Constants.equalsInteger(period, 1)) {
            // 本月:本月1号00:00:00 ~ 下月1号00:00:00
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            startTime = cal.getTime();
            cal.add(Calendar.MONTH, 1);
            endTime = cal.getTime();
        } else {
            // 今日:今天00:00:00 ~ 明天00:00:00
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            startTime = cal.getTime();
            cal.add(Calendar.DAY_OF_MONTH, 1);
            endTime = cal.getTime();
        }
        ShopSalesStatsVO vo = new ShopSalesStatsVO();
        // 1. 销售额 + 订单数:按订单创建时间,存件门店或取件门店是本门店
        // 存件门店
        List<Orders> depositSalesOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .ge(Orders::getCreateTime, startTime)
                .lt(Orders::getCreateTime, endTime)
                .eq(Orders::getDepositShopId, shopId));
        // 取件门店
        List<Orders> takeSalesOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .ge(Orders::getCreateTime, startTime)
                .lt(Orders::getCreateTime, endTime)
                .eq(Orders::getTakeShopId, shopId));
        long salesAmount = depositSalesOrders.stream().mapToLong(o -> o.getTotalAmount() != null ? o.getTotalAmount() : 0L).sum()
                + takeSalesOrders.stream().mapToLong(o -> o.getTotalAmount() != null ? o.getTotalAmount() : 0L).sum();
        vo.setSalesAmount(salesAmount);
        vo.setOrderCount(depositSalesOrders.size() + takeSalesOrders.size());
        // 2. 结算利润:按结算时间,根据门店角色取depositShopFee或takeShopFee
        // 存件门店 = 本门店 的订单,取 depositShopFee
        List<Orders> depositSettleOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getSettlementStatus, Constants.ONE)
                .ge(Orders::getSettlementTime, startTime)
                .lt(Orders::getSettlementTime, endTime)
                .eq(Orders::getDepositShopId, shopId));
        // 取件门店 = 本门店 的订单,取 takeShopFee
        List<Orders> takeSettleOrders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getSettlementStatus, Constants.ONE)
                .ge(Orders::getSettlementTime, startTime)
                .lt(Orders::getSettlementTime, endTime)
                .eq(Orders::getTakeShopId, shopId));
        long depositFee = depositSettleOrders.stream()
                .mapToLong(o -> o.getDepositShopFee() != null ? o.getDepositShopFee() : 0L).sum();
        long takeFee = takeSettleOrders.stream()
                .mapToLong(o -> o.getTakeShopFee() != null ? o.getTakeShopFee() : 0L).sum();
        vo.setSettlementProfit(depositFee + takeFee);
        // 3. 在库订单数
        // 3.1 存件门店=本门店,status in (2已寄存, 5待取件)
        Long depositStorageCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getDepositShopId, shopId)
                .in(Orders::getStatus,
                        Constants.OrderStatus.deposited.getStatus(),
                        Constants.OrderStatus.arrived.getStatus()));
        // 3.2 取件门店=本门店,status = 5待取件
        Long takeStorageCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getTakeShopId, shopId)
                .eq(Orders::getStatus, Constants.OrderStatus.arrived.getStatus()));
        vo.setStorageCount(depositStorageCount.intValue() + takeStorageCount.intValue());
        return vo;
    }
    /**
     * 商户账号密码登录
@@ -965,18 +1185,18 @@
    }
    @Override
    public ShopLoginVO shopSilentLogin(String openid) {
        if (StringUtils.isBlank(openid)) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "openid不能为空");
    public ShopLoginVO shopSilentLogin(Integer memberId) {
        Member member = memberMapper.selectById(memberId);
        if(Objects.isNull(member)||StringUtils.isBlank(member.getOpenid())){
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前登录会员身份异常,请联系管理员!");
        }
        ShopInfo shop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
                .eq(ShopInfo::getOpenid, openid)
                .eq(ShopInfo::getOpenid, member.getOpenid())
                .eq(ShopInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (shop == null) {
            return null;
        }
        // 创建token(generateTokenForRedis 已自动清除该用户旧token,保证唯一有效)
        String token = JwtTokenUtil.generateTokenForRedis(shop.getId(), Constants.TWO, JSONObject.toJSONString(shop), redisTemplate);
@@ -993,4 +1213,142 @@
        return vo;
    }
    /**
     * 校验城市定价规则配置是否完整
     * @param cityId 城市主键
     * @return 错误信息列表,空表示校验通过
     */
    private List<String> validateCityPricingRules(Integer cityId) {
        List<String> errors = new ArrayList<>();
        // type=0 就地存取规则:至少1条,fieldB不为空
        List<PricingRule> type0 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.ZERO)
                .eq(PricingRule::getCityId, cityId));
        if (type0.isEmpty()) {
            errors.add("缺少就地存取规则");
        } else if (type0.stream().allMatch(r -> StringUtils.isBlank(r.getFieldB()))) {
            errors.add("就地存取规则未配置收费单价");
        }
        // type=1 异地寄送规则:至少1条,fieldB/C/D/E不为空
        List<PricingRule> type1 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.ONE)
                .eq(PricingRule::getCityId, cityId));
        if (type1.isEmpty()) {
            errors.add("缺少异地寄送规则");
        } else if (type1.stream().allMatch(r -> StringUtils.isAnyBlank(r.getFieldB(), r.getFieldC(), r.getFieldD(), r.getFieldE()))) {
            errors.add("异地寄送规则配置不完整");
        }
        // type=2 预计时效:fieldA=1(标速达) 和 fieldA=2(极速达) 各1条
        List<PricingRule> type2 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.TWO)
                .eq(PricingRule::getCityId, cityId));
        Map<String, PricingRule> type2Map = type2.stream()
                .collect(Collectors.toMap(PricingRule::getFieldA, r -> r, (a, b) -> a));
        if (!type2Map.containsKey("1")) {
            errors.add("缺少预计时效-标速达配置");
        } else if (StringUtils.isAnyBlank(type2Map.get("1").getFieldB(), type2Map.get("1").getFieldC(),
                type2Map.get("1").getFieldD(), type2Map.get("1").getFieldE())) {
            errors.add("预计时效-标速达配置不完整");
        }
        if (!type2Map.containsKey("2")) {
            errors.add("缺少预计时效-极速达配置");
        } else if (StringUtils.isAnyBlank(type2Map.get("2").getFieldB(), type2Map.get("2").getFieldC(),
                type2Map.get("2").getFieldD(), type2Map.get("2").getFieldE())) {
            errors.add("预计时效-极速达配置不完整");
        }
        // type=3 门店注册押金:fieldA=0(企业) 和 fieldA=1(个人) 各1条,fieldB不为空
        List<PricingRule> type3 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.THREE)
                .eq(PricingRule::getCityId, cityId));
        Map<String, PricingRule> type3Map = type3.stream()
                .collect(Collectors.toMap(PricingRule::getFieldA, r -> r, (a, b) -> a));
        String[] depositNames = {"企业", "个人"};
        for (int i = 0; i <= 1; i++) {
            String key = String.valueOf(i);
            if (!type3Map.containsKey(key)) {
                errors.add("缺少门店注册押金-" + depositNames[i] + "配置");
            } else if (StringUtils.isBlank(type3Map.get(key).getFieldB())) {
                errors.add("门店注册押金-" + depositNames[i] + "未配置押金金额");
            }
        }
        // type=4 分成比例:fieldA=0~4 共5条,fieldB不为空
        List<PricingRule> type4 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.FOUR)
                .eq(PricingRule::getCityId, cityId));
        String[] shareNames = {"企业寄", "个人寄", "企业取", "个人取", "配送员"};
        Map<String, PricingRule> type4Map = type4.stream()
                .collect(Collectors.toMap(PricingRule::getFieldA, r -> r, (a, b) -> a));
        for (int i = 0; i <= 4; i++) {
            String key = String.valueOf(i);
            if (!type4Map.containsKey(key)) {
                errors.add("缺少分成比例-" + shareNames[i] + "配置");
            } else if (StringUtils.isBlank(type4Map.get(key).getFieldB())) {
                errors.add("分成比例-" + shareNames[i] + "未配置分成比例");
            }
        }
        return errors;
    }
    /**
     * 从PricingRule读取押金金额并赋值到门店记录
     */
    private void setDepositAmountFromPricingRule(ShopInfo shopInfo) {
        if (shopInfo.getCompanyType() == null) {
            return;
        }
        PricingRule pricingRule = pricingRuleMapper.selectOne(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.THREE)
                .eq(PricingRule::getFieldA, String.valueOf(shopInfo.getCompanyType()))
                .last("limit 1"));
        if (pricingRule != null && StringUtils.isNotBlank(pricingRule.getFieldB())) {
            shopInfo.setDepositAmount(Long.parseLong(pricingRule.getFieldB()));
        }
    }
    /**
     * 发送短信通知(失败不影响主业务)
     */
    private void sendSmsNotify(String phone, Constants.SmsNotify smsNotify, String... paramPairs) {
        if (StringUtils.isBlank(phone)) {
            return;
        }
        String content = smsNotify.format(paramPairs);
        try {
            JSONObject templateParam = new JSONObject();
            for (int i = 0; i < paramPairs.length - 1; i += 2) {
                templateParam.put(paramPairs[i], paramPairs[i + 1]);
            }
            boolean result = AliSmsService.sendSms(phone, smsNotify.getTemplateCode(),
                    templateParam.toJSONString());
            if (result) {
                log.info("短信发送成功: phone={}, template={}", phone, smsNotify.name());
            } else {
                log.warn("短信发送失败: phone={}, template={}", phone, smsNotify.name());
            }
            // 存储短信记录
            Smsrecord smsRecord = new Smsrecord();
            smsRecord.setPhone(phone);
            smsRecord.setContent(content);
            smsRecord.setType(Constants.ONE);
            smsRecord.setStatus(result ? Constants.ONE : Constants.ZERO);
            smsRecord.setCreateTime(new Date());
            smsRecord.setDeleted(Constants.ZERO);
            smsrecordMapper.insert(smsRecord);
        } catch (Exception e) {
            log.error("短信发送异常: phone={}, template={}, error={}", phone, smsNotify.name(), e.getMessage());
        }
    }
}