94e23a3d587846f10ea9517bd2e6705093d6a99a..dfa9258c8879dbb0d1bfc00d7f939031d0bb623b
2026-01-29 gongchunyi
Merge remote-tracking branch 'origin/dev_New' into dev_New
dfa925 对比 | 目录
2026-01-29 gongchunyi
feat: 支收对比分析统计DTO
eb63e4 对比 | 目录
2026-01-29 yaowanxin
Merge remote-tracking branch 'origin/dev_New' into dev_New
6f888b 对比 | 目录
2026-01-29 yaowanxin
发货审批页面的审批数据
d6adb8 对比 | 目录
2026-01-29 liyong
Merge remote-tracking branch 'origin/dev_New' into dev_New
7cc641 对比 | 目录
2026-01-29 liyong
feat(production): 添加生产核算功能和相关接口
37699e 对比 | 目录
2026-01-29 gongchunyi
feat: 财务分析大屏接口
62fe43 对比 | 目录
2026-01-29 gongchunyi
Merge remote-tracking branch 'origin/dev_New' into dev_New
2d1e90 对比 | 目录
2026-01-29 gongchunyi
fix: 查询库存数量为null赋予默认值
807c34 对比 | 目录
2026-01-29 maven
Merge remote-tracking branch 'origin/dev_New' into dev_New
c389e9 对比 | 目录
2026-01-29 maven
yys 修改发货接口
6a7995 对比 | 目录
2026-01-29 liyong
Merge remote-tracking branch 'origin/dev_New' into dev_New
edb9e5 对比 | 目录
2026-01-29 liyong
refactor(sales): 添加质检删除记录时删除库存报空错误
a66c1f 对比 | 目录
2026-01-29 zss
Merge remote-tracking branch 'origin/dev_New' into dev_New
bb6d3d 对比 | 目录
2026-01-29 zss
不合格处理状态数据过滤
eb7b3d 对比 | 目录
2026-01-29 maven
Merge remote-tracking branch 'origin/dev_New' into dev_New
e896ea 对比 | 目录
2026-01-29 maven
yys 修改注入方式
e0aaf2 对比 | 目录
2026-01-29 zss
Merge remote-tracking branch 'origin/dev_New' into dev_New
e2c5b3 对比 | 目录
2026-01-29 zss
不合格处理状态新增默认0,危险审批消息通知跳转路径修改
747dc6 对比 | 目录
2026-01-29 maven
yys 修改注入方式
896dd1 对比 | 目录
2026-01-29 maven
Merge remote-tracking branch 'origin/dev_New' into dev_New
61b180 对比 | 目录
2026-01-29 maven
yys 开票台账修改,发货台账修改,发货审核修改
02bab5 对比 | 目录
2026-01-29 zss
增加定时任务1.每个月清理一次已读数据2.每15分钟刷新培训计划状态
1a4f30 对比 | 目录
2026-01-29 zss
Merge remote-tracking branch 'origin/dev_New' into dev_New
f98599 对比 | 目录
2026-01-29 zss
安全生产--安全培训考核
0b0268 对比 | 目录
2026-01-29 maven
Merge remote-tracking branch 'origin/dev_New' into dev_New
ff00c0 对比 | 目录
2026-01-29 maven
yys 采购未审核通过限制来票,付款操作
1c06c5 对比 | 目录
2026-01-29 liyong
refactor(sales): 添加交货日期字段 ,添加采购审核通过才进行质检或者直接入库逻辑
194185 对比 | 目录
2026-01-29 liyong
refactor(sales): 添加销售订单字段=交货日期
ed4450 对比 | 目录
2026-01-29 huminmin
Merge branch 'dev_New' of http://114.132.189.42:9002/r/product-inventory-ma...
0b74a2 对比 | 目录
2026-01-29 huminmin
优化员工台账编辑
de3696 对比 | 目录
2026-01-29 gongchunyi
fix: 岗位查询判处处理
803c44 对比 | 目录
已添加26个文件
已修改50个文件
2496 ■■■■ 文件已修改
src/main/java/com/ruoyi/ScheduleTask.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/mapper/AccountExpenseMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/mapper/AccountIncomeMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/controller/HomeController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/dto/IncomeExpenseAnalysisDto.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/dto/MonthlyExpenditureDto.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/dto/MonthlyIncomeDto.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/HomeService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java 381 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/SalesLedgerProductionAccountingController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductOrderDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/SalesLedgerProductionAccountingDto.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/SalesLedgerProductionAccountingMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/SalesLedgerProductionAccountingService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/SalesLedgerProductionAccountingServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/mapper/PurchaseLedgerMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityUnqualifiedController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java 144 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/dto/SafeTrainingDetailsDto.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/dto/SafeTrainingDto.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/mapper/SafeTrainingDetailsMapper.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/mapper/SafeTrainingFileMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/mapper/SafeTrainingMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/pojo/SafeTraining.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/pojo/SafeTrainingDetails.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/pojo/SafeTrainingFile.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/service/SafeTrainingDetailsService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/service/SafeTrainingFileService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/service/SafeTrainingService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/service/impl/SafeTrainingDetailsServiceImpl.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/service/impl/SafeTrainingFileServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/service/impl/SafeTrainingServiceImpl.java 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLedgerProductDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/CommonFileServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/InvoiceLedgerServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/AccountExpenseMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/AccountIncomeMapper.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductOrderMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductMainMapper.xml 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/SalesLedgerProductionAccountingMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/safe/SafeAccidentMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/safe/SafeTrainingDetailsMapper.xml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/safe/SafeTrainingFileMapper.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/safe/SafeTrainingMapper.xml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerMapper.xml 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/ShippingInfoMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/safe-training-details.docx 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/safe-training.docx 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ScheduleTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.project.system.domain.SysNotice;
import com.ruoyi.project.system.mapper.SysNoticeMapper;
import com.ruoyi.safe.mapper.SafeTrainingMapper;
import com.ruoyi.safe.pojo.SafeTraining;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Component
//定时任务汇总
public class ScheduleTask {
    @Autowired
    private SafeTrainingMapper safeTrainingMapper;
    @Autowired
    private SysNoticeMapper noticeMapper;
    //定时任务(15分钟执行一次--判断培训计划数据,状态做变更)
    @Scheduled(cron = "0 0/15 * * * ?")
    public void testScheduleTask() {
        List<SafeTraining> safeTrainings = safeTrainingMapper.selectList(Wrappers.<SafeTraining>lambdaQuery().ne(SafeTraining::getState, 2));
        if (safeTrainings.size() > 0) {
            for (SafeTraining safeTraining : safeTrainings) {
                //根据时间判断培训状态
                String trainingDate = safeTraining.getTrainingDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                LocalDateTime openingTime = LocalDateTime.parse((trainingDate + safeTraining.getOpeningTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
                LocalDateTime endTime = LocalDateTime.parse((trainingDate + safeTraining.getEndTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
                if (LocalDateTime.now().isBefore(openingTime)) {
                    //未开始
                    safeTraining.setState(0);
                } else if (LocalDateTime.now().isAfter(endTime)) {
                    //已结束
                    safeTraining.setState(2);
                } else {
                    //进行中
                    safeTraining.setState(1);
                }
                safeTrainingMapper.updateById(safeTraining);
            }
        }
    }
    //已读数据做一个定时任务(每月1号1点清理一次上个月已读数据)
    @Scheduled(cron = "0 0 1 1 * ?")
    public void cleanReadData() {
        noticeMapper.delete(Wrappers.<SysNotice>lambdaQuery()
                .eq(SysNotice::getStatus,"1")
                .lt(SysNotice::getCreateTime, LocalDateTime.now()));
    }
}
src/main/java/com/ruoyi/account/mapper/AccountExpenseMapper.java
@@ -10,6 +10,7 @@
import com.ruoyi.dto.DateQueryDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.home.dto.IncomeExpenseAnalysisDto;
import java.math.BigDecimal;
import java.util.List;
@@ -24,4 +25,8 @@
    List<AccountDto2> report(@Param("dateQueryDto") DateQueryDto dateQueryDto);
    BigDecimal report1(@Param("dateQueryDto") DateQueryDto dateQueryDto, @Param("dictValue") String dictValue);
    List<IncomeExpenseAnalysisDto> selectAccountExpenseStats(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("dateFormat") String dateFormat);
    List<com.ruoyi.dto.MapDto> selectExpenseComposition(@Param("startDate") String startDate, @Param("endDate") String endDate);
}
src/main/java/com/ruoyi/account/mapper/AccountIncomeMapper.java
@@ -7,6 +7,7 @@
import com.ruoyi.account.pojo.AccountFile;
import com.ruoyi.account.pojo.AccountIncome;
import com.ruoyi.dto.DateQueryDto;
import com.ruoyi.home.dto.IncomeExpenseAnalysisDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -23,4 +24,7 @@
    List<AccountDto2> report(@Param("dateQueryDto") DateQueryDto dateQueryDto);
    BigDecimal report1(@Param("dateQueryDto") DateQueryDto dateQueryDto, @Param("dictValue") String dictValue);
    List<IncomeExpenseAnalysisDto> selectIncomeStats(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("dateFormat") String dateFormat);
}
src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
@@ -1,6 +1,7 @@
package com.ruoyi.approve.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -12,20 +13,21 @@
import com.ruoyi.approve.service.IApproveNodeService;
import com.ruoyi.approve.vo.ApproveProcessVO;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.mapper.DeviceRepairMapper;
import com.ruoyi.device.pojo.DeviceRepair;
import com.ruoyi.other.service.impl.TempFileServiceImpl;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.project.system.service.ISysNoticeService;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.mapper.SalesQuotationMapper;
import com.ruoyi.sales.mapper.SalesQuotationProductMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.purchase.service.impl.PurchaseLedgerServiceImpl;
import com.ruoyi.sales.mapper.*;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.pojo.SalesQuotation;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
@@ -42,7 +44,7 @@
import java.util.List;
@Service
@RequiredArgsConstructor
//@RequiredArgsConstructor
public class ApproveNodeServiceImpl extends ServiceImpl<ApproveNodeMapper, ApproveNode> implements IApproveNodeService {
    @Autowired
@@ -73,7 +75,13 @@
    @Autowired
    private CommonFileServiceImpl commonFileService;
    @Autowired
    private StockUtils stockUtils;
    @Autowired
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Autowired
    private PurchaseLedgerServiceImpl purchaseLedgerServiceImpl;
    public ApproveProcess getApproveById(String id) {
@@ -115,12 +123,14 @@
        LambdaQueryWrapper<ApproveNode> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApproveNode::getApproveProcessId, id);
        queryWrapper.eq(ApproveNode::getDeleteFlag, 0);
        queryWrapper.eq(ApproveNode::getApproveNodeStatus, 0);
        List<ApproveNode> list = list(queryWrapper);
        // æŒ‰ç…§ approveNodeOrder å­—段升序排序
        list.sort(Comparator.comparingInt(ApproveNode::getApproveNodeOrder));
        LambdaQueryWrapper<ApproveProcess> approveProcessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveId, id)
                .eq(ApproveProcess::getApproveDelete, 0)
                .eq(ApproveProcess::getApproveStatus, 0)
                .last("limit 1");
        ApproveProcess approveProcess = approveProcessMapper.selectOne(approveProcessLambdaQueryWrapper);
        if(approveProcess != null && approveProcess.getApproveStatus() == 3){
@@ -146,6 +156,7 @@
        LambdaQueryWrapper<ApproveProcess> approveProcessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveId, approveNode.getApproveProcessId())
                .eq(ApproveProcess::getApproveDelete, 0)
                .eq(ApproveProcess::getApproveStatus, 0)
                .last("limit 1");
        ApproveProcess approveProcess = approveProcessMapper.selectOne(approveProcessLambdaQueryWrapper);
        if(approveProcess == null) throw new RuntimeException("审批不存在");
@@ -153,6 +164,7 @@
        approveNodeLambdaQueryWrapper.eq(ApproveNode::getApproveProcessId, approveNode.getApproveProcessId())
                .eq(ApproveNode::getApproveNodeOrder, approveNode.getApproveNodeOrder() + 1)
                .eq(ApproveNode::getDeleteFlag, 0)
                .eq(ApproveNode::getApproveNodeStatus, 0)
                .last("limit 1");
        ApproveNode approveNode1 = approveNodeMapper.selectOne(approveNodeLambdaQueryWrapper);
        approveProcess.setApproveStatus(status);
@@ -186,6 +198,17 @@
                if (status.equals(2)) {
                    // åŒæ„
                    purchaseLedger.setApprovalStatus(3);
                    List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new QueryWrapper<SalesLedgerProduct>()
                            .lambda().eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()).eq(SalesLedgerProduct::getType, 2));
                    for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
                        // è´¨æ£€
                        if (salesLedgerProduct.getIsChecked()) {
                            purchaseLedgerServiceImpl.addQualityInspect(purchaseLedger, salesLedgerProduct);
                        }else {
                            //直接入库
                            stockUtils.addStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId());
                        }
                    }
                } else if (status.equals(3)) {
                    // æ‹’绝
                    purchaseLedger.setApprovalStatus(4);
@@ -222,7 +245,7 @@
                if(status.equals(2)){
                    shippingInfo.setStatus("审核通过");
                }else if(status.equals(3)){
                    shippingInfo.setType("审核拒绝");
                    shippingInfo.setStatus("审核拒绝");
                }else if(status.equals(1)){
                    shippingInfo.setStatus("审核中");
                }
@@ -248,10 +271,17 @@
                        .eq(ApproveProcess::getApproveId, approveNode.getApproveProcessId())).get(0);
                if (approveProcess.getApproveUserIds().split(",").length > nodeOrder){
                    String id = approveProcess.getApproveUserIds().split(",")[nodeOrder];
                    sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                            approveNode.getApproveProcessId()+"流程编号的审批需要您审核!!!!!",
                            Arrays.asList(Long.valueOf(id)),
                            "/collaborativeApproval/approvalProcess?approveType="+approveProcess.getApproveType()+"&approveId="+approveNode.getApproveProcessId());
                    if (approveProcess.getApproveType()==8){
                        sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                                approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!",
                                Arrays.asList(Long.valueOf(id)),
                                "/safeProduction/safeWorkApproval?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId());
                    }else {
                        sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                                approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!",
                                Arrays.asList(Long.valueOf(id)),
                                "/collaborativeApproval/approvalProcess?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId());
                    }
                }
                break;
            case 2:
