已添加9个文件
已修改34个文件
1231 ■■■■ 文件已修改
doc/万通新型建材.sql 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ai/assistant/FinancialIntentExecutor.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ai/context/AiSessionUserContext.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ai/controller/FinancialAiController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ai/schedule/AiSessionCleanupTask.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ai/tools/ApproveTodoTools.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ai/tools/FinancialAgentTools.java 209 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/OrderUtils.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/SubsidyConfigurationController.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/mapper/SubsidyConfigurationMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/pojo/StaffSalaryDetail.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/pojo/SubsidyConfiguration.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/SubsidyConfigurationService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java 107 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/SubsidyConfigurationServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-ckgm.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/financial-agent-prompt.txt 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/aftersalesservice/AfterSalesNearExpiryMapper.xml 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/aftersalesservice/AfterSalesServiceMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalInstanceMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/FinReimbursementMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/EnterpriseNewsMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/NoticeMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceMaintenanceMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceRepairMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/SparePartsMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/SparePartsRequisitionRecordMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/StaffLeaveMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/StaffOnJobMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/SubsidyConfigurationMapper.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysDeptMapper.xml 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysDictTypeMapper.xml 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysPostMapper.xml 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysRoleMapper.xml 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysUserDeptMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysUserMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/ÍòͨÐÂÐͽ¨²Ä.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
drop table if exists subsidy_configuration;
create table subsidy_configuration
(
    id          bigint auto_increment
        primary key,
    meal_amount decimal(16, 4) not null default 0 comment '餐补(元)',
    night_amount decimal(16, 4) not null default 0 comment '夜班补助(元)',
    create_time datetime null comment '录入时间',
    tenant_id   bigint not null default 0 comment '租户id'
);
alter table staff_salary_detail
    add day_days decimal(16, 4) default 0 not null comment '白班天数',
    add night_days decimal(16, 4) default 0 not null comment '夜班天数',
    add meal_amount decimal(16, 4) default 0 not null comment '餐补',
    add night_amount decimal(16, 4) default 0 not null comment '夜班补贴';
src/main/java/com/ruoyi/ai/assistant/FinancialIntentExecutor.java
@@ -14,9 +14,10 @@
public class FinancialIntentExecutor {
    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final Pattern LIMIT_PATTERN = Pattern.compile("(前|最近)?\\s*(\\d{1,2})\\s*条");
    private static final Pattern LIMIT_PATTERN = Pattern.compile("(?:前|最近|展示|返回)?\\s*(\\d{1,2})\\s*(?:条|个|名)");
    private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4}-\\d{2}-\\d{2})");
    private static final Pattern RELATIVE_DAY_PATTERN = Pattern.compile("(近|最近)?\\s*(\\d{1,3})\\s*天");
    private static final Pattern RELATIVE_DAY_PATTERN = Pattern.compile("(?:近|最近)\\s*(\\d{1,3})\\s*天");
    private static final Pattern FUTURE_MONTH_PATTERN = Pattern.compile("(?:未来|后续|接下来)\\s*(\\d{1,2})\\s*(?:个)?月");
    private final FinancialAgentTools financialAgentTools;
