feat: 完成履约管理模块全量功能迭代
本次迭代包含以下核心功能: 1. 新增履约时效总览可视化页面,支持多维度数据统计 2. 实现物料/客户/供应商的Excel批量导入导出功能 3. 新增订单批量结单功能,优化结单流程校验 4. 完善日志配置,新增文件日志落地 5. 修复分类查询逻辑,优化多租户数据隔离 6. 新增甲方履约结单管理页面与权限控制 7. 重构部分Mapper与Service接口,增强代码健壮性
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
package com.ruoyi.web.controller.bid;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.bid.BizClient;
|
||||
import com.ruoyi.system.service.bid.IBizClientService;
|
||||
|
||||
@@ -60,6 +63,35 @@ public class BizClientController extends BaseController {
|
||||
return toAjax(service.deleteBizClientByIds(clientIds));
|
||||
}
|
||||
|
||||
// ========== Excel 导入导出 ==========
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:client:export')")
|
||||
@Log(title = "甲方客户", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, BizClient query) {
|
||||
List<BizClient> list = service.selectBizClientList(query);
|
||||
ExcelUtil<BizClient> util = new ExcelUtil<BizClient>(BizClient.class);
|
||||
util.exportExcel(response, list, "客户数据");
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:client:import')")
|
||||
@Log(title = "甲方客户", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
|
||||
ExcelUtil<BizClient> util = new ExcelUtil<BizClient>(BizClient.class);
|
||||
List<BizClient> clientList = util.importExcel(file.getInputStream());
|
||||
Long tenantId = getDeptId();
|
||||
if (tenantId == null) { tenantId = 1L; }
|
||||
String message = service.importClient(clientList, updateSupport, getUsername(), tenantId);
|
||||
return success(message);
|
||||
}
|
||||
|
||||
@PostMapping("/importTemplate")
|
||||
public void importTemplate(HttpServletResponse response) {
|
||||
ExcelUtil<BizClient> util = new ExcelUtil<BizClient>(BizClient.class);
|
||||
util.importTemplateExcel(response, "客户数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询客户的关联历史发货单
|
||||
* 链路: client → client_quote → rfq → delivery_order
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.web.controller.bid;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -83,13 +84,27 @@ public class BizDeliveryOrderController extends BaseController {
|
||||
return toAjax(service.recall(id));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit')")
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit') or @ss.hasPermi('bid:clientdelivery:closeDate:edit')")
|
||||
@Log(title = "结单时间", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/{id}/closeDate")
|
||||
public AjaxResult setCloseDate(@PathVariable Long id, @RequestParam String closeDate) {
|
||||
return toAjax(service.setCloseDate(id, closeDate, getUsername()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit') or @ss.hasPermi('bid:clientdelivery:closeDate:edit')")
|
||||
@Log(title = "结单时间", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/batchCloseDate")
|
||||
public AjaxResult batchSetCloseDate(@RequestBody Map<String, Object> params) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> rawIds = (List<Integer>) params.get("ids");
|
||||
String closeDate = (String) params.get("closeDate");
|
||||
if (rawIds == null || rawIds.isEmpty()) {
|
||||
return AjaxResult.error("请选择要结单的订单");
|
||||
}
|
||||
List<Long> ids = rawIds.stream().map(Integer::longValue).collect(java.util.stream.Collectors.toList());
|
||||
return toAjax(service.batchSetCloseDate(ids, closeDate, getUsername()));
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════
|
||||
// 物料发货记录
|
||||
// ════════════════════════════════════════════
|
||||
@@ -107,18 +122,66 @@ public class BizDeliveryOrderController extends BaseController {
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:transit')")
|
||||
@GetMapping("/transit/stats")
|
||||
public AjaxResult transitStats() {
|
||||
return success(service.selectTransitStats());
|
||||
Long tenantId = getDeptId();
|
||||
return success(service.selectTransitStats(tenantId));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:history')")
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:history') or @ss.hasPermi('bid:clientdelivery:closeDate')")
|
||||
@GetMapping("/history/stats")
|
||||
public AjaxResult historyStats() {
|
||||
return success(service.selectHistoryStats());
|
||||
public AjaxResult historyStats(@RequestParam(required = false) String type) {
|
||||
Long tenantId = getDeptId();
|
||||
return success(service.selectHistoryStats(tenantId, type));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit')")
|
||||
@GetMapping("/closeDate/stats")
|
||||
public AjaxResult closeDateStats() {
|
||||
return success(service.selectCloseDateStats());
|
||||
Long tenantId = getDeptId();
|
||||
return success(service.selectCloseDateStats(tenantId));
|
||||
}
|
||||
|
||||
/** 履约时效可视化 - 时间线数据(供应商履约 + 甲方履约通用) */
|
||||
@PreAuthorize("@ss.hasPermi('bid:order:timeline') or @ss.hasPermi('bid:clientdelivery:timeline') or @ss.hasPermi('bid:order:transit') or @ss.hasPermi('bid:order:history')")
|
||||
@GetMapping("/timeline")
|
||||
public AjaxResult timeline(@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String dateFrom,
|
||||
@RequestParam(required = false) String dateTo) {
|
||||
Long tenantId = getDeptId();
|
||||
List<Map<String, Object>> orders = service.selectTimelineData(tenantId, type, status, dateFrom, dateTo);
|
||||
// 计算准时/延期统计数据
|
||||
long onTime = orders.stream().filter(o -> {
|
||||
Object cd = o.get("actualCloseDate");
|
||||
Object dd = o.get("delayDate");
|
||||
if (cd == null) return false;
|
||||
// 有延期日期时以延期日期为准,否则以约定交货日为准
|
||||
Object base = dd != null ? dd : o.get("deliveryDate");
|
||||
if (base == null) return false;
|
||||
try {
|
||||
return java.sql.Date.valueOf(cd.toString()).compareTo(java.sql.Date.valueOf(base.toString())) <= 0;
|
||||
} catch (Exception e) { return false; }
|
||||
}).count();
|
||||
|
||||
long delayed = orders.stream().filter(o -> {
|
||||
Object cd = o.get("actualCloseDate");
|
||||
Object dd = o.get("delayDate");
|
||||
if (cd == null) return false;
|
||||
Object base = dd != null ? dd : o.get("deliveryDate");
|
||||
if (base == null) return false;
|
||||
try {
|
||||
return java.sql.Date.valueOf(cd.toString()).compareTo(java.sql.Date.valueOf(base.toString())) > 0;
|
||||
} catch (Exception e) { return false; }
|
||||
}).count();
|
||||
|
||||
long pending = orders.stream().filter(o -> o.get("actualCloseDate") == null).count();
|
||||
|
||||
Map<String, Object> result = new java.util.HashMap<>();
|
||||
result.put("orders", orders);
|
||||
Map<String, Object> stats = new java.util.HashMap<>();
|
||||
stats.put("onTime", onTime);
|
||||
stats.put("delayed", delayed);
|
||||
stats.put("pending", pending);
|
||||
result.put("stats", stats);
|
||||
return success(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.ruoyi.web.controller.bid;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.bid.BizMaterial;
|
||||
import com.ruoyi.system.service.bid.IBizMaterialService;
|
||||
|
||||
@@ -61,6 +64,35 @@ public class BizMaterialController extends BaseController {
|
||||
return toAjax(service.deleteBizMaterialByIds(materialIds));
|
||||
}
|
||||
|
||||
// ========== Excel 导入导出 ==========
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:material:export')")
|
||||
@Log(title = "物料管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, BizMaterial query) {
|
||||
List<BizMaterial> list = service.selectBizMaterialList(query);
|
||||
ExcelUtil<BizMaterial> util = new ExcelUtil<BizMaterial>(BizMaterial.class);
|
||||
util.exportExcel(response, list, "物料数据");
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:material:import')")
|
||||
@Log(title = "物料管理", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
|
||||
ExcelUtil<BizMaterial> util = new ExcelUtil<BizMaterial>(BizMaterial.class);
|
||||
List<BizMaterial> materialList = util.importExcel(file.getInputStream());
|
||||
Long tenantId = getDeptId();
|
||||
if (tenantId == null) { tenantId = 1L; }
|
||||
String message = service.importMaterial(materialList, updateSupport, getUsername(), tenantId);
|
||||
return success(message);
|
||||
}
|
||||
|
||||
@PostMapping("/importTemplate")
|
||||
public void importTemplate(HttpServletResponse response) {
|
||||
ExcelUtil<BizMaterial> util = new ExcelUtil<BizMaterial>(BizMaterial.class);
|
||||
util.importTemplateExcel(response, "物料数据");
|
||||
}
|
||||
|
||||
// ========== 物料详情页接口 ==========
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:material:detail')")
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.ruoyi.web.controller.bid;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.bid.BizSupplier;
|
||||
import com.ruoyi.system.service.bid.IBizSupplierService;
|
||||
|
||||
@@ -54,4 +57,33 @@ public class BizSupplierController extends BaseController {
|
||||
public AjaxResult remove(@PathVariable Long[] supplierIds) {
|
||||
return toAjax(service.deleteBizSupplierByIds(supplierIds));
|
||||
}
|
||||
|
||||
// ========== Excel 导入导出 ==========
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:supplier:export')")
|
||||
@Log(title = "供应商管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, BizSupplier query) {
|
||||
List<BizSupplier> list = service.selectBizSupplierList(query);
|
||||
ExcelUtil<BizSupplier> util = new ExcelUtil<BizSupplier>(BizSupplier.class);
|
||||
util.exportExcel(response, list, "供应商数据");
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:supplier:import')")
|
||||
@Log(title = "供应商管理", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
|
||||
ExcelUtil<BizSupplier> util = new ExcelUtil<BizSupplier>(BizSupplier.class);
|
||||
List<BizSupplier> supplierList = util.importExcel(file.getInputStream());
|
||||
Long tenantId = getDeptId();
|
||||
if (tenantId == null) { tenantId = 1L; }
|
||||
String message = service.importSupplier(supplierList, updateSupport, getUsername(), tenantId);
|
||||
return success(message);
|
||||
}
|
||||
|
||||
@PostMapping("/importTemplate")
|
||||
public void importTemplate(HttpServletResponse response) {
|
||||
ExcelUtil<BizSupplier> util = new ExcelUtil<BizSupplier>(BizSupplier.class);
|
||||
util.importTemplateExcel(response, "供应商数据");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
<property name="log.path" value="logs" />
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
@@ -8,14 +9,38 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="com.ruoyi" level="info" />
|
||||
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/ruoyi.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/ruoyi.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>7</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="error-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<logger name="com.ruoyi" level="debug" />
|
||||
<logger name="org.springframework" level="warn" />
|
||||
<logger name="sys-user" level="info" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
<appender-ref ref="file" />
|
||||
<appender-ref ref="error-file" />
|
||||
</root>
|
||||
|
||||
<logger name="sys-user" level="info">
|
||||
<appender-ref ref="console" />
|
||||
</logger>
|
||||
</configuration>
|
||||
|
||||
Reference in New Issue
Block a user