@@ -294,7 +324,9 @@
            case 6:
                return "报价审批";
            case 7:
                return "出库审批";
                return "发货审批";
            case 8:
                return "危险作业审批";
        }
        return null;
    }
src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
@@ -51,22 +51,30 @@
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
//@RequiredArgsConstructor
public class ApproveProcessServiceImpl extends ServiceImpl<ApproveProcessMapper, ApproveProcess> implements IApproveProcessService {
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
    private final StringRedisTemplate redisTemplate;
    private final DailyRedisCounter dailyRedisCounter;
    private final SysDeptMapper sysDeptMapper;
    private final IApproveNodeService approveNodeService;
    private final SysUserMapper sysUserMapper;
    private final ApproveProcessMapper approveProcessMapper;
    private final TempFileServiceImpl tempFileService;
    private final CommonFileMapper commonFileMapper;
    private final CommonFileServiceImpl commonFileService;
    private final ISysNoticeService sysNoticeService;
    @Autowired
    private  StringRedisTemplate redisTemplate;
    @Autowired
    private  DailyRedisCounter dailyRedisCounter;
    @Autowired
    private  SysDeptMapper sysDeptMapper;
    @Autowired
    private  IApproveNodeService approveNodeService;
    @Autowired
    private  SysUserMapper sysUserMapper;
    @Autowired
    private  ApproveProcessMapper approveProcessMapper;
    @Autowired
    private  TempFileServiceImpl tempFileService;
    @Autowired
    private  CommonFileMapper commonFileMapper;
    @Autowired
    private  CommonFileServiceImpl commonFileService;
    @Autowired
    private  ISysNoticeService sysNoticeService;
    @Override
    public void addApprove(ApproveProcessVO approveProcessVO) throws Exception {
@@ -126,10 +134,17 @@
        tempFileService.migrateTempFilesToFormal(approveProcess.getId(), approveProcessVO.getTempFileIds(), FileNameType.ApproveProcess.getValue());
        /*消息通知*/
        String id = approveProcessVO.getApproveUserIds().split(",")[0];
        sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcessVO.getApproveType()),
                approveID + "流程编号的审批需要您审核!!!!!",
                Arrays.asList(Long.valueOf(id)),
                "/collaborativeApproval/approvalProcess?approveType=" + approveProcessVO.getApproveType() + "&approveId=" + approveID);
        if (approveProcess.getApproveType()==8){
            sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                    approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!",
                    Arrays.asList(Long.valueOf(id)),
                    "/safeProduction/safeWorkApproval?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId());
        }else {
            sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                    approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!",
                    Arrays.asList(Long.valueOf(id)),
                    "/collaborativeApproval/approvalProcess?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId());
        }
    }
    @Override
@@ -239,7 +254,8 @@
        return one;
    }
    private final ApproveNodeMapper approveNodeMapper;
    @Autowired
    private ApproveNodeMapper approveNodeMapper;
    // æŠ¥ä»·å®¡æ‰¹ç¼–辑审核人
    public void updateApproveUser(ApproveGetAndUpdateVo approveGetAndUpdateVo) {
@@ -276,10 +292,17 @@
        approveNodeService.initApproveNodes(approveGetAndUpdateVo.getApproveUserIds(), approveProcess.getApproveId(), approveProcess.getTenantId());
        /*消息通知*/
        String id = approveProcess.getApproveUserIds().split(",")[0];
        sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!",
                Arrays.asList(Long.valueOf(id)),
                "/collaborativeApproval/approvalProcess?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId());
        if (approveProcess.getApproveType()==8){
            sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                    approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!",
                    Arrays.asList(Long.valueOf(id)),
                    "/safeProduction/safeWorkApproval?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId());
        }else {
            sysNoticeService.simpleNoticeByUser(approveProcessType(approveProcess.getApproveType()),
                    approveProcess.getApproveId() + "流程编号的审批需要您审核!!!!!",
                    Arrays.asList(Long.valueOf(id)),
                    "/collaborativeApproval/approvalProcess?approveType=" + approveProcess.getApproveType() + "&approveId=" + approveProcess.getApproveId());
        }
    }
src/main/java/com/ruoyi/home/controller/HomeController.java
@@ -132,6 +132,41 @@
        return AjaxResult.success(list);
    }
    @GetMapping("/incomeExpenseAnalysis")
    @ApiOperation("支收对比分析")
    public AjaxResult incomeExpenseAnalysis(@RequestParam(value = "type", defaultValue = "1") Integer type) {
        List<Map<String, Object>> result = homeService.incomeExpenseAnalysis(type);
        return AjaxResult.success(result);
    }
    @GetMapping("/profitTrendAnalysis")
    @ApiOperation("利润趋势分析")
    public AjaxResult profitTrendAnalysis(){
        List<MapDto> list = homeService.profitTrendAnalysis();
        return AjaxResult.success(list);
    }
    @GetMapping("/expenseCompositionAnalysis")
    @ApiOperation("构成分析")
    public AjaxResult expenseCompositionAnalysis(@RequestParam(value = "type", defaultValue = "1") Integer type) {
        List<MapDto> list = homeService.expenseCompositionAnalysis(type);
        return AjaxResult.success(list);
    }
    @GetMapping("/monthlyIncome")
    @ApiOperation("月度收入")
    public AjaxResult monthlyIncome(){
        MonthlyIncomeDto dto = homeService.monthlyIncome();
        return AjaxResult.success(dto);
    }
   @GetMapping("/monthlyExpenditure")
   @ApiOperation("月度支出")
   public AjaxResult monthlyExpenditure(){
        MonthlyExpenditureDto dto = homeService.monthlyExpenditure();
        return AjaxResult.success(dto);
   }
    /********************************************************营销采购类**************************************************/
    @GetMapping("/business")
    @Log(title = "销售-采购-库存数据", businessType = BusinessType.OTHER)
src/main/java/com/ruoyi/home/dto/IncomeExpenseAnalysisDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.home.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
 * æ”¯æ”¶å¯¹æ¯”分析统计 DTO
 */
@Data
public class IncomeExpenseAnalysisDto {
    /**
     * æ—¥æœŸ
     */
    private String dateStr;
    /**
     * é‡‘额
     */
    private BigDecimal amount;
}
src/main/java/com/ruoyi/home/dto/MonthlyExpenditureDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.home.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
 * æœˆåº¦æ”¯å‡º DTO
 */
@Data
public class MonthlyExpenditureDto {
    /**
     * æœˆåº¦æ€»æ”¯å‡º
     */
    private BigDecimal monthlyExpenditure = BigDecimal.ZERO;
    /**
     * ä»˜æ¬¾çއ (百分比)
     */
    private String paymentRate = "0.00";
    /**
     * æ¯›åˆ©æ¶¦ (金额)
     */
    private BigDecimal grossProfit = BigDecimal.ZERO;
    /**
     * åˆ©æ¶¦çއ (百分比)
     */
    private String profitMarginRate = "0.00";
}
src/main/java/com/ruoyi/home/dto/MonthlyIncomeDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.home.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
 * æœˆåº¦æ”¶å…¥ DTO
 */
@Data
public class MonthlyIncomeDto {
    /**
     * æœˆåº¦æ”¶å…¥
     */
    private BigDecimal monthlyIncome = BigDecimal.ZERO;
    /**
     * å›žæ¬¾çއ
     */
    private String collectionRate = "0.00";
    /**
     * é€¾æœŸæ•°
     */
    private BigDecimal overdueNum = BigDecimal.ZERO;
    /**
     * é€¾æœŸçއ
     */
    private String overdueRate = "0.00";
}
src/main/java/com/ruoyi/home/service/HomeService.java
@@ -54,4 +54,14 @@
    List<Map<String, Object>> productInOutAnalysis(Integer type);
    List<MapDto> productTurnoverDays();
    List<Map<String, Object>> incomeExpenseAnalysis(Integer type);
    List<MapDto> profitTrendAnalysis();
    List<MapDto> expenseCompositionAnalysis(Integer type);
    MonthlyIncomeDto monthlyIncome();
    MonthlyExpenditureDto monthlyExpenditure();
}
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -3,7 +3,10 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.mapper.AccountIncomeMapper;
import com.ruoyi.account.pojo.AccountExpense;
import com.ruoyi.home.mapper.HomeMapper;
import com.ruoyi.account.mapper.AccountExpenseMapper;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.basic.mapper.CustomerMapper;
@@ -1098,4 +1101,382 @@
    public List<MapDto> productTurnoverDays() {
        return homeMapper.productTurnoverDays();
    }
    @Autowired
    private AccountExpenseMapper accountExpenseMapper;
    @Autowired
    private AccountIncomeMapper accountIncomeMapper;
    public List<Map<String, Object>> incomeExpenseAnalysis(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate;
        String dateFormat;
        DateTimeFormatter formatter;
        List<String> xAxis = new ArrayList<>();
        // 1. æ—¶é—´èŒƒå›´ & X è½´
        if (type == 3) {
            // æœ¬å­£åº¦ï¼ˆæŒ‰æœˆï¼‰
            Month currentMonth = today.getMonth();
            Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
            startDate = today.withMonth(firstMonthOfQuarter.getValue())
                    .with(TemporalAdjusters.firstDayOfMonth());
            endDate = startDate.plusMonths(2)
                    .with(TemporalAdjusters.lastDayOfMonth());
            dateFormat = "%Y-%m";
            formatter = DateTimeFormatter.ofPattern("yyyy-MM");
            LocalDate tmp = startDate;
            while (!tmp.isAfter(endDate)) {
                xAxis.add(tmp.format(formatter));
                tmp = tmp.plusMonths(1);
            }
        } else {
            // å‘¨ / æœˆï¼ˆæŒ‰å¤©ï¼‰
            dateFormat = "%Y-%m-%d";
            formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            if (type == 1) {
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
            } else {
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
            }
            LocalDate tmp = startDate;
            while (!tmp.isAfter(endDate)) {
                xAxis.add(tmp.format(formatter));
                tmp = tmp.plusDays(1);
            }
        }
        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // 2. æŸ¥è¯¢æ•°æ®
        List<IncomeExpenseAnalysisDto> incomeList = accountIncomeMapper.selectIncomeStats(startStr, endStr, dateFormat);
//        List<IncomeExpenseAnalysisDto> purchaseList =
//                purchaseLedgerMapper.selectPurchaseStats(startStr, endStr, dateFormat);
        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr, dateFormat);
        // 3. è½¬ Map(自动合并)
        Map<String, BigDecimal> incomeMap = incomeList.stream()
                .collect(Collectors.toMap(
                        IncomeExpenseAnalysisDto::getDateStr,
                        IncomeExpenseAnalysisDto::getAmount,
                        BigDecimal::add));
