CityServiceImpl.java 12.8 KB
package com.diligrp.rider.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.diligrp.rider.common.exception.BizException;
import com.diligrp.rider.dto.DeliveryPricingConfigDTO;
import com.diligrp.rider.dto.DeliveryPricingRuleDTO;
import com.diligrp.rider.entity.City;
import com.diligrp.rider.entity.DeliveryFeePlan;
import com.diligrp.rider.entity.DeliveryFeePlanDimension;
import com.diligrp.rider.entity.DeliveryFeePlanDistanceStep;
import com.diligrp.rider.entity.DeliveryFeePlanPieceRule;
import com.diligrp.rider.entity.DeliveryFeePlanTimeRule;
import com.diligrp.rider.mapper.CityMapper;
import com.diligrp.rider.mapper.DeliveryFeePlanDimensionMapper;
import com.diligrp.rider.mapper.DeliveryFeePlanDistanceStepMapper;
import com.diligrp.rider.mapper.DeliveryFeePlanMapper;
import com.diligrp.rider.mapper.DeliveryFeePlanPieceRuleMapper;
import com.diligrp.rider.mapper.DeliveryFeePlanTimeRuleMapper;
import com.diligrp.rider.service.CityService;
import com.diligrp.rider.vo.CityVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class CityServiceImpl implements CityService {

    private final CityMapper cityMapper;
    private final DeliveryFeePlanMapper deliveryFeePlanMapper;
    private final DeliveryFeePlanDimensionMapper deliveryFeePlanDimensionMapper;
    private final DeliveryFeePlanDistanceStepMapper deliveryFeePlanDistanceStepMapper;
    private final DeliveryFeePlanPieceRuleMapper deliveryFeePlanPieceRuleMapper;
    private final DeliveryFeePlanTimeRuleMapper deliveryFeePlanTimeRuleMapper;
    @Override
    public List<CityVO> getTree() {
        List<City> all = cityMapper.selectList(new LambdaQueryWrapper<City>().orderByAsc(City::getListOrder));
        return all.stream().map(this::toVO).collect(Collectors.toList());
    }

    @Override
    public List<CityVO> getOpenList() {
        return cityMapper.selectList(new LambdaQueryWrapper<City>()
                        .eq(City::getStatus, 1)
                        .orderByAsc(City::getListOrder))
                .stream().map(this::toVO).collect(Collectors.toList());
    }

    @Override
    public void add(City city) {
        if (city.getAreaCode() != null && !city.getAreaCode().isBlank()) {
            Long exists = cityMapper.selectCount(new LambdaQueryWrapper<City>()
                    .eq(City::getAreaCode, city.getAreaCode()));
            if (exists > 0) {
                throw new BizException("地区编号已存在");
            }
        }
        cityMapper.insert(city);
        initializeDefaultPlan(city.getId());
    }

    @Override
    public void edit(City city) {
        City existing = cityMapper.selectById(city.getId());
        if (existing == null) {
            throw new BizException("城市不存在");
        }
        cityMapper.updateById(city);
    }

    @Override
    public void setStatus(Long cityId, int status) {
        cityMapper.update(null, new LambdaUpdateWrapper<City>()
                .eq(City::getId, cityId)
                .set(City::getStatus, status));
    }

    @Override
    public DeliveryPricingConfigDTO getConfig(Long cityId) {
        City city = cityMapper.selectById(cityId);
        if (city == null) {
            throw new BizException("城市不存在");
        }

        DeliveryFeePlan plan = deliveryFeePlanMapper.selectOne(new LambdaQueryWrapper<DeliveryFeePlan>()
                .eq(DeliveryFeePlan::getCityId, cityId)
                .eq(DeliveryFeePlan::getIsDefault, 1)
                .last("LIMIT 1"));
        if (plan == null) {
            plan = deliveryFeePlanMapper.selectOne(new LambdaQueryWrapper<DeliveryFeePlan>()
                    .eq(DeliveryFeePlan::getCityId, cityId)
                    .orderByAsc(DeliveryFeePlan::getListOrder)
                    .orderByAsc(DeliveryFeePlan::getId)
                    .last("LIMIT 1"));
        }
        if (plan == null) {
            return createDefaultConfig();
        }

        List<DeliveryFeePlanDimension> dimensions = deliveryFeePlanDimensionMapper.selectList(
                new LambdaQueryWrapper<DeliveryFeePlanDimension>()
                        .eq(DeliveryFeePlanDimension::getPlanId, plan.getId()));
        Map<String, DeliveryFeePlanDimension> dimensionMap = new HashMap<>();
        for (DeliveryFeePlanDimension item : dimensions) {
            dimensionMap.put(item.getDimensionType(), item);
        }

        List<DeliveryFeePlanDistanceStep> distanceSteps = deliveryFeePlanDistanceStepMapper.selectList(
                new LambdaQueryWrapper<DeliveryFeePlanDistanceStep>()
                        .eq(DeliveryFeePlanDistanceStep::getPlanId, plan.getId())
                        .orderByAsc(DeliveryFeePlanDistanceStep::getListOrder)
                        .orderByAsc(DeliveryFeePlanDistanceStep::getId));
        List<DeliveryFeePlanPieceRule> pieceRules = deliveryFeePlanPieceRuleMapper.selectList(
                new LambdaQueryWrapper<DeliveryFeePlanPieceRule>()
                        .eq(DeliveryFeePlanPieceRule::getPlanId, plan.getId())
                        .orderByAsc(DeliveryFeePlanPieceRule::getListOrder)
                        .orderByAsc(DeliveryFeePlanPieceRule::getId));
        List<DeliveryFeePlanTimeRule> timeRules = deliveryFeePlanTimeRuleMapper.selectList(
                new LambdaQueryWrapper<DeliveryFeePlanTimeRule>()
                        .eq(DeliveryFeePlanTimeRule::getPlanId, plan.getId())
                        .orderByAsc(DeliveryFeePlanTimeRule::getListOrder)
                        .orderByAsc(DeliveryFeePlanTimeRule::getId));

        DeliveryPricingRuleDTO type6 = new DeliveryPricingRuleDTO();
        type6.setMinFee(nvl(plan.getMinFee()));
        type6.setFeeMode(2);

        DeliveryFeePlanDimension base = dimensionMap.get("base");
        if (base != null) {
            type6.setBaseSwitch(on(base.getEnabled()));
            type6.setBaseFee(nvl(base.getBaseFee()));
        }

        DeliveryFeePlanDimension distance = dimensionMap.get("distance");
        if (distance != null) {
            type6.setDistanceSwitch(on(distance.getEnabled()));
            type6.setDistanceBasic(nvl(distance.getStartDistance()));
            type6.setDistanceBasicMoney(nvl(distance.getStartFee()));
            type6.setDistanceType(2);
            type6.setDistanceSteps(distanceSteps.stream().map(step -> {
                DeliveryPricingRuleDTO.DistanceStepDTO dto = new DeliveryPricingRuleDTO.DistanceStepDTO();
                dto.setEndDistance(nvl(step.getEndDistance()));
                dto.setUnitDistance(nvl(step.getUnitDistance()));
                dto.setUnitFee(nvl(step.getUnitFee()));
                dto.setListOrder(step.getListOrder());
                return dto;
            }).collect(Collectors.toList()));
            if (!distanceSteps.isEmpty()) {
                type6.setDistanceMoreMoney(nvl(distanceSteps.get(distanceSteps.size() - 1).getUnitFee()));
            }
        }

        DeliveryFeePlanDimension weight = dimensionMap.get("weight");
        if (weight != null) {
            type6.setWeightSwitch(on(weight.getEnabled()));
            type6.setWeightFirst(nvl(weight.getFirstWeight()));
            type6.setWeightFirstFee(nvl(weight.getFirstFee()));
            type6.setWeightUnitFee(nvl(weight.getUnitWeightFee()));
            type6.setWeightCapFee(nvl(weight.getCapFee()));
            type6.setWeightBasic(nvl(weight.getFirstWeight()));
            type6.setWeightBasicMoney(nvl(weight.getFirstFee()));
            type6.setWeightMoreMoney(nvl(weight.getUnitWeightFee()));
        }

        DeliveryFeePlanDimension piece = dimensionMap.get("piece");
        if (piece != null) {
            type6.setPieceSwitch(on(piece.getEnabled()));
            type6.setPieceRules(pieceRules.stream().map(rule -> {
                DeliveryPricingRuleDTO.PieceRuleDTO dto = new DeliveryPricingRuleDTO.PieceRuleDTO();
                dto.setStartPiece(rule.getStartPiece());
                dto.setEndPiece(rule.getEndPiece());
                dto.setFee(nvl(rule.getFee()));
                dto.setListOrder(rule.getListOrder());
                return dto;
            }).collect(Collectors.toList()));
        }

        DeliveryFeePlanDimension time = dimensionMap.get("time");
        if (time != null) {
            type6.setTimes(timeRules.stream().map(rule -> {
                DeliveryPricingRuleDTO.TimePeriodDTO dto = new DeliveryPricingRuleDTO.TimePeriodDTO();
                dto.setStart(rule.getStartMinute());
                dto.setEnd(rule.getEndMinute());
                dto.setMoney(nvl(rule.getFee()));
                dto.setIsOpen(on(rule.getEnabled()));
                return dto;
            }).collect(Collectors.toList()));
        }

        DeliveryPricingConfigDTO config = createDefaultConfig();
        config.setType6(type6);
        config.setDistanceBasic(nvl(plan.getDistanceBasic()));
        config.setDistanceBasicTime(plan.getDistanceBasicTime() != null ? plan.getDistanceBasicTime() : 30);
        config.setDistanceMoreTime(plan.getDistanceMoreTime() != null ? plan.getDistanceMoreTime() : 10);
        config.setRiderDistance(nvl(plan.getRiderDistance()));
        config.setRiderTime(plan.getRiderTime());
        return config;
    }

    @Override
    public City getById(Long cityId) {
        return cityMapper.selectById(cityId);
    }

    private CityVO toVO(City c) {
        CityVO vo = new CityVO();
        vo.setId(c.getId());
        vo.setPid(c.getPid());
        vo.setName(c.getName());
        vo.setAreaCode(c.getAreaCode());
        vo.setStatus(c.getStatus());
        vo.setStatusName(c.getStatus() != null && c.getStatus() == 1 ? "已开通" : "未开通");
        vo.setRate(c.getRate());
        vo.setListOrder(c.getListOrder());
        return vo;
    }

    private DeliveryPricingConfigDTO createDefaultConfig() {
        DeliveryPricingConfigDTO config = new DeliveryPricingConfigDTO();
        config.setType(Collections.emptyList());
        config.setType6(new DeliveryPricingRuleDTO());
        config.setDistanceBasic(BigDecimal.valueOf(3));
        config.setDistanceBasicTime(30);
        config.setDistanceMoreTime(10);
        config.setRiderDistance(BigDecimal.valueOf(3));
        return config;
    }

    private void initializeDefaultPlan(Long cityId) {
        long now = System.currentTimeMillis() / 1000;
        DeliveryFeePlan plan = new DeliveryFeePlan();
        plan.setCityId(cityId);
        plan.setName("默认方案");
        plan.setIsDefault(1);
        plan.setStatus(1);
        plan.setListOrder(0);
        plan.setRemark("系统初始化");
        plan.setMinFee(BigDecimal.ZERO);
        plan.setDistanceBasic(BigDecimal.valueOf(3));
        plan.setDistanceBasicTime(30);
        plan.setDistanceMoreTime(10);
        plan.setRiderDistance(BigDecimal.valueOf(3));
        plan.setRiderTime(0);
        plan.setCreateTime(now);
        plan.setUpdateTime(now);
        deliveryFeePlanMapper.insert(plan);

        upsertDimension(plan.getId(), "base", 0, BigDecimal.ZERO, null, null, null, null, null, null, now);
        upsertDimension(plan.getId(), "distance", 1, null, BigDecimal.valueOf(3), BigDecimal.valueOf(4), null, null, null, null, now);
        upsertDimension(plan.getId(), "weight", 1, null, null, null, BigDecimal.valueOf(5), BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.valueOf(30), now);
        upsertDimension(plan.getId(), "piece", 0, null, null, null, null, null, null, null, now);
        upsertDimension(plan.getId(), "time", 0, null, null, null, null, null, null, null, now);
    }

    private void upsertDimension(Long planId, String type, Integer enabled, BigDecimal baseFee,
                                 BigDecimal startDistance, BigDecimal startFee, BigDecimal firstWeight,
                                 BigDecimal firstFee, BigDecimal unitWeightFee, BigDecimal capFee, long now) {
        DeliveryFeePlanDimension dimension = new DeliveryFeePlanDimension();
        dimension.setPlanId(planId);
        dimension.setDimensionType(type);
        dimension.setEnabled(on(enabled));
        dimension.setBaseFee(nvl(baseFee));
        dimension.setStartDistance(nvl(startDistance));
        dimension.setStartFee(nvl(startFee));
        dimension.setFirstWeight(nvl(firstWeight));
        dimension.setFirstFee(nvl(firstFee));
        dimension.setUnitWeightFee(nvl(unitWeightFee));
        dimension.setCapFee(nvl(capFee));
        dimension.setCreateTime(now);
        dimension.setUpdateTime(now);
        deliveryFeePlanDimensionMapper.insert(dimension);
    }

    private BigDecimal nvl(BigDecimal value) {
        return value != null ? value : BigDecimal.ZERO;
    }

    private Integer on(Integer value) {
        return value != null && value == 1 ? 1 : 0;
    }
}