|
@@ -1,12 +1,29 @@
|
|
|
package org.springblade.factory.service.impl;
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
|
|
+import org.springblade.core.mp.support.Query;
|
|
|
+import org.springblade.core.secure.utils.AuthUtil;
|
|
|
+import org.springblade.core.tool.api.R;
|
|
|
+import org.springblade.factory.common.FactoryUitls;
|
|
|
import org.springblade.factory.entity.PcBladeSalesForecastSummary;
|
|
|
import org.springblade.factory.mapper.PcBladeSalesForecastSummaryMapper;
|
|
|
import org.springblade.factory.service.PcBladeSalesForecastSummaryService;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+import java.time.temporal.TemporalAdjusters;
|
|
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.io.IOException;
|
|
|
+import java.time.DateTimeException;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Calendar;
|
|
|
+import java.util.Date;
|
|
|
import java.util.List;
|
|
|
|
|
|
@Service
|
|
@@ -32,4 +49,397 @@ public class PcBladeSalesForecastSummaryServiceImpl extends BaseServiceImpl<PcBl
|
|
|
public boolean updatePcBladeSalesForecastSummary(PcBladeSalesForecastSummary pcBladeSalesForecastSummary) {
|
|
|
return this.updateById(pcBladeSalesForecastSummary);
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /* ========== 业务接口 ========== */
|
|
|
+
|
|
|
+
|
|
|
+ private FactoryUitls factoryUitls;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查是否可以提报(当月21日为截止日期)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean checkCanSubmit() {
|
|
|
+ Calendar calendar = Calendar.getInstance();
|
|
|
+ int currentDay = calendar.get(Calendar.DAY_OF_MONTH);
|
|
|
+ // 直接判断当前日期是否超过21日
|
|
|
+ return currentDay <= 21;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查当前经销商是否已提报该月份的预测
|
|
|
+ * 按经销商、年份、月份、品牌、物料维度检查
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<PcBladeSalesForecastSummary> checkAlreadySubmitted(PcBladeSalesForecastSummary forecast) {
|
|
|
+ QueryWrapper<PcBladeSalesForecastSummary> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("CUSTOMER_ID", forecast.getCustomerId())
|
|
|
+ .eq("year", forecast.getYear())
|
|
|
+ .eq("month", forecast.getMonth());
|
|
|
+
|
|
|
+ // 如果有品牌ID,增加品牌维度检查
|
|
|
+ if (forecast.getBrandId() != null) {
|
|
|
+ queryWrapper.eq("BRAND_ID", forecast.getBrandId());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果有物料ID,增加物料维度检查
|
|
|
+ if (forecast.getItemId() != null) {
|
|
|
+ queryWrapper.eq("ITEM_ID", forecast.getItemId());
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.list(queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增或更新销售预测
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R<String> saveOrUpdateForecast(PcBladeSalesForecastSummary forecast) {
|
|
|
+ // 1. 检查日期是否超过21日
|
|
|
+ if (!checkCanSubmit()) {
|
|
|
+ return R.fail("本月21日后不能再提报预测");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long customerId = forecast.getCustomerId();
|
|
|
+ if (customerId == null) {
|
|
|
+ return R.fail("经销商ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 验证当前操作的经销商是否为登录用户,防止冒充填报
|
|
|
+ Long loginUserId = AuthUtil.getUserId();
|
|
|
+ if (!customerId.equals(loginUserId)) {
|
|
|
+ return R.fail("无权操作其他经销商的预测数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 检查是否已提报
|
|
|
+ List<PcBladeSalesForecastSummary> existingForecasts = checkAlreadySubmitted(forecast);
|
|
|
+ boolean isUpdate = !existingForecasts.isEmpty();
|
|
|
+
|
|
|
+ // 4. 设置公共字段
|
|
|
+ if (isUpdate) {
|
|
|
+ // 更新操作,获取已有记录的ID
|
|
|
+ forecast.setId(existingForecasts.get(0).getId());
|
|
|
+ forecast.setUpdateTime(new Date());
|
|
|
+ forecast.setUpdateUser(loginUserId);
|
|
|
+ // 重置审批状态为未审批
|
|
|
+ forecast.setApprovalStatus(0);
|
|
|
+ forecast.setApprovedBy(null);
|
|
|
+ forecast.setApprovedName(null);
|
|
|
+ forecast.setApprovedTime(null);
|
|
|
+ } else {
|
|
|
+ // 新增操作,生成ID
|
|
|
+ forecast.setId(IdWorker.getId()); // MyBatis-Plus自带的雪花算法ID生成
|
|
|
+ forecast.setCreateTime(new Date());
|
|
|
+ forecast.setCreateUser(loginUserId);
|
|
|
+ forecast.setUpdateTime(new Date());
|
|
|
+ forecast.setUpdateUser(loginUserId);
|
|
|
+ // 初始审批状态为未审批
|
|
|
+ forecast.setApprovalStatus(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 保存信息
|
|
|
+ boolean success = this.saveOrUpdate(forecast);
|
|
|
+ if (!success) {
|
|
|
+ return R.fail(isUpdate ? "更新预测失败" : "新增预测失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ return R.success(isUpdate ? "更新预测成功" : "新增预测成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量新增或更新销售预测
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R<String> batchSaveOrUpdateForecasts(List<PcBladeSalesForecastSummary> forecasts) {
|
|
|
+ if (forecasts == null || forecasts.isEmpty()) {
|
|
|
+ return R.fail("没有需要保存的数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 检查日期是否超过21日
|
|
|
+ if (!checkCanSubmit()) {
|
|
|
+ return R.fail("本月21日后不能再提报预测");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long loginUserId = AuthUtil.getUserId();
|
|
|
+
|
|
|
+ for (PcBladeSalesForecastSummary forecast : forecasts) {
|
|
|
+ Long customerId = forecast.getCustomerId();
|
|
|
+ if (customerId == null) {
|
|
|
+ return R.fail("经销商ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 验证权限,防止冒充填报
|
|
|
+ if (!customerId.equals(loginUserId)) {
|
|
|
+ return R.fail("无权操作其他经销商的预测数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 检查是否已提报
|
|
|
+ List<PcBladeSalesForecastSummary> existingForecasts = checkAlreadySubmitted(forecast);
|
|
|
+ boolean isUpdate = !existingForecasts.isEmpty();
|
|
|
+
|
|
|
+ // 4. 设置公共字段
|
|
|
+ if (isUpdate) {
|
|
|
+ forecast.setId(existingForecasts.get(0).getId());
|
|
|
+ forecast.setUpdateTime(new Date());
|
|
|
+ forecast.setUpdateUser(loginUserId);
|
|
|
+ forecast.setApprovalStatus(0);
|
|
|
+ forecast.setApprovedBy(null);
|
|
|
+ forecast.setApprovedName(null);
|
|
|
+ forecast.setApprovedTime(null);
|
|
|
+ } else {
|
|
|
+ forecast.setId(IdWorker.getId()); // MyBatis-Plus自带的雪花算法ID生成
|
|
|
+ forecast.setCreateTime(new Date());
|
|
|
+ forecast.setCreateUser(loginUserId);
|
|
|
+ forecast.setUpdateTime(new Date());
|
|
|
+ forecast.setUpdateUser(loginUserId);
|
|
|
+ forecast.setApprovalStatus(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 批量保存
|
|
|
+ boolean success = this.saveOrUpdateBatch(forecasts);
|
|
|
+ return success ? R.success("批量操作成功") : R.fail("批量操作失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据经销商ID获取最新的预测提报信息
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<PcBladeSalesForecastSummary> getLatestByCustomerId(Long customerId) {
|
|
|
+ if (customerId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前年月
|
|
|
+ LocalDate now = LocalDate.now();
|
|
|
+ int currentYear = now.getYear();
|
|
|
+ int currentMonth = now.getMonthValue();
|
|
|
+
|
|
|
+ QueryWrapper<PcBladeSalesForecastSummary> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("CUSTOMER_ID", customerId)
|
|
|
+ .eq("year", currentYear)
|
|
|
+ .eq("month", currentMonth)
|
|
|
+ .orderByDesc("create_time");
|
|
|
+
|
|
|
+ return this.list(queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出预测填报Excel模板
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void exportExcelTemplate(HttpServletResponse response) throws IOException {
|
|
|
+ // 设置响应头
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
+ response.setHeader("Content-Disposition", "attachment;filename=销售预测模板.xlsx");
|
|
|
+
|
|
|
+ // 实际项目中使用EasyExcel或POI生成模板
|
|
|
+ // 模板应包含需要填写的字段:年份、月份、品牌信息、物料信息、预测数量等
|
|
|
+ // 注意:主键ID、创建时间、更新时间等系统生成字段不需要在模板中出现
|
|
|
+
|
|
|
+ // 以下为示例代码框架
|
|
|
+ /*
|
|
|
+ List<PcBladeSalesForecastSummary> templateData = new ArrayList<>();
|
|
|
+ PcBladeSalesForecastSummary template = new PcBladeSalesForecastSummary();
|
|
|
+ template.setYear(LocalDate.now().getYear());
|
|
|
+ template.setMonth(LocalDate.now().getMonthValue());
|
|
|
+ // 设置其他模板提示信息
|
|
|
+ templateData.add(template);
|
|
|
+
|
|
|
+ EasyExcel.write(response.getOutputStream(), PcBladeSalesForecastSummary.class)
|
|
|
+ .sheet("销售预测")
|
|
|
+ .doWrite(templateData);
|
|
|
+ */
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导入预测填报Excel数据
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R<String> importExcelData(MultipartFile file) throws IOException {
|
|
|
+ // 检查是否可以提报
|
|
|
+ if (!checkCanSubmit()) {
|
|
|
+ return R.fail("本月21日后不能再提报预测");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long loginUserId = AuthUtil.getUserId();
|
|
|
+
|
|
|
+ // 这里实现Excel数据导入逻辑
|
|
|
+ // 使用EasyExcel或POI解析Excel文件
|
|
|
+ /*
|
|
|
+ List<PcBladeSalesForecastSummary> forecasts = EasyExcel.read(file.getInputStream())
|
|
|
+ .head(PcBladeSalesForecastSummary.class)
|
|
|
+ .sheet()
|
|
|
+ .doReadSync();
|
|
|
+
|
|
|
+ // 验证导入的数据
|
|
|
+ if (forecasts.isEmpty()) {
|
|
|
+ return R.fail("导入数据为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 统一设置经销商ID为登录用户ID,防止冒充填报
|
|
|
+ for (PcBladeSalesForecastSummary forecast : forecasts) {
|
|
|
+ forecast.setCustomerId(loginUserId);
|
|
|
+ // 清除可能手动填写的ID,由系统生成
|
|
|
+ forecast.setId(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调用批量保存方法
|
|
|
+ return batchSaveOrUpdateForecasts(forecasts);
|
|
|
+ */
|
|
|
+
|
|
|
+ return R.success("导入成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 工厂端审核预测数据
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R<String> approveForecast(Long id, Integer approvalStatus, String approvalRemark) {
|
|
|
+ if (id == null) {
|
|
|
+ return R.fail("预测ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (approvalStatus == null || (approvalStatus != 1 && approvalStatus != 2)) {
|
|
|
+ return R.fail("审批状态不正确");
|
|
|
+ }
|
|
|
+
|
|
|
+ PcBladeSalesForecastSummary forecast = this.getById(id);
|
|
|
+ if (forecast == null) {
|
|
|
+ return R.fail("预测数据不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置审批信息
|
|
|
+ forecast.setApprovalStatus(approvalStatus);
|
|
|
+ forecast.setApprovedBy(AuthUtil.getUserId());
|
|
|
+ forecast.setApprovedName(AuthUtil.getUserName());
|
|
|
+ forecast.setApprovedTime(LocalDateTime.now());
|
|
|
+ forecast.setUpdateTime(new Date());
|
|
|
+ forecast.setUpdateUser(AuthUtil.getUserId());
|
|
|
+
|
|
|
+ boolean success = this.updateById(forecast);
|
|
|
+ return success ? R.success("审核成功") : R.fail("审核失败");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据用户ID和日期条件查询销售预测列表(分页)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public IPage<PcBladeSalesForecastSummary> getForecastPageByUserId(Long customerId, LocalDate startDate, LocalDate endDate, Query query) {
|
|
|
+ // 创建分页对象
|
|
|
+ IPage<PcBladeSalesForecastSummary> page = new Page<>(query.getCurrent(), query.getSize());
|
|
|
+
|
|
|
+ // 构建查询条件
|
|
|
+ QueryWrapper<PcBladeSalesForecastSummary> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("CUSTOMER_ID", customerId);
|
|
|
+
|
|
|
+ // 处理日期区间查询
|
|
|
+ if (startDate != null && endDate != null) {
|
|
|
+ queryWrapper.ge("year", startDate.getYear())
|
|
|
+ .le("year", endDate.getYear());
|
|
|
+
|
|
|
+ // 如果是同一年,添加月份条件
|
|
|
+ if (startDate.getYear() == endDate.getYear()) {
|
|
|
+ queryWrapper.and(w -> w
|
|
|
+ .ge("month", startDate.getMonthValue())
|
|
|
+ .le("month", endDate.getMonthValue())
|
|
|
+ );
|
|
|
+ } else if (startDate.getYear() < endDate.getYear()) {
|
|
|
+ // 跨年度查询
|
|
|
+ queryWrapper.and(w -> w
|
|
|
+ .or(qw -> qw.eq("year", startDate.getYear()).ge("month", startDate.getMonthValue()))
|
|
|
+ .or(qw -> qw.eq("year", endDate.getYear()).le("month", endDate.getMonthValue()))
|
|
|
+ .or(qw -> qw.gt("year", startDate.getYear()).lt("year", endDate.getYear()))
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } else if (startDate != null) {
|
|
|
+ // 只有开始日期
|
|
|
+ queryWrapper.ge("year", startDate.getYear());
|
|
|
+ if (queryWrapper.getEntity().getYear().equals(startDate.getYear())) {
|
|
|
+ queryWrapper.ge("month", startDate.getMonthValue());
|
|
|
+ }
|
|
|
+ } else if (endDate != null) {
|
|
|
+ // 只有结束日期
|
|
|
+ queryWrapper.le("year", endDate.getYear());
|
|
|
+ if (queryWrapper.getEntity().getYear().equals(endDate.getYear())) {
|
|
|
+ queryWrapper.le("month", endDate.getMonthValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按年份和月份降序排列
|
|
|
+ queryWrapper.orderByDesc("year", "month", "create_time");
|
|
|
+
|
|
|
+ // 执行分页查询
|
|
|
+ return this.page(page, queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据用户ID和日期条件查询销售预测列表(不分页)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<PcBladeSalesForecastSummary> getForecastListByUserId(Long customerId, LocalDate startDate, LocalDate endDate) {
|
|
|
+ // 复用分页查询的条件构建逻辑
|
|
|
+ QueryWrapper<PcBladeSalesForecastSummary> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("CUSTOMER_ID", customerId);
|
|
|
+
|
|
|
+ // 处理日期区间查询(与分页方法相同)
|
|
|
+ if (startDate != null && endDate != null) {
|
|
|
+ queryWrapper.ge("year", startDate.getYear())
|
|
|
+ .le("year", endDate.getYear());
|
|
|
+
|
|
|
+ if (startDate.getYear() == endDate.getYear()) {
|
|
|
+ queryWrapper.and(w -> w
|
|
|
+ .ge("month", startDate.getMonthValue())
|
|
|
+ .le("month", endDate.getMonthValue())
|
|
|
+ );
|
|
|
+ } else if (startDate.getYear() < endDate.getYear()) {
|
|
|
+ queryWrapper.and(w -> w
|
|
|
+ .or(qw -> qw.eq("year", startDate.getYear()).ge("month", startDate.getMonthValue()))
|
|
|
+ .or(qw -> qw.eq("year", endDate.getYear()).le("month", endDate.getMonthValue()))
|
|
|
+ .or(qw -> qw.gt("year", startDate.getYear()).lt("year", endDate.getYear()))
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } else if (startDate != null) {
|
|
|
+ queryWrapper.ge("year", startDate.getYear());
|
|
|
+ if (queryWrapper.getEntity().getYear().equals(startDate.getYear())) {
|
|
|
+ queryWrapper.ge("month", startDate.getMonthValue());
|
|
|
+ }
|
|
|
+ } else if (endDate != null) {
|
|
|
+ queryWrapper.le("year", endDate.getYear());
|
|
|
+ if (queryWrapper.getEntity().getYear().equals(endDate.getYear())) {
|
|
|
+ queryWrapper.le("month", endDate.getMonthValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ queryWrapper.orderByDesc("year", "month", "create_time");
|
|
|
+
|
|
|
+ // 执行列表查询
|
|
|
+ return this.list(queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据用户ID和年月查询销售预测列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<PcBladeSalesForecastSummary> getForecastByUserAndMonth(Long customerId, Integer year, Integer month) {
|
|
|
+ QueryWrapper<PcBladeSalesForecastSummary> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("CUSTOMER_ID", customerId)
|
|
|
+ .eq("year", year)
|
|
|
+ .eq("month", month)
|
|
|
+ .orderByDesc("create_time");
|
|
|
+
|
|
|
+ return this.list(queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
}
|