//        Map<String, BigDecimal> purchaseMap = purchaseList.stream()
//                .collect(Collectors.toMap(
//                        IncomeExpenseAnalysisDto::getDateStr,
//                        IncomeExpenseAnalysisDto::getAmount,
//                        BigDecimal::add));
        Map<String, BigDecimal> expenseMap = expenseList.stream()
                .collect(Collectors.toMap(
                        IncomeExpenseAnalysisDto::getDateStr,
                        IncomeExpenseAnalysisDto::getAmount,
                        BigDecimal::add));
        // 4. ç»„装返回
        List<Map<String, Object>> result = new ArrayList<>();
        for (String dateStr : xAxis) {
            Map<String, Object> item = new HashMap<>();
            item.put("date", dateStr);
            // æ”¶å…¥
            BigDecimal income = incomeMap.getOrDefault(dateStr, BigDecimal.ZERO);
            item.put("income", income.setScale(2, RoundingMode.HALF_UP));
            // æ”¯å‡º = é‡‡è´­ + è´¢åŠ¡æ”¯å‡º
//            BigDecimal purchase = purchaseMap.getOrDefault(dateStr, BigDecimal.ZERO);
            BigDecimal expense = expenseMap.getOrDefault(dateStr, BigDecimal.ZERO);
//            BigDecimal totalExpense = purchase.add(expense);
            BigDecimal totalExpense = expense;
            item.put("expense", totalExpense.setScale(2, RoundingMode.HALF_UP));
            result.add(item);
        }
        return result;
    }
    @Override
    public List<MapDto> profitTrendAnalysis() {
        LocalDate today = LocalDate.now();
        LocalDate startDate = today.minusMonths(11).with(TemporalAdjusters.firstDayOfMonth());
        LocalDate endDate = today.with(TemporalAdjusters.lastDayOfMonth());
        String dateFormat = "%Y-%m";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        List<String> months = new ArrayList<>();
        LocalDate temp = startDate;
        while (!temp.isAfter(endDate)) {
            months.add(temp.format(formatter));
            temp = temp.plusMonths(1);
        }
        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//        List<IncomeExpenseAnalysisDto> incomeList = salesLedgerMapper.selectIncomeStats(startStr, endStr, dateFormat);
        List<IncomeExpenseAnalysisDto> incomeList = new ArrayList<>();
        List<IncomeExpenseAnalysisDto> purchaseList = purchaseLedgerMapper.selectPurchaseStats(startStr, endStr, dateFormat);
        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr, dateFormat);
        Map<String, BigDecimal> incomeMap = incomeList.stream().collect(Collectors.toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
        Map<String, BigDecimal> purchaseMap = purchaseList.stream().collect(Collectors.toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
        Map<String, BigDecimal> expenseMap = expenseList.stream().collect(Collectors.toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
        List<MapDto> result = new ArrayList<>();
        for (String month : months) {
            MapDto dto = new MapDto();
            dto.setName(month);
            BigDecimal income = incomeMap.getOrDefault(month, BigDecimal.ZERO);
            BigDecimal purchase = purchaseMap.getOrDefault(month, BigDecimal.ZERO);
             income = BigDecimal.ZERO;
            BigDecimal expense = expenseMap.getOrDefault(month, BigDecimal.ZERO);
            BigDecimal totalExpense = purchase.add(expense);
            BigDecimal profit = income.subtract(totalExpense);
            dto.setValue(profit.setScale(2, RoundingMode.HALF_UP).toString());
            result.add(dto);
        }
        return result;
    }
    @Override
    public List<MapDto> expenseCompositionAnalysis(Integer type) {
        List<MapDto> result = new ArrayList<>();
        if (type == 2) {
            List<MapDto> customerList = salesLedgerMapper.selectCustomerSalesComposition();
            if (customerList != null) {
                result.addAll(customerList);
            }
        } else {
            BigDecimal rawMaterialAmount = salesLedgerProductMapper.selectRawMaterialExpense();
            MapDto rawMaterialDto = new MapDto();
            rawMaterialDto.setName("原材料");
            rawMaterialDto.setValue(rawMaterialAmount != null ? rawMaterialAmount.toString() : "0");
            result.add(rawMaterialDto);
            List<MapDto> expenseList = accountExpenseMapper.selectExpenseComposition(null, null);
            if (expenseList != null) {
                result.addAll(expenseList);
            }
        }
        BigDecimal total = BigDecimal.ZERO;
        for (MapDto dto : result) {
            if (dto.getValue() != null) {
                total = total.add(new BigDecimal(dto.getValue()));
            }
        }
        if (total.compareTo(BigDecimal.ZERO) > 0) {
            for (MapDto dto : result) {
                if (dto.getValue() != null) {
                    BigDecimal val = new BigDecimal(dto.getValue());
                    String rate = val.divide(total, 4, RoundingMode.HALF_UP)
                            .multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP)
                            .toString();
                    dto.setRate(rate);
                } else {
                    dto.setRate("0.00");
                }
            }
        } else {
            for (MapDto dto : result) {
                dto.setRate("0.00");
            }
        }
        return result;
    }
    @Override
    public MonthlyIncomeDto monthlyIncome() {
        MonthlyIncomeDto dto = new MonthlyIncomeDto();
        LocalDate now = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(now);
        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
        LambdaQueryWrapper<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SalesLedgerProduct::getType, 1);
        wrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
        wrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(products)) {
            return dto;
        }
        BigDecimal collected = products.stream()
                .map(SalesLedgerProduct::getInvoiceTotal)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setMonthlyIncome(collected);
        BigDecimal overdue = products.stream()
                .map(SalesLedgerProduct::getPendingInvoiceTotal)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setOverdueNum(overdue);
        BigDecimal total = collected.add(overdue);
        if (total.compareTo(BigDecimal.ZERO) > 0) {
            String collectionRate = collected.divide(total, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setCollectionRate(collectionRate);
            String overdueRate = overdue.divide(total, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setOverdueRate(overdueRate);
        }
        return dto;
    }
    public MonthlyExpenditureDto monthlyExpenditure() {
        MonthlyExpenditureDto dto = new MonthlyExpenditureDto();
        //  å½“月时间范围
        LocalDate now = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(now);
        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
        //  é‡‡è´­å°è´¦ï¼ˆtype = 2)
        LambdaQueryWrapper<SalesLedgerProduct> purchaseWrapper = new LambdaQueryWrapper<>();
        purchaseWrapper.eq(SalesLedgerProduct::getType, 2);
        purchaseWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
        purchaseWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
        List<SalesLedgerProduct> purchaseProducts =
                salesLedgerProductMapper.selectList(purchaseWrapper);
        BigDecimal rawMaterialCost = BigDecimal.ZERO;   // åŽŸææ–™æˆæœ¬
        BigDecimal paidAmount = BigDecimal.ZERO;        // å·²ä»˜æ¬¾é‡‘额
        BigDecimal pendingAmount = BigDecimal.ZERO;     // å¾…付款金额
        if (!CollectionUtils.isEmpty(purchaseProducts)) {
            for (SalesLedgerProduct p : purchaseProducts) {
                if (p.getTaxInclusiveTotalPrice() != null) {
                    rawMaterialCost = rawMaterialCost.add(p.getTaxInclusiveTotalPrice());
                }
                if (p.getTicketsTotal() != null) {
                    paidAmount = paidAmount.add(p.getTicketsTotal());
                }
                if (p.getPendingTicketsTotal() != null) {
                    pendingAmount = pendingAmount.add(p.getPendingTicketsTotal());
                }
            }
        }
        //  å…¶ä»–费用
        LambdaQueryWrapper<AccountExpense> expenseWrapper = new LambdaQueryWrapper<>();
        expenseWrapper.ge(AccountExpense::getExpenseDate,
                java.sql.Date.valueOf(currentMonth.atDay(1)));
        expenseWrapper.le(AccountExpense::getExpenseDate,
                java.sql.Date.valueOf(currentMonth.atEndOfMonth()));
        List<AccountExpense> expenses =
                accountExpenseMapper.selectList(expenseWrapper);
        BigDecimal otherExpense = BigDecimal.ZERO;
        if (!CollectionUtils.isEmpty(expenses)) {
            for (AccountExpense e : expenses) {
                if (e.getExpenseMoney() != null) {
                    otherExpense = otherExpense.add(e.getExpenseMoney());
                }
            }
        }
        //  æœˆåº¦æ€»æ”¯å‡º
//        BigDecimal monthlyExpenditure = rawMaterialCost.add(otherExpense);
        BigDecimal monthlyExpenditure = otherExpense;
        dto.setMonthlyExpenditure(monthlyExpenditure);
        // å·²ä»˜æ¬¾ Ã·ï¼ˆå·²ä»˜æ¬¾ + å¾…付款)
        BigDecimal totalPayable = paidAmount.add(pendingAmount);
        if (totalPayable.compareTo(BigDecimal.ZERO) > 0) {
            String paymentRate = paidAmount
                    .divide(totalPayable, 4, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setPaymentRate(paymentRate);
        }
        // å”®å°è´¦ï¼ˆtype = 1)
        LambdaQueryWrapper<SalesLedgerProduct> salesWrapper = new LambdaQueryWrapper<>();
        salesWrapper.eq(SalesLedgerProduct::getType, 1);
        salesWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
        salesWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
        List<SalesLedgerProduct> salesProducts =
                salesLedgerProductMapper.selectList(salesWrapper);
        BigDecimal revenue = BigDecimal.ZERO;
        if (!CollectionUtils.isEmpty(salesProducts)) {
            for (SalesLedgerProduct s : salesProducts) {
                if (s.getInvoiceAmount() != null) {
                    revenue = revenue.add(s.getInvoiceAmount());
                }
            }
        }
        //  æ¯›åˆ©æ¶¦ & åˆ©æ¶¦çއ
        if (revenue.compareTo(BigDecimal.ZERO) > 0) {
            // æ¯›åˆ©æ¶¦ = é”€å”®æ”¶å…¥ - åŽŸææ–™æˆæœ¬
            BigDecimal grossProfit = revenue.subtract(rawMaterialCost);
            dto.setGrossProfit(grossProfit);
            // åˆ©æ¶¦çއ = (销售收入 - æœˆåº¦æ€»æ”¯å‡º) / é”€å”®æ”¶å…¥
            BigDecimal profit = revenue.subtract(monthlyExpenditure);
            String profitMarginRate = profit
                    .divide(revenue, 4, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setProfitMarginRate(profitMarginRate);
        }
        return dto;
    }
}
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -1,6 +1,7 @@
package com.ruoyi.procurementrecord.utils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.stock.dto.StockInRecordDto;
@@ -101,14 +102,16 @@
        StockInRecord one = stockInRecordService.getOne(new QueryWrapper<StockInRecord>()
                .lambda().eq(StockInRecord::getRecordId, recordId)
                .eq(StockInRecord::getRecordType, recordType));
        stockInRecordService.batchDelete(Collections.singletonList(one.getId()));
        if (ObjectUtils.isNotEmpty(one)) {
            stockInRecordService.batchDelete(Collections.singletonList(one.getId()));
        }
    }
    public void deleteStockOutRecord(Long recordId, String recordType) {
        StockOutRecord one = stockOutRecordService.getOne(new QueryWrapper<StockOutRecord>()
                .lambda().eq(StockOutRecord::getRecordId, recordId)
                .eq(StockOutRecord::getRecordType, recordType));
        stockOutRecordService.batchDelete(Collections.singletonList(one.getId()));
        if (ObjectUtils.isNotEmpty(one)) {
            stockOutRecordService.batchDelete(Collections.singletonList(one.getId()));
        }
    }
}
src/main/java/com/ruoyi/production/controller/SalesLedgerProductionAccountingController.java
@@ -8,11 +8,14 @@
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.dto.ProductOrderDto;
import com.ruoyi.production.dto.SalesLedgerProductionAccountingDto;
import com.ruoyi.production.pojo.SalesLedgerProductionAccounting;
import com.ruoyi.production.service.SalesLedgerProductionAccountingService;
import com.ruoyi.production.service.impl.SalesLedgerProductionAccountingServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -33,7 +36,7 @@
public class SalesLedgerProductionAccountingController extends BaseController {
    @Autowired
    private SalesLedgerProductionAccountingServiceImpl salesLedgerProductionAccountingService;
    private SalesLedgerProductionAccountingService salesLedgerProductionAccountingService;
    @GetMapping("/listPage")
    @ApiOperation("生产核算-分页查询")
@@ -53,5 +56,16 @@
        ExcelUtil<SalesLedgerProductionAccountingDto> util = new ExcelUtil<SalesLedgerProductionAccountingDto>(SalesLedgerProductionAccountingDto.class);
        util.exportExcel(response, list, "生产核算数据");
    }
//----------------------------------------------------------------------------------------------------------------------------------------------------
    @GetMapping("/page")
    @ApiModelProperty("查询工人生产工资信息")
    public R pageProductionAccounting(SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page){
        return R.ok(salesLedgerProductionAccountingService.pageProductionAccounting(salesLedgerProductionAccountingDto, page));
    }
    @GetMapping("/listProductionDetails")
    @ApiModelProperty("查询工人生产工资信息")
    public R listProductionDetails(SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page){
        return R.ok(salesLedgerProductionAccountingService.listProductionDetails(salesLedgerProductionAccountingDto,page));
    }
}
src/main/java/com/ruoyi/production/dto/ProductOrderDto.java
@@ -43,4 +43,7 @@
    @ApiModelProperty(value = "BOM编号")
    @Excel(name = "BOM编号")
    private String bomNo;
    @ApiModelProperty(value = "交期偏差")
    private Integer deliveryDaysDiff;
}
src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java
@@ -7,6 +7,7 @@
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@@ -46,4 +47,11 @@
    //销售合同号
    @Excel(name = "销售合同号")
    private String salesContractNo;
    private LocalDate scheduleDate;
    private String schedulingUserName;
    private String customerName;
    private String process;
    private BigDecimal workHours;
    private BigDecimal wages;
}
src/main/java/com/ruoyi/production/dto/SalesLedgerProductionAccountingDto.java
@@ -70,4 +70,10 @@
    @ApiModelProperty(value = "结束时间")
    private String entryDateEnd;
    private BigDecimal outputNum;
    private BigDecimal outputRate;
}
src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.production.dto.ProductionProductMainDto;
import com.ruoyi.production.dto.SalesLedgerProductionAccountingDto;
import com.ruoyi.production.pojo.ProductOrder;
import com.ruoyi.production.pojo.ProductionProductMain;
import org.apache.ibatis.annotations.Mapper;
@@ -27,4 +28,6 @@
     * @return
     */
    ProductOrder getOrderByMainId(@Param("productMainId") Long productMainId);
    IPage<ProductionProductMainDto> listProductionDetails(SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page);
}
src/main/java/com/ruoyi/production/mapper/SalesLedgerProductionAccountingMapper.java
@@ -17,4 +17,5 @@
    IPage<SalesLedgerProductionAccountingDto> listPage(Page page,@Param("salesLedgerDto") SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto);
    IPage<SalesLedgerProductionAccountingDto> pageProductionAccounting(Page page, @Param("ew") SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto);
}
src/main/java/com/ruoyi/production/service/SalesLedgerProductionAccountingService.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.dto.ProductionProductMainDto;
import com.ruoyi.production.dto.SalesLedgerProductionAccountingDto;
import com.ruoyi.production.pojo.SalesLedgerProductionAccounting;
@@ -16,4 +17,7 @@
    IPage<SalesLedgerProductionAccountingDto> listPage(Page page, SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto);
    IPage<SalesLedgerProductionAccountingDto> pageProductionAccounting(SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page);
    IPage<ProductionProductMainDto> listProductionDetails(SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page);
}
src/main/java/com/ruoyi/production/service/impl/SalesLedgerProductionAccountingServiceImpl.java
@@ -3,7 +3,9 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.production.dto.ProductionProductMainDto;
import com.ruoyi.production.dto.SalesLedgerProductionAccountingDto;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.mapper.SalesLedgerProductionAccountingMapper;
import com.ruoyi.production.pojo.SalesLedgerProductionAccounting;
import com.ruoyi.production.service.SalesLedgerProductionAccountingService;
@@ -23,7 +25,8 @@
public class SalesLedgerProductionAccountingServiceImpl extends ServiceImpl<SalesLedgerProductionAccountingMapper, SalesLedgerProductionAccounting> implements SalesLedgerProductionAccountingService {
  
