package com.doumee.service.business.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.doumee.core.constants.Constants; import com.doumee.core.constants.ResponseStatus; import com.doumee.core.exception.BusinessException; import com.doumee.core.model.PageData; import com.doumee.core.model.PageWrap; import com.doumee.core.utils.Utils; import com.doumee.dao.business.CategoryMapper; import com.doumee.dao.business.PricingRuleMapper; import com.doumee.dao.business.model.Category; import com.doumee.dao.business.model.PricingRule; import com.doumee.dao.dto.LocalStoragePricingItemDTO; import com.doumee.dao.dto.LocalStoragePricingSaveDTO; import com.doumee.dao.dto.RemoteDeliveryPricingItemDTO; import com.doumee.dao.dto.RemoteDeliveryPricingSaveDTO; import com.doumee.dao.dto.EstimatedDeliverySaveDTO; import com.doumee.dao.dto.StoreDepositItemDTO; import com.doumee.dao.dto.StoreDepositSaveDTO; import com.doumee.dao.dto.RevenueShareItemDTO; import com.doumee.dao.dto.RevenueShareSaveDTO; import com.doumee.dao.vo.LocalStoragePricingVO; import com.doumee.dao.vo.RemoteDeliveryPricingVO; import com.doumee.dao.vo.EstimatedDeliveryVO; import com.doumee.dao.vo.StoreDepositVO; import com.doumee.dao.vo.RevenueShareVO; import com.doumee.service.business.PricingRuleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.util.*; import java.util.stream.Collectors; /** * 计价规则配置Service实现 * @author rk * @date 2026/04/08 */ @Service public class PricingRuleServiceImpl implements PricingRuleService { @Autowired private PricingRuleMapper pricingRuleMapper; @Autowired private CategoryMapper categoryMapper; @Override public Integer create(PricingRule pricingRule) { pricingRuleMapper.insert(pricingRule); return pricingRule.getId(); } @Override public void deleteById(Integer id) { pricingRuleMapper.deleteById(id); } @Override public void delete(PricingRule pricingRule) { UpdateWrapper deleteWrapper = new UpdateWrapper<>(pricingRule); pricingRuleMapper.delete(deleteWrapper); } @Override public void deleteByIdInBatch(List ids) { if (CollectionUtils.isEmpty(ids)) { return; } pricingRuleMapper.deleteBatchIds(ids); } @Override public void updateById(PricingRule pricingRule) { pricingRuleMapper.updateById(pricingRule); } @Override public void updateByIdInBatch(List pricingRules) { if (CollectionUtils.isEmpty(pricingRules)) { return; } for (PricingRule pricingRule : pricingRules) { this.updateById(pricingRule); } } @Override public PricingRule findById(Integer id) { PricingRule pricingRule = pricingRuleMapper.selectById(id); if (Objects.isNull(pricingRule)) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } return pricingRule; } @Override public PricingRule findOne(PricingRule pricingRule) { QueryWrapper wrapper = new QueryWrapper<>(pricingRule); return pricingRuleMapper.selectOne(wrapper); } @Override public List findList(PricingRule pricingRule) { QueryWrapper wrapper = new QueryWrapper<>(pricingRule); return pricingRuleMapper.selectList(wrapper); } @Override public PageData findPage(PageWrap pageWrap) { IPage page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity()); QueryWrapper queryWrapper = new QueryWrapper<>(); Utils.MP.blankToNull(pageWrap.getModel()); pageWrap.getModel().setDeleted(Constants.ZERO); if (pageWrap.getModel().getId() != null) { queryWrapper.lambda().eq(PricingRule::getId, pageWrap.getModel().getId()); } if (pageWrap.getModel().getDeleted() != null) { queryWrapper.lambda().eq(PricingRule::getDeleted, pageWrap.getModel().getDeleted()); } if (pageWrap.getModel().getCreateUser() != null) { queryWrapper.lambda().eq(PricingRule::getCreateUser, pageWrap.getModel().getCreateUser()); } if (pageWrap.getModel().getCreateTime() != null) { queryWrapper.lambda().ge(PricingRule::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateTime())); queryWrapper.lambda().le(PricingRule::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateTime())); } if (pageWrap.getModel().getUpdateUser() != null) { queryWrapper.lambda().eq(PricingRule::getUpdateUser, pageWrap.getModel().getUpdateUser()); } if (pageWrap.getModel().getUpdateTime() != null) { queryWrapper.lambda().ge(PricingRule::getUpdateTime, Utils.Date.getStart(pageWrap.getModel().getUpdateTime())); queryWrapper.lambda().le(PricingRule::getUpdateTime, Utils.Date.getEnd(pageWrap.getModel().getUpdateTime())); } if (pageWrap.getModel().getRemark() != null) { queryWrapper.lambda().eq(PricingRule::getRemark, pageWrap.getModel().getRemark()); } if (pageWrap.getModel().getCityId() != null) { queryWrapper.lambda().eq(PricingRule::getCityId, pageWrap.getModel().getCityId()); } if (pageWrap.getModel().getType() != null) { queryWrapper.lambda().eq(PricingRule::getType, pageWrap.getModel().getType()); } if (pageWrap.getModel().getFieldA() != null) { queryWrapper.lambda().eq(PricingRule::getFieldA, pageWrap.getModel().getFieldA()); } if (pageWrap.getModel().getFieldB() != null) { queryWrapper.lambda().eq(PricingRule::getFieldB, pageWrap.getModel().getFieldB()); } if (pageWrap.getModel().getFieldC() != null) { queryWrapper.lambda().eq(PricingRule::getFieldC, pageWrap.getModel().getFieldC()); } if (pageWrap.getModel().getFieldD() != null) { queryWrapper.lambda().eq(PricingRule::getFieldD, pageWrap.getModel().getFieldD()); } if (pageWrap.getModel().getFieldE() != null) { queryWrapper.lambda().eq(PricingRule::getFieldE, pageWrap.getModel().getFieldE()); } for (PageWrap.SortData sortData : pageWrap.getSorts()) { if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) { queryWrapper.orderByDesc(sortData.getProperty()); } else { queryWrapper.orderByAsc(sortData.getProperty()); } } return PageData.from(pricingRuleMapper.selectPage(page, queryWrapper)); } @Override public long count(PricingRule pricingRule) { QueryWrapper wrapper = new QueryWrapper<>(pricingRule); return pricingRuleMapper.selectCount(wrapper); } @Override @Transactional(rollbackFor = Exception.class) public void batchSaveLocalStoragePricing(LocalStoragePricingSaveDTO request) { // 校验分类数据完整性 Set requestCategoryIds = request.getItems().stream() .map(LocalStoragePricingItemDTO::getCategoryId) .collect(Collectors.toSet()); validateCategoryType4(requestCategoryIds); // 逐项 upsert Date now = new Date(); for (LocalStoragePricingItemDTO item : request.getItems()) { // 查询已有规则 QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.ZERO) .eq(PricingRule::getFieldA, String.valueOf(item.getCategoryId())) .eq(PricingRule::getCityId, request.getCityId()) .eq(PricingRule::getDeleted, Constants.ZERO) .last("limit 1"); PricingRule existing = pricingRuleMapper.selectOne(qw); if (existing != null) { // 更新 existing.setFieldB(item.getUnitPrice()); existing.setUpdateTime(now); pricingRuleMapper.updateById(existing); } else { // 新建 PricingRule rule = new PricingRule(); rule.setType(Constants.ZERO); rule.setFieldA(String.valueOf(item.getCategoryId())); rule.setFieldB(item.getUnitPrice()); rule.setCityId(request.getCityId()); rule.setDeleted(Constants.ZERO); rule.setCreateTime(now); rule.setUpdateTime(now); pricingRuleMapper.insert(rule); } } } @Override public List listLocalStoragePricing(Integer cityId) { // 1. 查询所有 Category type=4, deleted=0 Category categoryQuery = new Category(); categoryQuery.setType(Constants.FOUR); categoryQuery.setDeleted(Constants.ZERO); List allCategories = categoryMapper.selectList(new QueryWrapper<>(categoryQuery)); Map categoryNameMap = allCategories.stream() .collect(Collectors.toMap(Category::getId, Category::getName)); // 2. 查询已有规则 QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.ZERO) .eq(PricingRule::getCityId, cityId) .eq(PricingRule::getDeleted, Constants.ZERO); List rules = pricingRuleMapper.selectList(qw); Map existingMap = rules.stream() .collect(Collectors.toMap(PricingRule::getFieldA, r -> r)); // 3. 基于所有分类组装 VO,无规则则返回空记录 return allCategories.stream().map(category -> { LocalStoragePricingVO vo = new LocalStoragePricingVO(); vo.setCategoryId(category.getId()); vo.setCategoryName(category.getName()); vo.setCityId(cityId); PricingRule rule = existingMap.get(String.valueOf(category.getId())); if (rule != null) { vo.setPricingRuleId(rule.getId()); vo.setUnitPrice(rule.getFieldB()); } return vo; }).collect(Collectors.toList()); } @Override @Transactional(rollbackFor = Exception.class) public void batchSaveRemoteDeliveryPricing(RemoteDeliveryPricingSaveDTO request) { // 校验分类数据完整性 Set requestCategoryIds = request.getItems().stream() .map(RemoteDeliveryPricingItemDTO::getCategoryId) .collect(Collectors.toSet()); Map categoryMap = validateCategoryType4(requestCategoryIds); // 逐项 upsert Date now = new Date(); for (RemoteDeliveryPricingItemDTO item : request.getItems()) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.ONE) .eq(PricingRule::getFieldA, String.valueOf(item.getCategoryId())) .eq(PricingRule::getCityId, request.getCityId()) .eq(PricingRule::getDeleted, Constants.ZERO) .last("limit 1"); PricingRule existing = pricingRuleMapper.selectOne(qw); if (existing != null) { existing.setFieldB(item.getStartDistance()); existing.setFieldC(item.getStartPrice()); existing.setFieldD(item.getExtraDistance()); existing.setFieldE(item.getExtraPrice()); existing.setUpdateTime(now); pricingRuleMapper.updateById(existing); } else { PricingRule rule = new PricingRule(); rule.setType(Constants.ONE); rule.setFieldA(String.valueOf(item.getCategoryId())); rule.setFieldB(item.getStartDistance()); rule.setFieldC(item.getStartPrice()); rule.setFieldD(item.getExtraDistance()); rule.setFieldE(item.getExtraPrice()); rule.setCityId(request.getCityId()); rule.setDeleted(Constants.ZERO); rule.setCreateTime(now); rule.setUpdateTime(now); pricingRuleMapper.insert(rule); } } } @Override public List listRemoteDeliveryPricing(Integer cityId) { // 1. 查询所有 Category type=4, deleted=0 Category categoryQuery = new Category(); categoryQuery.setType(Constants.FOUR); categoryQuery.setDeleted(Constants.ZERO); List allCategories = categoryMapper.selectList(new QueryWrapper<>(categoryQuery)); Map categoryNameMap = allCategories.stream() .collect(Collectors.toMap(Category::getId, Category::getName)); // 2. 查询已有规则 QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.ONE) .eq(PricingRule::getCityId, cityId) .eq(PricingRule::getDeleted, Constants.ZERO); List rules = pricingRuleMapper.selectList(qw); Map existingMap = rules.stream() .collect(Collectors.toMap(PricingRule::getFieldA, r -> r)); // 3. 基于所有分类组装 VO,无规则则返回空记录 return allCategories.stream().map(category -> { RemoteDeliveryPricingVO vo = new RemoteDeliveryPricingVO(); vo.setCategoryId(category.getId()); vo.setCategoryName(category.getName()); vo.setCityId(cityId); PricingRule rule = existingMap.get(String.valueOf(category.getId())); if (rule != null) { vo.setPricingRuleId(rule.getId()); vo.setStartDistance(rule.getFieldB()); vo.setStartPrice(rule.getFieldC()); vo.setExtraDistance(rule.getFieldD()); vo.setExtraPrice(rule.getFieldE()); } return vo; }).collect(Collectors.toList()); } /** * 校验请求中的 categoryId 与分类表 type=4 数据完全匹配 * @param requestCategoryIds 请求中的 categoryId 集合 * @return 分类表 id->name 映射 */ private Map validateCategoryType4(Set requestCategoryIds) { Category categoryQuery = new Category(); categoryQuery.setType(Constants.FOUR); categoryQuery.setDeleted(Constants.ZERO); List allType4Categories = categoryMapper.selectList(new QueryWrapper<>(categoryQuery)); Map categoryMap = allType4Categories.stream() .collect(Collectors.toMap(Category::getId, Category::getName)); Set allCategoryIds = categoryMap.keySet(); // 校验:请求中存在但分类表中不存在的 categoryId List invalidIds = requestCategoryIds.stream() .filter(id -> !allCategoryIds.contains(id)) .map(String::valueOf) .collect(Collectors.toList()); if (!invalidIds.isEmpty()) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "以下物品规格不存在: " + String.join(", ", invalidIds)); } // 校验:分类表中存在但请求中缺失的 categoryId List missingNames = allCategoryIds.stream() .filter(id -> !requestCategoryIds.contains(id)) .map(id -> categoryMap.get(id) + "(" + id + ")") .collect(Collectors.toList()); if (!missingNames.isEmpty()) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "缺少以下物品规格的定价配置: " + String.join(", ", missingNames)); } return categoryMap; } @Override public Integer createEstimatedDelivery(EstimatedDeliverySaveDTO request) { PricingRule rule = new PricingRule(); rule.setType(Constants.TWO); rule.setCityId(request.getCityId()); rule.setFieldA(request.getDistance()); rule.setFieldB(request.getDuration()); rule.setDeleted(Constants.ZERO); rule.setCreateTime(new Date()); rule.setUpdateTime(new Date()); pricingRuleMapper.insert(rule); return rule.getId(); } @Override public void updateEstimatedDelivery(EstimatedDeliverySaveDTO request) { if (request.getId() == null) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "主键不能为空"); } PricingRule existing = pricingRuleMapper.selectById(request.getId()); if (Objects.isNull(existing) || !Constants.equalsInteger(existing.getType(), Constants.TWO)) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } existing.setFieldA(request.getDistance()); existing.setFieldB(request.getDuration()); existing.setCityId(request.getCityId()); existing.setUpdateTime(new Date()); pricingRuleMapper.updateById(existing); } @Override public void deleteEstimatedDelivery(Integer id) { PricingRule existing = pricingRuleMapper.selectById(id); if (Objects.isNull(existing) || !Constants.equalsInteger(existing.getType(), Constants.TWO)) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } existing.setDeleted(Constants.ONE); existing.setUpdateTime(new Date()); pricingRuleMapper.updateById(existing); } @Override public List listEstimatedDelivery(Integer cityId) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.TWO) .eq(PricingRule::getCityId, cityId) .eq(PricingRule::getDeleted, Constants.ZERO) .orderByAsc(PricingRule::getFieldA); List rules = pricingRuleMapper.selectList(qw); if (CollectionUtils.isEmpty(rules)) { return new ArrayList<>(); } return rules.stream().map(rule -> { EstimatedDeliveryVO vo = new EstimatedDeliveryVO(); vo.setPricingRuleId(rule.getId()); vo.setCityId(rule.getCityId()); vo.setDistance(rule.getFieldA()); vo.setDuration(rule.getFieldB()); return vo; }).collect(Collectors.toList()); } @Override @Transactional(rollbackFor = Exception.class) public void batchSaveStoreDeposit(StoreDepositSaveDTO request) { // 校验:必须包含 fieldType=0(企业) 和 fieldType=1(个人) 各一条 Set fieldTypes = request.getItems().stream() .map(StoreDepositItemDTO::getFieldType) .collect(Collectors.toSet()); if (!fieldTypes.contains(Constants.ZERO) || !fieldTypes.contains(Constants.ONE)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "必须包含企业(0)和个人(1)两条数据"); } if (request.getItems().size() != 2) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "有且仅有2条数据"); } Date now = new Date(); for (StoreDepositItemDTO item : request.getItems()) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.THREE) .eq(PricingRule::getFieldA, String.valueOf(item.getFieldType())) .eq(PricingRule::getCityId, request.getCityId()) .eq(PricingRule::getDeleted, Constants.ZERO) .last("limit 1"); PricingRule existing = pricingRuleMapper.selectOne(qw); if (existing != null) { existing.setFieldB(item.getDepositAmount()); existing.setUpdateTime(now); pricingRuleMapper.updateById(existing); } else { PricingRule rule = new PricingRule(); rule.setType(Constants.THREE); rule.setFieldA(String.valueOf(item.getFieldType())); rule.setFieldB(item.getDepositAmount()); rule.setCityId(request.getCityId()); rule.setDeleted(Constants.ZERO); rule.setCreateTime(now); rule.setUpdateTime(now); pricingRuleMapper.insert(rule); } } } @Override public List listStoreDeposit(Integer cityId) { // 查询已有规则 QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.THREE) .eq(PricingRule::getCityId, cityId) .eq(PricingRule::getDeleted, Constants.ZERO); List rules = pricingRuleMapper.selectList(qw); // 构建已存在数据的映射 fieldA -> rule Map existingMap = rules.stream() .collect(Collectors.toMap(PricingRule::getFieldA, r -> r)); // 固定返回2条:企业(0)、个人(1) List result = new ArrayList<>(); String[] typeNames = {"企业", "个人"}; for (int i = 0; i <= 1; i++) { StoreDepositVO vo = new StoreDepositVO(); vo.setFieldType(i); vo.setFieldTypeName(typeNames[i]); vo.setCityId(cityId); PricingRule rule = existingMap.get(String.valueOf(i)); if (rule != null) { vo.setPricingRuleId(rule.getId()); vo.setDepositAmount(rule.getFieldB()); } result.add(vo); } return result; } @Override @Transactional(rollbackFor = Exception.class) public void batchSaveRevenueShare(RevenueShareSaveDTO request) { // 校验:必须包含 fieldType 0-4 各一条 Set fieldTypes = request.getItems().stream() .map(RevenueShareItemDTO::getFieldType) .collect(Collectors.toSet()); if (fieldTypes.size() != Constants.FIVE) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "必须包含企业寄(0)、个人寄(1)、企业取(2)、个人取(3)、配送员(4)共5条数据"); } for (int i = 0; i <= Constants.FOUR; i++) { if (!fieldTypes.contains(i)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "缺少类型" + i + "的数据"); } } Date now = new Date(); for (RevenueShareItemDTO item : request.getItems()) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.FOUR) .eq(PricingRule::getFieldA, String.valueOf(item.getFieldType())) .eq(PricingRule::getCityId, request.getCityId()) .eq(PricingRule::getDeleted, Constants.ZERO) .last("limit 1"); PricingRule existing = pricingRuleMapper.selectOne(qw); if (existing != null) { existing.setFieldB(item.getRatio()); existing.setUpdateTime(now); pricingRuleMapper.updateById(existing); } else { PricingRule rule = new PricingRule(); rule.setType(Constants.FOUR); rule.setFieldA(String.valueOf(item.getFieldType())); rule.setFieldB(item.getRatio()); rule.setCityId(request.getCityId()); rule.setDeleted(Constants.ZERO); rule.setCreateTime(now); rule.setUpdateTime(now); pricingRuleMapper.insert(rule); } } } @Override public List listRevenueShare(Integer cityId) { // 查询已有规则 QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(PricingRule::getType, Constants.FOUR) .eq(PricingRule::getCityId, cityId) .eq(PricingRule::getDeleted, Constants.ZERO); List rules = pricingRuleMapper.selectList(qw); // 构建已存在数据的映射 fieldA -> rule Map existingMap = rules.stream() .collect(Collectors.toMap(PricingRule::getFieldA, r -> r)); // 固定返回5条:企业寄(0)、个人寄(1)、企业取(2)、个人取(3)、配送员(4) List result = new ArrayList<>(); String[] typeNames = {"企业寄", "个人寄", "企业取", "个人取", "配送员"}; for (int i = 0; i <= Constants.FOUR; i++) { RevenueShareVO vo = new RevenueShareVO(); vo.setFieldType(i); vo.setFieldTypeName(typeNames[i]); vo.setCityId(cityId); PricingRule rule = existingMap.get(String.valueOf(i)); if (rule != null) { vo.setPricingRuleId(rule.getId()); vo.setRatio(rule.getFieldB()); } result.add(vo); } return result; } }