| New file |
| | |
| | | package com.ruoyi.chargingPile.api.dto; |
| | | |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplate; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplateDetail; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板数据传输对象 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Data |
| | | @ApiModel(value = "TBillingTemplateDTO对象", description = "计费模板") |
| | | public class TBillingTemplateDTO extends TBillingTemplate { |
| | | |
| | | @ApiModelProperty(value = "计费模板明细") |
| | | private List<TBillingTemplateDetail> details; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.api.model; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.web.domain.BasePojo; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板(与计费策略 t_accounting_strategy 完全独立) |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("t_billing_template") |
| | | @ApiModel(value = "TBillingTemplate对象", description = "计费模板") |
| | | public class TBillingTemplate extends BasePojo { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @ApiModelProperty(value = "主键") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty(value = "模板名称") |
| | | @TableField("name") |
| | | private String name; |
| | | |
| | | @ApiModelProperty(value = "模板描述") |
| | | @TableField("description") |
| | | private String description; |
| | | |
| | | @ApiModelProperty(value = "会员折扣") |
| | | @TableField("discount") |
| | | private BigDecimal discount; |
| | | |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.api.model; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.web.domain.BasePojo; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板明细(与计费策略明细 t_accounting_strategy_detail 完全独立) |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("t_billing_template_detail") |
| | | @ApiModel(value = "TBillingTemplateDetail对象", description = "计费模板明细") |
| | | public class TBillingTemplateDetail extends BasePojo { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @ApiModelProperty(value = "主键") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty(value = "模板id") |
| | | @TableField("template_id") |
| | | private Integer templateId; |
| | | |
| | | @ApiModelProperty(value = "阶段(1=尖阶段,2=峰阶段,3=平阶段,4=谷阶段)") |
| | | @TableField("type") |
| | | private Integer type; |
| | | |
| | | @ApiModelProperty(value = "开始时间") |
| | | @TableField("start_time") |
| | | private String startTime; |
| | | |
| | | @ApiModelProperty(value = "结束时间") |
| | | @TableField("end_time") |
| | | private String endTime; |
| | | |
| | | @ApiModelProperty(value = "电价") |
| | | @TableField("electrovalence") |
| | | private BigDecimal electrovalence; |
| | | |
| | | @ApiModelProperty(value = "服务费") |
| | | @TableField("service_charge") |
| | | private BigDecimal serviceCharge; |
| | | |
| | | @ApiModelProperty(value = "原价服务费") |
| | | @TableField("cost_service_charge") |
| | | private BigDecimal costServiceCharge; |
| | | |
| | | } |
| New file |
| | |
| | | package com.ruoyi.other.api.dto; |
| | | |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * 车队导出 Excel DTO(列与车队列表字段一致) |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Data |
| | | public class FleetExportDTO implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Excel(name = "车队名", width = 24) |
| | | private String fleetName; |
| | | |
| | | @Excel(name = "所属公司", width = 24) |
| | | private String companyName; |
| | | |
| | | @Excel(name = "VIN码车数", width = 12) |
| | | private String vinCount; |
| | | |
| | | @Excel(name = "可充电站数", width = 12) |
| | | private String stationCount; |
| | | |
| | | @Excel(name = "月充电", width = 12) |
| | | private String monthCharge; |
| | | |
| | | @Excel(name = "当前可用余额", width = 14) |
| | | private String availableBalance; |
| | | |
| | | @Excel(name = "车队状态", width = 14, readConverterExp = "1=启用,0=停用") |
| | | private String status; |
| | | |
| | | } |
| New file |
| | |
| | | package com.ruoyi.other.api.dto; |
| | | |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * 车队导入 Excel DTO(模板下载、导入解析共用,列与添加车队表单字段一致) |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Data |
| | | public class FleetImportDTO implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Excel(name = "公司名称", width = 24, prompt = "必填,系统内已存在的公司名称") |
| | | private String companyName; |
| | | |
| | | @Excel(name = "车队名称", width = 24, prompt = "必填,最多128个字符") |
| | | private String fleetName; |
| | | |
| | | @Excel(name = "联系人电话", width = 16) |
| | | private String contactPhone; |
| | | |
| | | @Excel(name = "联系人", width = 16) |
| | | private String contactName; |
| | | |
| | | @Excel(name = "车队状态", width = 14, readConverterExp = "1=启用,0=停用", combo = {"启用", "停用"}) |
| | | private String status; |
| | | |
| | | } |
| New file |
| | |
| | | package com.ruoyi.other.api.model; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.common.core.web.domain.BasePojo; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * 公司充值记录(公司管理-充值功能使用) |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("t_company_recharge_record") |
| | | @ApiModel(value = "TCompanyRechargeRecord对象", description = "公司充值记录") |
| | | public class TCompanyRechargeRecord extends BasePojo { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @ApiModelProperty(value = "主键") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty(value = "公司ID") |
| | | @TableField("company_id") |
| | | private Integer companyId; |
| | | |
| | | @ApiModelProperty(value = "公司名称") |
| | | @TableField(exist = false) |
| | | private String companyName; |
| | | |
| | | @ApiModelProperty(value = "实付金额") |
| | | @TableField("pay_amount") |
| | | private BigDecimal payAmount; |
| | | |
| | | @ApiModelProperty(value = "赠送金额") |
| | | @TableField("gift_amount") |
| | | private BigDecimal giftAmount; |
| | | |
| | | @ApiModelProperty(value = "到账金额") |
| | | @TableField("arrival_amount") |
| | | private BigDecimal arrivalAmount; |
| | | |
| | | @ApiModelProperty(value = "优惠方式(1=不优惠 2=按比例优惠 3=按优惠单价收费)") |
| | | @TableField("preferential_type") |
| | | private Integer preferentialType; |
| | | |
| | | @ApiModelProperty(value = "服务费优惠比例(%,仅按比例优惠时使用)") |
| | | @TableField("service_discount_rate") |
| | | private BigDecimal serviceDiscountRate; |
| | | |
| | | @ApiModelProperty(value = "计费模板ID(仅按优惠单价收费时使用)") |
| | | @TableField("billing_template_id") |
| | | private Integer billingTemplateId; |
| | | |
| | | @ApiModelProperty(value = "计费模板名称") |
| | | @TableField(exist = false) |
| | | private String billingTemplateName; |
| | | |
| | | @ApiModelProperty(value = "操作人ID") |
| | | @TableField("operator_id") |
| | | private Long operatorId; |
| | | |
| | | @ApiModelProperty(value = "操作人名称") |
| | | @TableField(exist = false) |
| | | private String operatorName; |
| | | |
| | | @ApiModelProperty(value = "充值时间") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @TableField("recharge_time") |
| | | private LocalDateTime rechargeTime; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.controller; |
| | | |
| | | import com.ruoyi.chargingPile.api.dto.TBillingTemplateDTO; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplate; |
| | | import com.ruoyi.chargingPile.service.TBillingTemplateService; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import com.ruoyi.common.log.annotation.Log; |
| | | import com.ruoyi.common.log.enums.BusinessType; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板 前端控制器(与计费策略完全独立) |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/t-billing-template") |
| | | public class TBillingTemplateController { |
| | | |
| | | @Resource |
| | | private TBillingTemplateService billingTemplateService; |
| | | |
| | | /** |
| | | * 分页列表 |
| | | */ |
| | | @GetMapping("/page") |
| | | @ApiOperation(tags = {"管理后台-计费模板"}, value = "计费模板分页列表") |
| | | public AjaxResult<PageInfo<TBillingTemplate>> page(@RequestParam(value = "current", defaultValue = "1") Integer current, |
| | | @RequestParam(value = "size", defaultValue = "10") Integer size, |
| | | @RequestParam(value = "name", required = false) String name) { |
| | | return AjaxResult.success(billingTemplateService.pageList(current, size, name)); |
| | | } |
| | | |
| | | /** |
| | | * 详情 |
| | | */ |
| | | @GetMapping("/detail") |
| | | @ApiOperation(tags = {"管理后台-计费模板"}, value = "计费模板详情") |
| | | public AjaxResult<TBillingTemplateDTO> detail(@RequestParam("id") Integer id) { |
| | | return AjaxResult.success(billingTemplateService.getDetail(id)); |
| | | } |
| | | |
| | | /** |
| | | * 新增 |
| | | */ |
| | | @PostMapping("/add") |
| | | @ApiOperation(tags = {"管理后台-计费模板"}, value = "新增计费模板") |
| | | @Log(title = "【计费模板】新增计费模板", businessType = BusinessType.INSERT) |
| | | public AjaxResult add(@RequestBody TBillingTemplateDTO dto) { |
| | | billingTemplateService.addTemplate(dto); |
| | | return AjaxResult.success(dto.getId()); |
| | | } |
| | | |
| | | /** |
| | | * 编辑 |
| | | */ |
| | | @PostMapping("/edit") |
| | | @ApiOperation(tags = {"管理后台-计费模板"}, value = "编辑计费模板") |
| | | @Log(title = "【计费模板】编辑计费模板", businessType = BusinessType.UPDATE) |
| | | public AjaxResult edit(@RequestBody TBillingTemplateDTO dto) { |
| | | billingTemplateService.editTemplate(dto); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | /** |
| | | * 删除 |
| | | */ |
| | | @DeleteMapping("/del") |
| | | @ApiOperation(tags = {"管理后台-计费模板"}, value = "删除计费模板") |
| | | @Log(title = "【计费模板】删除计费模板", businessType = BusinessType.DELETE) |
| | | public AjaxResult del(@RequestParam("id") Integer id) { |
| | | billingTemplateService.deleteTemplate(id); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | /** |
| | | * 下拉选择列表(用于充值弹窗) |
| | | */ |
| | | @GetMapping("/options") |
| | | @ApiOperation(tags = {"管理后台-计费模板"}, value = "计费模板下拉列表") |
| | | public AjaxResult<List<TBillingTemplate>> options() { |
| | | return AjaxResult.success(billingTemplateService.options()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplateDetail; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板明细 Mapper 接口 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Mapper |
| | | public interface TBillingTemplateDetailMapper extends BaseMapper<TBillingTemplateDetail> { |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplate; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板 Mapper 接口 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Mapper |
| | | public interface TBillingTemplateMapper extends BaseMapper<TBillingTemplate> { |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.chargingPile.api.dto.TBillingTemplateDTO; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplate; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板 服务类 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | public interface TBillingTemplateService extends IService<TBillingTemplate> { |
| | | |
| | | /** |
| | | * 计费模板分页列表 |
| | | */ |
| | | PageInfo<TBillingTemplate> pageList(Integer current, Integer size, String name); |
| | | |
| | | /** |
| | | * 计费模板详情(含明细) |
| | | */ |
| | | TBillingTemplateDTO getDetail(Integer id); |
| | | |
| | | /** |
| | | * 新增计费模板(主表 + 明细) |
| | | */ |
| | | void addTemplate(TBillingTemplateDTO dto); |
| | | |
| | | /** |
| | | * 编辑计费模板(主表 + 明细全量替换) |
| | | */ |
| | | void editTemplate(TBillingTemplateDTO dto); |
| | | |
| | | /** |
| | | * 删除计费模板(主表 + 明细) |
| | | */ |
| | | void deleteTemplate(Integer id); |
| | | |
| | | /** |
| | | * 下拉列表(仅启用数据) |
| | | */ |
| | | java.util.List<TBillingTemplate> options(); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.chargingPile.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.chargingPile.api.dto.TBillingTemplateDTO; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplate; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplateDetail; |
| | | import com.ruoyi.chargingPile.mapper.TBillingTemplateDetailMapper; |
| | | import com.ruoyi.chargingPile.mapper.TBillingTemplateMapper; |
| | | import com.ruoyi.chargingPile.service.TBillingTemplateService; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * 计费模板 服务实现类 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Service |
| | | public class TBillingTemplateServiceImpl extends ServiceImpl<TBillingTemplateMapper, TBillingTemplate> implements TBillingTemplateService { |
| | | |
| | | @Resource |
| | | private TBillingTemplateDetailMapper detailMapper; |
| | | |
| | | @Override |
| | | public PageInfo<TBillingTemplate> pageList(Integer current, Integer size, String name) { |
| | | Page<TBillingTemplate> page = new Page<>(current, size); |
| | | LambdaQueryWrapper<TBillingTemplate> wrapper = Wrappers.lambdaQuery(TBillingTemplate.class) |
| | | .eq(TBillingTemplate::getDelFlag, false) |
| | | .like(StringUtils.hasText(name), TBillingTemplate::getName, name) |
| | | .orderByDesc(TBillingTemplate::getId); |
| | | Page<TBillingTemplate> result = this.page(page, wrapper); |
| | | PageInfo<TBillingTemplate> pageInfo = new PageInfo<>(result.getCurrent(), result.getSize()); |
| | | pageInfo.setRecords(result.getRecords()); |
| | | pageInfo.setTotal(result.getTotal()); |
| | | return pageInfo; |
| | | } |
| | | |
| | | @Override |
| | | public TBillingTemplateDTO getDetail(Integer id) { |
| | | if (id == null) { |
| | | return new TBillingTemplateDTO(); |
| | | } |
| | | TBillingTemplate template = this.getById(id); |
| | | if (template == null) { |
| | | return new TBillingTemplateDTO(); |
| | | } |
| | | TBillingTemplateDTO dto = new TBillingTemplateDTO(); |
| | | BeanUtils.copyProperties(template, dto); |
| | | List<TBillingTemplateDetail> details = detailMapper.selectList( |
| | | Wrappers.lambdaQuery(TBillingTemplateDetail.class) |
| | | .eq(TBillingTemplateDetail::getTemplateId, id) |
| | | .eq(TBillingTemplateDetail::getDelFlag, false)); |
| | | dto.setDetails(details); |
| | | return dto; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void addTemplate(TBillingTemplateDTO dto) { |
| | | validateDetails(dto.getDetails()); |
| | | dto.setId(null); |
| | | dto.setDelFlag(false); |
| | | this.save(dto); |
| | | bindAndSaveDetails(dto.getId(), dto.getDetails()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void editTemplate(TBillingTemplateDTO dto) { |
| | | if (dto.getId() == null) { |
| | | throw new IllegalArgumentException("模板ID不能为空"); |
| | | } |
| | | validateDetails(dto.getDetails()); |
| | | TBillingTemplate update = new TBillingTemplate(); |
| | | BeanUtils.copyProperties(dto, update, "details", "delFlag"); |
| | | this.updateById(update); |
| | | detailMapper.delete(Wrappers.lambdaQuery(TBillingTemplateDetail.class).eq(TBillingTemplateDetail::getTemplateId, dto.getId())); |
| | | bindAndSaveDetails(dto.getId(), dto.getDetails()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteTemplate(Integer id) { |
| | | if (id == null) { |
| | | throw new IllegalArgumentException("模板ID不能为空"); |
| | | } |
| | | this.removeById(id); |
| | | detailMapper.delete(Wrappers.lambdaQuery(TBillingTemplateDetail.class).eq(TBillingTemplateDetail::getTemplateId, id)); |
| | | } |
| | | |
| | | @Override |
| | | public List<TBillingTemplate> options() { |
| | | return this.list(Wrappers.lambdaQuery(TBillingTemplate.class).eq(TBillingTemplate::getDelFlag, false).orderByDesc(TBillingTemplate::getId)); |
| | | } |
| | | |
| | | private void bindAndSaveDetails(Integer templateId, List<TBillingTemplateDetail> details) { |
| | | if (CollectionUtils.isEmpty(details)) { |
| | | return; |
| | | } |
| | | for (TBillingTemplateDetail d : details) { |
| | | d.setId(null); |
| | | d.setTemplateId(templateId); |
| | | d.setDelFlag(false); |
| | | detailMapper.insert(d); |
| | | } |
| | | } |
| | | |
| | | private void validateDetails(List<TBillingTemplateDetail> details) { |
| | | if (CollectionUtils.isEmpty(details)) { |
| | | throw new IllegalArgumentException("请填写费率明细"); |
| | | } |
| | | // 时间段连续性 |
| | | List<TBillingTemplateDetail> sorted = details.stream() |
| | | .sorted(Comparator.comparing(TBillingTemplateDetail::getStartTime)) |
| | | .collect(Collectors.toList()); |
| | | boolean firstValid = sorted.isEmpty() || !sorted.get(0).getStartTime().equals(sorted.get(sorted.size() - 1).getEndTime()); |
| | | boolean continuous = sorted.stream() |
| | | .skip(1) |
| | | .allMatch(d -> d.getStartTime().equals(sorted.get(sorted.indexOf(d) - 1).getEndTime())); |
| | | if (firstValid || !continuous) { |
| | | throw new IllegalArgumentException("计费模板时间设置有误差"); |
| | | } |
| | | // 同阶段价格一致 |
| | | Map<Integer, BigDecimal> phaseElectrovalence = new HashMap<>(); |
| | | Map<Integer, BigDecimal> phaseServiceFee = new HashMap<>(); |
| | | Map<Integer, BigDecimal> phaseCostServiceFee = new HashMap<>(); |
| | | for (TBillingTemplateDetail d : details) { |
| | | Integer type = d.getType(); |
| | | if (phaseElectrovalence.containsKey(type)) { |
| | | if (phaseElectrovalence.get(type).compareTo(d.getElectrovalence()) != 0 |
| | | || phaseServiceFee.get(type).compareTo(d.getServiceCharge()) != 0 |
| | | || phaseCostServiceFee.get(type).compareTo(d.getCostServiceCharge()) != 0) { |
| | | throw new IllegalArgumentException("同阶段费率值需一致"); |
| | | } |
| | | } else { |
| | | phaseElectrovalence.put(type, d.getElectrovalence()); |
| | | phaseServiceFee.put(type, d.getServiceCharge()); |
| | | phaseCostServiceFee.put(type, d.getCostServiceCharge()); |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.chargingPile.mapper.TBillingTemplateDetailMapper"> |
| | | |
| | | <!-- 通用查询映射结果 --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.chargingPile.api.model.TBillingTemplateDetail"> |
| | | <id column="id" property="id" /> |
| | | <result column="template_id" property="templateId" /> |
| | | <result column="`type`" property="type" /> |
| | | <result column="start_time" property="startTime" /> |
| | | <result column="end_time" property="endTime" /> |
| | | <result column="electrovalence" property="electrovalence" /> |
| | | <result column="service_charge" property="serviceCharge" /> |
| | | <result column="cost_service_charge" property="costServiceCharge" /> |
| | | </resultMap> |
| | | |
| | | <!-- 通用查询结果列 --> |
| | | <sql id="Base_Column_List"> |
| | | id, template_id, `type`, start_time, end_time, electrovalence, service_charge, cost_service_charge |
| | | </sql> |
| | | |
| | | </mapper> |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.chargingPile.mapper.TBillingTemplateMapper"> |
| | | |
| | | <!-- 通用查询映射结果 --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.chargingPile.api.model.TBillingTemplate"> |
| | | <id column="id" property="id" /> |
| | | <result column="name" property="name" /> |
| | | <result column="description" property="description" /> |
| | | <result column="discount" property="discount" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="del_flag" property="delFlag" /> |
| | | </resultMap> |
| | | |
| | | <!-- 通用查询结果列 --> |
| | | <sql id="Base_Column_List"> |
| | | id, name, description, discount, create_time, del_flag |
| | | </sql> |
| | | |
| | | </mapper> |
| New file |
| | |
| | | package com.ruoyi.other.controller; |
| | | |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import com.ruoyi.common.log.annotation.Log; |
| | | import com.ruoyi.common.log.enums.BusinessType; |
| | | import com.ruoyi.common.security.annotation.Logical; |
| | | import com.ruoyi.common.security.annotation.RequiresPermissions; |
| | | import com.ruoyi.other.api.model.TCompanyRechargeRecord; |
| | | import com.ruoyi.other.service.TCompanyRechargeRecordService; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | /** |
| | | * <p> |
| | | * 公司充值 前端控制器(公司管理-充值功能使用) |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/company-recharge") |
| | | public class TCompanyRechargeController { |
| | | |
| | | @Resource |
| | | private TCompanyRechargeRecordService rechargeRecordService; |
| | | |
| | | /** |
| | | * 公司充值 |
| | | */ |
| | | @RequiresPermissions(value = {"/companyManagement/list"}, logical = Logical.OR) |
| | | @PostMapping("/recharge") |
| | | @ApiOperation(tags = {"管理后台-公司管理"}, value = "公司充值") |
| | | @Log(title = "【公司管理】公司充值", businessType = BusinessType.INSERT) |
| | | public AjaxResult recharge(@RequestBody TCompanyRechargeRecord record) { |
| | | return AjaxResult.success(rechargeRecordService.recharge(record)); |
| | | } |
| | | |
| | | /** |
| | | * 充值记录分页 |
| | | */ |
| | | @RequiresPermissions(value = {"/companyManagement/list"}, logical = Logical.OR) |
| | | @GetMapping("/page") |
| | | @ApiOperation(tags = {"管理后台-公司管理"}, value = "公司充值记录分页") |
| | | public AjaxResult<PageInfo<TCompanyRechargeRecord>> page(@RequestParam(value = "current", defaultValue = "1") Integer current, |
| | | @RequestParam(value = "size", defaultValue = "10") Integer size, |
| | | @RequestParam(value = "companyId", required = false) Integer companyId) { |
| | | return AjaxResult.success(rechargeRecordService.pageList(current, size, companyId, null)); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.common.security.annotation.RequiresPermissions; |
| | | import com.ruoyi.other.api.domain.TFleet; |
| | | import com.ruoyi.other.api.domain.TGroupCompany; |
| | | import com.ruoyi.other.api.dto.FleetExcelDTO; |
| | | import com.ruoyi.other.api.dto.FleetExportDTO; |
| | | import com.ruoyi.other.api.dto.FleetImportDTO; |
| | | import com.ruoyi.other.api.dto.FleetQueryDTO; |
| | | import com.ruoyi.other.service.TFleetService; |
| | | import com.ruoyi.other.service.TGroupCompanyService; |
| | |
| | | @ApiOperation(value = "导入车队", tags = {"管理后台-车队管理"}) |
| | | @Log(title = "【车队管理】导入车队", businessType = BusinessType.IMPORT) |
| | | public AjaxResult<String> importData(@RequestPart("file") MultipartFile file) throws Exception { |
| | | List<FleetExcelDTO> items = new ExcelUtil<>(FleetExcelDTO.class).importExcel(file.getInputStream()); |
| | | List<FleetImportDTO> items = new ExcelUtil<>(FleetImportDTO.class).importExcel(file.getInputStream()); |
| | | return AjaxResult.success(fleetService.importFleets(items)); |
| | | } |
| | | |
| | |
| | | @GetMapping("/import-template") |
| | | @ApiOperation(value = "下载车队导入模板", tags = {"管理后台-车队管理"}) |
| | | public void template(HttpServletResponse response) { |
| | | new ExcelUtil<>(FleetExcelDTO.class).importTemplateExcel(response, "车队导入模板"); |
| | | new ExcelUtil<>(FleetImportDTO.class).importTemplateExcel(response, "车队导入模板"); |
| | | } |
| | | |
| | | /** |
| | |
| | | @ApiOperation(value = "导出车队", tags = {"管理后台-车队管理"}) |
| | | @Log(title = "【车队管理】导出车队", businessType = BusinessType.EXPORT) |
| | | public void export(HttpServletResponse response, @RequestBody(required = false) FleetQueryDTO query) { |
| | | new ExcelUtil<>(FleetExcelDTO.class).exportExcel(response, fleetService.selectExportList(query), "车队数据"); |
| | | new ExcelUtil<>(FleetExportDTO.class).exportExcel(response, fleetService.selectExportList(query), "车队数据"); |
| | | } |
| | | |
| | | /** |
| New file |
| | |
| | | package com.ruoyi.other.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.other.api.model.TCompanyRechargeRecord; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * 公司充值记录 Mapper 接口 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Mapper |
| | | public interface TCompanyRechargeRecordMapper extends BaseMapper<TCompanyRechargeRecord> { |
| | | } |
| New file |
| | |
| | | package com.ruoyi.other.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.other.api.model.TCompanyRechargeRecord; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | |
| | | /** |
| | | * <p> |
| | | * 公司充值记录 服务类 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | public interface TCompanyRechargeRecordService extends IService<TCompanyRechargeRecord> { |
| | | |
| | | /** |
| | | * 公司充值(更新公司余额 + 保存记录) |
| | | * |
| | | * @return 充值记录主键 |
| | | */ |
| | | Integer recharge(TCompanyRechargeRecord record); |
| | | |
| | | /** |
| | | * 充值记录分页 |
| | | */ |
| | | PageInfo<TCompanyRechargeRecord> pageList(Integer current, Integer size, Integer companyId, String companyName); |
| | | } |
| | |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.other.api.domain.TFleet; |
| | | import com.ruoyi.other.api.dto.FleetExcelDTO; |
| | | import com.ruoyi.other.api.dto.FleetExportDTO; |
| | | import com.ruoyi.other.api.dto.FleetImportDTO; |
| | | import com.ruoyi.other.api.dto.FleetQueryDTO; |
| | | |
| | | import java.util.List; |
| | |
| | | * @param items Excel 解析出的车队数据 |
| | | * @return 导入结果提示 |
| | | */ |
| | | String importFleets(List<FleetExcelDTO> items); |
| | | String importFleets(List<FleetImportDTO> items); |
| | | |
| | | /** |
| | | * 按条件查询车队导出列表(含公司名称回填) |
| | |
| | | * @param query 导出查询条件 |
| | | * @return 导出数据 |
| | | */ |
| | | List<FleetExcelDTO> selectExportList(FleetQueryDTO query); |
| | | List<FleetExportDTO> selectExportList(FleetQueryDTO query); |
| | | |
| | | } |
| New file |
| | |
| | | package com.ruoyi.other.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.core.web.page.PageInfo; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.other.api.domain.TGroupCompany; |
| | | import com.ruoyi.other.api.model.TCompanyRechargeRecord; |
| | | import com.ruoyi.other.mapper.TCompanyRechargeRecordMapper; |
| | | import com.ruoyi.other.mapper.TGroupCompanyMapper; |
| | | import com.ruoyi.other.service.TCompanyRechargeRecordService; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * 公司充值记录 服务实现类 |
| | | * </p> |
| | | * |
| | | * @author mxcd |
| | | * @since 2026-09-23 |
| | | */ |
| | | @Service |
| | | public class TCompanyRechargeRecordServiceImpl extends ServiceImpl<TCompanyRechargeRecordMapper, TCompanyRechargeRecord> implements TCompanyRechargeRecordService { |
| | | |
| | | @Resource |
| | | private TGroupCompanyMapper groupCompanyMapper; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Integer recharge(TCompanyRechargeRecord record) { |
| | | if (record.getCompanyId() == null) { |
| | | throw new IllegalArgumentException("公司ID不能为空"); |
| | | } |
| | | if (record.getPayAmount() == null || record.getPayAmount().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new IllegalArgumentException("实付金额不能为空且必须大于等于0"); |
| | | } |
| | | BigDecimal giftAmount = record.getGiftAmount() == null ? BigDecimal.ZERO : record.getGiftAmount(); |
| | | if (giftAmount.compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new IllegalArgumentException("赠送金额不能小于0"); |
| | | } |
| | | TGroupCompany company = groupCompanyMapper.selectById(record.getCompanyId()); |
| | | if (company == null) { |
| | | throw new IllegalArgumentException("公司不存在"); |
| | | } |
| | | BigDecimal arrival = record.getPayAmount().add(giftAmount); |
| | | record.setArrivalAmount(arrival); |
| | | record.setId(null); |
| | | record.setDelFlag(false); |
| | | record.setRechargeTime(LocalDateTime.now()); |
| | | try { |
| | | record.setOperatorId(SecurityUtils.getLoginUser().getUserid()); |
| | | } catch (Exception ignored) { |
| | | } |
| | | // 更新公司余额(原子加):拼接 toString 防止 SQL 注入,BigDecimal 是受控数值 |
| | | groupCompanyMapper.update(null, Wrappers.lambdaUpdate(TGroupCompany.class) |
| | | .eq(TGroupCompany::getId, record.getCompanyId()) |
| | | .setSql("available_balance = IFNULL(available_balance, 0) + " + arrival.toPlainString())); |
| | | this.save(record); |
| | | return record.getId(); |
| | | } |
| | | |
| | | @Override |
| | | public PageInfo<TCompanyRechargeRecord> pageList(Integer current, Integer size, Integer companyId, String companyName) { |
| | | Page<TCompanyRechargeRecord> page = new Page<>(current, size); |
| | | LambdaQueryWrapper<TCompanyRechargeRecord> wrapper = Wrappers.lambdaQuery(TCompanyRechargeRecord.class) |
| | | .eq(TCompanyRechargeRecord::getDelFlag, false) |
| | | .eq(companyId != null, TCompanyRechargeRecord::getCompanyId, companyId) |
| | | .orderByDesc(TCompanyRechargeRecord::getId); |
| | | Page<TCompanyRechargeRecord> result = this.page(page, wrapper); |
| | | PageInfo<TCompanyRechargeRecord> pageInfo = new PageInfo<>(result.getCurrent(), result.getSize()); |
| | | pageInfo.setRecords(result.getRecords()); |
| | | pageInfo.setTotal(result.getTotal()); |
| | | return pageInfo; |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.common.core.exception.ServiceException; |
| | | import com.ruoyi.other.api.domain.TFleet; |
| | | import com.ruoyi.other.api.domain.TGroupCompany; |
| | | import com.ruoyi.other.api.dto.FleetExcelDTO; |
| | | import com.ruoyi.other.api.dto.FleetExportDTO; |
| | | import com.ruoyi.other.api.dto.FleetImportDTO; |
| | | import com.ruoyi.other.api.dto.FleetQueryDTO; |
| | | import com.ruoyi.other.mapper.TFleetMapper; |
| | | import com.ruoyi.other.service.TFleetService; |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public String importFleets(List<FleetExcelDTO> items) { |
| | | public String importFleets(List<FleetImportDTO> items) { |
| | | if (items == null || items.isEmpty()) { |
| | | throw new ServiceException("导入数据不能为空"); |
| | | } |
| | | Set<String> fleetNames = new HashSet<>(); |
| | | Set<String> companyNames = new HashSet<>(); |
| | | int row = 1; |
| | | for (FleetExcelDTO item : items) { |
| | | for (FleetImportDTO item : items) { |
| | | row++; |
| | | if (item == null || !StringUtils.hasText(item.getFleetName())) { |
| | | throw new ServiceException("第" + row + "行车队名称不能为空"); |
| | |
| | | .stream() |
| | | .collect(Collectors.toMap(TGroupCompany::getName, TGroupCompany::getId, (a, b) -> a)); |
| | | row = 1; |
| | | for (FleetExcelDTO item : items) { |
| | | for (FleetImportDTO item : items) { |
| | | row++; |
| | | String companyName = item.getCompanyName().trim(); |
| | | if (!companyMap.containsKey(companyName)) { |
| | |
| | | } |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | for (FleetExcelDTO item : items) { |
| | | for (FleetImportDTO item : items) { |
| | | TFleet fleet = new TFleet(); |
| | | fleet.setCompanyId(companyMap.get(item.getCompanyName().trim())); |
| | | fleet.setFleetName(item.getFleetName().trim()); |
| | | fleet.setContactName(trim(item.getContactName())); |
| | | fleet.setContactPhone(trim(item.getContactPhone())); |
| | | fleet.setRemark(trim(item.getRemark())); |
| | | fleet.setStatus(parseStatus(item.getStatus(), 0)); |
| | | fleet.setDelFlag(false); |
| | | fleet.setCreateTime(now); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<FleetExcelDTO> selectExportList(FleetQueryDTO query) { |
| | | public List<FleetExportDTO> selectExportList(FleetQueryDTO query) { |
| | | FleetQueryDTO safe = query == null ? new FleetQueryDTO() : query; |
| | | List<TFleet> fleets = lambdaQuery() |
| | | .like(StringUtils.hasText(safe.getFleetName()), TFleet::getFleetName, safe.getFleetName()) |
| | |
| | | : groupCompanyService.listByIds(companyIds).stream() |
| | | .collect(Collectors.toMap(TGroupCompany::getId, TGroupCompany::getName, (a, b) -> a)); |
| | | return fleets.stream().map(item -> { |
| | | FleetExcelDTO dto = new FleetExcelDTO(); |
| | | FleetExportDTO dto = new FleetExportDTO(); |
| | | dto.setFleetName(item.getFleetName()); |
| | | dto.setCompanyName(nameMap.get(item.getCompanyId())); |
| | | dto.setContactName(item.getContactName()); |
| | | dto.setContactPhone(item.getContactPhone()); |
| | | // VIN码车数/可充电站数/月充电/当前可用余额后端暂无数据源,导出留空,与列表"-"占位一致 |
| | | dto.setStatus(item.getStatus() == null ? null : String.valueOf(item.getStatus())); |
| | | dto.setRemark(item.getRemark()); |
| | | return dto; |
| | | }).collect(Collectors.toList()); |
| | | } |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.other.mapper.TCompanyRechargeRecordMapper"> |
| | | |
| | | <!-- 通用查询映射结果 --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.other.api.model.TCompanyRechargeRecord"> |
| | | <id column="id" property="id" /> |
| | | <result column="company_id" property="companyId" /> |
| | | <result column="pay_amount" property="payAmount" /> |
| | | <result column="gift_amount" property="giftAmount" /> |
| | | <result column="arrival_amount" property="arrivalAmount" /> |
| | | <result column="preferential_type" property="preferentialType" /> |
| | | <result column="service_discount_rate" property="serviceDiscountRate" /> |
| | | <result column="billing_template_id" property="billingTemplateId" /> |
| | | <result column="operator_id" property="operatorId" /> |
| | | <result column="recharge_time" property="rechargeTime" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="del_flag" property="delFlag" /> |
| | | </resultMap> |
| | | |
| | | <!-- 通用查询结果列 --> |
| | | <sql id="Base_Column_List"> |
| | | id, company_id, pay_amount, gift_amount, arrival_amount, preferential_type, |
| | | service_discount_rate, billing_template_id, operator_id, recharge_time, create_time, del_flag |
| | | </sql> |
| | | |
| | | </mapper> |