    private final SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    private final ProductionProductMainMapper productionProductMainMapper;
    @Override
    public IPage<SalesLedgerProductionAccountingDto> listPage(Page page, SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto) {
        IPage<SalesLedgerProductionAccountingDto> list = salesLedgerProductionAccountingMapper.listPage(page, salesLedgerProductionAccountingDto);
@@ -37,6 +40,17 @@
        return list;
    }
    @Override
    public IPage<SalesLedgerProductionAccountingDto> pageProductionAccounting(SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page) {
        // TODO ç®¡ç†å‘˜æŸ¥è¯¢æ‰€æœ‰ï¼Œå…¶ä»–人只能查自己
        return salesLedgerProductionAccountingMapper.pageProductionAccounting(page, salesLedgerProductionAccountingDto);
    }
    @Override
    public IPage<ProductionProductMainDto> listProductionDetails(SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page) {
        return productionProductMainMapper.listProductionDetails(salesLedgerProductionAccountingDto, page);
    }
    public static boolean isNumeric(String str) {
        if (str == null || str.isEmpty()) {
            return false;
src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java
@@ -1,19 +1,22 @@
package com.ruoyi.project.system.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.project.system.domain.SysDept;
/**
 * éƒ¨é—¨ç®¡ç† æ•°æ®å±‚
 *
 *
 * @author ruoyi
 */
@Mapper
public interface SysDeptMapper
{
    /**
     * æŸ¥è¯¢éƒ¨é—¨ç®¡ç†æ•°æ®
     *
     *
     * @param dept éƒ¨é—¨ä¿¡æ¯
     * @return éƒ¨é—¨ä¿¡æ¯é›†åˆ
     */
@@ -21,7 +24,7 @@
    /**
     * æ ¹æ®è§’色ID查询部门树信息
     *
     *
     * @param roleId è§’色ID
     * @param deptCheckStrictly éƒ¨é—¨æ ‘选择项是否关联显示
     * @return é€‰ä¸­éƒ¨é—¨åˆ—表
@@ -30,7 +33,7 @@
    /**
     * æ ¹æ®éƒ¨é—¨ID查询信息
     *
     *
     * @param deptId éƒ¨é—¨ID
     * @return éƒ¨é—¨ä¿¡æ¯
     */
@@ -38,7 +41,7 @@
    /**
     * æ ¹æ®ID查询所有子部门
     *
     *
     * @param deptId éƒ¨é—¨ID
     * @return éƒ¨é—¨åˆ—表
     */
@@ -46,7 +49,7 @@
    /**
     * æ ¹æ®ID查询所有子部门(正常状态)
     *
     *
     * @param deptId éƒ¨é—¨ID
     * @return å­éƒ¨é—¨æ•°
     */
@@ -54,7 +57,7 @@
    /**
     * æ˜¯å¦å­˜åœ¨å­èŠ‚ç‚¹
     *
     *
     * @param deptId éƒ¨é—¨ID
     * @return ç»“æžœ
     */
@@ -62,7 +65,7 @@
    /**
     * æŸ¥è¯¢éƒ¨é—¨æ˜¯å¦å­˜åœ¨ç”¨æˆ·
     *
     *
     * @param deptId éƒ¨é—¨ID
     * @return ç»“æžœ
     */
@@ -70,7 +73,7 @@
    /**
     * æ ¡éªŒéƒ¨é—¨åç§°æ˜¯å¦å”¯ä¸€
     *
     *
     * @param deptName éƒ¨é—¨åç§°
     * @param parentId çˆ¶éƒ¨é—¨ID
     * @return ç»“æžœ
@@ -79,7 +82,7 @@
    /**
     * æ–°å¢žéƒ¨é—¨ä¿¡æ¯
     *
     *
     * @param dept éƒ¨é—¨ä¿¡æ¯
     * @return ç»“æžœ
     */
@@ -87,7 +90,7 @@
    /**
     * ä¿®æ”¹éƒ¨é—¨ä¿¡æ¯
     *
     *
     * @param dept éƒ¨é—¨ä¿¡æ¯
     * @return ç»“æžœ
     */
@@ -95,14 +98,14 @@
    /**
     * ä¿®æ”¹æ‰€åœ¨éƒ¨é—¨æ­£å¸¸çŠ¶æ€
     *
     *
     * @param deptIds éƒ¨é—¨ID组
     */
    public void updateDeptStatusNormal(Long[] deptIds);
    /**
     * ä¿®æ”¹å­å…ƒç´ å…³ç³»
     *
     *
     * @param depts å­å…ƒç´ 
     * @return ç»“æžœ
     */
@@ -110,7 +113,7 @@
    /**
     * åˆ é™¤éƒ¨é—¨ç®¡ç†ä¿¡æ¯
     *
     *
     * @param deptId éƒ¨é—¨ID
     * @return ç»“æžœ
     */
src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
@@ -1,5 +1,6 @@
package com.ruoyi.project.system.service.impl;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -22,6 +23,7 @@
import com.ruoyi.project.system.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import com.ruoyi.project.system.domain.SysNotice;
import com.ruoyi.project.system.mapper.SysNoticeMapper;
@@ -210,4 +212,5 @@
        sysNotice.setTenantId(tenantId);
        return sysNotice;
    }
}
src/main/java/com/ruoyi/purchase/mapper/PurchaseLedgerMapper.java
@@ -7,6 +7,7 @@
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.home.dto.IncomeExpenseAnalysisDto;
import java.math.BigDecimal;
import java.util.List;
@@ -24,4 +25,8 @@
    IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, @Param("c") PurchaseLedgerDto purchaseLedger);
    List<PaymentRegistrationDto> getPaymentRegistrationDtoById(Long id);
    List<IncomeExpenseAnalysisDto> selectPurchaseStats(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("dateFormat") String dateFormat);
    BigDecimal selectTotalPurchaseAmount(@Param("startDate") String startDate, @Param("endDate") String endDate);
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -55,6 +55,7 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@@ -84,52 +85,71 @@
 * @date 2025-05-09
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PurchaseLedgerServiceImpl extends ServiceImpl<PurchaseLedgerMapper, PurchaseLedger> implements IPurchaseLedgerService {
    private final AccountExpenseService accountExpenseService;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
    @Autowired
    private  AccountExpenseService accountExpenseService;
    @Autowired
    private  PurchaseLedgerMapper purchaseLedgerMapper;
    private final SalesLedgerMapper salesLedgerMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    @Autowired
    private  SalesLedgerMapper salesLedgerMapper;
    @Autowired
    private  SalesLedgerProductMapper salesLedgerProductMapper;
    private final SysUserMapper userMapper;
    @Autowired
    private  SysUserMapper userMapper;
    private final TempFileMapper tempFileMapper;
    @Autowired
    private  TempFileMapper tempFileMapper;
    private final CommonFileMapper commonFileMapper;
    @Autowired
    private  CommonFileMapper commonFileMapper;
    private final SupplierManageMapper supplierManageMapper;
    @Autowired
    private  SupplierManageMapper supplierManageMapper;
    private final ProductMapper productMapper;
    @Autowired
    private  ProductMapper productMapper;
    private final ProductModelMapper productModelMapper;
    @Autowired
    private  ProductModelMapper productModelMapper;
    private final SysUserMapper sysUserMapper;
    @Autowired
    private  SysUserMapper sysUserMapper;
    private final TicketRegistrationMapper ticketRegistrationMapper;
    @Autowired
    private  TicketRegistrationMapper ticketRegistrationMapper;
    private final ProductRecordMapper productRecordMapper;
    @Autowired
    private  ProductRecordMapper productRecordMapper;
    private final PaymentRegistrationMapper paymentRegistrationMapper;
    private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    private final StringRedisTemplate redisTemplate;
    private final QualityInspectMapper qualityInspectMapper;
    private final CommonFileServiceImpl commonFileService;
    private final QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
    private final QualityTestStandardParamMapper qualityTestStandardParamMapper;
    private final QualityTestStandardMapper qualityTestStandardMapper;
    private final QualityInspectParamMapper qualityInspectParamMapper;
    private final ApproveProcessServiceImpl approveProcessService;
    private final ProcurementRecordMapper procurementRecordStorageMapper;
    private final PurchaseLedgerTemplateMapper purchaseLedgerTemplateMapper;
    private final SalesLedgerProductTemplateMapper salesLedgerProductTemplateMapper;
    @Autowired
    private  PaymentRegistrationMapper paymentRegistrationMapper;
    @Autowired
    private  InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    @Autowired
    private  StringRedisTemplate redisTemplate;
    @Autowired
    private  QualityInspectMapper qualityInspectMapper;
    @Autowired
    private  CommonFileServiceImpl commonFileService;
    @Autowired
    private  QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
    @Autowired
    private  QualityTestStandardParamMapper qualityTestStandardParamMapper;
    @Autowired
    private  QualityTestStandardMapper qualityTestStandardMapper;
    @Autowired
    private  QualityInspectParamMapper qualityInspectParamMapper;
    @Autowired
    private  ApproveProcessServiceImpl approveProcessService;
    @Autowired
    private  ProcurementRecordMapper procurementRecordStorageMapper;
    @Autowired
    private  PurchaseLedgerTemplateMapper purchaseLedgerTemplateMapper;
    @Autowired
    private  SalesLedgerProductTemplateMapper salesLedgerProductTemplateMapper;
    @Value("${file.upload-dir}")
    private String uploadDir;
@@ -205,7 +225,8 @@
        return 1;
    }
    private void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) {
    public void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) {
        QualityInspect qualityInspect = new QualityInspect();
        qualityInspect.setInspectType(0);
        qualityInspect.setSupplier(purchaseLedger.getSupplierName());
@@ -400,6 +421,12 @@
        if (ids == null || ids.length == 0) {
           throw new BaseException("请选中至少一条数据");
        }
        for (Long id : ids) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
            if (purchaseLedger.getApprovalStatus().equals(3)) {
                throw new BaseException(purchaseLedger.getPurchaseContractNumber()+"已经审批通过,不允许删除");
            }
        }
        // æ‰¹é‡åˆ é™¤å…³è”的采购入库记录
        LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductQueryWrapper = new LambdaQueryWrapper<>();
        salesLedgerProductQueryWrapper.in(SalesLedgerProduct::getSalesLedgerId, ids)
src/main/java/com/ruoyi/quality/controller/QualityUnqualifiedController.java
@@ -29,6 +29,7 @@
     */
    @PostMapping("/add")
    public AjaxResult add(@RequestBody QualityUnqualified qualityUnqualified) {
        qualityUnqualified.setInspectState(0);
        return AjaxResult.success(qualityUnqualifiedService.save(qualityUnqualified));
    }
src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
@@ -63,82 +63,84 @@
    public int deal(QualityUnqualified qualityUnqualified) {
        QualityUnqualified unqualified = qualityUnqualifiedMapper.selectById(qualityUnqualified.getId());
        QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId());
        switch (qualityUnqualified.getDealResult()) {
            case "返修":
            case "返工":
                //判断质检表是否有相关的报工id,如果有报工id,那么返工需要重新创建生产订单重新生产
                if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) {
                    //返工需要重新创建生产订单重新生产
                    ProductOrder productOrder = productionProductMainMapper.getOrderByMainId(qualityInspect.getProductMainId());
                    ProductOrder order = new ProductOrder();
                    BeanUtils.copyProperties(productOrder, order);
                    order.setId(null);
                    order.setQuantity(unqualified.getQuantity());
                    order.setCompleteQuantity(BigDecimal.ZERO);
                    order.setStartTime(null);
                    order.setEndTime(null);
                    productOrderService.save(order);
                    //新增生产订单下的工艺路线主表
                    ProductProcessRoute productProcessRoute = productProcessRouteMapper.selectList(Wrappers.<ProductProcessRoute>lambdaQuery().eq(ProductProcessRoute::getProductOrderId, productOrder.getId()).orderByDesc(ProductProcessRoute::getId)).get(0);
                    ProductProcessRoute newProcessRoute = new ProductProcessRoute();
                    BeanUtils.copyProperties(productProcessRoute, newProcessRoute);
                    newProcessRoute.setId(null);
                    newProcessRoute.setProductOrderId(order.getId());
                    productProcessRouteMapper.insert(newProcessRoute);
                    //新增生产订单下的工艺路线子表
                    List<ProductProcessRouteItem> processRouteItems = productProcessRouteItemMapper.selectList(new QueryWrapper<ProductProcessRouteItem>().lambda().eq(ProductProcessRouteItem::getProductRouteId, productProcessRoute.getId()));
                    // ç”Ÿæˆå½“前日期的前缀:年月日
                    String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                    for (ProductProcessRouteItem processRouteItem : processRouteItems) {
                        ProductProcessRouteItem productProcessRouteItem = new ProductProcessRouteItem();
                        BeanUtils.copyProperties(processRouteItem, productProcessRouteItem);
                        productProcessRouteItem.setId(null);
                        productProcessRouteItem.setProductRouteId(newProcessRoute.getId());
                        int insert = productProcessRouteItemMapper.insert(productProcessRouteItem);
                        if (insert > 0) {
                            // æŸ¥è¯¢ä»Šæ—¥å·²å­˜åœ¨çš„æœ€å¤§å·¥å•号
                            QueryWrapper<ProductWorkOrder> queryWrapper = new QueryWrapper<>();
                            queryWrapper.likeRight("work_order_no", datePrefix)
                                    .orderByDesc("work_order_no")
                                    .last("LIMIT 1");
                            ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectOne(queryWrapper);
                            int sequenceNumber = 1; // é»˜è®¤åºå·
                            if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) {
                                String lastNo = lastWorkOrder.getWorkOrderNo().toString();
                                if (lastNo.startsWith(datePrefix)) {
                                    String seqStr = lastNo.substring(datePrefix.length());
                                    try {
                                        sequenceNumber = Integer.parseInt(seqStr) + 1;
                                    } catch (NumberFormatException e) {
                                        sequenceNumber = 1;
        if (ObjectUtils.isNotNull(qualityInspect) && qualityInspect.getInspectType()!=0) {
            switch (qualityUnqualified.getDealResult()) {
                case "返修":
                case "返工":
                    //判断质检表是否有相关的报工id,如果有报工id,那么返工需要重新创建生产订单重新生产
                    if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) {
                        //返工需要重新创建生产订单重新生产
                        ProductOrder productOrder = productionProductMainMapper.getOrderByMainId(qualityInspect.getProductMainId());
                        ProductOrder order = new ProductOrder();
                        BeanUtils.copyProperties(productOrder, order);
                        order.setId(null);
                        order.setQuantity(unqualified.getQuantity());
                        order.setCompleteQuantity(BigDecimal.ZERO);
                        order.setStartTime(null);
                        order.setEndTime(null);
                        productOrderService.save(order);
                        //新增生产订单下的工艺路线主表
                        ProductProcessRoute productProcessRoute = productProcessRouteMapper.selectList(Wrappers.<ProductProcessRoute>lambdaQuery().eq(ProductProcessRoute::getProductOrderId, productOrder.getId()).orderByDesc(ProductProcessRoute::getId)).get(0);
                        ProductProcessRoute newProcessRoute = new ProductProcessRoute();
                        BeanUtils.copyProperties(productProcessRoute, newProcessRoute);
                        newProcessRoute.setId(null);
                        newProcessRoute.setProductOrderId(order.getId());
                        productProcessRouteMapper.insert(newProcessRoute);
                        //新增生产订单下的工艺路线子表
                        List<ProductProcessRouteItem> processRouteItems = productProcessRouteItemMapper.selectList(new QueryWrapper<ProductProcessRouteItem>().lambda().eq(ProductProcessRouteItem::getProductRouteId, productProcessRoute.getId()));
                        // ç”Ÿæˆå½“前日期的前缀:年月日
                        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                        for (ProductProcessRouteItem processRouteItem : processRouteItems) {
                            ProductProcessRouteItem productProcessRouteItem = new ProductProcessRouteItem();
                            BeanUtils.copyProperties(processRouteItem, productProcessRouteItem);
                            productProcessRouteItem.setId(null);
                            productProcessRouteItem.setProductRouteId(newProcessRoute.getId());
                            int insert = productProcessRouteItemMapper.insert(productProcessRouteItem);
                            if (insert > 0) {
                                // æŸ¥è¯¢ä»Šæ—¥å·²å­˜åœ¨çš„æœ€å¤§å·¥å•号
                                QueryWrapper<ProductWorkOrder> queryWrapper = new QueryWrapper<>();
                                queryWrapper.likeRight("work_order_no", datePrefix)
                                        .orderByDesc("work_order_no")
                                        .last("LIMIT 1");
                                ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectOne(queryWrapper);
                                int sequenceNumber = 1; // é»˜è®¤åºå·
                                if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) {
                                    String lastNo = lastWorkOrder.getWorkOrderNo().toString();
                                    if (lastNo.startsWith(datePrefix)) {
                                        String seqStr = lastNo.substring(datePrefix.length());
                                        try {
                                            sequenceNumber = Integer.parseInt(seqStr) + 1;
                                        } catch (NumberFormatException e) {
                                            sequenceNumber = 1;
                                        }
                                    }
                                }
                                // ç”Ÿæˆå®Œæ•´çš„工单号
                                String workOrderNoStr = String.format("%s%03d", datePrefix, sequenceNumber);
                                ProductWorkOrder productWorkOrder = new ProductWorkOrder();
                                productWorkOrder.setProductProcessRouteItemId(productProcessRouteItem.getId());
                                productWorkOrder.setProductOrderId(order.getId());
                                productWorkOrder.setPlanQuantity(order.getQuantity());
                                productWorkOrder.setWorkOrderNo(workOrderNoStr);
                                productWorkOrder.setStatus(1);
                                productWorkOrderMapper.insert(productWorkOrder);
                            }
                            // ç”Ÿæˆå®Œæ•´çš„工单号
                            String workOrderNoStr = String.format("%s%03d", datePrefix, sequenceNumber);
                            ProductWorkOrder productWorkOrder = new ProductWorkOrder();
                            productWorkOrder.setProductProcessRouteItemId(productProcessRouteItem.getId());
                            productWorkOrder.setProductOrderId(order.getId());
                            productWorkOrder.setPlanQuantity(order.getQuantity());
                            productWorkOrder.setWorkOrderNo(workOrderNoStr);
                            productWorkOrder.setStatus(1);
                            productWorkOrderMapper.insert(productWorkOrder);
                        }
                    }
                }
                break;
            case "报废":
                //调用不合格库存接口 å…¥ä¸åˆæ ¼åº“
                stockUtils.addUnStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockUnQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(),unqualified.getId());
                break;
            case "让步放行":
                //调用提交合格的接口
                stockUtils.addStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(),unqualified.getId());
                qualityInspect.setCheckResult("合格");
                qualityInspectService.submit(qualityInspect);
                break;
            default:
                break;
                    break;
                case "报废":
                    //调用不合格库存接口 å…¥ä¸åˆæ ¼åº“
                    stockUtils.addUnStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockUnQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId());
                    break;
                case "让步放行":
                    //调用提交合格的接口
                    stockUtils.addStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId());
                    qualityInspect.setCheckResult("合格");
                    qualityInspectService.submit(qualityInspect);
                    break;
                default:
                    break;
            }
        }
        qualityUnqualified.setInspectState(1);//已处理
        return qualityUnqualifiedMapper.updateById(qualityUnqualified);
src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java
@@ -48,7 +48,7 @@
    @ApiOperation("删除事故上报记录")
    @DeleteMapping("/{ids}")
    public R delSafeCertification(@RequestBody List<Integer> ids) {
    public R delSafeAccident(@RequestBody List<Integer> ids) {
        return R.ok(safeAccidentService.removeBatchByIds(ids));
    }
src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.pojo.SafeAccident;
import com.ruoyi.safe.pojo.SafeTraining;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.ruoyi.safe.service.SafeAccidentService;
import com.ruoyi.safe.service.SafeTrainingDetailsService;
import com.ruoyi.safe.service.SafeTrainingService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:06
 */
@RestController
@RequestMapping("/safeTraining")
@Api(tags = "安全生产--安全培训考核")
public class SafeTrainingController {
    @Autowired
    private SafeTrainingService safeTrainingService;
    @Autowired
    private SafeTrainingDetailsService safeTrainingDetailsService;
    @GetMapping("/page")
    @ApiOperation("分页查询")
    public R page(Page page, SafeTrainingDto safeTrainingDto) {
        return R.ok(safeTrainingService.pageSafeTraining(page, safeTrainingDto));
    }
    @ApiOperation("新增/编辑安全培训考核")
    @PostMapping()
    public R addOrUpdate(@RequestBody SafeTraining safeTraining) {
        return R.ok(safeTrainingService.addOrUpdate(safeTraining));
    }
    @ApiOperation("签到")
    @PostMapping ("/sign")
    public R sign(@RequestBody SafeTrainingDetails safeTrainingDetails) {
        return R.ok(safeTrainingDetailsService.save(safeTrainingDetails));
    }
    @ApiOperation("结果明细查询")
    @GetMapping ("/getSafeTraining")
    public R getSafeTraining(Long id) {
        return R.ok(safeTrainingService.getSafeTraining(id));
    }
    @ApiOperation("结果明细保存")
    @PostMapping ("/saveSafeTraining")
    public R saveSafeTraining(@RequestBody SafeTrainingDto safeTrainingDto) {
        return R.ok(safeTrainingService.saveSafeTraining(safeTrainingDto));
    }
    @ApiOperation("删除安全培训考核")
    @DeleteMapping("/{ids}")
    public R delSafeTraining(@RequestBody List<Integer> ids) {
        return R.ok(safeTrainingService.delSafeTraining(ids));
    }
    @ApiOperation("导出")
    @PostMapping ("/export")
    public void export(HttpServletResponse response, @RequestBody SafeTraining safeTraining) {
        safeTrainingService.export(response,safeTraining.getId());
    }
}
src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.pojo.SafeTraining;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.ruoyi.safe.service.SafeTrainingDetailsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--记录详情 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:15
 */
@RestController
@RequestMapping("/safeTrainingDetails")
@Api(tags = "安全生产--安全培训考核--记录详情")
public class SafeTrainingDetailsController {
    @Autowired
    private SafeTrainingDetailsService safeTrainingDetailsService;
    @GetMapping("/page")
    @ApiOperation("分页查询")
    public R page(Page page, SafeTrainingDetails safeTrainingDetails) {
        return R.ok(safeTrainingDetailsService.pageDetails(page, safeTrainingDetails));
    }
    @ApiOperation("导出")
    @PostMapping("/export")
    public void export(HttpServletResponse response, @RequestBody SafeTrainingDetails safeTrainingDetails) {
        safeTrainingDetailsService.export(response,safeTrainingDetails.getUserId());
    }
}
src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeHiddenFile;
import com.ruoyi.safe.pojo.SafeTrainingFile;
import com.ruoyi.safe.service.SafeTrainingFileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--附件 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:23
 */
@RestController
@RequestMapping("/safeTrainingFile")
@Api(tags = "安全生产--安全培训考核--附件")
public class SafeTrainingFileController {
    @Resource
    private SafeTrainingFileService safeTrainingFileService;
    /**
     * æ–°å¢ž
     * @param safeHiddenFile
     * @return
     */
    @PostMapping("/add")
    @ApiOperation("新增")
    public R add(@RequestBody SafeTrainingFile safeHiddenFile) {
        return R.ok(safeTrainingFileService.save(safeHiddenFile));
    }
    /**
     * åˆ é™¤
     * @param ids
     * @return
     */
    @DeleteMapping("/del")
    @ApiOperation("删除")
    public R delSafeHiddenFile(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return R.fail("请选择至少一条数据");
        }
        //删除检验附件
        return R.ok(safeTrainingFileService.removeBatchByIds(ids));
    }
    /**
     *分页查询
     * @param page
     * @param safeTrainingFile
     * @return
     */
    @GetMapping("/listPage")
    @ApiOperation("分页查询")
    public R listPage(Page page, SafeTrainingFile safeTrainingFile) {
        return R.ok(safeTrainingFileService.page(page, Wrappers.<SafeTrainingFile>lambdaQuery().eq(SafeTrainingFile::getSafeTrainingId,safeTrainingFile.getSafeTrainingId())));
    }
}
src/main/java/com/ruoyi/safe/dto/SafeTrainingDetailsDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.safe.dto;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class SafeTrainingDetailsDto extends SafeTrainingDetails {
    @ApiModelProperty("培训人员编号")
    private String userName;
    @ApiModelProperty("培训人员名称")
    private String nickName;
    @ApiModelProperty("手机号码")
    private String phonenumber;
}
src/main/java/com/ruoyi/safe/dto/SafeTrainingDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.safe.dto;
import com.ruoyi.safe.pojo.SafeTraining;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.ruoyi.safe.pojo.SafeTrainingFile;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class SafeTrainingDto extends SafeTraining {
    @ApiModelProperty("报名人数")
    private Integer nums;
    @ApiModelProperty("评价人")
    private String assessmentUserName;
    @ApiModelProperty("附件集合")
    private List<SafeTrainingFile> safeTrainingFileList;
    @ApiModelProperty("培训记录人员详情集合")
    private List<SafeTrainingDetailsDto> safeTrainingDetailsDtoList;
}
src/main/java/com/ruoyi/safe/mapper/SafeTrainingDetailsMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.ruoyi.safe.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.safe.dto.SafeTrainingDetailsDto;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--记录详情 Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:15
 */
@Mapper
public interface SafeTrainingDetailsMapper extends BaseMapper<SafeTrainingDetails> {
    List<SafeTrainingDetailsDto> getSafeTraining(@Param("id") Long id);
    IPage<SafeTrainingDetails> pageDetails(Page page, @Param("c") SafeTrainingDetails safeTrainingDetails);
}
src/main/java/com/ruoyi/safe/mapper/SafeTrainingFileMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.safe.mapper;
import com.ruoyi.safe.pojo.SafeTrainingFile;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--附件 Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:23
 */
@Mapper
public interface SafeTrainingFileMapper extends BaseMapper<SafeTrainingFile> {
}
src/main/java/com/ruoyi/safe/mapper/SafeTrainingMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.ruoyi.safe.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.pojo.SafeTraining;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核 Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:06
 */
@Mapper
public interface SafeTrainingMapper extends BaseMapper<SafeTraining> {
    IPage<SafeTrainingDto> pageSafeTraining(Page page, @Param("c") SafeTrainingDto safeTrainingDto);
    SafeTrainingDto getSafeTraining(@Param("id") Long id);
}
src/main/java/com/ruoyi/safe/pojo/SafeTraining.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package com.ruoyi.safe.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:06
 */
@Getter
@Setter
@TableName("safe_training")
@ApiModel(value = "SafeTraining对象", description = "安全生产--安全培训考核")
public class SafeTraining implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty("课程编号")
    private String courseCode;
    @ApiModelProperty("培训目标")
    private String trainingObjectives;
    @ApiModelProperty("培训内容")
    private String trainingContent;
    @ApiModelProperty("培训方式")
    private String trainingMode;
    @ApiModelProperty("状态(0:未开始1:进行中;2:已结束)")
    private Integer state;
    @ApiModelProperty("参加对象")
    private String participants;
    @ApiModelProperty("培训地点")
    private String placeTraining;
    @ApiModelProperty("培训讲师")
    private String trainingLecturer;
    @ApiModelProperty("培训日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @NotBlank(message = "培训日期不能为空")
    private LocalDate trainingDate;
    @ApiModelProperty("开始时间(时分秒)")
    @NotBlank(message = "开始时间不能为空")
    private String openingTime;
    @ApiModelProperty("结束时间(时分秒)")
    @NotBlank(message = "结束时间不能为空")
    private String endTime;
    @ApiModelProperty("课题学分")
    private String projectCredits;
    @ApiModelProperty("课时")
    private Double classHour;
    @ApiModelProperty("考核方式")
    private String assessmentMethod;
    @ApiModelProperty("本次培训综合评价")
    private String comprehensiveAssessment;
    @ApiModelProperty("备注")
    private String remarks;
    @ApiModelProperty("评价人id")
    private Integer assessmentUserId;
    @ApiModelProperty("评价时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate assessmentDate;
    @ApiModelProperty("培训摘要")
    private String trainingAbstract;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
}
src/main/java/com/ruoyi/safe/pojo/SafeTrainingDetails.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package com.ruoyi.safe.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
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 java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--记录详情
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:15
 */
@Getter
@Setter
@TableName("safe_training_details")
@ApiModel(value = "SafeTrainingDetails对象", description = "安全生产--安全培训考核--记录详情")
public class SafeTrainingDetails implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty("用户表格(user)主键")
    @NotBlank(message = "用户id不能为空")
    private Long userId;
    @ApiModelProperty("关联安全培训考核id")
    private Integer safeTrainingId;
    @ApiModelProperty("考核结果")
    private String examinationResults;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
    @ApiModelProperty("培训日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @TableField(exist = false)
    private LocalDate trainingDate;
    @ApiModelProperty("课程编号")
    @TableField(exist = false)
    private String courseCode;
    @ApiModelProperty("培训内容")
    @TableField(exist = false)
    private String trainingContent;
    @ApiModelProperty("培训课时")
    @TableField(exist = false)
    private Double classHour;
    @ApiModelProperty("课题学分")
    @TableField(exist = false)
    private String projectCredits;
    @ApiModelProperty("备注")
    @TableField(exist = false)
    private String remarks;
}
src/main/java/com/ruoyi/safe/pojo/SafeTrainingFile.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package com.ruoyi.safe.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
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 java.io.Serializable;
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--附件
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:23
 */
@Getter
@Setter
@TableName("safe_training_file")
@ApiModel(value = "SafeTrainingFile对象", description = "安全生产--安全培训考核--附件")
public class SafeTrainingFile implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty("关联安全培训考核id")
    private Integer safeTrainingId;
    private String name;
    private String url;
    private Object fileSize;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
}
src/main/java/com/ruoyi/safe/service/SafeTrainingDetailsService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.safe.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.servlet.http.HttpServletResponse;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--记录详情 æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:15
 */
public interface SafeTrainingDetailsService extends IService<SafeTrainingDetails> {
    IPage<SafeTrainingDetails> pageDetails(Page page, SafeTrainingDetails safeTrainingDetails);
    void export(HttpServletResponse response, Long userId);
}
src/main/java/com/ruoyi/safe/service/SafeTrainingFileService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.safe.service;
import com.ruoyi.safe.pojo.SafeTrainingFile;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--附件 æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:23
 */
