feat(oa): 添加邮寄资费最优方案功能模块
- 新增CrmShippingTariff实体类定义邮寄资费最优方案对象 - 新增CrmShippingTariffBo业务对象用于数据传输验证 - 新增CrmShippingTariffVo视图对象用于数据展示 - 新增CrmShippingTariffController提供REST API接口 - 新增CrmShippingTariffMapper数据访问接口 - 新增CrmShippingTariffService业务逻辑接口及实现dev
parent
e227c46364
commit
15c694acf5
@ -0,0 +1,76 @@
|
||||
package org.dromara.oa.crm.domain.bo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import org.dromara.oa.crm.domain.CrmShippingTariff;
|
||||
|
||||
/**
|
||||
* 邮寄资费最优方案业务对象
|
||||
*
|
||||
* @author yangk
|
||||
* @date 2026-03-16
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = CrmShippingTariff.class, reverseConvertGenerate = false)
|
||||
public class CrmShippingTariffBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
|
||||
private Long tariffId;
|
||||
|
||||
/**
|
||||
* 省份
|
||||
*/
|
||||
@NotBlank(message = "省份不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@ExcelProperty(value = "省份")
|
||||
private String province;
|
||||
|
||||
/**
|
||||
* 重量(kg)
|
||||
*/
|
||||
@NotNull(message = "重量不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@ExcelProperty(value = "重量")
|
||||
private Integer weightKg;
|
||||
|
||||
/**
|
||||
* 查询条件
|
||||
*/
|
||||
@ExcelProperty(value = "查询条件")
|
||||
private String queryCond;
|
||||
|
||||
/**
|
||||
* 最优惠方案
|
||||
*/
|
||||
@NotBlank(message = "最优惠方案不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@ExcelProperty(value = "最优惠方案")
|
||||
private String solutionName;
|
||||
|
||||
/**
|
||||
* 最优惠价格
|
||||
*/
|
||||
@NotNull(message = "价格不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@ExcelProperty(value = "最优惠价格")
|
||||
private BigDecimal price;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package org.dromara.oa.crm.domain.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import org.dromara.oa.crm.domain.CrmShippingTariff;
|
||||
|
||||
/**
|
||||
* 邮寄资费最优方案视图对象
|
||||
*
|
||||
* @author yangk
|
||||
* @date 2026-03-16
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = CrmShippingTariff.class)
|
||||
public class CrmShippingTariffVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@ExcelProperty(value = "主键")
|
||||
private Long tariffId;
|
||||
|
||||
/**
|
||||
* 省份
|
||||
*/
|
||||
@ExcelProperty(value = "省份")
|
||||
private String province;
|
||||
|
||||
/**
|
||||
* 重量(kg)
|
||||
*/
|
||||
@ExcelProperty(value = "重量(kg)")
|
||||
private Integer weightKg;
|
||||
|
||||
/**
|
||||
* 查询条件
|
||||
*/
|
||||
@ExcelProperty(value = "查询条件")
|
||||
private String queryCond;
|
||||
|
||||
/**
|
||||
* 最优惠方案
|
||||
*/
|
||||
@ExcelProperty(value = "最优惠方案")
|
||||
private String solutionName;
|
||||
|
||||
/**
|
||||
* 最优惠价格
|
||||
*/
|
||||
@ExcelProperty(value = "最优惠价格")
|
||||
private BigDecimal price;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@ExcelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package org.dromara.oa.crm.mapper;
|
||||
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.oa.crm.domain.CrmShippingTariff;
|
||||
import org.dromara.oa.crm.domain.vo.CrmShippingTariffVo;
|
||||
|
||||
/**
|
||||
* 邮寄资费最优方案Mapper接口
|
||||
*
|
||||
* @author yangk
|
||||
* @date 2026-03-16
|
||||
*/
|
||||
public interface CrmShippingTariffMapper extends BaseMapperPlus<CrmShippingTariff, CrmShippingTariffVo> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,148 @@
|
||||
package org.dromara.oa.crm.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.oa.crm.domain.CrmShippingTariff;
|
||||
import org.dromara.oa.crm.domain.bo.CrmShippingTariffBo;
|
||||
import org.dromara.oa.crm.domain.vo.CrmShippingTariffVo;
|
||||
import org.dromara.oa.crm.mapper.CrmShippingTariffMapper;
|
||||
import org.dromara.oa.crm.service.ICrmShippingTariffService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 邮寄资费最优方案Service业务层处理
|
||||
*
|
||||
* @author hm
|
||||
* @date 2026-03-16
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class CrmShippingTariffServiceImpl implements ICrmShippingTariffService {
|
||||
|
||||
private final CrmShippingTariffMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public CrmShippingTariffVo queryById(Long tariffId) {
|
||||
return baseMapper.selectVoById(tariffId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<CrmShippingTariffVo> queryPageList(CrmShippingTariffBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<CrmShippingTariff> lqw = buildQueryWrapper(bo);
|
||||
Page<CrmShippingTariffVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmShippingTariffVo> queryList(CrmShippingTariffBo bo) {
|
||||
LambdaQueryWrapper<CrmShippingTariff> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<CrmShippingTariff> buildQueryWrapper(CrmShippingTariffBo bo) {
|
||||
LambdaQueryWrapper<CrmShippingTariff> lqw = Wrappers.lambdaQuery();
|
||||
lqw.like(StringUtils.isNotBlank(bo.getProvince()), CrmShippingTariff::getProvince, bo.getProvince());
|
||||
lqw.eq(bo.getWeightKg() != null, CrmShippingTariff::getWeightKg, bo.getWeightKg());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getSolutionName()), CrmShippingTariff::getSolutionName, bo.getSolutionName());
|
||||
lqw.orderByAsc(CrmShippingTariff::getProvince).orderByAsc(CrmShippingTariff::getWeightKg);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertByBo(CrmShippingTariffBo bo) {
|
||||
CrmShippingTariff add = MapstructUtils.convert(bo, CrmShippingTariff.class);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setTariffId(add.getTariffId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(CrmShippingTariffBo bo) {
|
||||
CrmShippingTariff update = MapstructUtils.convert(bo, CrmShippingTariff.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String importData(List<CrmShippingTariffBo> list, Boolean isUpdateSupport, String operName) {
|
||||
if (ObjectUtil.isNull(list) || list.isEmpty()) {
|
||||
throw new ServiceException("导入数据不能为空!");
|
||||
}
|
||||
int successNum = 0;
|
||||
int failureNum = 0;
|
||||
StringBuilder successMsg = new StringBuilder();
|
||||
StringBuilder failureMsg = new StringBuilder();
|
||||
|
||||
for (CrmShippingTariffBo bo : list) {
|
||||
try {
|
||||
// 判断是否存在同省份同公斤级的数据
|
||||
LambdaQueryWrapper<CrmShippingTariff> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(CrmShippingTariff::getProvince, bo.getProvince())
|
||||
.eq(CrmShippingTariff::getWeightKg, bo.getWeightKg());
|
||||
CrmShippingTariff exist = baseMapper.selectOne(lqw);
|
||||
|
||||
if (ObjectUtil.isNull(exist)) {
|
||||
this.insertByBo(bo);
|
||||
successNum++;
|
||||
} else if (isUpdateSupport) {
|
||||
bo.setTariffId(exist.getTariffId());
|
||||
this.updateByBo(bo);
|
||||
successNum++;
|
||||
} else {
|
||||
failureNum++;
|
||||
failureMsg.append("<br/>").append(failureNum).append("、记录 ").append(bo.getProvince()).append("-").append(bo.getWeightKg()).append(" 已存在");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
failureNum++;
|
||||
String msg = "<br/>" + failureNum + "、记录 " + bo.getProvince() + "-" + bo.getWeightKg() + " 导入失败:";
|
||||
failureMsg.append(msg).append(e.getMessage());
|
||||
}
|
||||
}
|
||||
if (failureNum > 0) {
|
||||
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
||||
throw new ServiceException(failureMsg.toString());
|
||||
} else {
|
||||
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
|
||||
}
|
||||
return successMsg.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmShippingTariffVo queryBestSolution(String province, BigDecimal weight) {
|
||||
if (StringUtils.isBlank(province) || weight == null || weight.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 根据前端输入的重量向上取整求出计费公斤数
|
||||
int targetWeightKg = weight.setScale(0, RoundingMode.UP).intValue();
|
||||
|
||||
LambdaQueryWrapper<CrmShippingTariff> lqw = Wrappers.lambdaQuery();
|
||||
// 精确匹配省份,如果有需要也可以做前缀匹配(如"北京" -> "北京市"),为了严谨最好先用 like 进行容错
|
||||
lqw.like(CrmShippingTariff::getProvince, province)
|
||||
.eq(CrmShippingTariff::getWeightKg, targetWeightKg)
|
||||
.last("LIMIT 1");
|
||||
|
||||
return baseMapper.selectVoOne(lqw);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue