zhouhui.jiang

Merge branch 'master' of http://gitlab.erry.com/zhouhui.jiang/test_cursor

# Conflicts:
#	frontend/src/views/delivery/index.vue
package com.apple.erp.dto.request;
import lombok.Data;
import javax.validation.constraints.*;
import java.math.BigDecimal;
/**
* 返利新增请求DTO
*
* @author Apple ERP
*/
@Data
public class RebateAddReq {
/**
* 返利明细编号
*/
@Size(max = 30, message = "返利明细编号不能超过30个字符")
private String rebateNo;
/**
* 关联订单编号
*/
@NotBlank(message = "关联订单编号不能为空")
@Size(max = 30, message = "订单编号不能超过30个字符")
private String orderNo;
/**
* 经销商编码
*/
@NotBlank(message = "经销商编码不能为空")
@Size(max = 20, message = "经销商编码不能超过20个字符")
private String dealerCode;
/**
* 经销商名称
*/
@NotBlank(message = "经销商名称不能为空")
@Size(max = 100, message = "经销商名称不能超过100个字符")
private String dealerName;
/**
* 产品编码
*/
@NotBlank(message = "产品编码不能为空")
@Size(max = 30, message = "产品编码不能超过30个字符")
private String productCode;
/**
* 返利金额
*/
@NotNull(message = "返利金额不能为空")
@DecimalMin(value = "0", message = "返利金额不能小于0")
private BigDecimal rebateAmount;
/**
* 返利记录日期
*/
@NotBlank(message = "返利记录日期不能为空")
private String rebateDate;
/**
* 操作类型:1-新增,2-扣减
*/
@NotNull(message = "操作类型不能为空")
@Min(value = 1, message = "操作类型值无效")
@Max(value = 2, message = "操作类型值无效")
private Integer operateType;
/**
* 返利计算状态:0-未计算,1-已计算
*/
private Integer calcFlag;
}
package com.apple.erp.dto.request;
import lombok.Data;
/**
* 返利台账明细查询请求DTO
*
* @author Apple ERP Team
* @version 1.0.0
* @since 2024-01-01
*/
@Data
public class RebateQueryReq {
/**
* 返利明细编号
*/
private String rebateNo;
/**
* 关联订单编号
*/
private String orderNo;
/**
* 经销商编码
*/
private String dealerCode;
/**
* 经销商名称
*/
private String dealerName;
/**
* 产品编码
*/
private String productCode;
/**
* 操作类型:1-新增,2-扣减
*/
private Integer operateType;
/**
* 返利计算状态:0-未计算,1-已计算
*/
private Integer calcFlag;
/**
* 返利记录开始日期
*/
private String rebateStartDate;
/**
* 返利记录结束日期
*/
private String rebateEndDate;
/**
* 页码
*/
private Integer pageNum = 1;
/**
* 页面大小
*/
private Integer pageSize = 10;
}
package com.apple.erp.dto.request;
import lombok.Data;
import javax.validation.constraints.*;
import java.math.BigDecimal;
/**
* 返利更新请求DTO
*
* @author Apple ERP
*/
@Data
public class RebateUpdateReq {
/**
* 返利明细ID
*/
@NotNull(message = "返利明细ID不能为空")
private Long rebateId;
/**
* 返利明细编号
*/
@Size(max = 30, message = "返利明细编号不能超过30个字符")
private String rebateNo;
/**
* 关联订单编号
*/
@NotBlank(message = "关联订单编号不能为空")
@Size(max = 30, message = "订单编号不能超过30个字符")
private String orderNo;
/**
* 经销商编码
*/
@NotBlank(message = "经销商编码不能为空")
@Size(max = 20, message = "经销商编码不能超过20个字符")
private String dealerCode;
/**
* 经销商名称
*/
@NotBlank(message = "经销商名称不能为空")
@Size(max = 100, message = "经销商名称不能超过100个字符")
private String dealerName;
/**
* 产品编码
*/
@NotBlank(message = "产品编码不能为空")
@Size(max = 30, message = "产品编码不能超过30个字符")
private String productCode;
/**
* 返利金额
*/
@NotNull(message = "返利金额不能为空")
@DecimalMin(value = "0", message = "返利金额不能小于0")
private BigDecimal rebateAmount;
/**
* 返利记录日期
*/
@NotBlank(message = "返利记录日期不能为空")
private String rebateDate;
/**
* 操作类型:1-新增,2-扣减
*/
@NotNull(message = "操作类型不能为空")
@Min(value = 1, message = "操作类型值无效")
@Max(value = 2, message = "操作类型值无效")
private Integer operateType;
/**
* 返利计算状态:0-未计算,1-已计算
*/
private Integer calcFlag;
}
package com.apple.erp.dto.response;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 返利响应DTO
*
* @author Apple ERP
*/
@Data
public class RebateRes {
/**
* 返利明细ID
*/
private Long rebateId;
/**
* 返利明细编号
*/
private String rebateNo;
/**
* 关联订单编号
*/
private String orderNo;
/**
* 经销商编码
*/
private String dealerCode;
/**
* 经销商名称
*/
private String dealerName;
/**
* 产品编码
*/
private String productCode;
/**
* 返利金额
*/
private BigDecimal rebateAmount;
/**
* 返利记录日期
*/
private LocalDate rebateDate;
/**
* 操作类型:1-新增,2-扣减
*/
private Integer operateType;
/**
* 操作类型文本
*/
private String operateTypeText;
/**
* 返利计算状态:0-未计算,1-已计算
*/
private Integer calcFlag;
/**
* 计算状态文本
*/
private String calcFlagText;
/**
* 数据上传时间
*/
private LocalDateTime uploadTime;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 创建者
*/
private String createBy;
/**
* 更新者
*/
private String updateBy;
}
package com.apple.erp.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 返利实体类
*
* @author Apple ERP
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_rebate_detail")
public class Rebate {
/**
* 返利明细ID
*/
@TableId(value = "rebate_id", type = IdType.AUTO)
private Long rebateId;
/**
* 返利明细编号
*/
@TableField("rebate_no")
private String rebateNo;
/**
* 关联订单编号
*/
@TableField("order_no")
private String orderNo;
/**
* 经销商编码
*/
@TableField("dealer_code")
private String dealerCode;
/**
* 经销商名称
*/
@TableField("dealer_name")
private String dealerName;
/**
* 产品编码
*/
@TableField("product_code")
private String productCode;
/**
* 返利金额
*/
@TableField("rebate_amount")
private BigDecimal rebateAmount;
/**
* 返利记录日期
*/
@TableField("rebate_date")
private LocalDate rebateDate;
/**
* 操作类型:1-新增,2-扣减
*/
@TableField("operate_type")
private Integer operateType;
/**
* 返利计算状态:0-未计算,1-已计算
*/
@TableField("calc_flag")
private Integer calcFlag;
/**
* 数据上传时间
*/
@TableField("upload_time")
private LocalDateTime uploadTime;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 创建者
*/
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
/**
* 更新者
*/
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 删除标志:0-存在,2-删除
*/
@TableField("del_flag")
@TableLogic(value = "0", delval = "2")
private String delFlag;
}
package com.apple.erp.mapper;
import com.apple.erp.entity.Rebate;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
* 返利台账明细Mapper接口
*
* @author Apple ERP Team
* @version 1.0.0
* @since 2024-01-01
*/
@Mapper
public interface RebateMapper extends BaseMapper<Rebate> {
/**
* 分页查询返利明细列表
*
* @param page 分页参数
* @param params 查询参数
* @return 返利明细分页列表
*/
IPage<Rebate> selectRebatePage(Page<Rebate> page, @Param("params") Map<String, Object> params);
/**
* 根据经销商编码统计返利总金额
*
* @param dealerCode 经销商编码
* @param operateType 操作类型
* @return 返利总金额
*/
BigDecimal sumRebateAmountByDealer(@Param("dealerCode") String dealerCode, @Param("operateType") Integer operateType);
/**
* 根据产品编码统计返利总金额
*
* @param productCode 产品编码
* @param operateType 操作类型
* @return 返利总金额
*/
BigDecimal sumRebateAmountByProduct(@Param("productCode") String productCode, @Param("operateType") Integer operateType);
/**
* 统计各操作类型的返利数量
*
* @return 操作类型统计
*/
List<Map<String, Object>> countRebateByOperateType();
/**
* 查询未计算的返利明细列表
*
* @return 未计算返利明细列表
*/
List<Rebate> selectUnCalcRebates();
/**
* 根据返利编号查询返利明细
*
* @param rebateNo 返利编号
* @return 返利明细信息
*/
Rebate selectByRebateNo(@Param("rebateNo") String rebateNo);
/**
* 获取返利月度统计数据
*
* @param year 年份
* @return 月度统计数据
*/
List<Map<String, Object>> getRebateMonthlyStats(@Param("year") Integer year);
/**
* 获取返利状态统计数据
*
* @return 状态统计数据
*/
Map<String, Object> getRebateStatusStats();
/**
* 获取返利趋势统计数据
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return 趋势统计数据
*/
List<Map<String, Object>> getRebateTrendStats(@Param("startDate") String startDate, @Param("endDate") String endDate);
}
package com.apple.erp.service;
import com.apple.erp.dto.request.RebateAddReq;
import com.apple.erp.dto.request.RebateQueryReq;
import com.apple.erp.dto.request.RebateUpdateReq;
import com.apple.erp.dto.response.RebateRes;
import com.apple.erp.entity.Rebate;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
* 返利台账明细服务接口
*
* @author Apple ERP Team
* @version 1.0.0
* @since 2024-01-01
*/
public interface RebateService extends IService<Rebate> {
/**
* 分页查询返利明细列表
*
* @param queryReq 查询请求
* @return 返利明细分页列表
*/
IPage<RebateRes> getRebatePage(RebateQueryReq queryReq);
/**
* 根据ID查询返利明细详情
*
* @param rebateId 返利明细ID
* @return 返利明细详情
*/
RebateRes getRebateById(Long rebateId);
/**
* 新增返利明细
*
* @param addReq 新增请求
* @return 是否成功
*/
boolean addRebate(RebateAddReq addReq);
/**
* 更新返利明细
*
* @param updateReq 更新请求
* @return 是否成功
*/
boolean updateRebate(RebateUpdateReq updateReq);
/**
* 删除返利明细
*
* @param rebateId 返利明细ID
* @return 是否成功
*/
boolean deleteRebate(Long rebateId);
/**
* 批量删除返利明细
*
* @param rebateIds 返利明细ID列表
* @return 是否成功
*/
boolean batchDeleteRebate(List<Long> rebateIds);
/**
* 根据经销商编码统计返利总金额
*
* @param dealerCode 经销商编码
* @param operateType 操作类型(可选)
* @return 返利总金额
*/
BigDecimal sumRebateAmountByDealer(String dealerCode, Integer operateType);
/**
* 根据产品编码统计返利总金额
*
* @param productCode 产品编码
* @param operateType 操作类型(可选)
* @return 返利总金额
*/
BigDecimal sumRebateAmountByProduct(String productCode, Integer operateType);
/**
* 统计各操作类型的返利数量
*
* @return 操作类型统计
*/
List<Map<String, Object>> countRebateByOperateType();
/**
* 查询未计算的返利明细列表
*
* @return 未计算返利明细列表
*/
List<RebateRes> getUnCalcRebates();
/**
* 根据返利编号查询返利明细
*
* @param rebateNo 返利编号
* @return 返利明细信息
*/
RebateRes getRebateByNo(String rebateNo);
/**
* 生成返利编号
*
* @return 返利编号
*/
String generateRebateNo();
/**
* 获取返利月度统计数据
*
* @param year 年份
* @return 月度统计数据
*/
List<Map<String, Object>> getRebateMonthlyStats(Integer year);
/**
* 获取返利状态统计数据
*
* @return 状态统计数据
*/
Map<String, Object> getRebateStatusStats();
/**
* 获取返利趋势统计数据
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return 趋势统计数据
*/
List<Map<String, Object>> getRebateTrendStats(String startDate, String endDate);
}
package com.apple.erp.service.impl;
import com.apple.erp.dto.request.RebateAddReq;
import com.apple.erp.dto.request.RebateQueryReq;
import com.apple.erp.dto.request.RebateUpdateReq;
import com.apple.erp.dto.response.RebateRes;
import com.apple.erp.entity.Rebate;
import com.apple.erp.mapper.RebateMapper;
import com.apple.erp.service.RebateService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 返利台账明细服务实现类
*
* @author Apple ERP Team
* @version 1.0.0
* @since 2024-01-01
*/
@Slf4j
@Service
public class RebateServiceImpl extends ServiceImpl<RebateMapper, Rebate> implements RebateService {
@Autowired
private RebateMapper rebateMapper;
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public IPage<RebateRes> getRebatePage(RebateQueryReq queryReq) {
// 构建查询参数
Map<String, Object> params = buildQueryParams(queryReq);
// 创建分页对象
Page<Rebate> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
// 分页查询
IPage<Rebate> rebatePage = rebateMapper.selectRebatePage(page, params);
// 转换为响应DTO
Page<RebateRes> resultPage = new Page<>(rebatePage.getCurrent(), rebatePage.getSize(), rebatePage.getTotal());
List<RebateRes> rebateResList = rebatePage.getRecords().stream()
.map(this::convertToRebateRes)
.collect(Collectors.toList());
resultPage.setRecords(rebateResList);
return resultPage;
}
@Override
public RebateRes getRebateById(Long rebateId) {
Rebate rebate = getById(rebateId);
return rebate != null ? convertToRebateRes(rebate) : null;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean addRebate(RebateAddReq addReq) {
Rebate rebate = new Rebate();
BeanUtils.copyProperties(addReq, rebate);
// 设置返利编号(如果未提供)
if (addReq.getRebateNo() == null || addReq.getRebateNo().isEmpty()) {
rebate.setRebateNo(generateRebateNo());
}
// 转换日期字符串
if (addReq.getRebateDate() != null && !addReq.getRebateDate().isEmpty()) {
rebate.setRebateDate(LocalDate.parse(addReq.getRebateDate(), DATE_FORMATTER));
}
// 设置上传时间
rebate.setUploadTime(LocalDateTime.now());
// 默认计算状态为未计算
if (rebate.getCalcFlag() == null) {
rebate.setCalcFlag(0);
}
return save(rebate);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateRebate(RebateUpdateReq updateReq) {
Rebate rebate = getById(updateReq.getRebateId());
if (rebate == null) {
throw new RuntimeException("返利记录不存在");
}
BeanUtils.copyProperties(updateReq, rebate);
// 转换日期字符串
if (updateReq.getRebateDate() != null && !updateReq.getRebateDate().isEmpty()) {
rebate.setRebateDate(LocalDate.parse(updateReq.getRebateDate(), DATE_FORMATTER));
}
return updateById(rebate);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteRebate(Long rebateId) {
return removeById(rebateId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean batchDeleteRebate(List<Long> rebateIds) {
return removeByIds(rebateIds);
}
@Override
public BigDecimal sumRebateAmountByDealer(String dealerCode, Integer operateType) {
BigDecimal amount = rebateMapper.sumRebateAmountByDealer(dealerCode, operateType);
return amount != null ? amount : BigDecimal.ZERO;
}
@Override
public BigDecimal sumRebateAmountByProduct(String productCode, Integer operateType) {
BigDecimal amount = rebateMapper.sumRebateAmountByProduct(productCode, operateType);
return amount != null ? amount : BigDecimal.ZERO;
}
@Override
public List<Map<String, Object>> countRebateByOperateType() {
return rebateMapper.countRebateByOperateType();
}
@Override
public List<RebateRes> getUnCalcRebates() {
List<Rebate> rebates = rebateMapper.selectUnCalcRebates();
return rebates.stream()
.map(this::convertToRebateRes)
.collect(Collectors.toList());
}
@Override
public RebateRes getRebateByNo(String rebateNo) {
Rebate rebate = rebateMapper.selectByRebateNo(rebateNo);
return rebate != null ? convertToRebateRes(rebate) : null;
}
@Override
public String generateRebateNo() {
String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String prefix = "RB" + dateStr;
// 查询当天已有的返利编号数量
LambdaQueryWrapper<Rebate> wrapper = new LambdaQueryWrapper<>();
wrapper.likeRight(Rebate::getRebateNo, prefix);
long count = count(wrapper);
// 生成4位序号
String sequence = String.format("%04d", count + 1);
return prefix + sequence;
}
/**
* 构建查询参数
*/
private Map<String, Object> buildQueryParams(RebateQueryReq queryReq) {
Map<String, Object> params = new HashMap<>();
if (queryReq.getRebateNo() != null && !queryReq.getRebateNo().isEmpty()) {
params.put("rebateNo", queryReq.getRebateNo());
}
if (queryReq.getOrderNo() != null && !queryReq.getOrderNo().isEmpty()) {
params.put("orderNo", queryReq.getOrderNo());
}
if (queryReq.getDealerCode() != null && !queryReq.getDealerCode().isEmpty()) {
params.put("dealerCode", queryReq.getDealerCode());
}
if (queryReq.getDealerName() != null && !queryReq.getDealerName().isEmpty()) {
params.put("dealerName", queryReq.getDealerName());
}
if (queryReq.getProductCode() != null && !queryReq.getProductCode().isEmpty()) {
params.put("productCode", queryReq.getProductCode());
}
if (queryReq.getOperateType() != null) {
params.put("operateType", queryReq.getOperateType());
}
if (queryReq.getCalcFlag() != null) {
params.put("calcFlag", queryReq.getCalcFlag());
}
if (queryReq.getRebateStartDate() != null && !queryReq.getRebateStartDate().isEmpty()) {
params.put("rebateStartDate", queryReq.getRebateStartDate());
}
if (queryReq.getRebateEndDate() != null && !queryReq.getRebateEndDate().isEmpty()) {
params.put("rebateEndDate", queryReq.getRebateEndDate());
}
return params;
}
/**
* 转换为响应DTO
*/
private RebateRes convertToRebateRes(Rebate rebate) {
RebateRes rebateRes = new RebateRes();
BeanUtils.copyProperties(rebate, rebateRes);
// 设置操作类型文本
rebateRes.setOperateTypeText(getOperateTypeText(rebate.getOperateType()));
// 设置计算状态文本
rebateRes.setCalcFlagText(getCalcFlagText(rebate.getCalcFlag()));
return rebateRes;
}
/**
* 获取操作类型文本
*/
private String getOperateTypeText(Integer operateType) {
if (operateType == null) {
return "";
}
switch (operateType) {
case 1:
return "新增";
case 2:
return "扣减";
default:
return "未知";
}
}
/**
* 获取计算状态文本
*/
private String getCalcFlagText(Integer calcFlag) {
if (calcFlag == null) {
return "";
}
switch (calcFlag) {
case 0:
return "未计算";
case 1:
return "已计算";
default:
return "未知";
}
}
@Override
public List<Map<String, Object>> getRebateMonthlyStats(Integer year) {
log.info("获取返利月度统计数据,年份:{}", year);
return rebateMapper.getRebateMonthlyStats(year);
}
@Override
public Map<String, Object> getRebateStatusStats() {
log.info("获取返利状态统计数据");
return rebateMapper.getRebateStatusStats();
}
@Override
public List<Map<String, Object>> getRebateTrendStats(String startDate, String endDate) {
log.info("获取返利趋势统计数据,开始日期:{},结束日期:{}", startDate, endDate);
return rebateMapper.getRebateTrendStats(startDate, endDate);
}
}
......@@ -17,8 +17,8 @@ spring:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/apple_erp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: MySQL@123
username: apple_erp_app
password: StrongPassw0rd!
druid:
# 初始连接数
initial-size: 5
......@@ -70,7 +70,7 @@ spring:
redis:
host: localhost
port: 6379
password:
password: 123456
database: 0
timeout: 10000ms
lettuce:
......
<?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.apple.erp.mapper.RebateMapper">
<!-- 返利明细基础字段 -->
<sql id="baseColumns">
rebate_id, rebate_no, order_no, dealer_code, dealer_name, product_code,
rebate_amount, rebate_date, operate_type, calc_flag, upload_time,
create_by, create_time, update_by, update_time
</sql>
<!-- 分页查询返利明细列表 -->
<select id="selectRebatePage" resultType="com.apple.erp.entity.Rebate">
SELECT
<include refid="baseColumns"/>
FROM t_rebate_detail
WHERE del_flag = '0'
<if test="params.rebateNo != null and params.rebateNo != ''">
AND rebate_no LIKE CONCAT('%', #{params.rebateNo}, '%')
</if>
<if test="params.orderNo != null and params.orderNo != ''">
AND order_no LIKE CONCAT('%', #{params.orderNo}, '%')
</if>
<if test="params.dealerCode != null and params.dealerCode != ''">
AND dealer_code = #{params.dealerCode}
</if>
<if test="params.dealerName != null and params.dealerName != ''">
AND dealer_name LIKE CONCAT('%', #{params.dealerName}, '%')
</if>
<if test="params.productCode != null and params.productCode != ''">
AND product_code = #{params.productCode}
</if>
<if test="params.operateType != null">
AND operate_type = #{params.operateType}
</if>
<if test="params.calcFlag != null">
AND calc_flag = #{params.calcFlag}
</if>
<if test="params.rebateStartDate != null and params.rebateStartDate != ''">
AND rebate_date &gt;= #{params.rebateStartDate}
</if>
<if test="params.rebateEndDate != null and params.rebateEndDate != ''">
AND rebate_date &lt;= #{params.rebateEndDate}
</if>
ORDER BY create_time DESC
</select>
<!-- 根据经销商编码统计返利总金额 -->
<select id="sumRebateAmountByDealer" resultType="java.math.BigDecimal">
SELECT COALESCE(SUM(rebate_amount), 0)
FROM t_rebate_detail
WHERE del_flag = '0'
AND dealer_code = #{dealerCode}
<if test="operateType != null">
AND operate_type = #{operateType}
</if>
</select>
<!-- 根据产品编码统计返利总金额 -->
<select id="sumRebateAmountByProduct" resultType="java.math.BigDecimal">
SELECT COALESCE(SUM(rebate_amount), 0)
FROM t_rebate_detail
WHERE del_flag = '0'
AND product_code = #{productCode}
<if test="operateType != null">
AND operate_type = #{operateType}
</if>
</select>
<!-- 统计各操作类型的返利数量 -->
<select id="countRebateByOperateType" resultType="java.util.Map">
SELECT
operate_type,
COUNT(*) as count,
COALESCE(SUM(rebate_amount), 0) as totalAmount
FROM t_rebate_detail
WHERE del_flag = '0'
GROUP BY operate_type
</select>
<!-- 查询未计算的返利明细列表 -->
<select id="selectUnCalcRebates" resultType="com.apple.erp.entity.Rebate">
SELECT
<include refid="baseColumns"/>
FROM t_rebate_detail
WHERE del_flag = '0'
AND calc_flag = 0
ORDER BY create_time ASC
</select>
<!-- 根据返利编号查询返利明细 -->
<select id="selectByRebateNo" resultType="com.apple.erp.entity.Rebate">
SELECT
<include refid="baseColumns"/>
FROM t_rebate_detail
WHERE del_flag = '0'
AND rebate_no = #{rebateNo}
</select>
<!-- 获取返利月度统计数据 -->
<select id="getRebateMonthlyStats" resultType="java.util.Map">
SELECT
MONTH(rebate_date) as month,
COALESCE(SUM(CASE WHEN operate_type = 1 THEN rebate_amount ELSE -rebate_amount END), 0) as totalAmount,
COALESCE(SUM(CASE WHEN calc_flag = 1 AND operate_type = 1 THEN rebate_amount
WHEN calc_flag = 1 AND operate_type = 2 THEN -rebate_amount
ELSE 0 END), 0) as calculatedAmount,
COUNT(*) as totalCount,
COUNT(CASE WHEN calc_flag = 1 THEN 1 END) as calculatedCount
FROM t_rebate_detail
WHERE del_flag = '0'
AND YEAR(rebate_date) = #{year}
GROUP BY MONTH(rebate_date)
ORDER BY month
</select>
<!-- 获取返利状态统计数据 -->
<select id="getRebateStatusStats" resultType="java.util.Map">
SELECT
COUNT(CASE WHEN calc_flag = 1 THEN 1 END) as calculatedCount,
COUNT(CASE WHEN calc_flag = 0 THEN 1 END) as unCalculatedCount,
COUNT(*) as totalCount,
COALESCE(SUM(CASE WHEN calc_flag = 1 AND operate_type = 1 THEN rebate_amount
WHEN calc_flag = 1 AND operate_type = 2 THEN -rebate_amount
ELSE 0 END), 0) as calculatedAmount,
COALESCE(SUM(CASE WHEN operate_type = 1 THEN rebate_amount ELSE -rebate_amount END), 0) as totalAmount
FROM t_rebate_detail
WHERE del_flag = '0'
</select>
<!-- 获取返利趋势统计数据 -->
<select id="getRebateTrendStats" resultType="java.util.Map">
SELECT
DATE(rebate_date) as date,
COALESCE(SUM(CASE WHEN operate_type = 1 THEN rebate_amount ELSE -rebate_amount END), 0) as totalAmount,
COALESCE(SUM(CASE WHEN calc_flag = 1 AND operate_type = 1 THEN rebate_amount
WHEN calc_flag = 1 AND operate_type = 2 THEN -rebate_amount
ELSE 0 END), 0) as calculatedAmount,
COUNT(*) as totalCount,
COUNT(CASE WHEN calc_flag = 1 THEN 1 END) as calculatedCount
FROM t_rebate_detail
WHERE del_flag = '0'
<if test="startDate != null and startDate != ''">
AND rebate_date >= #{startDate}
</if>
<if test="endDate != null and endDate != ''">
AND rebate_date &lt;= #{endDate}
</if>
GROUP BY DATE(rebate_date)
ORDER BY date
</select>
</mapper>
......@@ -51,7 +51,8 @@
</select>
<select id="selectMenuTreeByRoleId" parameterType="Long" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.menu_type, m.status, m.perms, m.icon, m.create_by, m.create_time, m.update_by, m.update_time, m.del_flag
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.menu_type, m.status, m.perms, m.icon, m.create_by, m.create_time, m.update_by, m.update_time, m.del_flag,
m.sort
from t_sys_menu m
left join t_sys_role_menu rm on m.menu_id = rm.menu_id
where rm.role_id = #{roleId} and m.status = 1 and m.del_flag = '0'
......
......@@ -7,21 +7,10 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
ActionBar: typeof import('./src/components/common/ActionBar.vue')['default']
DataTable: typeof import('./src/components/common/DataTable.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElLink: typeof import('element-plus/es')['ElLink']
Header: typeof import('./src/components/layout/Header.vue')['default']
Pagination: typeof import('./src/components/common/Pagination.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SearchBar: typeof import('./src/components/common/SearchBar.vue')['default']
Sidebar: typeof import('./src/components/layout/Sidebar.vue')['default']
SidebarItem: typeof import('./src/components/layout/SidebarItem.vue')['default']
TableToolbar: typeof import('./src/components/common/TableToolbar.vue')['default']
}
}
......
import request from '@/utils/request'
// 返利台账明细类型定义
export interface Rebate {
rebateId?: number
rebateNo?: string
orderNo: string
dealerCode: string
dealerName: string
productCode: string
rebateAmount: number
rebateDate: string
operateType: number
operateTypeText?: string
calcFlag?: number
calcFlagText?: string
uploadTime?: string
createTime?: string
updateTime?: string
createBy?: string
updateBy?: string
}
// 返利查询参数
export interface RebateSearchParams {
pageNum: number
pageSize: number
rebateNo?: string
orderNo?: string
dealerCode?: string
dealerName?: string
productCode?: string
operateType?: number
calcFlag?: number
rebateStartDate?: string
rebateEndDate?: string
}
// 返利新增参数
export interface RebateAddParams {
rebateNo?: string
orderNo: string
dealerCode: string
dealerName: string
productCode: string
rebateAmount: number
rebateDate: string
operateType: number
calcFlag?: number
}
// 返利更新参数
export interface RebateUpdateParams extends RebateAddParams {
rebateId: number
}
// 分页查询返利列表
export const getRebatePage = (params: RebateSearchParams) => {
return request({
url: '/api/rebate/page',
method: 'post',
data: params
})
}
// 根据ID查询返利详情
export const getRebateById = (rebateId: number) => {
return request({
url: `/api/rebate/${rebateId}`,
method: 'get'
})
}
// 新增返利
export const addRebate = (data: RebateAddParams) => {
return request({
url: '/api/rebate/add',
method: 'post',
data
})
}
// 更新返利
export const updateRebate = (data: RebateUpdateParams) => {
return request({
url: '/api/rebate/update',
method: 'post',
data
})
}
// 删除返利
export const deleteRebate = (rebateId: number) => {
return request({
url: `/api/rebate/${rebateId}`,
method: 'delete'
})
}
// 批量删除返利
export const batchDeleteRebate = (rebateIds: number[]) => {
return request({
url: '/api/rebate/batchDelete',
method: 'post',
data: rebateIds
})
}
// 标记返利为已计算
export const markRebateCalculated = (rebateId: number) => {
return request({
url: `/api/rebate/markCalculated/${rebateId}`,
method: 'post'
})
}
// 批量标记返利为已计算
export const batchMarkRebateCalculated = (rebateIds: number[]) => {
return request({
url: '/api/rebate/batchMarkCalculated',
method: 'post',
data: rebateIds
})
}
// 根据经销商编码统计返利总金额
export const sumRebateAmountByDealer = (dealerCode: string, operateType?: number) => {
return request({
url: `/api/rebate/sumByDealer/${dealerCode}`,
method: 'get',
params: { operateType }
})
}
// 根据产品编码统计返利总金额
export const sumRebateAmountByProduct = (productCode: string, operateType?: number) => {
return request({
url: `/api/rebate/sumByProduct/${productCode}`,
method: 'get',
params: { operateType }
})
}
// 统计各操作类型的返利数量
export const countRebateByOperateType = () => {
return request({
url: '/api/rebate/countByOperateType',
method: 'get'
})
}
// 查询未计算的返利明细列表
export const getUnCalcRebates = () => {
return request({
url: '/api/rebate/unCalc',
method: 'get'
})
}
// 根据返利编号查询返利
export const getRebateByNo = (rebateNo: string) => {
return request({
url: `/api/rebate/byNo/${rebateNo}`,
method: 'get'
})
}
// 生成返利编号
export const generateRebateNo = () => {
return request({
url: '/api/rebate/generateNo',
method: 'get'
})
}
// 导出返利数据
export const exportRebate = (params: RebateSearchParams) => {
return request({
url: '/api/rebate/export',
method: 'post',
data: params,
responseType: 'blob'
})
}
// 获取返利月度统计数据
export const getRebateMonthlyStats = (year?: number) => {
return request({
url: '/api/rebate/monthlyStats',
method: 'get',
params: { year: year || new Date().getFullYear() }
})
}
// 获取返利状态统计数据
export const getRebateStatusStats = () => {
return request({
url: '/api/rebate/statusStats',
method: 'get'
})
}
// 获取返利趋势统计数据
export const getRebateTrendStats = (startDate?: string, endDate?: string) => {
return request({
url: '/api/rebate/trendStats',
method: 'get',
params: { startDate, endDate }
})
}
export default {
getRebatePage,
getRebateById,
addRebate,
updateRebate,
deleteRebate,
batchDeleteRebate,
markRebateCalculated,
batchMarkRebateCalculated,
sumRebateAmountByDealer,
sumRebateAmountByProduct,
countRebateByOperateType,
getUnCalcRebates,
getRebateByNo,
generateRebateNo,
exportRebate,
getRebateMonthlyStats,
getRebateStatusStats,
getRebateTrendStats
}
......@@ -146,6 +146,7 @@ const menuItems = ref([
{ name: '发票管理', path: '/main/invoice', icon: '🧾' },
{ name: '产品管理', path: '/main/product', icon: '📦' },
{ name: '经销商管理', path: '/main/dealer', icon: '🏢' },
{ name: '返利管理', path: '/main/rebate', icon: '💰' },
{
name: '系统设置',
icon: '⚙️',
......
......@@ -99,24 +99,6 @@ const staticRoutes: RouteRecordRaw[] = [
}
},
{
path: 'dicts',
name: 'Dicts',
component: () => import('@/views/dicts/index.vue'),
meta: {
title: '字典管理',
requiresAuth: true
}
},
{
path: 'logs',
name: 'Logs',
component: () => import('@/views/logs/index.vue'),
meta: {
title: '日志管理',
requiresAuth: true
}
},
{
path: 'product',
name: 'Product',
component: () => import('@/views/product/index.vue'),
......@@ -162,6 +144,15 @@ const staticRoutes: RouteRecordRaw[] = [
}
},
{
path: 'rebate',
name: 'Rebate',
component: () => import('@/views/rebate/index.vue'),
meta: {
title: '返利管理',
requiresAuth: true
}
},
{
path: 'settings',
name: 'Settings',
component: () => import('@/views/settings/index.vue'),
......
......@@ -93,27 +93,27 @@
<th>出库日期</th>
<th>关联订单编号</th>
<th>出库状态</th>
<th>出库时间</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="order in orderList" :key="order.deliveryId">
<td>{{ order.deliveryId }}</td>
<td>{{ order.deliveryNo }}</td>
<td>{{ order.dealerCode }}</td>
<td>{{ order.dealerName }}</td>
<td>{{ formatDate(order.deliveryDate) }}</td>
<td>{{ order.orderNo }}</td>
<tr v-for="delivery in deliveryList" :key="delivery.deliveryId">
<td>{{ delivery.deliveryId }}</td>
<td>{{ delivery.deliveryNo }}</td>
<td>{{ delivery.dealerCode }}</td>
<td>{{ delivery.dealerName }}</td>
<td>{{ formatDate(delivery.deliveryDate) }}</td>
<td>{{ delivery.orderNo }}</td>
<td>
<span :class="getDeliveryStatusClass(order.deliveryStatus)">
{{ getDeliveryStatusText(order.deliveryStatus) }}
<span :class="getDeliveryStatusClass(delivery.deliveryStatus)">
{{ getDeliveryStatusText(delivery.deliveryStatus) }}
</span>
</td>
<td>{{ formatTime(order.uploadTime || '') }}</td>
<td>{{ formatTime(delivery.createTime || '') }}</td>
<td class="action-col">
<button @click="handleView(order)" class="view-btn">👁️ 查看</button>
<button @click="handleDelete(order)" class="delete-btn">🗑️ 删除</button>
<button @click="handleView(delivery)" class="view-btn">👁️ 查看</button>
<button @click="handleDelete(delivery)" class="delete-btn">🗑️ 删除</button>
</td>
</tr>
</tbody>
......@@ -173,7 +173,7 @@
<button @click="closeDetailDialog" class="close-btn">×</button>
</div>
<div class="dialog-body">
<div v-if="orderDetail" class="detail-content">
<div v-if="deliveryDetail" class="detail-content">
<div class="detail-section">
<h4>出库基本信息</h4>
<div class="detail-grid">
......@@ -190,12 +190,16 @@
<span>{{ orderDetail.orderNo }}</span>
</div>
<div class="detail-item">
<label>关联订单编号:</label>
<span>{{ deliveryDetail.orderNo }}</span>
</div>
<div class="detail-item">
<label>经销商编码:</label>
<span>{{ orderDetail.dealerCode }}</span>
<span>{{ deliveryDetail.dealerCode }}</span>
</div>
<div class="detail-item">
<label>经销商名称:</label>
<span>{{ orderDetail.dealerName }}</span>
<span>{{ deliveryDetail.dealerName }}</span>
</div>
<div class="detail-item">
<label>出库日期:</label>
......@@ -203,8 +207,8 @@
</div>
<div class="detail-item">
<label>出库状态:</label>
<span :class="getDeliveryStatusClass(orderDetail.deliveryStatus)">
{{ getDeliveryStatusText(orderDetail.deliveryStatus) }}
<span :class="getDeliveryStatusClass(deliveryDetail.deliveryStatus)">
{{ getDeliveryStatusText(deliveryDetail.deliveryStatus) }}
</span>
</div>
<div class="detail-item">
......@@ -433,9 +437,9 @@ import { deliveryApi, type DeliveryAddReq, type DeliveryInfo, type DeliveryQuery
// 响应式数据
const loading = ref(false)
const orderList = ref<DeliveryInfo[]>([])
const deliveryList = ref<DeliveryInfo[]>([])
const showDetailDialog = ref(false)
const orderDetail = ref<DeliveryInfo | null>(null)
const deliveryDetail = ref<DeliveryInfo | null>(null)
// 新增出库相关数据
const showAddDialog = ref(false)
......@@ -453,6 +457,7 @@ const addFormData = reactive({
// 搜索参数
const searchParams = reactive({
orderNo: '',
deliveryNo: '',
dealerCode: '',
dealerName: '',
......@@ -461,6 +466,8 @@ const searchParams = reactive({
dataSource: '',
deliveryStartDate: '',
deliveryEndDate: '',
startDate: '',
endDate: '',
pageNum: 1,
pageSize: 10
})
......@@ -529,7 +536,7 @@ const getPageNumbers = () => {
}
// 方法
const fetchOrders = async () => {
const fetchDeliveries = async () => {
try {
loading.value = true
const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
......@@ -538,12 +545,12 @@ const fetchOrders = async () => {
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.records) {
orderList.value = response.records || []
deliveryList.value = response.records || []
pagination.total = response.total || 0
pagination.pageNum = response.current || 1
pagination.pageSize = response.size || 10
} else {
orderList.value = []
deliveryList.value = []
pagination.total = 0
showMessage('获取出库列表失败', 'error')
}
......@@ -557,7 +564,7 @@ const fetchOrders = async () => {
const handleSearch = () => {
pagination.pageNum = 1
fetchOrders()
fetchDeliveries()
}
const handleReset = () => {
......@@ -570,33 +577,33 @@ const handleReset = () => {
endDate: ''
})
pagination.pageNum = 1
fetchOrders()
fetchDeliveries()
}
const handleView = async (order: DeliveryInfo) => {
const handleView = async (delivery: DeliveryInfo) => {
try {
const response = await deliveryApi.getDeliveryDetail(order.deliveryId) as any
const response = await deliveryApi.getDeliveryDetail(delivery.deliveryId) as any
console.log('出库查询详情API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.deliveryId) {
orderDetail.value = response
deliveryDetail.value = response
showDetailDialog.value = true
} else {
showMessage('获取订单详情失败', 'error')
showMessage('获取出库详情失败', 'error')
}
} catch (error) {
console.error('获取订单详情失败:', error)
showMessage('获取订单详情失败, 请重试', 'error')
console.error('获取出库详情失败:', error)
showMessage('获取出库详情失败, 请重试', 'error')
}
}
const handleDelete = async (order: DeliveryInfo) => {
if (confirm(`确定要删除出库单"${order.deliveryNo}"吗?`)) {
const handleDelete = async (delivery: DeliveryInfo) => {
if (confirm(`确定要删除出库单"${delivery.deliveryNo}"吗?`)) {
try {
await deliveryApi.deleteDelivery(order.deliveryId)
await deliveryApi.deleteDelivery(delivery.deliveryId)
showMessage('删除出库单成功', 'success')
fetchOrders() // 刷新列表
fetchDeliveries() // 刷新列表
} catch (error) {
console.error('删除出库单失败:', error)
showMessage('删除出库单失败, 请重试', 'error')
......@@ -659,7 +666,7 @@ const handleSubmitAdd = async () => {
// 将 YYYY-MM-DDTHH:MM:SS 格式转换为 YYYY-MM-DD HH:MM:SS 格式
formattedDeliveryDate = formattedDeliveryDate.replace('T', ' ')
}
// 构造新增出库的请求数据
const deliveryData: DeliveryAddReq = {
deliveryNo: addFormData.deliveryNo,
......@@ -670,19 +677,19 @@ const handleSubmitAdd = async () => {
deliveryStatus: 1, // 默认已出库
warehouseCode: addFormData.warehouseCode,
dataSource: addFormData.dataSource || '系统录入',
deliveryItems: addFormData.deliveryItems.map(item => ({
productCode: item.productCode,
productName: item.productName,
deliveryQty: item.deliveryQty,
deliveryPrice: item.deliveryPrice,
deliveryAmount: item.deliveryAmount
}))
deliveryItems: addFormData.deliveryItems.map(item => ({
productCode: item.productCode,
productName: item.productName,
productQty: item.deliveryQty,
unitPrice: item.deliveryPrice,
totalPrice: item.deliveryAmount
}))
}
await deliveryApi.addDelivery(deliveryData)
showMessage('新增出库成功', 'success')
closeAddDialog()
fetchOrders() // 刷新列表
fetchDeliveries() // 刷新列表
} catch (error) {
console.error('新增出库失败:', error)
showMessage('新增出库失败, 请重试', 'error')
......@@ -694,13 +701,13 @@ const handleSubmitAdd = async () => {
const handlePageChange = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
pagination.pageNum = page
fetchOrders()
fetchDeliveries()
}
}
const handlePageSizeChange = () => {
pagination.pageNum = 1
fetchOrders()
fetchDeliveries()
}
const handleTableSearch = () => {
......@@ -708,7 +715,7 @@ const handleTableSearch = () => {
}
const handleTableRefresh = () => {
fetchOrders()
fetchDeliveries()
}
const handleTableExport = () => {
......@@ -721,12 +728,12 @@ const handleTableViewToggle = () => {
const closeDetailDialog = () => {
showDetailDialog.value = false
orderDetail.value = null
deliveryDetail.value = null
}
// 生命周期
onMounted(() => {
fetchOrders()
fetchDeliveries()
})
</script>
......
......@@ -5,9 +5,9 @@
<div class="search-row">
<div class="search-item">
<label>发票编号:</label>
<input
v-model="searchParams.invoiceNo"
class="search-input"
<input
v-model="searchParams.invoiceNo"
class="search-input"
placeholder="请输入发票编号"
/>
</div>
......@@ -48,7 +48,7 @@
<div class="search-item">
<label>开票开始日期:</label>
<input
v-model="searchParams.invoiceStartDate"
v-model="searchParams.invoiceStartDate"
class="search-input"
type="date"
/>
......@@ -56,7 +56,7 @@
<div class="search-item">
<label>开票结束日期:</label>
<input
v-model="searchParams.invoiceEndDate"
v-model="searchParams.invoiceEndDate"
class="search-input"
type="date"
/>
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.