public interface SafeTrainingFileService extends IService<SafeTrainingFile> {
}
src/main/java/com/ruoyi/safe/service/SafeTrainingService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.safe.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.pojo.SafeTraining;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核 æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:06
 */
public interface SafeTrainingService extends IService<SafeTraining> {
    IPage<SafeTrainingDto> pageSafeTraining(Page page, SafeTrainingDto safeTrainingDto);
    int addOrUpdate(SafeTraining safeTraining);
    SafeTrainingDto getSafeTraining(Long id);
    int saveSafeTraining(SafeTrainingDto safeTrainingDto);
    int delSafeTraining(List<Integer> ids);
    void export(HttpServletResponse response, Long id);
}
src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java
@@ -47,7 +47,7 @@
    @Override
    public int add(SafeHidden safeHidden) {
        safeHiddenMapper.insert(safeHidden);
        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
        String no = "YH" + String.format("%s%03d", datePrefix, safeHidden.getId());
        safeHidden.setHiddenCode(no);
        safeHiddenMapper.updateById(safeHidden);
src/main/java/com/ruoyi/safe/service/impl/SafeTrainingDetailsServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package com.ruoyi.safe.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.safe.dto.SafeTrainingDetailsDto;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.ruoyi.safe.mapper.SafeTrainingDetailsMapper;
import com.ruoyi.safe.service.SafeTrainingDetailsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--记录详情 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:15
 */
@Service
public class SafeTrainingDetailsServiceImpl extends ServiceImpl<SafeTrainingDetailsMapper, SafeTrainingDetails> implements SafeTrainingDetailsService {
    @Autowired
    private SafeTrainingDetailsMapper safeTrainingDetailsMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
    public IPage<SafeTrainingDetails> pageDetails(Page page, SafeTrainingDetails safeTrainingDetails) {
        return safeTrainingDetailsMapper.pageDetails(page, safeTrainingDetails);
    }
    @Override
    public void export(HttpServletResponse response, Long userId) {
        SafeTrainingDetails safeTrainingDetails = new SafeTrainingDetails();
        safeTrainingDetails.setUserId(userId);
        SysUser sysUser = sysUserMapper.selectUserById(userId);
        List<SafeTrainingDetails> safeTrainingDetailsList = safeTrainingDetailsMapper.pageDetails(new Page(1, -1), safeTrainingDetails).getRecords();
        InputStream inputStream = this.getClass().getResourceAsStream("/static/safe-training-details.docx");
        Configure configure = Configure.builder()
                .bind("safeTrainingDetailsList", new HackLoopTableRenderPolicy())
                .build();
        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
                new HashMap<String, Object>() {{
                    put("user", sysUser);
                    put("safeTrainingDetailsList", safeTrainingDetailsList);
                }});
        try {
            response.setContentType("application/msword");
            String fileName = URLEncoder.encode(
                    "培训与考核记录", "UTF-8");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-disposition",
                    "attachment;filename=" + fileName + ".docx");
            OutputStream os = response.getOutputStream();
            template.write(os);
            os.flush();
            os.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("导出失败");
        }
    }
}
src/main/java/com/ruoyi/safe/service/impl/SafeTrainingFileServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.safe.service.impl;
import com.ruoyi.safe.pojo.SafeTrainingFile;
import com.ruoyi.safe.mapper.SafeTrainingFileMapper;
import com.ruoyi.safe.service.SafeTrainingFileService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核--附件 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:23
 */
@Service
public class SafeTrainingFileServiceImpl extends ServiceImpl<SafeTrainingFileMapper, SafeTrainingFile> implements SafeTrainingFileService {
}
src/main/java/com/ruoyi/safe/service/impl/SafeTrainingServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,179 @@
package com.ruoyi.safe.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
import com.ruoyi.production.pojo.ProductOrder;
import com.ruoyi.production.pojo.ProductWorkOrder;
import com.ruoyi.project.system.domain.SysNotice;
import com.ruoyi.safe.dto.SafeTrainingDetailsDto;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.mapper.SafeTrainingDetailsMapper;
import com.ruoyi.safe.mapper.SafeTrainingFileMapper;
import com.ruoyi.safe.pojo.SafeTraining;
import com.ruoyi.safe.mapper.SafeTrainingMapper;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.ruoyi.safe.pojo.SafeTrainingFile;
import com.ruoyi.safe.service.SafeTrainingService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
 * <p>
 * å®‰å…¨ç”Ÿäº§--安全培训考核 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-01-29 10:54:06
 */
@Service
public class SafeTrainingServiceImpl extends ServiceImpl<SafeTrainingMapper, SafeTraining> implements SafeTrainingService {
    @Autowired
    private SafeTrainingMapper safeTrainingMapper;
    @Autowired
    private SafeTrainingFileMapper safeTrainingFileMapper;
    @Autowired
    private SafeTrainingDetailsMapper safeTrainingDetailsMapper;
    @Override
    public IPage<SafeTrainingDto> pageSafeTraining(Page page, SafeTrainingDto safeTrainingDto) {
        return safeTrainingMapper.pageSafeTraining(page, safeTrainingDto);
    }
    @Override
    public int addOrUpdate(SafeTraining safeTraining) {
        if (ObjectUtils.isNull(safeTraining.getId())) {
            String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
            // æŸ¥è¯¢ä»Šæ—¥å·²å­˜åœ¨çš„æœ€å¤§è¯¾ç¨‹ç¼–号
            QueryWrapper<SafeTraining> queryWrapper = new QueryWrapper<>();
            queryWrapper.likeRight("course_code", datePrefix)
                    .orderByDesc("course_code")
                    .last("LIMIT 1");
            SafeTraining lastSafeTraining = safeTrainingMapper.selectOne(queryWrapper);
            int sequenceNumber = 1; // é»˜è®¤åºå·
            if (lastSafeTraining != null && lastSafeTraining.getCourseCode() != null) {
                String lastNo = lastSafeTraining.getCourseCode().toString();
                if (lastNo.startsWith(datePrefix)) {
                    String seqStr = lastNo.substring(datePrefix.length());
                    try {
                        sequenceNumber = Integer.parseInt(seqStr) + 1;
                    } catch (NumberFormatException e) {
                        sequenceNumber = 1;
                    }
                }
            }
            // ç”Ÿæˆå®Œæ•´çš„课程编号
            String no = "KC-" + String.format("%s%03d", datePrefix, sequenceNumber);
            safeTraining.setCourseCode(no);
        }
        //根据时间判断培训状态
        String trainingDate = safeTraining.getTrainingDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LocalDateTime openingTime = LocalDateTime.parse((trainingDate + safeTraining.getOpeningTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
        LocalDateTime endTime = LocalDateTime.parse((trainingDate + safeTraining.getEndTime()), DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss"));
        if (LocalDateTime.now().isBefore(openingTime)) {
            //未开始
            safeTraining.setState(0);
        } else if (LocalDateTime.now().isAfter(endTime)) {
            //已结束
            safeTraining.setState(2);
        } else {
            //进行中
            safeTraining.setState(1);
        }
        //新增或更新
        saveOrUpdate(safeTraining);
        return 0;
    }
    @Override
    public SafeTrainingDto getSafeTraining(Long id) {
        //主表数据
        SafeTrainingDto safeTrainingDto = safeTrainingMapper.getSafeTraining(id);
        //附件
        List<SafeTrainingFile> safeTrainingFiles = safeTrainingFileMapper.selectList(Wrappers.<SafeTrainingFile>lambdaQuery().eq(SafeTrainingFile::getSafeTrainingId, id));
        safeTrainingDto.setSafeTrainingFileList(safeTrainingFiles);
        //培训记录详情
        List<SafeTrainingDetailsDto> safeTrainingDetailsDto = safeTrainingDetailsMapper.getSafeTraining(id);
        safeTrainingDto.setSafeTrainingDetailsDtoList(safeTrainingDetailsDto);
        return safeTrainingDto;
    }
    @Override
    public int saveSafeTraining(SafeTrainingDto safeTrainingDto) {
        //更新主表
        safeTrainingMapper.updateById(safeTrainingDto);
        //更新培训记录详情
        safeTrainingDto.getSafeTrainingDetailsDtoList().forEach(safeTrainingDetailsDto -> {
            safeTrainingDetailsMapper.updateById(safeTrainingDetailsDto);
        });
        return 0;
    }
    @Override
    public int delSafeTraining(List<Integer> ids) {
        //删除主表
        safeTrainingMapper.deleteBatchIds(ids);
        //删除附件
        safeTrainingFileMapper.delete(Wrappers.<SafeTrainingFile>lambdaQuery().in(SafeTrainingFile::getSafeTrainingId, ids));
        //删除培训记录
        safeTrainingDetailsMapper.delete(Wrappers.<SafeTrainingDetails>lambdaQuery().in(SafeTrainingDetails::getSafeTrainingId, ids));
        return 0;
    }
    @Override
    public void export(HttpServletResponse response, Long id) {
        SafeTrainingDto safeTrainingDto = safeTrainingMapper.getSafeTraining(id);
        List<SafeTrainingDetailsDto> safeTrainingDetailsDtoList = safeTrainingDetailsMapper.getSafeTraining(id);
        InputStream inputStream = this.getClass().getResourceAsStream("/static/safe-training.docx");
        Configure configure = Configure.builder()
                .bind("safeTrainingDetailsDtoList", new HackLoopTableRenderPolicy())
                .build();
        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
                new HashMap<String, Object>() {{
                    put("safeTrainingDto", safeTrainingDto);
                    put("safeTrainingDetailsDtoList", safeTrainingDetailsDtoList);
                }});
        try {
            response.setContentType("application/msword");
            String fileName = URLEncoder.encode(
                    safeTrainingDto.getCourseCode() + "培训与考核计划", "UTF-8");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-disposition",
                    "attachment;filename=" + fileName + ".docx");
            OutputStream os = response.getOutputStream();
            template.write(os);
            os.flush();
            os.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("导出失败");
        }
    }
}
src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
@@ -64,7 +64,7 @@
    @GetMapping("/listPage")
    @ApiOperation("发货信息列表")
    public AjaxResult listPage(Page page, ShippingInfo req) {
        IPage<ShippingInfo> listPage = shippingInfoService.listPage(page,req);
        IPage<ShippingInfoDto> listPage = shippingInfoService.listPage(page,req);
        return AjaxResult.success(listPage);
    }
@@ -96,8 +96,6 @@
    @Transactional(rollbackFor = Exception.class)
    @Log(title = "发货信息管理", businessType = BusinessType.UPDATE)
    public AjaxResult deductStock(@RequestBody ShippingInfoDto req) throws IOException {
        return shippingInfoService.deductStock( req) ? AjaxResult.success() : AjaxResult.error();
    }
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
@@ -54,4 +54,7 @@
    @ApiModelProperty(value = "付款方式")
    private String paymentMethod;
    @ApiModelProperty(value = "交货日期")
    private LocalDate deliveryDate;
}
src/main/java/com/ruoyi/sales/dto/SalesLedgerProductDto.java
@@ -55,4 +55,6 @@
     */
    private Long supplierId;
    private Integer approvalStatus;
}
src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
@@ -5,6 +5,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.home.dto.IncomeExpenseAnalysisDto;
import com.ruoyi.sales.dto.SalesLedgerDto;
import com.ruoyi.sales.dto.SalesTrendDto;
import com.ruoyi.sales.dto.StatisticsTableDto;
@@ -23,6 +24,10 @@
 * @date 2025-05-08
 */
public interface SalesLedgerMapper extends BaseMapper<SalesLedger> {
    List<IncomeExpenseAnalysisDto> selectIncomeStats(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("dateFormat") String dateFormat);
    List<com.ruoyi.dto.MapDto> selectCustomerSalesComposition();
    /**
     * æŸ¥è¯¢æŒ‡å®šæ—¥æœŸçš„æ‰€æœ‰åˆåŒåºåˆ—号
     * @param datePart æ—¥æœŸéƒ¨åˆ†ï¼ˆæ ¼å¼ï¼šyyyyMMdd)
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
@@ -40,4 +40,6 @@
    List<Map<String, Object>> selectRawMaterialPurchaseAnalysis();
    int selectProductCountByTypeAndDate(@Param("type") Integer type, @Param("startDate") String startDate, @Param("endDate") String endDate);
    BigDecimal selectRawMaterialExpense();
}
src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.pojo.ShippingInfo;
import org.apache.ibatis.annotations.Param;
@@ -13,7 +14,7 @@
 * @date : 2025/10/22 9:32
 */
public interface ShippingInfoMapper extends BaseMapper<ShippingInfo> {
    IPage<ShippingInfo> listPage(Page page,@Param("req") ShippingInfo req);
    IPage<ShippingInfoDto> listPage(Page page, @Param("req") ShippingInfo req);
    List<ShippingInfo> listAll();
}
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -130,5 +130,14 @@
    @TableField(exist = false)
    @ApiModelProperty(value = "生产状态")
    private String productionStatus = "未开始";
    //交货日期
    @ApiModelProperty(value = "交货日期")
    @TableField(value = "delivery_date")
    private LocalDate deliveryDate;
    @TableField(exist = false)
    @ApiModelProperty(value = "交货天数差")
    private Integer deliveryDaysDiff;
}
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
@@ -14,7 +14,7 @@
 * @date : 2025/10/22 9:33
 */
public interface ShippingInfoService extends IService<ShippingInfo>{
    IPage<ShippingInfo> listPage(Page page, ShippingInfo req);
    IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req);
    boolean deductStock(ShippingInfoDto req) throws IOException;