@@ -42,18 +43,18 @@
        String endDate = dateRange.endDate();
        String timeRange = dateRange.label();
        if (containsAny(text, "成本核算", "产品成本", "工序成本", "人工成本", "折旧", "材料损耗")) {
        if (containsAny(text, "成本核算", "产品成本", "工序成本", "人工成本", "折旧", "材料损耗", "成本最高")) {
            return financialAgentTools.calculateIntelligentCost(memoryId, startDate, endDate, timeRange, keyword, limit);
        }
        if (containsAny(text, "利润分析", "订单利润", "亏损订单", "低利润",
                "最赚钱客户", "哪个客户最赚钱", "客户最赚钱", "利润最高客户", "利润贡献最高客户", "利润下降")) {
        if (containsAny(text, "利润分析", "订单利润", "亏损订单", "低利润", "最赚钱客户", "哪个客户最赚钱",
                "客户最赚钱", "利润最高客户", "利润贡献最高", "利润下降")) {
            return financialAgentTools.analyzeOrderProfit(memoryId, startDate, endDate, timeRange, keyword, limit);
        }
        if (containsAny(text, "库存资金", "库存积压", "呆滞库存", "资金占用", "周转率", "库存周转")) {
            return financialAgentTools.analyzeInventoryCapital(memoryId, startDate, endDate, timeRange, keyword, limit);
        }
        if (containsAny(text, "现金流", "回款风险", "付款压力", "资金缺口", "应收", "应付", "回款预测")) {
            return financialAgentTools.forecastCashFlow(memoryId, startDate, endDate, timeRange, limit);
            return financialAgentTools.forecastCashFlow(memoryId, startDate, endDate, timeRange, extractForecastMonths(text));
        }
        if (containsAny(text, "异常预警", "经营异常", "风险预警", "成本异常", "利润异常", "回款异常", "订单风险")) {
            return financialAgentTools.detectBusinessAnomalies(memoryId, startDate, endDate, timeRange, limit);
@@ -73,30 +74,35 @@
    private String tryExecuteQuickPrompt(String memoryId, String text) {
        String normalized = normalizeForMatch(text);
        if ("生成本周经营周报利润与现金流".equals(normalized) || "生成本周经营周报".equals(normalized) || "生成周报".equals(normalized)) {
            DateRange range = weekRange();
            return financialAgentTools.generateOperationReport(memoryId, range.startDate(), range.endDate(), range.label(), "weekly");
        }
        if ("分析本月利润下降原因".equals(normalized)) {
            DateRange range = monthRange();
            return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, null);
        }
        if ("近30天哪个客户利润贡献最高".equals(normalized)) {
            DateRange range = recentDaysRange(30);
            return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, null);
        }
        if ("查看本月经营驾驶舱".equals(normalized) || "查看经营驾驶舱".equals(normalized)) {
            DateRange range = monthRange();
            return financialAgentTools.getBusinessCockpit(memoryId, range.startDate(), range.endDate(), range.label());
        }
        if ("查询近30天亏损订单".equals(normalized) || "哪个订单亏损".equals(normalized)) {
        if ("查询近30天亏损订单".equals(normalized)) {
            DateRange range = recentDaysRange(30);
            return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, 20);
            return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, null);
        }
        if ("生成本周经营周报".equals(normalized) || "生成周报".equals(normalized)) {
            DateRange range = weekRange();
            return financialAgentTools.generateOperationReport(memoryId, range.startDate(), range.endDate(), range.label(), "weekly");
        if ("分析近30天库存资金占用".equals(normalized)) {
            DateRange range = recentDaysRange(30);
            return financialAgentTools.analyzeInventoryCapital(memoryId, range.startDate(), range.endDate(), range.label(), null, null);
        }
        if ("为什么利润下降".equals(normalized)) {
            DateRange range = monthRange();
            return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, 20);
        if ("预测未来3个月现金流".equals(normalized)) {
            return financialAgentTools.forecastCashFlow(memoryId, null, null, null, 3);
        }
        if ("哪个客户最赚钱".equals(normalized)
                || "最近哪个客户最赚钱".equals(normalized)
                || "本月哪个客户最赚钱".equals(normalized)
                || "近30天哪个客户最赚钱".equals(normalized)
                || "哪个客户利润最高".equals(normalized)
                || "哪个客户利润贡献最高".equals(normalized)) {
            DateRange range = extractDateRange(text);
            return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, 20);
        if ("哪个工序成本最高".equals(normalized)) {
            return financialAgentTools.calculateIntelligentCost(memoryId, null, null, null, null, null);
        }
        return null;
    }
@@ -112,7 +118,12 @@
    private Integer extractLimit(String text) {
        Matcher matcher = LIMIT_PATTERN.matcher(text);
        return matcher.find() ? Integer.parseInt(matcher.group(2)) : 10;
        return matcher.find() ? Integer.parseInt(matcher.group(1)) : null;
    }
    private Integer extractForecastMonths(String text) {
        Matcher matcher = FUTURE_MONTH_PATTERN.matcher(text);
        return matcher.find() ? Integer.parseInt(matcher.group(1)) : null;
    }
    private DateRange extractDateRange(String text) {
@@ -128,7 +139,7 @@
        if (text.contains("上月")) {
            return lastMonthRange();
        }
        if (text.contains("本年") || text.contains("今年")) {
        if (text.contains("今年") || text.contains("本年")) {
            return yearRange();
        }
        if (text.contains("本周")) {
@@ -136,17 +147,17 @@
        }
        Matcher relativeDayMatcher = RELATIVE_DAY_PATTERN.matcher(text);
        if (relativeDayMatcher.find()) {
            int days = Integer.parseInt(relativeDayMatcher.group(2));
            int days = Integer.parseInt(relativeDayMatcher.group(1));
            return recentDaysRange(days);
        }
        return new DateRange(null, null, "近30天");
        return new DateRange(null, null, null);
    }
    private DateRange buildDateRange(String start, String end, String label) {
        LocalDate startDate = parseDate(start);
        LocalDate endDate = parseDate(end);
        if (startDate == null || endDate == null) {
            return new DateRange(null, null, "近30天");
            return new DateRange(null, null, null);
        }
        if (startDate.isAfter(endDate)) {
            LocalDate temp = startDate;
@@ -200,7 +211,11 @@
        if (!StringUtils.hasText(text)) {
            return "";
        }
        return text.replace(",", "")
        return text.replace("(", "")
                .replace(")", "")
                .replace("(", "")
                .replace(")", "")
                .replace(",", "")
                .replace(",", "")
                .replace("。", "")
                .replace(".", "")
@@ -218,13 +233,16 @@
    private String extractKeyword(String text) {
        String cleaned = text
                .replaceAll("\\d{4}-\\d{2}-\\d{2}", "")
                .replaceAll("(?:近|最近)\\s*\\d{1,3}\\s*天", "")
                .replaceAll("(?:前|最近|展示|返回)?\\s*\\d{1,2}\\s*(?:条|个|名)", "")
                .replace("查询", "")
                .replace("查看", "")
                .replace("看下", "")
                .replace("看看", "")
                .replace("帮我", "")
                .replace("请", "")
                .replace("一下", "")
                .replace("一个", "")
                .replace("为什么", "")
                .replace("哪个客户最赚钱", "")
                .replace("最近哪个客户最赚钱", "")
src/main/java/com/ruoyi/ai/context/AiSessionUserContext.java
@@ -4,6 +4,8 @@
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -11,18 +13,26 @@
public class AiSessionUserContext {
    private final Map<String, LoginUser> loginUserByMemoryId = new ConcurrentHashMap<>();
    private final Map<String, Instant> lastAccessTimeByMemoryId = new ConcurrentHashMap<>();
    private static final Duration SESSION_TIMEOUT = Duration.ofHours(24);
    public void bind(String memoryId, LoginUser loginUser) {
        if (!StringUtils.hasText(memoryId) || loginUser == null) {
            return;
        }
        loginUserByMemoryId.put(memoryId, loginUser);
        lastAccessTimeByMemoryId.put(memoryId, Instant.now());
    }
    public LoginUser get(String memoryId) {
        if (!StringUtils.hasText(memoryId)) {
            return null;
        }
        if (isExpired(memoryId)) {
            remove(memoryId);
            return null;
        }
        lastAccessTimeByMemoryId.put(memoryId, Instant.now());
        return loginUserByMemoryId.get(memoryId);
    }
@@ -31,5 +41,25 @@
            return;
        }
        loginUserByMemoryId.remove(memoryId);
        lastAccessTimeByMemoryId.remove(memoryId);
    }
    public void cleanExpiredSessions() {
        Instant now = Instant.now();
        lastAccessTimeByMemoryId.entrySet().removeIf(entry -> {
            boolean expired = Duration.between(entry.getValue(), now).compareTo(SESSION_TIMEOUT) > 0;
            if (expired) {
                loginUserByMemoryId.remove(entry.getKey());
            }
            return expired;
        });
    }
    private boolean isExpired(String memoryId) {
        Instant lastAccess = lastAccessTimeByMemoryId.get(memoryId);
        if (lastAccess == null) {
            return true;
        }
        return Duration.between(lastAccess, Instant.now()).compareTo(SESSION_TIMEOUT) > 0;
    }
}
src/main/java/com/ruoyi/ai/controller/FinancialAiController.java
@@ -82,6 +82,16 @@
            return Flux.just(directResponse);
        }
        if (isBusinessDataIntent(userMessage)) {
            String noGuessResponse = "未识别到可执行的数据查询条件。为保证结果准确,当前不会推测或编造数据,请补充明确时间范围、客户、供应商或单号后再查询。";
            mongoChatMemoryStore.appendMessages(
                    memoryId,
                    List.of(UserMessage.from(userMessage), AiMessage.from(noGuessResponse))
            );
            aiChatSessionService.refreshSessionStats(memoryId, loginUser);
            return Flux.just(noGuessResponse);
        }
        return financialAgent.chat(memoryId, userMessage, currentDateForPrompt())
                .doOnComplete(() -> aiChatSessionService.refreshSessionStats(memoryId, loginUser))
                .doOnError(ex -> aiChatSessionService.refreshSessionStats(memoryId, loginUser));
@@ -109,4 +119,26 @@
    private String currentDateForPrompt() {
        return LocalDate.now(CHINA_ZONE_ID).format(CURRENT_DATE_FMT);
    }
    private boolean isBusinessDataIntent(String message) {
        if (!StringUtils.hasText(message)) {
            return false;
        }
        String text = message.trim();
        return containsAny(text,
                "查询", "查看", "统计", "分析", "建议", "成本核算", "产品成本", "工序成本",
                "订单利润", "亏损订单", "低利润", "库存资金", "库存积压", "呆滞库存",
                "现金流", "回款风险", "付款压力", "资金缺口", "应收", "应付",
                "异常预警", "经营异常", "风险预警", "驾驶舱", "经营看板", "经营总览",
                "日报", "周报", "经营报告", "分析报告", "业财融合", "口径", "指标解释");
    }
    private boolean containsAny(String text, String... keywords) {
        for (String keyword : keywords) {
            if (text.contains(keyword)) {
                return true;
            }
        }
        return false;
    }
}
src/main/java/com/ruoyi/ai/schedule/AiSessionCleanupTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.ai.schedule;
import com.ruoyi.ai.context.AiSessionUserContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class AiSessionCleanupTask {
    private final AiSessionUserContext aiSessionUserContext;
    public AiSessionCleanupTask(AiSessionUserContext aiSessionUserContext) {
        this.aiSessionUserContext = aiSessionUserContext;
    }
    @Scheduled(cron = "0 0 2 * * ?")
    public void cleanupExpiredSessions() {
        try {
            aiSessionUserContext.cleanExpiredSessions();
        } catch (Exception e) {
            System.err.println("清理过期AI会话失败: " + e.getMessage());
        }
    }
}
src/main/java/com/ruoyi/ai/tools/ApproveTodoTools.java
@@ -22,11 +22,10 @@
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
@@ -46,7 +45,7 @@
    private static final int DEFAULT_LIMIT = 10;
    private static final int MAX_LIMIT = 20;
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private final ApproveProcessMapper approveProcessMapper;
@@ -740,7 +739,10 @@
    }
    private String formatDate(Date value) {
        return value == null ? "" : DATE_FORMAT.format(value);
        if (value == null) {
            return "";
        }
        return value.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DATE_FORMATTER);
    }
    private long countByStatus(List<ApproveProcess> processes, int status) {
@@ -824,8 +826,9 @@
    private Date parseDate(String dateText) {
        try {
            return DATE_FORMAT.parse(dateText);
        } catch (ParseException e) {
            LocalDate localDate = LocalDate.parse(dateText, DATE_FORMATTER);
            return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
        } catch (Exception e) {
            throw new IllegalArgumentException("日期格式必须是 yyyy-MM-dd");
        }
    }
src/main/java/com/ruoyi/ai/tools/FinancialAgentTools.java
@@ -88,7 +88,14 @@
    private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
    private static final int DEFAULT_LIMIT = 10;
    private static final int MAX_LIMIT = 50;
    private static final BigDecimal DEFAULT_FALLBACK_MATERIAL_COST_RATE = new BigDecimal("0.65");
    private static final BigDecimal DEFAULT_FALLBACK_MATERIAL_COST_RATE = new BigDecimal("0.60");
    private static final BigDecimal DEFAULT_LABOR_COST_RATE = new BigDecimal("0.15");
    private static final BigDecimal DEFAULT_OVERHEAD_COST_RATE = new BigDecimal("0.10");
    private static final BigDecimal SME_RECEIVABLE_RISK_THRESHOLD = new BigDecimal("500000");
    private static final BigDecimal SME_INVENTORY_RISK_THRESHOLD = new BigDecimal("1000000");
    private static final BigDecimal SME_PROFIT_WARNING_RATE = new BigDecimal("0.08");
    private final SalesLedgerMapper salesLedgerMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
@@ -194,13 +201,17 @@
                                           @P(value = "关键词,可匹配合同号/客户/项目", required = false) String keyword,
                                           @P(value = "返回条数,默认10,最大50", required = false) Integer limit) {
        LoginUser loginUser = currentLoginUser(memoryId);
        if (loginUser == null) {
            return jsonResponse(false, "financial_cost_accounting", "用户信息获取失败", Map.of(), Map.of(), Map.of());
        }
        DateRange range = resolveDateRange(startDate, endDate, timeRange, "近30天");
        AnalysisBundle bundle = buildOrderProfitBundle(loginUser, range, keyword, limit);
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("timeRange", range.label());
        summary.put("startDate", range.start().toString());
        summary.put("endDate", range.end().toString());
        summary.put("startDate", displayDate(range.start()));
        summary.put("endDate", displayDate(range.end()));
        summary.put("orderCount", bundle.orderMetrics().size());
        summary.put("totalRevenue", bundle.totalRevenue());
        summary.put("totalMaterialCost", bundle.totalMaterialCost());
@@ -248,12 +259,16 @@
                                     @P(value = "关键词,可匹配合同号/客户/项目", required = false) String keyword,
                                     @P(value = "返回条数,默认10,最大50", required = false) Integer limit) {
        LoginUser loginUser = currentLoginUser(memoryId);
        if (loginUser == null) {
            return jsonResponse(false, "financial_order_profit_analysis", "用户信息获取失败", Map.of(), Map.of(), Map.of());
        }
        DateRange range = resolveDateRange(startDate, endDate, timeRange, "近30天");
        AnalysisBundle bundle = buildOrderProfitBundle(loginUser, range, keyword, limit);
        List<OrderProfitMetric> metrics = bundle.orderMetrics();
        List<OrderProfitMetric> riskyOrders = metrics.stream()
                .filter(item -> item.profit().compareTo(BigDecimal.ZERO) < 0 || item.profitRate().compareTo(new BigDecimal("0.08")) < 0)
                .filter(item -> item.profit().compareTo(BigDecimal.ZERO) < 0 || item.profitRate().compareTo(SME_PROFIT_WARNING_RATE) < 0)
                .sorted(Comparator.comparing(OrderProfitMetric::profitRate)
                        .thenComparing(OrderProfitMetric::profit))
                .toList();
@@ -268,8 +283,8 @@
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("timeRange", range.label());
        summary.put("startDate", range.start().toString());
        summary.put("endDate", range.end().toString());
        summary.put("startDate", displayDate(range.start()));
        summary.put("endDate", displayDate(range.end()));
        summary.put("orderCount", metrics.size());
        summary.put("lossOrderCount", metrics.stream().filter(item -> item.profit().compareTo(BigDecimal.ZERO) < 0).count());
        summary.put("lowProfitOrderCount", riskyOrders.size());
@@ -394,8 +409,8 @@
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("timeRange", range.label());
        summary.put("startDate", range.start().toString());
        summary.put("endDate", range.end().toString());
        summary.put("startDate", displayDate(range.start()));
        summary.put("endDate", displayDate(range.end()));
        summary.put("actualIncomeTotal", collections.stream().map(AccountSalesCollection::getCollectionAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
        summary.put("actualExpenseTotal", payments.stream().map(AccountPurchasePayment::getPaymentAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
        summary.put("receivableBalance", receivableTotal);
@@ -481,8 +496,8 @@
        List<Map<String, Object>> topAnomalies = anomalyItems.stream().limit(finalLimit).toList();
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("timeRange", range.label());
        summary.put("startDate", range.start().toString());
        summary.put("endDate", range.end().toString());
        summary.put("startDate", displayDate(range.start()));
        summary.put("endDate", displayDate(range.end()));
        summary.put("anomalyCount", topAnomalies.size());
        summary.put("highRiskCount", topAnomalies.stream().filter(item -> "high".equals(item.get("riskLevel"))).count());
        summary.put("mediumRiskCount", topAnomalies.stream().filter(item -> "medium".equals(item.get("riskLevel"))).count());
@@ -521,8 +536,8 @@
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("timeRange", range.label());
        summary.put("startDate", range.start().toString());
        summary.put("endDate", range.end().toString());
        summary.put("startDate", displayDate(range.start()));
        summary.put("endDate", displayDate(range.end()));
        summary.put("outputValue", outputValue);
        summary.put("profit", profitBundle.totalProfit());
        summary.put("profitRate", toPercent(profitRate));
@@ -594,18 +609,18 @@
        if (lossCount > 0) {
            riskSuggestions.add(riskSuggestion("利润风险", "高", "复核亏损订单BOM和工序工资定额,必要时调整报价与交付节奏。"));
        }
        if (snapshot.receivableTotal().compareTo(new BigDecimal("1000000")) > 0) {
        if (snapshot.receivableTotal().compareTo(SME_RECEIVABLE_RISK_THRESHOLD) > 0) {
            riskSuggestions.add(riskSuggestion("回款风险", "中", "对应收TOP客户建立周度回款计划,并设置预警阈值。"));
        }
        if (inventoryValue.compareTo(new BigDecimal("3000000")) > 0) {
        if (inventoryValue.compareTo(SME_INVENTORY_RISK_THRESHOLD) > 0) {
            riskSuggestions.add(riskSuggestion("库存风险", "中", "对高金额呆滞库存执行降价、替代和生产消耗策略。"));
        }
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("reportType", type);
        summary.put("timeRange", range.label());
        summary.put("startDate", range.start().toString());
        summary.put("endDate", range.end().toString());
        summary.put("startDate", displayDate(range.start()));
        summary.put("endDate", displayDate(range.end()));
        summary.put("orderCount", bundle.orderMetrics().size());
        summary.put("lossOrderCount", lossCount);
        summary.put("riskSuggestionCount", riskSuggestions.size());
@@ -721,9 +736,11 @@
        applyTenantFilter(outWrapper, loginUser.getTenantId(), ProcurementRecordOut::getTenantId);
        applyDeptFilter(outWrapper, loginUser.getCurrentDeptId(), ProcurementRecordOut::getDeptId);
        outWrapper.eq(ProcurementRecordOut::getType, 2)
                .in(ProcurementRecordOut::getSalesLedgerProductId, ledgerProductIds)
                .ge(ProcurementRecordOut::getCreateTime, range.start().atStartOfDay())
                .lt(ProcurementRecordOut::getCreateTime, range.end().plusDays(1).atStartOfDay());
                .in(ProcurementRecordOut::getSalesLedgerProductId, ledgerProductIds);
        if (range.hasDateFilter()) {
            outWrapper.ge(ProcurementRecordOut::getCreateTime, range.start().atStartOfDay())
                    .lt(ProcurementRecordOut::getCreateTime, range.end().plusDays(1).atStartOfDay());
        }
        List<ProcurementRecordOut> outList = defaultList(procurementRecordOutMapper.selectList(outWrapper));
        Set<Integer> storageIds = outList.stream()
@@ -778,8 +795,10 @@
        LambdaQueryWrapper<ProductionOrder> orderWrapper = new LambdaQueryWrapper<>();
        applyDeptFilter(orderWrapper, loginUser.getCurrentDeptId(), ProductionOrder::getDeptId);
        orderWrapper.ge(ProductionOrder::getCreateTime, range.start().atStartOfDay().minusMonths(2))
                .lt(ProductionOrder::getCreateTime, range.end().plusDays(1).atStartOfDay().plusMonths(1));
        if (range.hasDateFilter()) {
            orderWrapper.ge(ProductionOrder::getCreateTime, range.start().atStartOfDay().minusMonths(2))
                    .lt(ProductionOrder::getCreateTime, range.end().plusDays(1).atStartOfDay().plusMonths(1));
        }
        List<ProductionOrder> orders = defaultList(productionOrderMapper.selectList(orderWrapper));
        Map<Long, Set<Long>> orderIdToLedgerIds = new HashMap<>();
@@ -815,9 +834,11 @@
        LambdaQueryWrapper<ProductionProductMain> mainWrapper = new LambdaQueryWrapper<>();
        applyDeptFilter(mainWrapper, loginUser.getCurrentDeptId(), ProductionProductMain::getDeptId);
        mainWrapper.in(ProductionProductMain::getProductionOperationTaskId, taskIdToOrderId.keySet())
                .ge(ProductionProductMain::getCreateTime, range.start().atStartOfDay().minusMonths(2))
                .lt(ProductionProductMain::getCreateTime, range.end().plusDays(1).atStartOfDay().plusMonths(1));
        mainWrapper.in(ProductionProductMain::getProductionOperationTaskId, taskIdToOrderId.keySet());
        if (range.hasDateFilter()) {
            mainWrapper.ge(ProductionProductMain::getCreateTime, range.start().atStartOfDay().minusMonths(2))
                    .lt(ProductionProductMain::getCreateTime, range.end().plusDays(1).atStartOfDay().plusMonths(1));
        }
        List<ProductionProductMain> mainList = defaultList(productionProductMainMapper.selectList(mainWrapper));
        Map<Long, Set<Long>> mainIdToLedgers = new HashMap<>();
        for (ProductionProductMain main : mainList) {
@@ -837,9 +858,11 @@
        LambdaQueryWrapper<ProductionAccount> accountWrapper = new LambdaQueryWrapper<>();
        applyDeptFilter(accountWrapper, loginUser.getCurrentDeptId(), ProductionAccount::getDeptId);
        accountWrapper.in(ProductionAccount::getProductionProductMainId, mainIdToLedgers.keySet())
                .ge(ProductionAccount::getSchedulingDate, range.start().atStartOfDay())
                .lt(ProductionAccount::getSchedulingDate, range.end().plusDays(1).atStartOfDay());
        accountWrapper.in(ProductionAccount::getProductionProductMainId, mainIdToLedgers.keySet());
        if (range.hasDateFilter()) {
            accountWrapper.ge(ProductionAccount::getSchedulingDate, range.start().atStartOfDay())
                    .lt(ProductionAccount::getSchedulingDate, range.end().plusDays(1).atStartOfDay());
        }
        List<ProductionAccount> accountList = defaultList(productionAccountMapper.selectList(accountWrapper));
        Map<String, BigDecimal> salaryQuotaByOperation = defaultList(technologyOperationMapper.selectList(new LambdaQueryWrapper<TechnologyOperation>()
@@ -868,9 +891,11 @@
        LambdaQueryWrapper<ProductionProductOutput> outputWrapper = new LambdaQueryWrapper<>();
        applyDeptFilter(outputWrapper, loginUser.getCurrentDeptId(), ProductionProductOutput::getDeptId);
        outputWrapper.in(ProductionProductOutput::getProductionProductMainId, mainIdToLedgers.keySet())
                .ge(ProductionProductOutput::getCreateTime, range.start().atStartOfDay())
                .lt(ProductionProductOutput::getCreateTime, range.end().plusDays(1).atStartOfDay());
        outputWrapper.in(ProductionProductOutput::getProductionProductMainId, mainIdToLedgers.keySet());
        if (range.hasDateFilter()) {
            outputWrapper.ge(ProductionProductOutput::getCreateTime, range.start().atStartOfDay())
                    .lt(ProductionProductOutput::getCreateTime, range.end().plusDays(1).atStartOfDay());
        }
        List<ProductionProductOutput> outputList = defaultList(productionProductOutputMapper.selectList(outputWrapper));
        Map<Long, BigDecimal> scrapCostByLedger = new HashMap<>();
        for (ProductionProductOutput output : outputList) {
@@ -909,10 +934,14 @@
                    .or().like(SalesLedger::getProjectName, keyword)
                    .or().like(SalesLedger::getSalesman, keyword));
        }
        wrapper.ge(SalesLedger::getEntryDate, toDate(range.start()))
                .lt(SalesLedger::getEntryDate, toExclusiveEndDate(range.end()))
                .orderByDesc(SalesLedger::getEntryDate, SalesLedger::getId)
                .last("limit " + normalizeLimit(limit));
        if (range.hasDateFilter()) {
            wrapper.ge(SalesLedger::getEntryDate, toDate(range.start()))
                    .lt(SalesLedger::getEntryDate, toExclusiveEndDate(range.end()));
        }
        wrapper.orderByDesc(SalesLedger::getEntryDate, SalesLedger::getId);
        if (limit != null && limit > 0) {
            wrapper.last("limit " + normalizeLimit(limit));
        }
        return defaultList(salesLedgerMapper.selectList(wrapper));
    }
@@ -1002,8 +1031,10 @@
        if (productModelIds != null && !productModelIds.isEmpty()) {
            wrapper.in(ProcurementRecordOut::getProductModelId, productModelIds);
        }
        wrapper.ge(ProcurementRecordOut::getCreateTime, range.start().atStartOfDay())
                .lt(ProcurementRecordOut::getCreateTime, range.end().plusDays(1).atStartOfDay());
        if (range.hasDateFilter()) {
            wrapper.ge(ProcurementRecordOut::getCreateTime, range.start().atStartOfDay())
                    .lt(ProcurementRecordOut::getCreateTime, range.end().plusDays(1).atStartOfDay());
        }
        List<ProcurementRecordOut> outList = defaultList(procurementRecordOutMapper.selectList(wrapper));
        if (outList.isEmpty()) {
            return OutboundStats.empty();
@@ -1098,18 +1129,22 @@
    private List<AccountSalesCollection> queryCollections(LoginUser loginUser, DateRange range) {
        LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>();
        applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId);
        wrapper.ge(AccountSalesCollection::getCollectionDate, range.start())
                .le(AccountSalesCollection::getCollectionDate, range.end())
                .orderByAsc(AccountSalesCollection::getCollectionDate);
        if (range.hasDateFilter()) {
            wrapper.ge(AccountSalesCollection::getCollectionDate, range.start())
                    .le(AccountSalesCollection::getCollectionDate, range.end());
        }
        wrapper.orderByAsc(AccountSalesCollection::getCollectionDate);
        return defaultList(accountSalesCollectionMapper.selectList(wrapper));
    }
    private List<AccountPurchasePayment> queryPayments(LoginUser loginUser, DateRange range) {
        LambdaQueryWrapper<AccountPurchasePayment> wrapper = new LambdaQueryWrapper<>();
        applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountPurchasePayment::getDeptId);
        wrapper.ge(AccountPurchasePayment::getPaymentDate, range.start())
                .le(AccountPurchasePayment::getPaymentDate, range.end())
                .orderByAsc(AccountPurchasePayment::getPaymentDate);
        if (range.hasDateFilter()) {
            wrapper.ge(AccountPurchasePayment::getPaymentDate, range.start())
                    .le(AccountPurchasePayment::getPaymentDate, range.end());
        }
        wrapper.orderByAsc(AccountPurchasePayment::getPaymentDate);
        return defaultList(accountPurchasePaymentMapper.selectList(wrapper));
    }
@@ -1118,8 +1153,12 @@
                                                       List<AccountPurchasePayment> payments) {
        Map<YearMonth, BigDecimal> incomeByMonth = new LinkedHashMap<>();
        Map<YearMonth, BigDecimal> expenseByMonth = new LinkedHashMap<>();
        YearMonth startMonth = YearMonth.from(range.start());
        YearMonth endMonth = YearMonth.from(range.end());
        DateRange monthlyRange = range.hasDateFilter() ? range : inferCashFlowRange(collections, payments);
        if (!monthlyRange.hasDateFilter()) {
            return List.of();
        }
        YearMonth startMonth = YearMonth.from(monthlyRange.start());
        YearMonth endMonth = YearMonth.from(monthlyRange.end());
        for (YearMonth month = startMonth; !month.isAfter(endMonth); month = month.plusMonths(1)) {
            incomeByMonth.put(month, BigDecimal.ZERO);
            expenseByMonth.put(month, BigDecimal.ZERO);
@@ -1151,6 +1190,27 @@
            result.add(new MonthlyCashFlow(month.toString(), income, expense, income.subtract(expense)));
        }
        return result;
    }
    private DateRange inferCashFlowRange(List<AccountSalesCollection> collections,
                                         List<AccountPurchasePayment> payments) {
        LocalDate min = null;
        LocalDate max = null;
        for (AccountSalesCollection row : defaultList(collections)) {
            if (row.getCollectionDate() == null) {
                continue;
            }
            min = min == null || row.getCollectionDate().isBefore(min) ? row.getCollectionDate() : min;
            max = max == null || row.getCollectionDate().isAfter(max) ? row.getCollectionDate() : max;
        }
        for (AccountPurchasePayment row : defaultList(payments)) {
            if (row.getPaymentDate() == null) {
                continue;
            }
            min = min == null || row.getPaymentDate().isBefore(min) ? row.getPaymentDate() : min;
            max = max == null || row.getPaymentDate().isAfter(max) ? row.getPaymentDate() : max;
        }
        return min == null || max == null ? new DateRange(null, null, "全部") : new DateRange(min, max, "全部");
    }
    private List<MonthlyCashFlow> forecastMonthlyCashFlow(List<MonthlyCashFlow> actual, int forecastMonths) {
@@ -1274,7 +1334,35 @@
                return productAmount;
            }
        }
        return revenue.multiply(DEFAULT_FALLBACK_MATERIAL_COST_RATE);
        BigDecimal materialCost = revenue.multiply(DEFAULT_FALLBACK_MATERIAL_COST_RATE);
        BigDecimal laborCost = revenue.multiply(DEFAULT_LABOR_COST_RATE);
        BigDecimal overheadCost = revenue.multiply(DEFAULT_OVERHEAD_COST_RATE);
        return materialCost.add(laborCost).add(overheadCost);
    }
    private BigDecimal estimateTotalCost(BigDecimal revenue, List<SalesLedgerProduct> products) {
        if (revenue == null || revenue.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal materialCost = BigDecimal.ZERO;
        if (products != null && !products.isEmpty()) {
            materialCost = products.stream()
                    .map(SalesLedgerProduct::getTaxExclusiveTotalPrice)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        if (materialCost.compareTo(BigDecimal.ZERO) <= 0) {
            materialCost = revenue.multiply(DEFAULT_FALLBACK_MATERIAL_COST_RATE);
        }
        BigDecimal laborCost = revenue.multiply(DEFAULT_LABOR_COST_RATE);
        BigDecimal overheadCost = revenue.multiply(DEFAULT_OVERHEAD_COST_RATE);
        return materialCost.add(laborCost).add(overheadCost);
    }
    private Map<String, String> queryCustomerNameMap(Set<String> idSet) {
@@ -1352,6 +1440,9 @@
    }
    private DateRange previousSameLengthRange(DateRange range) {
        if (!range.hasDateFilter()) {
            return new DateRange(null, null, "全部");
        }
        long days = daysBetween(range.start(), range.end()) + 1L;
        LocalDate prevEnd = range.start().minusDays(1);
        LocalDate prevStart = prevEnd.minusDays(days - 1L);
@@ -1770,20 +1861,7 @@
        }
        if (!StringUtils.hasText(timeRange)) {
            if ("今天".equals(defaultLabel)) {
                return new DateRange(today, today, "今天");
            }
            if ("本周".equals(defaultLabel)) {
                LocalDate start = today.minusDays(today.getDayOfWeek().getValue() - 1L);
                return new DateRange(start, today, "本周");
            }
            if ("本月".equals(defaultLabel)) {
                return new DateRange(today.withDayOfMonth(1), today, "本月");
            }
            if ("近90天".equals(defaultLabel)) {
                return new DateRange(today.minusDays(89), today, "近90天");
            }
            return new DateRange(today.minusDays(29), today, defaultLabel);
            return new DateRange(null, null, "全部");
        }
        String text = timeRange.trim();
@@ -1840,7 +1918,7 @@
                return new DateRange(start, end, start + "至" + end);
            }
        }
        return new DateRange(today.minusDays(29), today, "近30天");
        return new DateRange(null, null, "全部");
    }
    private LocalDate parseLocalDate(String text) {
@@ -1870,6 +1948,10 @@
    }
    private String formatDate(LocalDate date) {
        return date == null ? "" : date.format(DATE_FMT);
    }
    private String displayDate(LocalDate date) {
        return date == null ? "" : date.format(DATE_FMT);
    }
@@ -1996,8 +2078,8 @@
    private Map<String, Object> rangeSummary(DateRange range, int count, String keyword) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("timeRange", range.label());
        summary.put("startDate", range.start().toString());
        summary.put("endDate", range.end().toString());
        summary.put("startDate", displayDate(range.start()));
        summary.put("endDate", displayDate(range.end()));
        summary.put("count", count);
        summary.put("keyword", safe(keyword));
        return summary;
@@ -2080,6 +2162,9 @@
    }
    private record DateRange(LocalDate start, LocalDate end, String label) {
        private boolean hasDateFilter() {
            return start != null && end != null;
        }
    }
    private record OrderProfitMetric(Long ledgerId,
src/main/java/com/ruoyi/common/utils/OrderUtils.java
@@ -62,6 +62,9 @@
     * @return è®¢å•编号
     */
    public static <T> String countTodayByCreateTime(BaseMapper<T> mapper,String preFix,String code, LocalDateTime createTime) {
        if (createTime == null) {
            createTime = LocalDateTime.now();
        }
        LocalDate today = createTime.toLocalDate();
        LocalDateTime todayStart = today.atStartOfDay();
        LocalDateTime tomorrowStart = today.plusDays(1).atStartOfDay();
@@ -110,6 +113,9 @@
     * @return å½“天记录数量
     */
    public static <T> String countAfterServiceTodayByCreateTime(BaseMapper<T> mapper,String preFix, LocalDateTime createTime) {
        if (createTime == null) {
            createTime = LocalDateTime.now();
        }
        LocalDate localDate = createTime.toLocalDate();
        LocalDateTime todayStart = LocalDateTime.of(
                localDate,
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
@@ -1,5 +1,6 @@
package com.ruoyi.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.bean.BeanUtils;
@@ -34,7 +35,7 @@
    @Override
    public AjaxResult listPage(Page page, MaintenanceTask maintenanceTask) {
        Page<MaintenanceTask> taskPage = maintenanceTaskMapper.selectPage(page, null);
        Page<MaintenanceTask> taskPage = maintenanceTaskMapper.selectPage(page, new QueryWrapper<MaintenanceTask>().orderByDesc("create_time"));
        // 2. å¦‚果没有数据,直接返回空分页
        if (taskPage.getRecords().isEmpty()) {
            return AjaxResult.success(taskPage);
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
@@ -67,6 +67,7 @@
        if (StringUtils.isNotBlank(inspectionTaskDto.getInspectionProject())) {
            queryWrapper.like(InspectionTask::getInspectionProject, inspectionTaskDto.getInspectionProject());
        }
        queryWrapper.orderByDesc(InspectionTask::getCreateTime);
        IPage<InspectionTask> entityPage = inspectionTaskMapper.selectPage(page, queryWrapper);
        //  æ— æ•°æ®æå‰è¿”回
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
@@ -53,6 +53,7 @@
        if (timingTask.getIsEnabled() != null) {
            queryWrapper.eq(TimingTask::getIsEnabled, timingTask.getIsEnabled());
        }
        queryWrapper.orderByDesc(TimingTask::getCreateTime);
        IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, queryWrapper);
        // 2. å¦‚果没有数据,直接返回空分页
src/main/java/com/ruoyi/staff/controller/SubsidyConfigurationController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package com.ruoyi.staff.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.staff.mapper.SubsidyConfigurationMapper;
import com.ruoyi.staff.pojo.StaffContract;
import com.ruoyi.staff.pojo.SubsidyConfiguration;
import com.ruoyi.staff.service.SubsidyConfigurationService;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
 * <p>
 *  å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-05-27 02:54:03
 */
@RestController
@RequestMapping("/subsidyConfiguration")
@RequiredArgsConstructor
@Tag(name = "补贴配置管理")
public class SubsidyConfigurationController {
    private final SubsidyConfigurationService subsidyConfigurationService;
    @GetMapping("/list")
    @Log(title = "补贴配置列表", businessType = BusinessType.OTHER)
    @Operation(summary = "补贴配置列表")
    public R list() {
        return R.ok(subsidyConfigurationService.list());
    }
    /**
     * ä¿å­˜è¡¥è´´é…ç½®
     * @param subsidyConfiguration
     * @return
     */
    @PostMapping("/save")
    @Log(title = "保存补贴配置", businessType = BusinessType.UPDATE)
    @Operation(summary = "保存补贴配置")
    public R save(@RequestBody SubsidyConfiguration subsidyConfiguration) {
        return R.ok(subsidyConfigurationService.saveOrUpdate(subsidyConfiguration));
    }
}
src/main/java/com/ruoyi/staff/mapper/SubsidyConfigurationMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.staff.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.staff.pojo.SubsidyConfiguration;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 *  Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-05-27 02:54:03
 */
@Mapper
public interface SubsidyConfigurationMapper extends BaseMapper<SubsidyConfiguration> {
}
src/main/java/com/ruoyi/staff/pojo/StaffSalaryDetail.java
@@ -50,6 +50,18 @@
    @Schema(description = "基本工资")
    private BigDecimal basicSalary;
    @Schema(description = "白班天数")
    private BigDecimal dayDays;
    @Schema(description = "夜班天数")
    private BigDecimal nightDays;
    @Schema(description = "餐补")
    private BigDecimal mealAmount;
    @Schema(description = "夜班补贴")
    private BigDecimal nightAmount;
    @Schema(description = "计件工资")
    private BigDecimal pieceSalary;
src/main/java/com/ruoyi/staff/pojo/SubsidyConfiguration.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package com.ruoyi.staff.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 com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
 *
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-05-27 02:54:03
 */
@Getter
@Setter
@ToString
@TableName("subsidy_configuration")
@ApiModel(value = "SubsidyConfiguration对象", description = "")
public class SubsidyConfiguration implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * é¤è¡¥ï¼ˆå…ƒï¼‰
     */
    @ApiModelProperty("餐补(元)")
    private BigDecimal mealAmount;
    /**
     * å¤œç­è¡¥åŠ©ï¼ˆå…ƒï¼‰
     */
    @ApiModelProperty("夜班补助(元)")
    private BigDecimal nightAmount;
    /**
     * å½•入时间
     */
    @ApiModelProperty("录入时间")
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    /**
     * ç§Ÿæˆ·id
     */
    @ApiModelProperty("租户id")
    private Long tenantId;
}
src/main/java/com/ruoyi/staff/service/SubsidyConfigurationService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.staff.service;
import com.ruoyi.staff.pojo.SubsidyConfiguration;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 *  æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-05-27 02:54:03
 */
public interface SubsidyConfigurationService extends IService<SubsidyConfiguration> {
}
src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java
@@ -13,12 +13,8 @@
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.staff.controller.TaxCalculator;
import com.ruoyi.staff.mapper.SchemeApplicableStaffMapper;
import com.ruoyi.staff.mapper.SchemeInsuranceDetailMapper;
import com.ruoyi.staff.mapper.StaffOnJobMapper;
import com.ruoyi.staff.pojo.SchemeApplicableStaff;
import com.ruoyi.staff.pojo.SchemeInsuranceDetail;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.staff.mapper.*;
import com.ruoyi.staff.pojo.*;
import com.ruoyi.staff.service.SchemeApplicableStaffService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -48,7 +44,10 @@
    private final SysUserMapper sysUserMapper;
    private final SysDeptMapper sysDeptMapper;
    private final StaffOnJobMapper staffOnJobMapper;
    private final PersonalShiftMapper personalShiftMapper;
    private final PersonalAttendanceLocationConfigMapper personalAttendanceLocationConfigMapper;
    private final SalesLedgerProductionAccountingService salesLedgerProductionAccountingService;
    private final SubsidyConfigurationMapper subsidyConfigurationMapper;
    @Override
@@ -191,15 +190,93 @@
        // å®žå‘工资
        BigDecimal netSalary = new BigDecimal("0.00");
        map.put("netSalary", netSalary);
        // è°ƒç”¨åŸºæœ¬å·¥èµ„
        // æ ¹æ®æŽ’班数据计算白班天数和夜班天数
        BigDecimal dayDays = new BigDecimal("0.00");
        BigDecimal nightDays = new BigDecimal("0.00");
        // æŸ¥è¯¢å½“月排班记录
        List<PersonalShift> shiftList = personalShiftMapper.selectList(new LambdaQueryWrapper<PersonalShift>()
                .eq(PersonalShift::getStaffOnJobId, staffId)
                .like(PersonalShift::getWorkTime, date));
        if(!CollectionUtils.isEmpty(shiftList)){
            // æ”¶é›†æ‰€æœ‰ç­æ¬¡é…ç½®ID
            List<Integer> configIds = shiftList.stream()
                    .map(PersonalShift::getPersonalAttendanceLocationConfigId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(configIds)){
                // æŸ¥è¯¢ç­æ¬¡é…ç½®ä¿¡æ¯
                List<PersonalAttendanceLocationConfig> configList = personalAttendanceLocationConfigMapper.selectList(
                        new LambdaQueryWrapper<PersonalAttendanceLocationConfig>()
                                .in(PersonalAttendanceLocationConfig::getId, configIds));
                // æž„建班次配置映射
                Map<Integer, String> configMap = configList.stream()
                        .collect(Collectors.toMap(
                                PersonalAttendanceLocationConfig::getId,
                                PersonalAttendanceLocationConfig::getShift,
                                (a, b) -> a));
                // ç»Ÿè®¡ç™½ç­å’Œå¤œç­å¤©æ•°
                for (PersonalShift shift : shiftList) {
                    Integer configId = shift.getPersonalAttendanceLocationConfigId();
                    String shiftName = configMap.get(configId);
                    if(shiftName != null){
                        // æ ¹æ®ç­æ¬¡åç§°åˆ¤æ–­ï¼šåŒ…含"白"或"日"为白班,包含"夜"为夜班
                        if(shiftName.contains("白") || shiftName.contains("日")){
                            dayDays = dayDays.add(BigDecimal.ONE);
                        } else if(shiftName.contains("夜")){
                            nightDays = nightDays.add(BigDecimal.ONE);
                        }
                    }
                }
            }
        }
        map.put("dayDays", dayDays); // ç™½ç­å¤©æ•°
        map.put("nightDays", nightDays); // å¤œç­å¤©æ•°
        // æŸ¥è¯¢é¤è¡¥å’Œå¤œç­è¡¥è´´
        // è®¡ç®—餐补(回族特有)和夜班补助
        BigDecimal mealAmount = new BigDecimal("0.00"); // é¤è¡¥
        BigDecimal nightAmount = new BigDecimal("0.00"); // å¤œç­è¡¥åŠ©
        // èŽ·å–é¤è¡¥å’Œå¤œç­è¡¥è´´æ ‡å‡†ï¼ˆä»Žè¡¥è´´é…ç½®ä¸­èŽ·å–ï¼‰
        SubsidyConfiguration subsidyConfig = subsidyConfigurationMapper.selectOne(null);
        BigDecimal mealStandard = new BigDecimal("0.00"); // é¤è¡¥æ ‡å‡†
        BigDecimal nightStandard = new BigDecimal("0.00"); // å¤œç­è¡¥è´´æ ‡å‡†
        if(subsidyConfig != null){
            mealStandard = subsidyConfig.getMealAmount() != null ? subsidyConfig.getMealAmount() : new BigDecimal("0.00");
            nightStandard = subsidyConfig.getNightAmount() != null ? subsidyConfig.getNightAmount() : new BigDecimal("0.00");
        }
        // é¤è¡¥ï¼šæ°‘族为回族特有,计算方式为(白班天数+夜班天数)*餐补标准
        StaffOnJob staffOnJobDto = staffOnJobMapper.selectById(staffId);
        if(staffOnJobDto == null){
            return;
        }
        String nation = staffOnJobDto.getNation();
        if("回族".equals(nation)){
            mealAmount = dayDays.add(nightDays).multiply(mealStandard);
        }
        // å¤œç­è¡¥åŠ©ï¼šå¤œç­å¤©æ•°*夜班补贴标准
        nightAmount = nightDays.multiply(nightStandard);
        map.put("mealAmount", mealAmount); // é¤è¡¥
        map.put("nightAmount", nightAmount); // å¤œç­è¡¥åŠ©
        // è°ƒç”¨åŸºæœ¬å·¥èµ„
        basicSalary = staffOnJobDto.getBasicSalary();
        map.put("basicSalary", basicSalary);
        // åº”发工资
        map.put("grossSalary", basicSalary);
        // åº”发工资(基本工资+餐补+夜班补助+其他收入)
        grossSalary = basicSalary.add(mealAmount).add(nightAmount).add(otherIncome);
        map.put("grossSalary", grossSalary);
        // å®žå‘工资初始值
        netSalary = grossSalary;
        map.put("netSalary", netSalary);
        // ä¸ªç¨Žé‡‘额(无社保版)
        BigDecimal bigDecimal = TaxCalculator.calculateMonthlyTax(basicSalary, socialPersonal, fundPersonal);
        map.put("salaryTax", bigDecimal);
@@ -242,10 +319,14 @@
        // ä¸ªç¨Žé‡‘额(社保版)
        bigDecimal = TaxCalculator.calculateMonthlyTax(basicSalary, socialPersonal, fundPersonal);
        map.put("salaryTax", bigDecimal);
        // åº”扣工资
        map.put("deductSalary", bigDecimal.add(fundPersonal).add(socialPersonal));
        // å®žå‘工资
        map.put("netSalary", basicSalary.subtract(bigDecimal).subtract(fundPersonal).subtract(socialPersonal));
        // åº”扣工资 = ä¸ªç¨Ž + å…¬ç§¯é‡‘个人 + ç¤¾ä¿ä¸ªäºº + å…¶ä»–支出
        deductSalary = bigDecimal.add(fundPersonal).add(socialPersonal).add(otherDeduct);
        map.put("deductSalary", deductSalary);
        // å®žå‘工资 = åº”发工资 - åº”扣工资
        netSalary = grossSalary.subtract(deductSalary);
        map.put("netSalary", netSalary);
    }
src/main/java/com/ruoyi/staff/service/impl/SubsidyConfigurationServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.staff.service.impl;
import com.ruoyi.staff.pojo.SubsidyConfiguration;
import com.ruoyi.staff.mapper.SubsidyConfigurationMapper;
import com.ruoyi.staff.service.SubsidyConfigurationService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * <p>
 *  æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-05-27 02:54:03
 */
@Service
public class SubsidyConfigurationServiceImpl extends ServiceImpl<SubsidyConfigurationMapper, SubsidyConfiguration> implements SubsidyConfigurationService {
}
src/main/resources/application-ckgm.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,268 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/file
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
  # ååŒå®¡æ‰¹ç¼–号前缀(配置文件后缀命名)
  approvalNumberPrefix: NEW
  # ä¸ªæŽ¨ Unipush é…ç½®
  getui:
    appId: PfjyAAE0FK64FaO1w2CMb1
    appKey: zTMb831OEL6J4GK1uE3Ob4
    masterSecret: K1GFtsv42v61tXGnF7SGE5
    domain: https://restapi.getui.cn/v2/
    # ç¦»çº¿æŽ¨é€ä½¿ç”¨çš„包名/组件名
    intentComponent: uni.app.UNI099A590/io.dcloud.PandoraEntry
# å¼€å‘环境配置
server:
  # æœåŠ¡å™¨çš„HTTP端口,默认为8080
  port: 9003
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100
# æ—¥å¿—配置
logging:
  level:
    com.ruoyi: warn
    org.springframework: warn
minio:
  endpoint: http://114.132.189.42/
  port: 7019
  secure: false
  accessKey: admin
  secretKey: 12345678
  preview-expiry: 24 # é¢„览地址默认24小时
  default-bucket: jxc
# ç”¨æˆ·é…ç½®
user:
  password:
    # å¯†ç æœ€å¤§é”™è¯¯æ¬¡æ•°
    maxRetryCount: 5
    # å¯†ç é”å®šæ—¶é—´ï¼ˆé»˜è®¤10分钟)
    lockTime: 10
# Spring配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://172.17.0.1:3306/product-inventory-management-ckgm?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      slave:
        # ä»Žæ•°æ®æºå¼€å…³/默认关闭
        enabled: false
        url:
        username:
        password:
      # åˆå§‹è¿žæŽ¥æ•°
      initialSize: 5
      # æœ€å°è¿žæŽ¥æ± æ•°é‡
      minIdle: 10
      # æœ€å¤§è¿žæŽ¥æ± æ•°é‡
      maxActive: 20
      # é…ç½®èŽ·å–è¿žæŽ¥ç­‰å¾…è¶…æ—¶çš„æ—¶é—´
      maxWait: 60000
      # é…ç½®è¿žæŽ¥è¶…æ—¶æ—¶é—´
      connectTimeout: 30000
      # é…ç½®ç½‘络超时时间
      socketTimeout: 60000
      # é…ç½®é—´éš”多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位是毫秒
      minEvictableIdleTimeMillis: 300000
      # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å¤§ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # è®¾ç½®ç™½åå•,不填则允许所有访问
        allow:
        url-pattern: /druid/*
        # æŽ§åˆ¶å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç 
        login-username: ruoyi
        login-password: 123456
      filter:
        stat:
          enabled: true
          # æ…¢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
  # èµ„源信息
  messages:
    # å›½é™…化资源文件路径
    basename: i18n/messages
  # æ–‡ä»¶ä¸Šä¼ 
  servlet:
    multipart:
      # å•个文件大小
      max-file-size: 1GB
      # è®¾ç½®æ€»ä¸Šä¼ çš„æ–‡ä»¶å¤§å°
      max-request-size: 2GB
  # æœåŠ¡æ¨¡å—
  devtools:
    restart:
      # çƒ­éƒ¨ç½²å¼€å…³
      enabled: false
  # redis é…ç½®
  data:
    mongodb:
      uri: mongodb://114.132.189.42:9028/chat_memory_db_ckgm
    # redis é…ç½®
    redis:
      # åœ°å€
#      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      timeout: 10s
      lettuce:
        pool:
          # è¿žæŽ¥æ± ä¸­çš„æœ€å°ç©ºé—²è¿žæŽ¥
          min-idle: 0
          # è¿žæŽ¥æ± ä¸­çš„æœ€å¤§ç©ºé—²è¿žæŽ¥
          max-idle: 8
          # è¿žæŽ¥æ± çš„æœ€å¤§æ•°æ®åº“连接数
          max-active: 8
          # #连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
  # Quartz定时任务配置(新增部分)
  quartz:
    job-store-type: jdbc  # ä½¿ç”¨æ•°æ®åº“存储
    jdbc:
      initialize-schema: never  # é¦–次运行时自动创建表结构,成功后改为never
      schema: classpath:org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql  # MySQL表结构脚本
    properties:
      org:
        quartz:
          scheduler:
            instanceName: RuoYiScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate  # MySQL适配
            tablePrefix: qrtz_  # è¡¨åå‰ç¼€ï¼Œä¸Žè„šæœ¬ä¸€è‡´
            isClustered: false  # å•节点模式(集群需改为true)
            clusterCheckinInterval: 10000
            txIsolationLevelSerializable: true
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10  # çº¿ç¨‹æ± å¤§å°
            threadPriority: 5
            makeThreadsDaemons: true
          updateCheck: false  # å…³é—­ç‰ˆæœ¬æ£€æŸ¥
# token配置
token:
  # ä»¤ç‰Œè‡ªå®šä¹‰æ ‡è¯†
  header: Authorization
  # ä»¤ç‰Œå¯†é’¥
  secret: xpAVjhCjQDaDB7mjPAzMDSbQWXNu2zYkTdDNUsPMS5Xx8QMmQVYN7n74eZrYJxDJ
  # ä»¤ç‰Œæœ‰æ•ˆæœŸï¼ˆé»˜è®¤30分钟)
  expireTime: 450
# MyBatis Plus配置
mybatis-plus:
  # æœç´¢æŒ‡å®šåŒ…别名   æ ¹æ®è‡ªå·±çš„项目来
  typeAliasesPackage: com.ruoyi.**.pojo
  # é…ç½®mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # åŠ è½½å…¨å±€çš„é…ç½®æ–‡ä»¶
  configLocation: classpath:mybatis/mybatis-config.xml
  global-config:
    enable-sql-runner: true
    db-config:
      id-type: auto
# PageHelper分页插件
pagehelper:
  helperDialect: mysql
  supportMethodsArguments: true
  params: count=countSql
# Swagger配置
swagger:
  # æ˜¯å¦å¼€å¯swagger
  enabled: true
  # è¯·æ±‚前缀
  pathMapping: /dev-api
# é˜²æ­¢XSS攻击
xss:
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接(多个用逗号分隔)
  excludes: /system/notice
  # åŒ¹é…é“¾æŽ¥
  urlPatterns: /system/*,/monitor/*,/tool/*
# ä»£ç ç”Ÿæˆ
gen:
  # ä½œè€…
  author: ruoyi
  # é»˜è®¤ç”ŸæˆåŒ…路径 system éœ€æ”¹æˆè‡ªå·±çš„æ¨¡å—名称 å¦‚ system monitor tool
  packageName: com.ruoyi.project.system
  # è‡ªåŠ¨åŽ»é™¤è¡¨å‰ç¼€ï¼Œé»˜è®¤æ˜¯true
  autoRemovePre: false
  # è¡¨å‰ç¼€ï¼ˆç”Ÿæˆç±»åä¸ä¼šåŒ…含表前缀,多个用逗号分隔)
  tablePrefix: sys_
  # æ˜¯å¦å…è®¸ç”Ÿæˆæ–‡ä»¶è¦†ç›–到本地(自定义路径),默认不允许
  allowOverwrite: false
# æ–‡ä»¶ä¸Šä¼ é…ç½®
file:
  temp-dir: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://1.15.17.182:9071 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/financial-agent-prompt.txt
@@ -2,10 +2,10 @@
当前日期:{{currentDate}}(中国时区)。
工作规则:
1. ç”¨æˆ·æå‡ºâ€œæŸ¥ã€é—®ã€ç»Ÿè®¡ã€åˆ†æžã€é¢„警、建议、报告”需求时,优先调用工具返回结构化 JSON,不编造业务数据。
2. å‘½ä¸­æˆæœ¬ã€åˆ©æ¶¦ã€åº“存资金、现金流、预警、驾驶舱、日报周报场景时,优先调用对应工具。
1. ç”¨æˆ·æå‡ºâ€œæŸ¥ã€é—®ã€ç»Ÿè®¡ã€åˆ†æžã€é¢„警、建议、报告”类需求时,优先调用工具返回结构化 JSON,不编造业务数据。
2. å‘½ä¸­æˆæœ¬ã€åˆ©æ¶¦ã€åº“存资金、现金流、预警、驾驶舱、日报、周报等场景时,优先调用对应工具。
3. å·¥å…·è¿”回 JSON æ—¶ï¼Œç›´æŽ¥è¾“出原始 JSON å­—符串,不要额外包裹 Markdown,也不要在前后追加解释文本。
4. å½“用户问题缺少时间范围时,默认使用工具内置口径(如近30天、本月、近90天),并在后续可提醒用户补充范围。
5. ç”¨æˆ·é—®â€œä¸ºä»€ä¹ˆåˆ©æ¶¦ä¸‹é™â€â€œå“ªä¸ªè®¢å•亏损”“哪个客户最赚钱”“哪个客户利润贡献最高”“哪个车间/工序成本最高”等问题时,优先基于订单利润与工序成本分析工具作答。
6. å›žç­”必须使用中文;若数据不足以得出结论,明确指出缺少哪些关键字段或筛选条件。
7. ç”¨æˆ·æåˆ°â€œä»Šå¹´/本月/今天/最近/上月/去年”等相对时间时,必须严格基于“当前日期”换算,禁止自行假设年份。
4. ç”¨æˆ·æ²¡æœ‰æ˜Žç¡®ç»™å‡ºæ—¶é—´ã€å®¢æˆ·ã€ä¾›åº”商、产品、订单、数量等筛选条件时,不要自行补充条件;工具参数保持为空,由工具按当前业务口径查询。
5. ç”¨æˆ·æåˆ°â€œæœ¬å‘¨ã€æœ¬æœˆã€ä»Šå¹´ã€ä»Šå¤©ã€æœ€è¿‘、近30天、上月、去年”等相对时间时,必须严格基于“当前日期”换算,禁止自行假设年份。
6. ç”¨æˆ·é—®â€œä¸ºä»€ä¹ˆåˆ©æ¶¦ä¸‹é™â€â€œå“ªä¸ªè®¢å•亏损”“哪个客户最赚钱”“哪个客户利润贡献最高”“哪个工序成本最高”等问题时,优先基于订单利润与工序成本分析工具作答。
7. å›žç­”必须使用中文;若数据不足以得出结论,明确指出缺少哪些关键字段或筛选条件。
src/main/resources/mapper/aftersalesservice/AfterSalesNearExpiryMapper.xml
@@ -25,17 +25,18 @@
    <select id="listPage" resultType="com.ruoyi.aftersalesservice.pojo.AfterSalesNearExpiry">
        select * from after_sales_near_expiry
        where 1 = 1
        <if test="req.expireDate != null">
            AND expire_date = #{req.expireDate}
        </if>
        <if test="req.disDate != null">
            AND dis_date = #{req.disDate}
        </if>
        <if test="req.status != null">
            AND status = #{req.status}
        </if>
        <where>
            <if test="req.expireDate != null">
                AND expire_date = #{req.expireDate}
            </if>
            <if test="req.disDate != null">
                AND dis_date = #{req.disDate}
            </if>
            <if test="req.status != null">
                AND status = #{req.status}
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>
</mapper>
src/main/resources/mapper/aftersalesservice/AfterSalesServiceMapper.xml
@@ -32,7 +32,7 @@
        <if test="req.serviceType != null">
            and service_type = #{req.serviceType}
        </if>
        order by update_time desc
        order by create_time desc
    </select>
    <select id="countAfterSalesService" resultType="com.ruoyi.aftersalesservice.dto.CountDto">
        select
@@ -42,4 +42,4 @@
        group by status
    </select>
</mapper>
</mapper>
src/main/resources/mapper/approve/ApprovalInstanceMapper.xml
@@ -52,7 +52,7 @@
                and ai.applicant_name = #{ew.applicantName}
            </if>
        </where>
        order by ai.id desc
        order by ai.create_time desc
    </select>
</mapper>
src/main/resources/mapper/approve/FinReimbursementMapper.xml
@@ -56,6 +56,7 @@
                and create_time &lt;= #{ew.createTimeEnd}
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>
src/main/resources/mapper/collaborativeApproval/EnterpriseNewsMapper.xml
@@ -41,6 +41,7 @@
                and en.create_time between #{enterpriseNewsDto.createTimeStart} and #{enterpriseNewsDto.createTimeEnd}
            </if>
        </where>
        order by en.create_time desc
    </select>
</mapper>
src/main/resources/mapper/collaborativeApproval/NoticeMapper.xml
@@ -17,6 +17,6 @@
                and n.status = #{ew.status}
            </if>
        </where>
        order by n.id desc
        order by n.create_time desc
    </select>
</mapper>
src/main/resources/mapper/device/DeviceMaintenanceMapper.xml
@@ -26,7 +26,6 @@
        left join device_ledger dl on dm.device_ledger_id = dl.id
        left join sys_user su on dm.create_user = su.user_id
        <where>
            1 = 1
            <if test="deviceMaintenanceDto.deviceName != null">
                and dl.device_name like concat('%',#{deviceMaintenanceDto.deviceName},'%')
            </if>
@@ -50,6 +49,7 @@
                and dm.maintenance_actually_time &lt; date_add(str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d'), interval 1 day)
            </if>
        </where>
        order by dm.create_time desc
    </select>
    <select id="detailById" resultType="com.ruoyi.device.vo.DeviceMaintenanceVo">
        select dm.id,
src/main/resources/mapper/device/DeviceRepairMapper.xml
@@ -29,7 +29,6 @@
        from device_repair dr
        left join device_ledger dl on dr.device_ledger_id = dl.id
        <where>
            1 = 1
            <if test="deviceRepairDto.deviceName != null">
                and dl.device_name like concat('%',#{deviceRepairDto.deviceName},'%')
            </if>
@@ -53,6 +52,7 @@
                and dr.maintenance_time like concat('%',#{deviceRepairDto.maintenanceTimeStr},'%')
            </if>
        </where>
        order by dr.create_time desc
    </select>
    <select id="detailById" resultType="com.ruoyi.device.vo.DeviceRepairVo">
        select dr.id,
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml
@@ -62,6 +62,6 @@
                AND record_date = DATE_FORMAT(#{req.recordDate},'%Y-%m-%d')
            </if>
        </where>
        ORDER BY update_time DESC
        ORDER BY create_time DESC
    </select>
</mapper>
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml
@@ -32,7 +32,7 @@
                AND t1.record_date = DATE_FORMAT(#{req.recordDate},'%Y-%m-%d')
            </if>
        </where>
        ORDER BY t1.update_time DESC
        ORDER BY t1.create_time DESC
    </select>
    <select id="list" resultType="com.ruoyi.measuringinstrumentledger.pojo.MeasuringInstrumentLedgerRecord">
        SELECT
@@ -65,4 +65,4 @@
        </where>
        ORDER BY t1.update_time DESC
    </select>
</mapper>
</mapper>
src/main/resources/mapper/measuringinstrumentledger/SparePartsMapper.xml
@@ -10,5 +10,6 @@
                and sp.name like concat('%',#{spareParts.name},'%')
            </if>
        </where>
        order by sp.create_time desc
    </select>
</mapper>
</mapper>
src/main/resources/mapper/measuringinstrumentledger/SparePartsRequisitionRecordMapper.xml
@@ -32,5 +32,6 @@
                and sprr.source_type = #{params.sourceType}
            </if>
        </where>
        order by sprr.create_time desc
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -132,10 +132,12 @@
        from sales_ledger sl
        left join purchase_ledger pl on sl.id = pl.sales_ledger_id
        left join customer c on sl.customer_id = c.id
        where 1=1
        <if test="customerName != null and customerName != '' ">
            and c.customer_name like concat('%',#{customerName},'%')
        </if>
        <where>
            <if test="customerName != null and customerName != '' ">
                and c.customer_name like concat('%',#{customerName},'%')
            </if>
        </where>
        order by sl.create_time desc
    </select>
</mapper>
src/main/resources/mapper/staff/StaffLeaveMapper.xml
@@ -35,7 +35,7 @@
        <if test="c.staffName != null and c.staffName != '' ">
            AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%')
        </if>
        order by staff_leave.id desc
        order by staff_leave.create_time desc
    </select>
    <select id="staffLeaveList" resultType="com.ruoyi.staff.dto.StaffLeaveDto">
        SELECT
@@ -62,10 +62,12 @@
        sys_post sp ON sp.post_id = soj.sys_post_id
        LEFT JOIN
        sys_dept sd ON sd.dept_id = soj.sys_dept_id
        where 1=1
        <if test="c.staffName != null and c.staffName != '' ">
            AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%')
        </if>
        <where>
            <if test="c.staffName != null and c.staffName != '' ">
                AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%')
            </if>
        </where>
        order by staff_leave.create_time desc
    </select>
    <select id="staffLeaveReasonAnalytics" resultType="com.ruoyi.staff.dto.StaffLeaveDto">
src/main/resources/mapper/staff/StaffOnJobMapper.xml
@@ -74,12 +74,15 @@
            <if test="staffOnJob.sysDeptId != null">
                AND staff_on_job.sys_dept_id = #{staffOnJob.sysDeptId}
            </if>
            <if test="staffOnJob.nation != null and staffOnJob.nation != '' ">
                AND staff_on_job.nation = #{staffOnJob.nation}
            </if>
        </where>
        GROUP BY staff_on_job.id
        <if test="staffOnJob.contractStartTime != null">
            HAVING MIN(t1.contract_start_time) = #{staffOnJob.contractStartTime}
        </if>
        order by staff_on_job.id desc
        order by staff_on_job.create_time desc
    </select>
    <select id="staffOnJobList" resultType="com.ruoyi.staff.dto.StaffOnJobDto">
        SELECT
@@ -91,13 +94,18 @@
        sys_post sp ON sp.post_id = staff_on_job.sys_post_id
        LEFT JOIN
        sys_dept sd ON sd.dept_id = staff_on_job.sys_dept_id
        where 1=1
        <if test="staffOnJob.staffState != null">
            AND staff_state = #{staffOnJob.staffState}
        </if>
        <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
            AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
        </if>
        <where>
            <if test="staffOnJob.staffState != null">
                AND staff_state = #{staffOnJob.staffState}
            </if>
            <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
                AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
            </if>
            <if test="staffOnJob.nation != null and staffOnJob.nation != '' ">
                AND staff_on_job.nation = #{staffOnJob.nation}
            </if>
        </where>
        order by staff_on_job.create_time desc
    </select>
    <!-- ç»Ÿè®¡æŒ‡å®šæ—¥æœŸçš„在职员工数 -->
    <select id="countOnJobStaffByDate" resultType="java.lang.Integer">
src/main/resources/mapper/staff/SubsidyConfigurationMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
<?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.staff.mapper.SubsidyConfigurationMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.staff.pojo.SubsidyConfiguration">
        <id column="id" property="id" />
        <result column="meal_amount" property="mealAmount" />
        <result column="night_amount" property="nightAmount" />
        <result column="create_time" property="createTime" />
        <result column="tenant_id" property="tenantId" />
    </resultMap>
</mapper>
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -222,7 +222,7 @@
        unit,
        product_name,
        product_id
        order by combined.id desc
        order by combined.create_time desc
    </select>
    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
src/main/resources/mapper/system/SysDeptMapper.xml
@@ -22,10 +22,11 @@
        <result property="updateTime" column="update_time" />
        <result property="staffCount" column="staff_count" />
    </resultMap>
    <sql id="selectDeptVo">
        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
        from sys_dept d
        order by d.create_time desc
    </sql>
    <select id="selectDeptList" parameterType="com.ruoyi.project.system.domain.SysDept" resultMap="SysDeptResult">
@@ -49,9 +50,9 @@
        <!-- æ•°æ®èŒƒå›´è¿‡æ»¤ -->
        ${params.dataScope}
        group by d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
        order by d.parent_id, d.order_num
        order by d.parent_id asc, d.order_num asc, d.create_time desc
    </select>
    <select id="selectDeptListByRoleId" resultType="java.lang.Long">
        select d.dept_id
        from sys_dept d
@@ -60,39 +61,40 @@
            <if test="deptCheckStrictly">
              and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId})
            </if>
        order by d.parent_id, d.order_num
        order by d.parent_id asc, d.order_num asc, d.create_time desc
    </select>
    <select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,
            (select dept_name from sys_dept where dept_id = d.parent_id) parent_name,
            d.dept_nick
        from sys_dept d
        where d.dept_id = #{deptId}
        order by d.create_time desc
    </select>
    <select id="checkDeptExistUser" parameterType="Long" resultType="int">
        select count(1) from sys_user_dept where dept_id = #{deptId}
    </select>
    <select id="hasChildByDeptId" parameterType="Long" resultType="int">
        select count(1) from sys_dept
        where del_flag = '0' and parent_id = #{deptId} limit 1
    </select>
    <select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult">
        select * from sys_dept where find_in_set(#{deptId}, ancestors)
    </select>
    <select id="selectNormalChildrenDeptById" parameterType="Long" resultType="int">
        select count(*) from sys_dept where status = 0 and del_flag = '0' and find_in_set(#{deptId}, ancestors)
    </select>
    <select id="checkDeptNameUnique" resultMap="SysDeptResult">
        <include refid="selectDeptVo"/>
        where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
    </select>
    <insert id="insertDept" parameterType="com.ruoyi.project.system.domain.SysDept">
         insert into sys_dept(
             <if test="deptId != null and deptId != 0">dept_id,</if>
@@ -122,7 +124,7 @@
             sysdate()
         )
    </insert>
    <update id="updateDept" parameterType="com.ruoyi.project.system.domain.SysDept">
         update sys_dept
         <set>
@@ -140,7 +142,7 @@
         </set>
         where dept_id = #{deptId}
    </update>
    <update id="updateDeptChildren" parameterType="java.util.List">
        update sys_dept set ancestors =
        <foreach collection="depts" item="item" index="index"
@@ -153,14 +155,14 @@
            #{item.deptId}
        </foreach>
    </update>
    <update id="updateDeptStatusNormal" parameterType="Long">
         update sys_dept set status = '0' where dept_id in
         update sys_dept set status = '0' where dept_id in
         <foreach collection="array" item="deptId" open="(" separator="," close=")">
            #{deptId}
        </foreach>
    </update>
    <delete id="deleteDeptById" parameterType="Long">
        update sys_dept set del_flag = '2' where dept_id = #{deptId}
    </delete>
@@ -184,4 +186,4 @@
        WHERE parent_id = 100;
    </select>
</mapper>
</mapper>
src/main/resources/mapper/system/SysDictTypeMapper.xml
@@ -14,9 +14,9 @@
        <result property="updateBy"   column="update_by"   />
        <result property="updateTime" column="update_time" />
    </resultMap>
    <sql id="selectDictTypeVo">
        select dict_id, dict_name, dict_type, status, create_by, create_time, remark
        select dict_id, dict_name, dict_type, status, create_by, create_time, remark
        from sys_dict_type
    </sql>
@@ -39,36 +39,37 @@
                and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
            </if>
        </where>
        order by create_time desc
    </select>
    <select id="selectDictTypeAll" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
    </select>
    <select id="selectDictTypeById" parameterType="Long" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
        where dict_id = #{dictId}
    </select>
    <select id="selectDictTypeByType" parameterType="String" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
        where dict_type = #{dictType}
    </select>
    <select id="checkDictTypeUnique" parameterType="String" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
        where dict_type = #{dictType} limit 1
    </select>
    <delete id="deleteDictTypeById" parameterType="Long">
         delete from sys_dict_type where dict_id = #{dictId}
     </delete>
     <delete id="deleteDictTypeByIds" parameterType="Long">
         delete from sys_dict_type where dict_id in
         <foreach collection="array" item="dictId" open="(" separator="," close=")">
             #{dictId}
        </foreach>
        </foreach>
     </delete>
     <update id="updateDictType" parameterType="com.ruoyi.project.system.domain.SysDictType">
@@ -83,7 +84,7 @@
         </set>
         where dict_id = #{dictId}
    </update>
     <insert id="insertDictType" parameterType="com.ruoyi.project.system.domain.SysDictType">
         insert into sys_dict_type(
             <if test="dictName != null and dictName != ''">dict_name,</if>
@@ -101,5 +102,5 @@
             sysdate()
         )
    </insert>
</mapper>
</mapper>
src/main/resources/mapper/system/SysPostMapper.xml
@@ -16,12 +16,12 @@
        <result property="updateTime"    column="update_time"   />
        <result property="remark"        column="remark"        />
    </resultMap>
    <sql id="selectPostVo">
        select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark
        select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark
        from sys_post
    </sql>
    <select id="selectPostList" parameterType="com.ruoyi.project.system.domain.SysPost" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
        <where>
@@ -35,17 +35,18 @@
                AND post_name like concat('%', #{postName}, '%')
            </if>
        </where>
        order by create_time desc
    </select>
    <select id="selectPostAll" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
    </select>
    <select id="selectPostById" parameterType="Long" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
        where post_id = #{postId}
    </select>
    <select id="selectPostListByUserId" parameterType="Long" resultType="Long">
        select p.post_id
        from sys_post p
@@ -53,7 +54,7 @@
            left join sys_user u on u.user_id = up.user_id
        where u.user_id = #{userId}
    </select>
    <select id="selectPostsByUserName" parameterType="String" resultMap="SysPostResult">
        select p.post_id, p.post_name, p.post_code
        from sys_post p
@@ -61,17 +62,17 @@
             left join sys_user u on u.user_id = up.user_id
        where u.user_name = #{userName}
    </select>
    <select id="checkPostNameUnique" parameterType="String" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
         where post_name=#{postName} limit 1
    </select>
    <select id="checkPostCodeUnique" parameterType="String" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
         where post_code=#{postCode} limit 1
    </select>
    <update id="updatePost" parameterType="com.ruoyi.project.system.domain.SysPost">
         update sys_post
         <set>
@@ -85,7 +86,7 @@
         </set>
         where post_id = #{postId}
    </update>
     <insert id="insertPost" parameterType="com.ruoyi.project.system.domain.SysPost" useGeneratedKeys="true" keyProperty="postId">
         insert into sys_post(
             <if test="postId != null and postId != 0">post_id,</if>
@@ -107,16 +108,16 @@
             sysdate()
         )
    </insert>
    <delete id="deletePostById" parameterType="Long">
        delete from sys_post where post_id = #{postId}
    </delete>
    <delete id="deletePostByIds" parameterType="Long">
         delete from sys_post where post_id in
         <foreach collection="array" item="postId" open="(" separator="," close=")">
             #{postId}
        </foreach>
        </foreach>
     </delete>
</mapper>
</mapper>
src/main/resources/mapper/system/SysRoleMapper.xml
@@ -20,10 +20,10 @@
        <result property="updateTime"         column="update_time"           />
        <result property="remark"             column="remark"                />
    </resultMap>
    <sql id="selectRoleVo">
        select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,
            r.status, r.del_flag, r.create_time, r.remark
            r.status, r.del_flag, r.create_time, r.remark
        from sys_role r
            left join sys_user_role ur on ur.role_id = r.role_id
            left join sys_user u on u.user_id = ur.user_id
@@ -54,17 +54,17 @@
        ${params.dataScope}
        order by r.role_sort
    </select>
    <select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult">
        <include refid="selectRoleVo"/>
        WHERE r.del_flag = '0' and ur.user_id = #{userId}
    </select>
    <select id="selectRoleAll" resultMap="SysRoleResult">
        <include refid="selectRoleVo"/>
        WHERE r.del_flag = '0'
    </select>
    <select id="selectRoleListByUserId" parameterType="Long" resultType="Long">
        select r.role_id
        from sys_role r
@@ -72,27 +72,27 @@
            left join sys_user u on u.user_id = ur.user_id
        where u.user_id = #{userId}
    </select>
    <select id="selectRoleById" parameterType="Long" resultMap="SysRoleResult">
        <include refid="selectRoleVo"/>
        where r.role_id = #{roleId}
    </select>
    <select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult">
        <include refid="selectRoleVo"/>
        WHERE r.del_flag = '0' and u.user_name = #{userName}
    </select>
    <select id="checkRoleNameUnique" parameterType="String" resultMap="SysRoleResult">
        <include refid="selectRoleVo"/>
         where r.role_name=#{roleName} and r.del_flag = '0' limit 1
    </select>
    <select id="checkRoleKeyUnique" parameterType="String" resultMap="SysRoleResult">
        <include refid="selectRoleVo"/>
         where r.role_key=#{roleKey} and r.del_flag = '0' limit 1
    </select>
     <insert id="insertRole" parameterType="com.ruoyi.project.system.domain.SysRole" useGeneratedKeys="true" keyProperty="roleId">
         insert into sys_role(
             <if test="roleId != null and roleId != 0">role_id,</if>
@@ -120,7 +120,7 @@
             sysdate()
         )
    </insert>
    <update id="updateRole" parameterType="com.ruoyi.project.system.domain.SysRole">
         update sys_role
         <set>
@@ -137,16 +137,16 @@
         </set>
         where role_id = #{roleId}
    </update>
    <delete id="deleteRoleById" parameterType="Long">
         update sys_role set del_flag = '2' where role_id = #{roleId}
     </delete>
     <delete id="deleteRoleByIds" parameterType="Long">
         update sys_role set del_flag = '2' where role_id in
         <foreach collection="array" item="roleId" open="(" separator="," close=")">
             #{roleId}
        </foreach>
        </foreach>
     </delete>
</mapper>
</mapper>
src/main/resources/mapper/system/SysUserDeptMapper.xml
@@ -10,7 +10,8 @@
        T1.staff_name as staffName,
        T2.dept_id as deptId,
        T2.dept_name as deptName,
        T1.staff_state as staffState
        T1.staff_state as staffState,
        T1.nation as nation
        FROM
        staff_on_job T1
        LEFT JOIN sys_dept T2 ON T1.sys_dept_id = T2.dept_id
@@ -41,4 +42,4 @@
            </if>
        </where>
    </select>
</mapper>
</mapper>
src/main/resources/mapper/system/SysUserMapper.xml
@@ -97,6 +97,7 @@
        </if>
        <!-- æ•°æ®èŒƒå›´è¿‡æ»¤ -->
        ${params.dataScope}
        ORDER BY u.create_time DESC
    </select>
    <select id="selectAllocatedList" parameterType="com.ruoyi.project.system.domain.SysUser" resultMap="SysUserResult">
@@ -130,17 +131,20 @@
        </if>
        <!-- æ•°æ®èŒƒå›´è¿‡æ»¤ -->
        ${params.dataScope}
        ORDER BY u.create_time DESC
    </select>
    <select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
        <include refid="selectUserVo"/>
        where u.user_name = #{userName} and u.del_flag = '0'
        ORDER BY u.create_time DESC
    </select>
    <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
        <include refid="selectUserVo"/>
        where u.user_id = #{userId}
        and u.del_flag = '0'
        ORDER BY u.create_time DESC
    </select>
    <select id="checkUserNameUnique" parameterType="String" resultMap="SysUserResult">
@@ -165,6 +169,7 @@
            </if>
            and u.del_flag = '0'
        </where>
        ORDER BY u.create_time DESC
    </select>
    <select id="selectRegistrantIds" resultType="com.ruoyi.project.system.domain.SysUser">
        SELECT user_id, nick_name FROM sys_user
@@ -179,6 +184,7 @@
                1=0  <!-- ç©ºåˆ—表时返回空结果 -->
            </if>
        </where>
        ORDER BY sys_user.create_time DESC
    </select>
    <select id="selectUsersByIds" resultType="com.ruoyi.project.system.domain.SysUser">
        SELECT user_id, nick_name
@@ -188,6 +194,7 @@
            #{id}
        </foreach>
        and del_flag = '0'
        ORDER BY sys_user.create_time DESC
    </select>
    <select id="selectUserByNickName" resultType="com.ruoyi.project.system.domain.SysUser"
            parameterType="java.lang.String">
@@ -281,6 +288,7 @@
          and sr.status = '0'
          and su.status = '0'
          and su.del_flag = '0'
        ORDER BY su.create_time DESC
    </select>
    <select id="getUserByPerms" resultType="java.lang.Long">
        select distinct t5.user_id