src/main/java/com/ruoyi/sales/service/impl/CommonFileServiceImpl.java
@@ -40,6 +40,11 @@
    @Value("${file.upload-dir}")
    private String uploadDir;
    public List<CommonFile> getFileListByBusinessId(Long businessId,Integer type) {
        return commonFileMapper.selectList(new LambdaQueryWrapper<CommonFile>().eq(CommonFile::getCommonId, businessId)
                .eq(CommonFile::getType, type));
    }
    public void deleteByBusinessId(Long businessId,Integer type) {
        commonFileMapper.delete(new LambdaQueryWrapper<CommonFile>().eq(CommonFile::getCommonId, businessId)
                .eq(CommonFile::getType, type));
src/main/java/com/ruoyi/sales/service/impl/InvoiceLedgerServiceImpl.java
@@ -326,6 +326,14 @@
        if(ObjectUtils.isEmpty(invoiceRegistrationProductDto)){
            throw new RuntimeException("产品开票台账查找失败");
        }
        List<InvoiceRegistrationProduct> invoiceRegistrationProducts = invoiceRegistrationProductMapper.selectList(new LambdaQueryWrapper<InvoiceRegistrationProduct>()
                .eq(InvoiceRegistrationProduct::getSalesLedgerProductId, invoiceRegistrationProductDto.getSalesLedgerProductId()));
        if(CollectionUtils.isNotEmpty(invoiceRegistrationProducts)){
            invoiceRegistrationProductDto.setNoInvoiceNum(invoiceRegistrationProductDto.getQuantity()
                    .subtract(invoiceRegistrationProducts.stream().map(InvoiceRegistrationProduct::getInvoiceNum).reduce(BigDecimal.ZERO, BigDecimal::add)));
            invoiceRegistrationProductDto.setNoInvoiceAmount(invoiceRegistrationProductDto.getTaxInclusiveTotalPrice()
                    .subtract(invoiceRegistrationProducts.stream().map(InvoiceRegistrationProduct::getInvoiceAmount).reduce(BigDecimal.ZERO, BigDecimal::add)));
        }
        // æŸ¥è¯¢é™„ä»¶
        QueryWrapper<InvoiceLedgerFile> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("invoice_ledger_id", invoiceRegistrationProductDto.getInvoiceLedgerId());
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -42,6 +42,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
@@ -217,6 +218,7 @@
        int result;
        Long salesLedgerId = salesLedgerProduct.getSalesLedgerId();
        if (salesLedgerProduct.getId() == null) {
            salesLedgerProduct.setRegisterDate(LocalDateTime.now());
            result = salesLedgerProductMapper.insert(salesLedgerProduct);
            addProductionData(salesLedgerProduct);
        } else {
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -263,75 +263,51 @@
    @Override
    public List<MonthlyAmountDto> getAmountHalfYear(Integer type) {
        LocalDate now = LocalDate.now();
        LocalDateTime currentDateTime = LocalDateTime.now();
        // æ ¹æ® type ç¡®å®šæŸ¥è¯¢çš„æ—¶é—´é—´éš”(天数)和总查询天数
        int daysPerPeriod;
        int totalDays;
        switch (type) {
            case 1:
                daysPerPeriod = 5;   // æ¯5天查一次
                totalDays = 30;       // 6次 Ã— 5天 = 30天
                break;
            case 2:
                daysPerPeriod = 15;    // æ¯15天查一次
                totalDays = 90;       // 6次 Ã— 15天 = 90天
                break;
            case 3:
                daysPerPeriod = 30;   // æ¯30天查一次
                totalDays = 180;      // 6次 Ã— 30天 = 180天
                break;
            default:
                throw new IllegalArgumentException("Invalid type value: " + type);
        }
        List<MonthlyAmountDto> result = new ArrayList<>();
        // å¾ªçޝ6次,每次查询一个时间段的数据
        for (int i = 0; i < 6; i++) {
            // è®¡ç®—当前时间段的起始和结束时间
            LocalDateTime endTime = currentDateTime.minusDays(i * daysPerPeriod);
            LocalDateTime startTime = endTime.minusDays(daysPerPeriod);
        for (int i = 5; i >= 0; i--) {
            YearMonth yearMonth = YearMonth.from(now.minusMonths(i));
            LocalDateTime startTime = yearMonth.atDay(1).atStartOfDay();
            LocalDateTime endTime = yearMonth.atEndOfMonth().atTime(23, 59, 59);
            // æŸ¥è¯¢å›žæ¬¾é‡‘额
            //  å›žæ¬¾é‡‘额
            LambdaQueryWrapper<ReceiptPayment> receiptPaymentQuery = new LambdaQueryWrapper<>();
            receiptPaymentQuery
                    .ge(ReceiptPayment::getCreateTime, startTime)
                    .lt(ReceiptPayment::getCreateTime, endTime);
            List<ReceiptPayment> receiptPayments = receiptPaymentMapper.selectList(receiptPaymentQuery);
                    .le(ReceiptPayment::getCreateTime, endTime);
            // æŸ¥è¯¢å¼€ç¥¨é‡‘额
            LambdaQueryWrapper<InvoiceLedger> invoiceLedgerQuery = new LambdaQueryWrapper<>();
            invoiceLedgerQuery
                    .ge(InvoiceLedger::getCreateTime, startTime)
                    .lt(InvoiceLedger::getCreateTime, endTime);
            List<InvoiceLedger> invoiceLedgers = invoiceLedgerMapper.selectList(invoiceLedgerQuery);
            List<ReceiptPayment> receiptPayments =
                    receiptPaymentMapper.selectList(receiptPaymentQuery);
            // è®¡ç®—回款总额
            BigDecimal receiptAmount = receiptPayments.stream()
                    .map(ReceiptPayment::getReceiptPaymentAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // è®¡ç®—开票总额
            //  å¼€ç¥¨é‡‘额
            LambdaQueryWrapper<InvoiceLedger> invoiceLedgerQuery = new LambdaQueryWrapper<>();
            invoiceLedgerQuery
                    .ge(InvoiceLedger::getCreateTime, startTime)
                    .le(InvoiceLedger::getCreateTime, endTime);
            List<InvoiceLedger> invoiceLedgers =
                    invoiceLedgerMapper.selectList(invoiceLedgerQuery);
            BigDecimal invoiceAmount = invoiceLedgers.stream()
                    .map(InvoiceLedger::getInvoiceTotal)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // æž„造返回的 DTO
            MonthlyAmountDto dto = new MonthlyAmountDto();
            dto.setMonth(startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " ~ " +
                    endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            dto.setMonth(yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")));
            dto.setReceiptAmount(receiptAmount);
            dto.setInvoiceAmount(invoiceAmount);
            result.add(dto);
        }
        // åè½¬åˆ—表,使时间顺序从早到晚
        Collections.reverse(result);
        return result;
    }
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -49,9 +49,11 @@
    private ApproveProcessServiceImpl approveProcessService;
    @Override
    public IPage<ShippingInfo> listPage(Page page, ShippingInfo req) {
        IPage<ShippingInfo> listPage = shippingInfoMapper.listPage(page, req);
    public IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req) {
        IPage<ShippingInfoDto> listPage = shippingInfoMapper.listPage(page, req);
        listPage.getRecords().forEach(item ->{
            item.setCommonFileList(commonFileService.getFileListByBusinessId(item.getId(), FileNameType.SHIP.getValue()));
        });
        return listPage;
    }
@@ -61,14 +63,16 @@
        if (byId == null) {
            throw new RuntimeException("发货信息不存在");
        }
        //扣减库存
        if(!"已发货".equals(byId.getStatus())){
            SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(byId.getSalesLedgerProductId());
            stockUtils.substractStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), req.getId());
        }
        byId.setExpressNumber(req.getExpressNumber());
        byId.setExpressCompany(req.getExpressCompany());
        byId.setStatus("已发货");
        byId.setShippingCarNumber(req.getShippingCarNumber());
        boolean update = this.updateById(req);
        //扣减库存
        SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(req.getSalesLedgerProductId());
        stockUtils.substractStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), req.getId());
        boolean update = this.updateById(byId);
        // è¿ç§»æ–‡ä»¶
        if(CollectionUtils.isNotEmpty(req.getTempFileIds())){
            tempFileService.migrateTempFilesToFormal(req.getId(), req.getTempFileIds(), FileNameType.SHIP.getValue());
src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
@@ -69,8 +69,10 @@
        // æ›´æ–°å¯¹åº”用户状态为停用
        // æ ¹æ®å‘˜å·¥ç¼–号查询用户
        SysUser sysUser = sysUserMapper.selectUserByUserName(staffOnJob.getStaffNo());
        sysUser.setStatus("1");
        sysUserMapper.updateUser(sysUser);
        if (sysUser != null) {
            sysUser.setStatus("1");
            sysUserMapper.updateUser(sysUser);
        }
        // æ›´æ–°ç¦»èŒçŠ¶æ€ä¸ºç¦»èŒ
        staffOnJob.setStaffState(0);
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
@@ -90,7 +90,7 @@
            throw new BaseException("编号为"+staffOnJobParams.getStaffNo()+"的员工不存在,无法更新!!!");
        }
        String[] ignoreProperties = {"id"};//排除id属性
        String[] ignoreProperties = {"id"};//排除更新属性
        // èŽ·å–æœ€æ–°åˆåŒæ•°æ®ï¼Œå¹¶ä¸”æ›´æ–°
        StaffContract contract = staffContractMapper.selectOne(Wrappers.<StaffContract>lambdaQuery()
@@ -103,9 +103,8 @@
        }
        // æ›´æ–°å‘˜å·¥æ•°æ®
        BeanUtils.copyProperties(staffOnJobParams,job,ignoreProperties);
        job.setContractExpireTime(staffOnJobParams.getContractEndTime());
        return staffOnJobMapper.updateById(job);
        staffOnJobParams.setContractExpireTime(staffOnJobParams.getContractEndTime());
        return staffOnJobMapper.updateById(staffOnJobParams);
    }
    //删除入职
@@ -155,8 +154,12 @@
        StaffOnJobDto staffOnJobDto = new StaffOnJobDto();
        BeanUtils.copyProperties(staffOnJob, staffOnJobDto);
        // æŸ¥è¯¢å²—位名称
        SysPost post = sysPostMapper.selectPostById((long) staffOnJob.getSysPostId());
        staffOnJobDto.setPostName(post.getPostName());
        if (staffOnJob.getSysPostId() != null) {
            SysPost post = sysPostMapper.selectPostById(staffOnJob.getSysPostId().longValue());
            if (post != null) {
                staffOnJobDto.setPostName(post.getPostName());
            }
        }
        // æŸ¥è¯¢åˆåŒä¿¡æ¯
        StaffContract contract = staffContractMapper.selectOne(Wrappers.<StaffContract>lambdaQuery()
src/main/resources/mapper/account/AccountExpenseMapper.xml
@@ -70,4 +70,26 @@
        </if>
    </select>
    <select id="selectAccountExpenseStats" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
        SELECT DATE_FORMAT(expense_date, #{dateFormat}) as dateStr, IFNULL(SUM(expense_money), 0) as amount
        FROM account_expense
        WHERE expense_date BETWEEN #{startDate} AND #{endDate}
        GROUP BY dateStr
    </select>
    <select id="selectExpenseComposition" resultType="com.ruoyi.dto.MapDto">
        SELECT sdd.dict_label as name, CAST(IFNULL(SUM(ae.expense_money), 0) AS CHAR) as value
        FROM account_expense ae
        LEFT JOIN sys_dict_data sdd ON ae.expense_type = sdd.dict_value AND sdd.dict_type = 'expense_types'
        <where>
            <if test="startDate != null and startDate != ''">
                AND ae.expense_date &gt;= #{startDate}
            </if>
            <if test="endDate != null and endDate != ''">
                AND ae.expense_date &lt;= #{endDate}
            </if>
        </where>
        GROUP BY ae.expense_type, sdd.dict_label
    </select>
</mapper>
src/main/resources/mapper/account/AccountIncomeMapper.xml
@@ -69,4 +69,17 @@
            AND income_date &lt;= DATE_FORMAT(#{dateQueryDto.entryDateEnd},'%Y-%m-%d')
        </if>
    </select>
    <select id="selectIncomeStats"
            resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
        SELECT DATE_FORMAT(income_date, #{dateFormat}) AS dateStr,
               IFNULL(SUM(income_money), 0)            AS amount
        FROM account_income
        WHERE income_date BETWEEN #{startDate} AND #{endDate}
          AND business_type = 1
        GROUP BY dateStr
        ORDER BY dateStr
    </select>
</mapper>
src/main/resources/mapper/production/ProductOrderMapper.xml
@@ -21,7 +21,8 @@
        slp.specification_model,
        ppr.process_route_code,
        pb.bom_no,
        ROUND(po.complete_quantity / po.quantity * 100, 2) AS completionStatus
        ROUND(po.complete_quantity / po.quantity * 100, 2) AS completionStatus,
        DATEDIFF(sl.delivery_date, CURDATE()) AS delivery_days_diff
        from product_order po
        left join sales_ledger sl on po.sales_ledger_id = sl.id
        left join sales_ledger_product slp on po.product_model_id = slp.id
src/main/resources/mapper/production/ProductionProductMainMapper.xml
@@ -14,24 +14,24 @@
    <select id="listPageProductionProductMainDto" resultType="com.ruoyi.production.dto.ProductionProductMainDto">
        select ppm.*,
               pwo.work_order_no as workOrderNo,
               pwo.status as workOrderStatus,
               u.nick_name as nickName,
               p.product_name as productName,
               pm.model as productModelName,
               ppo.quantity,
               ppo.scrap_qty,
               pm.unit,
               sl.sales_contract_no salesContractNo
        pwo.work_order_no as workOrderNo,
        pwo.status as workOrderStatus,
        u.nick_name as nickName,
        p.product_name as productName,
        pm.model as productModelName,
        ppo.quantity,
        ppo.scrap_qty,
        pm.unit,
        sl.sales_contract_no salesContractNo
        from
            production_product_main ppm
                left join product_work_order pwo on pwo.id = ppm.work_order_id
                left join product_order po on po.id = pwo.product_order_id
                left join production_product_output ppo on ppm.id = ppo.product_main_id
                left join product_model pm on pm.id = ppo.product_model_id
                left join product p on p.id = pm.product_id
                left join sales_ledger sl on sl.id = po.sales_ledger_id
                left join sys_user u on u.user_id = ppm.user_id
        production_product_main ppm
        left join product_work_order pwo on pwo.id = ppm.work_order_id
        left join product_order po on po.id = pwo.product_order_id
        left join production_product_output ppo on ppm.id = ppo.product_main_id
        left join product_model pm on pm.id = ppo.product_model_id
        left join product p on p.id = pm.product_id
        left join sales_ledger sl on sl.id = po.sales_ledger_id
        left join sys_user u on u.user_id = ppm.user_id
        <where>
            <if test="c.nickName != null and c.nickName != ''">
                and u.nick_name like concat('%',#{c.nickName},'%')
@@ -52,9 +52,33 @@
    <select id="getOrderByMainId" resultType="com.ruoyi.production.pojo.ProductOrder">
        select po.*
        from product_order po
        left join  product_work_order pwo on po.id = pwo.product_order_id
        left join production_product_main pm on work_order_id=pwo.id
        where pm.id=#{productMainId}
                 left join product_work_order pwo on po.id = pwo.product_order_id
                 left join production_product_main pm on work_order_id = pwo.id
        where pm.id = #{productMainId}
    </select>
    <select id="listProductionDetails" resultType="com.ruoyi.production.dto.ProductionProductMainDto">
        SELECT
            slpa.scheduling_date,
            slpa.scheduling_user_name,
            sl.sales_contract_no,
            sl.customer_name,
            p.product_name,
            pm.model,
            pm.unit,
            slpa.process,
            ppo.quantity,
            slpa.work_hours,
            slpa.work_hours * slpa.finished_num AS wages
        FROM
            production_product_main ppm
                LEFT JOIN sales_ledger_production_accounting slpa ON slpa.sales_ledger_work_id = ppm.id
                LEFT JOIN production_product_output ppo ON ppm.id = ppo.product_main_id
                LEFT JOIN product_work_order pwo ON pwo.id = ppm.work_order_id
                LEFT JOIN product_order po ON po.id = pwo.product_order_id
                LEFT JOIN process_route pr ON pr.id = po.route_id
                LEFT JOIN product_model pm ON po.product_model_id = pm.id
                LEFT JOIN product p ON p.id = pm.product_id
                LEFT JOIN sales_ledger sl ON po.sales_ledger_id = sl.id
    </select>
    <delete id="deleteByWorkOrderIds" parameterType="java.util.List">
src/main/resources/mapper/production/SalesLedgerProductionAccountingMapper.xml
@@ -50,4 +50,24 @@
        group by t4.id
        order by t4.scheduling_date desc
    </select>
    <select id="pageProductionAccounting"
            resultType="com.ruoyi.production.dto.SalesLedgerProductionAccountingDto">
        SELECT
            slpa.scheduling_user_id,
            slpa.scheduling_user_name,
               sum(ppout.quantity) as output_num,
               sum(slpa.finished_num * work_hours) as  wages,
               SUM(slpa.finished_num) / SUM(ppout.quantity) as output_rate
        FROM sales_ledger_production_accounting slpa
                 LEFT JOIN production_product_main ppm ON slpa.sales_ledger_work_id = ppm.id
                 LEFT JOIN production_product_output ppout ON ppm.id = ppout.product_main_id
        <where>
            <if test="ew.schedulingUserName != null and ew.schedulingUserName !=''" >
                and slpa.scheduling_user_name  = #{ew.schedulingUserName}
            </if>
        </where>
        GROUP BY slpa.scheduling_user_name
    </select>
</mapper>
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml
@@ -83,4 +83,25 @@
        WHERE
            T1.sales_ledger_product_id = #{id}
    </select>
    <select id="selectPurchaseStats" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
        SELECT DATE_FORMAT(entry_date, #{dateFormat}) as dateStr, IFNULL(SUM(contract_amount), 0) as amount
        FROM purchase_ledger
        WHERE entry_date BETWEEN #{startDate} AND #{endDate}
        GROUP BY dateStr
    </select>
    <select id="selectTotalPurchaseAmount" resultType="java.math.BigDecimal">
        SELECT IFNULL(SUM(contract_amount), 0)
        FROM purchase_ledger
        <where>
            <if test="startDate != null and startDate != ''">
                AND entry_date &gt;= #{startDate}
            </if>
            <if test="endDate != null and endDate != ''">
                AND entry_date &lt;= #{endDate}
            </if>
        </where>
    </select>
</mapper>
src/main/resources/mapper/safe/SafeAccidentMapper.xml
@@ -27,7 +27,7 @@
    </resultMap>
    <select id="pageSafeAccident" resultType="com.ruoyi.safe.pojo.SafeAccident">
        select sa.*,
               su.nick_name createUserName,
               su.nick_name createUserName
        from safe_accident sa
        left join sys_user su on sa.create_user = su.user_id
        where 1=1
@@ -35,13 +35,13 @@
            and sa.accident_code like concat('%', #{c.accidentCode}, '%')
        </if>
        <if test="c.accidentName != null and c.accidentName != ''">
            and sa.accident_name like concat('%', #{accidentName}, '%')
            and sa.accident_name like concat('%', #{c.accidentName}, '%')
        </if>
        <if test="c.accidentType != null and c.accidentType != ''">
            and sa.accident_type like concat('%', #{accidentType}, '%')
            and sa.accident_type like concat('%', #{c.accidentType}, '%')
        </if>
        <if test="c.accidentGrade != null and c.accidentGrade != ''">
            and sa.accident_grade like concat('%', #{accidentGrade}, '%')
            and sa.accident_grade like concat('%', #{c.accidentGrade}, '%')
        </if>
    </select>
src/main/resources/mapper/safe/SafeTrainingDetailsMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
<?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.safe.mapper.SafeTrainingDetailsMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.safe.pojo.SafeTrainingDetails">
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="safe_training_id" property="safeTrainingId"/>
        <result column="examination_results" property="examinationResults"/>
        <result column="create_time" property="createTime"/>
        <result column="create_user" property="createUser"/>
        <result column="update_time" property="updateTime"/>
        <result column="update_user" property="updateUser"/>
        <result column="tenant_id" property="tenantId"/>
    </resultMap>
    <select id="getSafeTraining" resultType="com.ruoyi.safe.dto.SafeTrainingDetailsDto">
        select std.*,
               su.user_name,
               su.nick_name,
               su.phonenumber
        from safe_training_details std
                 left join sys_user su on std.user_id = su.user_id
        where std.safe_training_id = #{id}
    </select>
    <select id="pageDetails" resultType="com.ruoyi.safe.pojo.SafeTrainingDetails">
        select std.*,
               st.*
        from safe_training_details std
                 left join safe_training st on std.safe_training_id = st.id
        where std.user_id = #{c.userId}
        <if test="c.trainingDate != null">
            and st.training_date = #{c.trainingDate}
        </if>
    </select>
</mapper>
src/main/resources/mapper/safe/SafeTrainingFileMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
<?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.safe.mapper.SafeTrainingFileMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.safe.pojo.SafeTrainingFile">
        <id column="id" property="id" />
        <result column="safe_training_id" property="safeTrainingId" />
        <result column="name" property="name" />
        <result column="url" property="url" />
        <result column="file_size" property="fileSize" />
        <result column="create_time" property="createTime" />
        <result column="create_user" property="createUser" />
        <result column="update_time" property="updateTime" />
        <result column="update_user" property="updateUser" />
        <result column="tenant_id" property="tenantId" />
    </resultMap>
</mapper>
src/main/resources/mapper/safe/SafeTrainingMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
<?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.safe.mapper.SafeTrainingMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.safe.pojo.SafeTraining">
        <id column="id" property="id" />
        <result column="course_code" property="courseCode" />
        <result column="training_objectives" property="trainingObjectives" />
        <result column="training_content" property="trainingContent" />
        <result column="training_mode" property="trainingMode" />
        <result column="state" property="state" />
        <result column="participants" property="participants" />
        <result column="place_training" property="placeTraining" />
        <result column="training_lecturer" property="trainingLecturer" />
        <result column="training_date" property="trainingDate" />
        <result column="opening_time" property="openingTime" />
        <result column="end_time" property="endTime" />
        <result column="project_credits" property="projectCredits" />
        <result column="class_hour" property="classHour" />
        <result column="assessment_method" property="assessmentMethod" />
        <result column="comprehensive_assessment" property="comprehensiveAssessment" />
        <result column="remarks" property="remarks" />
        <result column="assessment_user_id" property="assessmentUserId" />
        <result column="assessment_date" property="assessmentDate" />
        <result column="training_abstract" property="trainingAbstract" />
        <result column="create_time" property="createTime" />
        <result column="create_user" property="createUser" />
        <result column="update_time" property="updateTime" />
        <result column="update_user" property="updateUser" />
        <result column="tenant_id" property="tenantId" />
    </resultMap>
    <select id="pageSafeTraining" resultType="com.ruoyi.safe.dto.SafeTrainingDto">
        select st.*,
               su.nick_name assessmentUserName,
                count(std.id) nums
        from safe_training st
        left join safe_training_details std on std.safe_training_id = st.id
        left join sys_user su on st.assessment_user_id = su.user_id
        where 1=1
        <if test="c.placeTraining != null and c.placeTraining != ''">
            and st.place_training like concat('%', #{c.placeTraining}, '%')
        </if>
        <if test="c.trainingDate != null and c.trainingDate != ''">
            and st.training_date = #{c.trainingDate}
        </if>
        <if test="c.state != null and c.state != ''">
            and st.state like concat('%', #{c.state}, '%')
        </if>
    </select>
    <select id="getSafeTraining" resultType="com.ruoyi.safe.dto.SafeTrainingDto">
         select st.*,
                su.nick_name assessmentUserName
         from safe_training st
                  left join sys_user su on st.assessment_user_id = su.user_id
         where st.id=#{id}
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -15,24 +15,25 @@
    <select id="selectSalesLedgerList" resultType="com.ruoyi.sales.pojo.SalesLedger">
        SELECT
            T1.id,
            T1.sales_contract_no,
            T1.customer_contract_no,
            T1.project_name,
            T1.entry_date,
            T1.salesman,
            T1.customer_id,
            T1.customer_name,
            T1.entry_person,
            T1.remarks,
            T1.attachment_materials,
            T1.tenant_id,
            T1.contract_amount,
            T1.execution_date,
            T2.nick_name AS entry_person_name,
            T1.payment_method
        T1.id,
        T1.sales_contract_no,
        T1.customer_contract_no,
        T1.project_name,
        T1.entry_date,
        T1.salesman,
        T1.customer_id,
        T1.customer_name,
        T1.entry_person,
        T1.remarks,
        T1.attachment_materials,
        T1.tenant_id,
        T1.contract_amount,
        T1.execution_date,
        T2.nick_name AS entry_person_name,
        T1.payment_method,
        DATEDIFF(T1.delivery_date, CURDATE()) AS delivery_days_diff
        FROM
            sales_ledger T1
        sales_ledger T1
        LEFT JOIN sys_user T2 ON T1.entry_person = T2.user_id
        <where>
            <if test="salesLedgerDto.customerName != null and salesLedgerDto.customerName != '' ">
@@ -59,7 +60,9 @@
        T1.contract_amount as noInvoiceAmountTotal,
        T1.execution_date,
        T2.nick_name AS entry_person_name,
        T1.payment_method
        T1.payment_method,
        T1.delivery_date,
        DATEDIFF(T1.delivery_date, CURDATE()) AS delivery_days_diff
        FROM
        sales_ledger T1
        LEFT JOIN sys_user T2 ON T1.entry_person = T2.user_id
@@ -85,4 +88,17 @@
        </where>
    order by T1.entry_date desc
    </select>
    <select id="selectIncomeStats" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
        SELECT DATE_FORMAT(entry_date, #{dateFormat}) as dateStr, IFNULL(SUM(contract_amount), 0) as amount
        FROM sales_ledger
        WHERE entry_date BETWEEN #{startDate} AND #{endDate}
        GROUP BY dateStr
    </select>
    <select id="selectCustomerSalesComposition" resultType="com.ruoyi.dto.MapDto">
        SELECT customer_name as name, CAST(IFNULL(SUM(contract_amount), 0) AS CHAR) as value
        FROM sales_ledger
        GROUP BY customer_name
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -81,6 +81,9 @@
            <if test="req.purchaseContractNumber != null and req.purchaseContractNumber != '' ">
                AND sl.purchase_contract_number like concat('%',#{req.purchaseContractNumber},'%')
            </if>
            <if test="req.approvalStatus != null and req.approvalStatus != ''">
                and sl.approval_status = #{req.approvalStatus}
            </if>
            <if test="req.customerContractNo != null and req.customerContractNo != '' ">
                AND sl.customer_contract_no like concat('%',#{req.customerContractNo},'%')
            </if>
@@ -159,7 +162,7 @@
        left join product p on pm.product_id = p.id
    </select>
    <select id="selectProductSalesAnalysis" resultType="java.util.LinkedHashMap">
    <select id="selectProductSalesAnalysis" resultType="java.util.Map">
        SELECT
            product_category as name,
            SUM( tax_inclusive_total_price ) AS value
@@ -174,7 +177,7 @@
        LIMIT 5
    </select>
    <select id="selectRawMaterialPurchaseAnalysis" resultType="java.util.LinkedHashMap">
    <select id="selectRawMaterialPurchaseAnalysis" resultType="java.util.Map">
        SELECT
            pr.product_name AS name,
            SUM( slp.tax_inclusive_total_price ) AS value
@@ -200,4 +203,21 @@
        AND register_date &lt;= #{endDate}
    </select>
    <select id="selectRawMaterialExpense" resultType="java.math.BigDecimal">
        WITH RECURSIVE product_tree AS (SELECT id
                                        FROM product
                                        WHERE product_name = '原材料'
                                        UNION ALL
                                        SELECT p.id
                                        FROM product p
                                                 INNER JOIN product_tree pt ON p.parent_id = pt.id)
        SELECT IFNULL(SUM(slp.tax_inclusive_total_price), 0)
        FROM sales_ledger_product slp
        WHERE slp.type = 2
          AND slp.product_id IN (SELECT id
                                 FROM product_tree);
    </select>
</mapper>
src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -2,7 +2,7 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.sales.mapper.ShippingInfoMapper">
    <select id="listPage" resultType="com.ruoyi.sales.pojo.ShippingInfo">
    <select id="listPage" resultType="com.ruoyi.sales.dto.ShippingInfoDto">
        SELECT
        s.id,
        s.sales_ledger_id,
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -188,11 +188,12 @@
        select ifnull(sum(qualitity), 0)
        from stock_inventory
    </select>
    <select id="selectTotalByDate" resultType="java.math.BigDecimal">
        select sum(qualitity)
        select IFNULL(sum(qualitity), 0)
        from stock_inventory
        where
           create_time &gt;= #{now} and create_time &lt; DATE_ADD(#{now}, INTERVAL 1 DAY)
        where create_time &gt;= #{now}
          and create_time &lt; DATE_ADD(#{now}, INTERVAL 1 DAY)
    </select>
    <select id="selectStorageProductCountByDate" resultType="int">
src/main/resources/static/safe-training-details.docx
Binary files differ
src/main/resources/static/safe-training.docx
Binary files differ