Merge dev_New_pro into dev_山西_晋和园_pro
Co-authored-by: Cursor <cursoragent@cursor.com>
| | |
| | | if (accountSubjectDto != null && accountSubjectDto.getStatus() != null) { |
| | | queryWrapper.eq(AccountSubject::getStatus, accountSubjectDto.getStatus()); |
| | | } |
| | | queryWrapper.orderByAsc(AccountSubject::getSubjectCode).orderByAsc(AccountSubject::getId); |
| | | queryWrapper.orderByDesc(AccountSubject::getSubjectCode).orderByDesc(AccountSubject::getId); |
| | | return queryWrapper; |
| | | } |
| | | |
| | |
| | | } |
| | | List<AccountSubject> sortedSubjects = new ArrayList<>(subjects); |
| | | sortedSubjects.sort(Comparator |
| | | .comparing(AccountSubject::getSubjectCode, Comparator.nullsLast(String::compareTo)) |
| | | .thenComparing(AccountSubject::getId, Comparator.nullsLast(Long::compareTo))); |
| | | .comparing(AccountSubject::getSubjectCode, Comparator.nullsFirst(String::compareTo)).reversed() |
| | | .thenComparing(AccountSubject::getId, Comparator.nullsFirst(Long::compareTo)).reversed()); |
| | | |
| | | Map<Long, AccountSubjectVo> subjectVoMap = new LinkedHashMap<>(); |
| | | for (AccountSubject subject : sortedSubjects) { |
| | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | |
| | | import com.ruoyi.aftersalesservice.service.AfterSalesServiceService; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | |
| | | if(sysUser == null) throw new RuntimeException("å®¡æ ¸äººä¸åå¨"); |
| | | afterSalesServiceNewDto.setCheckNickName(sysUser.getNickName()); |
| | | if (StringUtils.isEmpty(afterSalesServiceNewDto.getAfterSalesServiceNo())) { |
| | | String string = OrderUtils.countAfterServiceTodayByCreateTime(afterSalesServiceMapper, "SH_"); |
| | | String string = OrderUtils.countAfterServiceTodayByCreateTime(afterSalesServiceMapper, "SH_", afterSalesServiceNewDto.getCreateTime() != null ? afterSalesServiceNewDto.getCreateTime() : LocalDateTime.now()); |
| | | afterSalesServiceNewDto.setAfterSalesServiceNo(string); |
| | | } |
| | | return this.save(afterSalesServiceNewDto); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.assistant; |
| | | |
| | | import com.ruoyi.ai.tools.FinancialAgentTools; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.YearMonth; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | @Component |
| | | 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 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 FUTURE_MONTH_PATTERN = Pattern.compile("(?:æªæ¥|åç»|æ¥ä¸æ¥)\\s*(\\d{1,2})\\s*(?:个)?æ"); |
| | | |
| | | private final FinancialAgentTools financialAgentTools; |
| | | |
| | | public FinancialIntentExecutor(FinancialAgentTools financialAgentTools) { |
| | | this.financialAgentTools = financialAgentTools; |
| | | } |
| | | |
| | | public String tryExecute(String memoryId, String message) { |
| | | if (!StringUtils.hasText(message)) { |
| | | return null; |
| | | } |
| | | String text = message.trim(); |
| | | |
| | | String quickPromptResponse = tryExecuteQuickPrompt(memoryId, text); |
| | | if (StringUtils.hasText(quickPromptResponse)) { |
| | | return quickPromptResponse; |
| | | } |
| | | |
| | | DateRange dateRange = extractDateRange(text); |
| | | Integer limit = extractLimit(text); |
| | | String keyword = extractKeyword(text); |
| | | String startDate = dateRange.startDate(); |
| | | String endDate = dateRange.endDate(); |
| | | String timeRange = dateRange.label(); |
| | | |
| | | if (containsAny(text, "ææ¬æ ¸ç®", "äº§åææ¬", "å·¥åºææ¬", "äººå·¥ææ¬", "ææ§", "æææè", "ææ¬æé«")) { |
| | | return financialAgentTools.calculateIntelligentCost(memoryId, startDate, endDate, timeRange, keyword, limit); |
| | | } |
| | | 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, extractForecastMonths(text)); |
| | | } |
| | | if (containsAny(text, "å¼å¸¸é¢è¦", "ç»è¥å¼å¸¸", "é£é©é¢è¦", "ææ¬å¼å¸¸", "婿¶¦å¼å¸¸", "忬¾å¼å¸¸", "订åé£é©")) { |
| | | return financialAgentTools.detectBusinessAnomalies(memoryId, startDate, endDate, timeRange, limit); |
| | | } |
| | | if (containsAny(text, "驾驶è±", "ç»è¥çæ¿", "ç»è¥æ»è§", "ç»è¥ä»ªè¡¨ç", "ç»è¥å¤§ç")) { |
| | | return financialAgentTools.getBusinessCockpit(memoryId, startDate, endDate, timeRange); |
| | | } |
| | | if (containsAny(text, "æ¥æ¥", "卿¥", "ç»è¥æ¥å", "åææ¥å")) { |
| | | return financialAgentTools.generateOperationReport(memoryId, startDate, endDate, timeRange, |
| | | containsAny(text, "卿¥") ? "weekly" : "daily"); |
| | | } |
| | | if (containsAny(text, "ä¸è´¢èå", "ä¸è´¢èå¨", "å£å¾", "ææ è§£é", "为ä»ä¹")) { |
| | | return financialAgentTools.retrieveFinancialKnowledge(memoryId, text); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | 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)) { |
| | | DateRange range = recentDaysRange(30); |
| | | return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, null); |
| | | } |
| | | if ("åæè¿30天åºåèµéå ç¨".equals(normalized)) { |
| | | DateRange range = recentDaysRange(30); |
| | | return financialAgentTools.analyzeInventoryCapital(memoryId, range.startDate(), range.endDate(), range.label(), null, null); |
| | | } |
| | | if ("颿µæªæ¥3个æç°éæµ".equals(normalized)) { |
| | | return financialAgentTools.forecastCashFlow(memoryId, null, null, null, 3); |
| | | } |
| | | if ("åªä¸ªå·¥åºææ¬æé«".equals(normalized)) { |
| | | return financialAgentTools.calculateIntelligentCost(memoryId, null, null, null, null, null); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean containsAny(String text, String... keywords) { |
| | | for (String keyword : keywords) { |
| | | if (text.contains(keyword)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private Integer extractLimit(String text) { |
| | | Matcher matcher = LIMIT_PATTERN.matcher(text); |
| | | 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) { |
| | | Matcher matcher = DATE_PATTERN.matcher(text); |
| | | if (matcher.find()) { |
| | | String first = matcher.group(1); |
| | | String second = matcher.find() ? matcher.group(1) : first; |
| | | return buildDateRange(first, second, first + "è³" + second); |
| | | } |
| | | if (text.contains("æ¬æ")) { |
| | | return monthRange(); |
| | | } |
| | | if (text.contains("䏿")) { |
| | | return lastMonthRange(); |
| | | } |
| | | if (text.contains("ä»å¹´") || text.contains("æ¬å¹´")) { |
| | | return yearRange(); |
| | | } |
| | | if (text.contains("æ¬å¨")) { |
| | | return weekRange(); |
| | | } |
| | | Matcher relativeDayMatcher = RELATIVE_DAY_PATTERN.matcher(text); |
| | | if (relativeDayMatcher.find()) { |
| | | int days = Integer.parseInt(relativeDayMatcher.group(1)); |
| | | return recentDaysRange(days); |
| | | } |
| | | 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, null); |
| | | } |
| | | if (startDate.isAfter(endDate)) { |
| | | LocalDate temp = startDate; |
| | | startDate = endDate; |
| | | endDate = temp; |
| | | } |
| | | return new DateRange(formatDate(startDate), formatDate(endDate), label); |
| | | } |
| | | |
| | | private DateRange recentDaysRange(int days) { |
| | | LocalDate end = LocalDate.now(); |
| | | int safeDays = Math.max(days, 1); |
| | | LocalDate start = end.minusDays(safeDays - 1L); |
| | | return new DateRange(formatDate(start), formatDate(end), "è¿" + safeDays + "天"); |
| | | } |
| | | |
| | | private DateRange monthRange() { |
| | | LocalDate today = LocalDate.now(); |
| | | return new DateRange(formatDate(today.withDayOfMonth(1)), formatDate(today), "æ¬æ"); |
| | | } |
| | | |
| | | private DateRange weekRange() { |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDate start = today.minusDays(today.getDayOfWeek().getValue() - 1L); |
| | | return new DateRange(formatDate(start), formatDate(today), "æ¬å¨"); |
| | | } |
| | | |
| | | private DateRange lastMonthRange() { |
| | | YearMonth lastMonth = YearMonth.now().minusMonths(1); |
| | | return new DateRange(formatDate(lastMonth.atDay(1)), formatDate(lastMonth.atEndOfMonth()), "䏿"); |
| | | } |
| | | |
| | | private DateRange yearRange() { |
| | | LocalDate today = LocalDate.now(); |
| | | return new DateRange(formatDate(today.withDayOfYear(1)), formatDate(today), "ä»å¹´"); |
| | | } |
| | | |
| | | private LocalDate parseDate(String text) { |
| | | try { |
| | | return LocalDate.parse(text, DATE_FMT); |
| | | } catch (Exception ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private String formatDate(LocalDate date) { |
| | | return date == null ? null : date.format(DATE_FMT); |
| | | } |
| | | |
| | | private String normalizeForMatch(String text) { |
| | | if (!StringUtils.hasText(text)) { |
| | | return ""; |
| | | } |
| | | return text.replace("ï¼", "") |
| | | .replace("ï¼", "") |
| | | .replace("(", "") |
| | | .replace(")", "") |
| | | .replace("ï¼", "") |
| | | .replace(",", "") |
| | | .replace("ã", "") |
| | | .replace(".", "") |
| | | .replace("ï¼", "") |
| | | .replace("!", "") |
| | | .replace("ï¼", "") |
| | | .replace("?", "") |
| | | .replace("ï¼", "") |
| | | .replace(":", "") |
| | | .replace("ï¼", "") |
| | | .replace(";", "") |
| | | .replace(" ", "") |
| | | .trim(); |
| | | } |
| | | |
| | | 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("æ¬æåªä¸ªå®¢æ·æèµé±", "") |
| | | .replace("è¿30天åªä¸ªå®¢æ·æèµé±", "") |
| | | .replace("æèµé±å®¢æ·", "") |
| | | .replace("å®¢æ·æèµé±", "") |
| | | .replace("åªä¸ªå®¢æ·å©æ¶¦æé«", "") |
| | | .replace("婿¶¦æé«å®¢æ·", "") |
| | | .replace("åªä¸ªå®¢æ·å©æ¶¦è´¡ç®æé«", "") |
| | | .replace("婿¶¦è´¡ç®æé«å®¢æ·", "") |
| | | .replace("æ¬æ", "") |
| | | .replace("æ¬å¨", "") |
| | | .replace("æ¬å¹´", "") |
| | | .replace("ä»å¹´", "") |
| | | .replace("䏿", "") |
| | | .replace("è¿30天", "") |
| | | .replace("è¿7天", "") |
| | | .replace("è¿90天", "") |
| | | .replace("å10æ¡", "") |
| | | .replace("æè¿10æ¡", "") |
| | | .replace("å20æ¡", "") |
| | | .replace("æè¿20æ¡", "") |
| | | .replace("订å婿¶¦åæ", "") |
| | | .replace("婿¶¦åæ", "") |
| | | .replace("åºåèµéåæ", "") |
| | | .replace("ç°éæµé¢æµ", "") |
| | | .replace("ç»è¥é©¾é©¶è±", "") |
| | | .replace("æ¥æ¥", "") |
| | | .replace("卿¥", "") |
| | | .replace("å¼å¸¸é¢è¦", "") |
| | | .replace("æ¡", "") |
| | | .trim(); |
| | | return cleaned.length() >= 2 ? cleaned : null; |
| | | } |
| | | |
| | | private record DateRange(String startDate, String endDate, String label) { |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.context; |
| | | |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | 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; |
| | | |
| | | @Component |
| | | 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); |
| | | } |
| | | |
| | | public void remove(String memoryId) { |
| | | if (!StringUtils.hasText(memoryId)) { |
| | | 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; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.controller; |
| | | |
| | | import com.ruoyi.ai.assistant.FinancialAgent; |
| | | import com.ruoyi.ai.assistant.FinancialIntentExecutor; |
| | | import com.ruoyi.ai.bean.ChatForm; |
| | | import com.ruoyi.ai.context.AiSessionUserContext; |
| | | import com.ruoyi.ai.service.AiChatSessionService; |
| | | import com.ruoyi.ai.store.MongoChatMemoryStore; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import dev.langchain4j.data.message.AiMessage; |
| | | import dev.langchain4j.data.message.UserMessage; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.List; |
| | | |
| | | @Tag(name = "è´¢å¡æºè½ä½") |
| | | @RestController |
| | | @RequestMapping("/financial-ai") |
| | | public class FinancialAiController extends BaseController { |
| | | |
| | | private static final DateTimeFormatter CURRENT_DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | private static final ZoneId CHINA_ZONE_ID = ZoneId.of("Asia/Shanghai"); |
| | | |
| | | private final FinancialAgent financialAgent; |
| | | private final FinancialIntentExecutor financialIntentExecutor; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | private final MongoChatMemoryStore mongoChatMemoryStore; |
| | | private final AiChatSessionService aiChatSessionService; |
| | | |
| | | public FinancialAiController(FinancialAgent financialAgent, |
| | | FinancialIntentExecutor financialIntentExecutor, |
| | | AiSessionUserContext aiSessionUserContext, |
| | | MongoChatMemoryStore mongoChatMemoryStore, |
| | | AiChatSessionService aiChatSessionService) { |
| | | this.financialAgent = financialAgent; |
| | | this.financialIntentExecutor = financialIntentExecutor; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | this.mongoChatMemoryStore = mongoChatMemoryStore; |
| | | this.aiChatSessionService = aiChatSessionService; |
| | | } |
| | | |
| | | @Operation(summary = "è´¢å¡æºè½ä½å¯¹è¯") |
| | | @PostMapping(value = "/chat", produces = "text/stream;charset=utf-8") |
| | | public Flux<String> chat(@RequestBody ChatForm chatForm) { |
| | | if (!StringUtils.hasText(chatForm.getMemoryId())) { |
| | | return Flux.just("memoryIdä¸è½ä¸ºç©º"); |
| | | } |
| | | if (!StringUtils.hasText(chatForm.getMessage())) { |
| | | return Flux.just("messageä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | String memoryId = chatForm.getMemoryId(); |
| | | String userMessage = chatForm.getMessage(); |
| | | |
| | | aiSessionUserContext.bind(memoryId, loginUser); |
| | | aiChatSessionService.touchSession(memoryId, loginUser, userMessage); |
| | | |
| | | String directResponse = financialIntentExecutor.tryExecute(memoryId, userMessage); |
| | | if (StringUtils.isNotEmpty(directResponse)) { |
| | | mongoChatMemoryStore.appendMessages( |
| | | memoryId, |
| | | List.of(UserMessage.from(userMessage), AiMessage.from(directResponse)) |
| | | ); |
| | | aiChatSessionService.refreshSessionStats(memoryId, loginUser); |
| | | 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)); |
| | | } |
| | | |
| | | @Operation(summary = "è´¢å¡æºè½ä½ä¼è¯å表") |
| | | @GetMapping("/history/sessions") |
| | | public AjaxResult listSessions() { |
| | | return success(aiChatSessionService.listCurrentUserSessions(SecurityUtils.getLoginUser())); |
| | | } |
| | | |
| | | @Operation(summary = "è´¢å¡æºè½ä½ä¼è¯æ¶æ¯") |
| | | @GetMapping("/history/messages/{memoryId}") |
| | | public AjaxResult listMessages(@PathVariable String memoryId) { |
| | | return success(aiChatSessionService.listCurrentUserMessages(memoryId, SecurityUtils.getLoginUser())); |
| | | } |
| | | |
| | | @Operation(summary = "å é¤è´¢å¡æºè½ä½ä¼è¯") |
| | | @DeleteMapping("/history/{memoryId}") |
| | | public AjaxResult deleteSession(@PathVariable String memoryId) { |
| | | aiSessionUserContext.remove(memoryId); |
| | | return toAjax(aiChatSessionService.deleteCurrentUserSession(memoryId, SecurityUtils.getLoginUser())); |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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()); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.tools; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.ruoyi.ai.context.AiSessionUserContext; |
| | | import com.ruoyi.approve.mapper.ApproveLogMapper; |
| | | import com.ruoyi.approve.mapper.ApproveNodeMapper; |
| | | import com.ruoyi.approve.mapper.ApproveProcessMapper; |
| | | import com.ruoyi.approve.pojo.ApproveLog; |
| | | import com.ruoyi.approve.pojo.ApproveNode; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import com.ruoyi.approve.service.IApproveNodeService; |
| | | import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import dev.langchain4j.agent.tool.P; |
| | | import dev.langchain4j.agent.tool.Tool; |
| | | import dev.langchain4j.agent.tool.ToolMemoryId; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | 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; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.StringJoiner; |
| | | import java.util.regex.Matcher; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Component |
| | | public class ApproveTodoTools { |
| | | |
| | | private static final int DEFAULT_LIMIT = 10; |
| | | private static final int MAX_LIMIT = 20; |
| | | 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; |
| | | private final ApproveNodeMapper approveNodeMapper; |
| | | private final ApproveLogMapper approveLogMapper; |
| | | private final IApproveNodeService approveNodeService; |
| | | private final ApproveProcessServiceImpl approveProcessService; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | |
| | | public ApproveTodoTools(ApproveProcessMapper approveProcessMapper, |
| | | ApproveNodeMapper approveNodeMapper, |
| | | ApproveLogMapper approveLogMapper, |
| | | IApproveNodeService approveNodeService, |
| | | ApproveProcessServiceImpl approveProcessService, |
| | | AiSessionUserContext aiSessionUserContext) { |
| | | this.approveProcessMapper = approveProcessMapper; |
| | | this.approveNodeMapper = approveNodeMapper; |
| | | this.approveLogMapper = approveLogMapper; |
| | | this.approveNodeService = approveNodeService; |
| | | this.approveProcessService = approveProcessService; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹å¾
åå表", value = "æ¥è¯¢å½åç»å½äººç¸å
³ç审æ¹å¾
åï¼ä¼å
è¿åèªå·±å¾
å¤çç审æ¹ï¼æ¯ææç¶æãç±»åãå
³é®ååèå´è¿æ»¤ã") |
| | | public String listTodos(@ToolMemoryId String memoryId, |
| | | @P(value = "审æ¹ç¶æï¼å¯éå¼ï¼allãpendingãprocessingãapprovedãrejectedãresubmitted", required = false) String status, |
| | | @P(value = "审æ¹ç±»åç¼å·ï¼å¯ä¸ä¼ ", required = false) Integer approveType, |
| | | @P(value = "å
³é®åï¼å¯å¹é
æµç¨ç¼å·ãæ é¢ãç³è¯·äººãå½å审æ¹äºº", required = false) String keyword, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§20", required = false) Integer limit, |
| | | @P(value = "æ¥è¯¢èå´ï¼å¯éå¼ï¼relatedãapplicantãapproverï¼related 表示å½åç¨æ·ç¸å
³ï¼applicant 表示æåèµ·çï¼approver 表示å¾
æå¤çç", required = false) String scope, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼ä¾å¦ ä»å¤©ãæ¬æãè¿30天ï¼å¯ä¸ä¼ ", required = false) String timeRange) { |
| | | |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | Long userId = loginUser.getUserId(); |
| | | Integer statusCode = parseStatus(status); |
| | | String normalizedScope = normalizeScope(scope); |
| | | boolean hasDateFilter = StringUtils.hasText(startDate) || StringUtils.hasText(endDate) || StringUtils.hasText(timeRange); |
| | | DateRange dateRange = hasDateFilter ? resolveDateRange(startDate, endDate, timeRange) : null; |
| | | |
| | | LambdaQueryWrapper<ApproveProcess> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(ApproveProcess::getApproveDelete, 0); |
| | | if (statusCode == null) { |
| | | wrapper.ne(ApproveProcess::getApproveStatus, 2); |
| | | } |
| | | |
| | | if (approveType != null) { |
| | | wrapper.eq(ApproveProcess::getApproveType, approveType); |
| | | } |
| | | if (StringUtils.hasText(keyword)) { |
| | | wrapper.and(w -> w.like(ApproveProcess::getApproveId, keyword) |
| | | .or().like(ApproveProcess::getApproveReason, keyword) |
| | | .or().like(ApproveProcess::getApproveUserName, keyword) |
| | | .or().like(ApproveProcess::getApproveUserCurrentName, keyword)); |
| | | } |
| | | if ("applicant".equals(normalizedScope)) { |
| | | wrapper.eq(ApproveProcess::getApproveUser, userId); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } else if ("approver".equals(normalizedScope)) { |
| | | wrapper.eq(ApproveProcess::getApproveUserCurrentId, userId); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } else if (statusCode != null && (statusCode == 0 || statusCode == 1)) { |
| | | wrapper.eq(ApproveProcess::getApproveUserCurrentId, userId); |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } else { |
| | | wrapper.and(w -> w.eq(ApproveProcess::getApproveUser, userId) |
| | | .or().eq(ApproveProcess::getApproveUserCurrentId, userId) |
| | | .or().apply("FIND_IN_SET({0}, approve_user_ids)", userId)); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } |
| | | |
| | | if (dateRange != null) { |
| | | wrapper.ge(ApproveProcess::getCreateTime, dateRange.start().atStartOfDay()) |
| | | .lt(ApproveProcess::getCreateTime, dateRange.end().plusDays(1).atStartOfDay()); |
| | | } |
| | | |
| | | wrapper.orderByDesc(ApproveProcess::getCreateTime) |
| | | .last("limit " + normalizeLimit(limit)); |
| | | |
| | | List<ApproveProcess> processes = defaultList(approveProcessMapper.selectList(wrapper)); |
| | | if (processes.isEmpty()) { |
| | | return jsonResponse(true, "todo_list", "æªæ¥è¯¢å°å½åç¨æ·ç¬¦åæ¡ä»¶ç审æ¹å¾
åã", |
| | | Map.of("count", 0), |
| | | Map.of("columns", todoColumns(), "items", List.of()), |
| | | Map.of()); |
| | | } |
| | | |
| | | List<Map<String, Object>> items = processes.stream() |
| | | .filter(process -> canView(process, userId)) |
| | | .sorted(Comparator |
| | | .comparing((ApproveProcess process) -> !Objects.equals(process.getApproveUserCurrentId(), userId)) |
| | | .thenComparing(ApproveProcess::getCreateTime, Comparator.nullsLast(Comparator.reverseOrder()))) |
| | | .map(process -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("approveId", process.getApproveId()); |
| | | item.put("approveType", approveTypeName(process.getApproveType())); |
| | | item.put("approveUserName", safe(process.getApproveUserName())); |
| | | item.put("approveUserCurrentName", safe(process.getApproveUserCurrentName())); |
| | | item.put("approveReason", safe(process.getApproveReason())); |
| | | item.put("approveStatus", approveStatusName(process.getApproveStatus())); |
| | | item.put("createTime", formatDateTime(process.getCreateTime())); |
| | | item.put("relation", relationName(process, userId)); |
| | | return item; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | |
| | | return jsonResponse(true, "todo_list", "å·²è¿åå½åç¨æ·ç¸å
³å®¡æ¹å表ã", |
| | | Map.of( |
| | | "count", items.size(), |
| | | "statusFilter", StringUtils.hasText(status) ? status : "all", |
| | | "approveType", approveType == null ? "" : approveType, |
| | | "keyword", keyword == null ? "" : keyword, |
| | | "scope", normalizedScope, |
| | | "timeRange", dateRange == null ? "all" : dateRange.label(), |
| | | "startDate", dateRange == null ? "" : dateRange.start().toString(), |
| | | "endDate", dateRange == null ? "" : dateRange.end().toString() |
| | | ), |
| | | Map.of("columns", todoColumns(), "items", items), |
| | | Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹å¾
å详æ
", value = "æ ¹æ®æµç¨ç¼å·æ¥è¯¢å½åç»å½äººå¯è§ç审æ¹è¯¦æ
ã") |
| | | public String getTodoDetail(@ToolMemoryId String memoryId, |
| | | @P("æµç¨ç¼å· approveId") String approveId) { |
| | | ApproveProcess process = getAccessibleProcess(memoryId, approveId); |
| | | if (process == null) { |
| | | return "æªæ¾å°å¯¹åºå®¡æ¹ï¼æå½åç¨æ·æ ææ¥ç该æµç¨ã"; |
| | | } |
| | | |
| | | StringJoiner detail = new StringJoiner("\n"); |
| | | detail.add("审æ¹è¯¦æ
"); |
| | | detail.add("æµç¨ç¼å·: " + safe(process.getApproveId())); |
| | | detail.add("审æ¹ç±»å: " + approveTypeName(process.getApproveType())); |
| | | detail.add("ç³è¯·äºº: " + safe(process.getApproveUserName())); |
| | | detail.add("ç³è¯·é¨é¨: " + safe(process.getApproveDeptName())); |
| | | detail.add("å½å审æ¹äºº: " + safe(process.getApproveUserCurrentName())); |
| | | detail.add("æ é¢: " + safe(process.getApproveReason())); |
| | | detail.add("ç¶æ: " + approveStatusName(process.getApproveStatus())); |
| | | detail.add("ç³è¯·æ¥æ: " + formatDate(process.getApproveTime())); |
| | | detail.add("å¼å§æ¥æ: " + formatDate(process.getStartDate())); |
| | | detail.add("ç»ææ¥æ: " + formatDate(process.getEndDate())); |
| | | detail.add("å°ç¹: " + safe(process.getLocation())); |
| | | detail.add("éé¢: " + (process.getPrice() == null ? "" : process.getPrice().toPlainString())); |
| | | detail.add("夿³¨: " + safe(process.getApproveRemark())); |
| | | detail.add("å建æ¶é´: " + formatDateTime(process.getCreateTime())); |
| | | detail.add("ä¸å½åç¨æ·å
³ç³»: " + relationName(process, currentUserId(memoryId))); |
| | | return detail.toString(); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹æµè½¬è®°å½", value = "æ ¹æ®æµç¨ç¼å·æ¥è¯¢å®¡æ¹èç¹åå®¡æ¹æ¥å¿ï¼ç¨äºåçè¿åº¦ãå½åå¡ç¹ååå²å¤çè®°å½ã") |
| | | public String getTodoProgress(@ToolMemoryId String memoryId, |
| | | @P("æµç¨ç¼å· approveId") String approveId) { |
| | | ApproveProcess process = getAccessibleProcess(memoryId, approveId); |
| | | if (process == null) { |
| | | return jsonResponse(false, "todo_progress", "æªæ¾å°å¯¹åºå®¡æ¹ï¼æå½åç¨æ·æ ææ¥ç该æµç¨ã", |
| | | Map.of("approveId", safe(approveId)), |
| | | Map.of(), |
| | | Map.of()); |
| | | } |
| | | |
| | | List<ApproveNode> nodes = listNodes(process); |
| | | List<ApproveLog> logs = listLogs(process.getId()); |
| | | ApproveNode currentNode = findCurrentNode(nodes); |
| | | |
| | | List<Map<String, Object>> nodeItems = nodes.stream().map(node -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("approveNodeOrder", node.getApproveNodeOrder()); |
| | | item.put("approveNodeUser", safe(node.getApproveNodeUser())); |
| | | item.put("approveNodeUserId", node.getApproveNodeUserId()); |
| | | item.put("approveNodeStatus", approveNodeStatusName(node.getApproveNodeStatus())); |
| | | item.put("approveNodeTime", formatDate(node.getApproveNodeTime())); |
| | | item.put("approveNodeReason", safe(node.getApproveNodeReason())); |
| | | item.put("approveNodeRemark", safe(node.getApproveNodeRemark())); |
| | | item.put("isCurrent", currentNode != null && Objects.equals(currentNode.getId(), node.getId())); |
| | | return item; |
| | | }).collect(Collectors.toList()); |
| | | |
| | | List<Map<String, Object>> logItems = logs.stream().map(log -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("approveNodeOrder", log.getApproveNodeOrder()); |
| | | item.put("approveUser", log.getApproveUser()); |
| | | item.put("approveStatus", approveStatusName(log.getApproveStatus())); |
| | | item.put("approveTime", formatDate(log.getApproveTime())); |
| | | item.put("approveRemark", safe(log.getApproveRemark())); |
| | | return item; |
| | | }).collect(Collectors.toList()); |
| | | |
| | | return jsonResponse(true, "todo_progress", "å·²è¿åå®¡æ¹æµè½¬è®°å½ã", |
| | | Map.of( |
| | | "approveId", safe(process.getApproveId()), |
| | | "currentStatus", approveStatusName(process.getApproveStatus()), |
| | | "currentApprover", safe(process.getApproveUserCurrentName()), |
| | | "currentNodeOrder", currentNode == null ? "" : currentNode.getApproveNodeOrder(), |
| | | "nodeCount", nodeItems.size(), |
| | | "logCount", logItems.size() |
| | | ), |
| | | Map.of("nodes", nodeItems, "logs", logItems), |
| | | Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "ç»è®¡å®¡æ¹å¾
åæ°æ®", value = "æç¨æ·æå®çæ¶é´èå´ç»è®¡å½åç»å½äººç¸å
³å®¡æ¹çç¶æåå¸ãç±»ååå¸åè¶å¿ï¼æªæå®æ¶é»è®¤è¿7天ã") |
| | | public String getTodoStats(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼ä¾å¦ ä»å¤©ãæ¬æãè¿30天ã2026-04-01å°2026-04-27", required = false) String timeRange) { |
| | | Long userId = currentUserId(memoryId); |
| | | List<ApproveProcess> processes = defaultList(approveProcessMapper.selectList(new LambdaQueryWrapper<ApproveProcess>() |
| | | .eq(ApproveProcess::getApproveDelete, 0) |
| | | .and(w -> w.eq(ApproveProcess::getApproveUser, userId) |
| | | .or().eq(ApproveProcess::getApproveUserCurrentId, userId) |
| | | .or().apply("FIND_IN_SET({0}, approve_user_ids)", userId)))); |
| | | |
| | | DateRange dateRange = resolveDateRange(startDate, endDate, timeRange); |
| | | List<ApproveProcess> filteredProcesses = processes.stream() |
| | | .filter(process -> withinDateRange(process.getCreateTime(), dateRange)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (filteredProcesses.isEmpty()) { |
| | | return jsonResponse(true, "todo_stats", "å½åç¨æ·æ²¡æç¸å
³å®¡æ¹æ°æ®ã", |
| | | Map.of( |
| | | "total", 0, |
| | | "startDate", dateRange.start().toString(), |
| | | "endDate", dateRange.end().toString(), |
| | | "timeRange", dateRange.label() |
| | | ), |
| | | Map.of( |
| | | "statusDistribution", Map.of(), |
| | | "typeDistribution", Map.of(), |
| | | "trend", List.of() |
| | | ), |
| | | Map.of()); |
| | | } |
| | | |
| | | Map<String, Long> statusStats = filteredProcesses.stream() |
| | | .collect(Collectors.groupingBy(p -> approveStatusName(p.getApproveStatus()), LinkedHashMap::new, Collectors.counting())); |
| | | Map<String, Long> typeStats = filteredProcesses.stream() |
| | | .collect(Collectors.groupingBy(p -> approveTypeName(p.getApproveType()), LinkedHashMap::new, Collectors.counting())); |
| | | |
| | | long pendingCount = countByStatus(filteredProcesses, 0); |
| | | long processingCount = countByStatus(filteredProcesses, 1); |
| | | long approvedCount = countByStatus(filteredProcesses, 2); |
| | | long rejectedCount = countByStatus(filteredProcesses, 3); |
| | | long resubmittedCount = countByStatus(filteredProcesses, 4); |
| | | |
| | | TrendRange trendRange = buildTrendRange(dateRange.start(), dateRange.end(), filteredProcesses); |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("total", filteredProcesses.size()); |
| | | summary.put("pending", pendingCount); |
| | | summary.put("processing", processingCount); |
| | | summary.put("approved", approvedCount); |
| | | summary.put("rejected", rejectedCount); |
| | | summary.put("resubmitted", resubmittedCount); |
| | | summary.put("approvalCompletionRate", calculateRate(approvedCount, filteredProcesses.size())); |
| | | summary.put("rejectionRate", calculateRate(rejectedCount, filteredProcesses.size())); |
| | | summary.put("startDate", dateRange.start().toString()); |
| | | summary.put("endDate", dateRange.end().toString()); |
| | | summary.put("timeRange", dateRange.label()); |
| | | summary.put("trendGranularity", trendRange.granularity()); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("statusBarOption", buildStatusBarOption(statusStats)); |
| | | charts.put("typePieOption", buildTypePieOption(typeStats)); |
| | | charts.put("trendLineOption", buildTrendLineOption(trendRange.labels(), trendRange.values(), trendRange.label())); |
| | | |
| | | return jsonResponse(true, "todo_stats", "å·²è¿åå½åç¨æ·ç¸å
³å®¡æ¹ç»è®¡ã", |
| | | summary, |
| | | Map.of( |
| | | "statusDistribution", statusStats, |
| | | "typeDistribution", typeStats, |
| | | "trend", toTrendItems(trendRange.labels(), trendRange.values()) |
| | | ), |
| | | charts); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Tool(name = "审æ¹å¾
å", value = "æ§è¡å®¡æ¹å¨ä½ï¼action ä»
æ¯æ approve æ rejectï¼ä¸åªè½å¤çå½åç»å½äººèªå·±çå¾
审èç¹ã") |
| | | public String reviewTodo(@ToolMemoryId String memoryId, |
| | | @P("æµç¨ç¼å· approveId") String approveId, |
| | | @P("å¨ä½ï¼approve=éè¿ï¼reject=驳å") String action, |
| | | @P(value = "审æ¹å¤æ³¨ï¼å¯ä¸ä¼ ", required = false) String remark) { |
| | | |
| | | ApproveProcess process = getAccessibleProcess(memoryId, approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "review_action", "æªæ¾å°å¯¹åºå®¡æ¹ï¼æå½åç¨æ·æ æè®¿é®è¯¥æµç¨ã", approveId, null); |
| | | } |
| | | if (!canOperate(process, currentUserId(memoryId))) { |
| | | return actionResult(false, "review_action", "å½åç»å½äººä¸æ¯è¯¥å®¡æ¹çå½åå¤ç人ã", approveId, null); |
| | | } |
| | | if (process.getApproveStatus() != null && (process.getApproveStatus() == 2 || process.getApproveStatus() == 3)) { |
| | | return actionResult(false, "review_action", "该审æ¹å·²ç»æï¼ä¸è½éå¤å¤çã", approveId, null); |
| | | } |
| | | |
| | | List<ApproveNode> nodes = listNodes(process); |
| | | ApproveNode currentNode = findCurrentNode(nodes); |
| | | if (currentNode == null || !Objects.equals(currentNode.getApproveNodeUserId(), currentUserId(memoryId))) { |
| | | return actionResult(false, "review_action", "æªæ¾å°å½åç¨æ·å¯å¤çç审æ¹èç¹ã", approveId, null); |
| | | } |
| | | |
| | | String normalizedAction = action == null ? "" : action.trim().toLowerCase(); |
| | | currentNode.setApproveNodeRemark(remark); |
| | | currentNode.setApproveNodeReason("reject".equals(normalizedAction) ? remark : null); |
| | | currentNode.setUpdateUser(currentUserId(memoryId)); |
| | | currentNode.setUpdateTime(LocalDateTime.now()); |
| | | currentNode.setIsLast(isLastNode(nodes, currentNode)); |
| | | |
| | | try { |
| | | switch (normalizedAction) { |
| | | case "approve" -> currentNode.setApproveNodeStatus(1); |
| | | case "reject" -> currentNode.setApproveNodeStatus(2); |
| | | default -> { |
| | | return actionResult(false, "review_action", "action åªæ¯æ approve æ rejectã", approveId, null); |
| | | } |
| | | } |
| | | approveNodeService.updateApproveNode(currentNode); |
| | | } catch (IOException e) { |
| | | throw new RuntimeException("审æ¹å¤ç失败", e); |
| | | } |
| | | |
| | | ApproveProcess refreshed = getProcessByApproveId(approveId); |
| | | writeApproveLog(memoryId, refreshed, currentNode, remark); |
| | | ApproveNode nextNode = refreshed == null ? null : findCurrentNode(listNodes(refreshed)); |
| | | |
| | | return actionResult(true, "review_action", |
| | | "approve".equals(normalizedAction) ? "审æ¹å·²éè¿ã" : "审æ¹å·²é©³åã", |
| | | approveId, |
| | | Map.of( |
| | | "action", normalizedAction, |
| | | "currentStatus", refreshed == null ? "" : approveStatusName(refreshed.getApproveStatus()), |
| | | "nextApprover", nextNode == null ? "" : safe(nextNode.getApproveNodeUser()), |
| | | "remark", safe(remark) |
| | | )); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Tool(name = "忶审æ¹å¾
åå®¡æ ¸", value = "æ¤éæè¿ä¸æ¬¡å®¡æ ¸ç»æï¼ä»
å
许æè¿ä¸æ¬¡å®¡æ ¸äººæç³è¯·äººæä½ã") |
| | | public String cancelReviewTodo(@ToolMemoryId String memoryId, |
| | | @P("æµç¨ç¼å· approveId") String approveId, |
| | | @P(value = "åæ¶åå ï¼å¯ä¸ä¼ ", required = false) String reason) { |
| | | |
| | | ApproveProcess process = getAccessibleProcess(memoryId, approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "cancel_review_action", "æªæ¾å°å¯¹åºå®¡æ¹ï¼æå½åç¨æ·æ æè®¿é®è¯¥æµç¨ã", approveId, null); |
| | | } |
| | | |
| | | List<ApproveNode> nodes = listNodes(process); |
| | | ApproveNode lastReviewedNode = nodes.stream() |
| | | .filter(node -> node.getApproveNodeStatus() != null && node.getApproveNodeStatus() != 0) |
| | | .max(Comparator.comparing(ApproveNode::getApproveNodeOrder)) |
| | | .orElse(null); |
| | | if (lastReviewedNode == null) { |
| | | return actionResult(false, "cancel_review_action", "å½åæµç¨æ²¡æå¯æ¤éçå®¡æ ¸è®°å½ã", approveId, null); |
| | | } |
| | | |
| | | Long userId = currentUserId(memoryId); |
| | | if (!isAdmin(userId) |
| | | && !Objects.equals(process.getApproveUser(), userId) |
| | | && !Objects.equals(lastReviewedNode.getApproveNodeUserId(), userId)) { |
| | | return actionResult(false, "cancel_review_action", "åªæç³è¯·äººãæè¿ä¸æ¬¡å®¡æ ¸äººæç®¡çåå¯ä»¥æ¤éã", approveId, null); |
| | | } |
| | | |
| | | lastReviewedNode.setApproveNodeStatus(0); |
| | | lastReviewedNode.setApproveNodeTime(null); |
| | | lastReviewedNode.setApproveNodeReason(null); |
| | | lastReviewedNode.setApproveNodeRemark(reason); |
| | | lastReviewedNode.setUpdateUser(userId); |
| | | lastReviewedNode.setUpdateTime(LocalDateTime.now()); |
| | | approveNodeMapper.updateById(lastReviewedNode); |
| | | |
| | | ApproveLog latestLog = listLogs(process.getId()).stream() |
| | | .max(Comparator.comparing(ApproveLog::getApproveNodeOrder) |
| | | .thenComparing(ApproveLog::getApproveTime, Comparator.nullsLast(Date::compareTo))) |
| | | .orElse(null); |
| | | if (latestLog != null) { |
| | | approveLogMapper.deleteById(latestLog.getId()); |
| | | } |
| | | |
| | | process.setApproveOverTime(null); |
| | | process.setApproveRemark(reason); |
| | | process.setApproveStatus(lastReviewedNode.getApproveNodeOrder() == null || lastReviewedNode.getApproveNodeOrder() <= 1 ? 0 : 1); |
| | | process.setApproveUserCurrentId(lastReviewedNode.getApproveNodeUserId()); |
| | | process.setApproveUserCurrentName(lastReviewedNode.getApproveNodeUser()); |
| | | approveProcessMapper.updateById(process); |
| | | |
| | | return actionResult(true, "cancel_review_action", "æè¿ä¸æ¬¡å®¡æ ¸å·²æ¤éã", approveId, Map.of( |
| | | "rollbackNodeOrder", lastReviewedNode.getApproveNodeOrder(), |
| | | "currentStatus", approveStatusName(process.getApproveStatus()), |
| | | "currentApprover", safe(process.getApproveUserCurrentName()), |
| | | "reason", safe(reason) |
| | | )); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Tool(name = "ä¿®æ¹å®¡æ¹å¾
å", value = "ä¿®æ¹å®¡æ¹ååºç¡ä¿¡æ¯ï¼ä»
å
许ç³è¯·äººä¿®æ¹ï¼ä¸æ¯æéè¿ AI 忴审æ¹ç±»åã") |
| | | public String updateTodo(@ToolMemoryId String memoryId, |
| | | @P("æµç¨ç¼å· approveId") String approveId, |
| | | @P(value = "æ°çæ é¢ï¼å¯ä¸ä¼ ", required = false) String approveReason, |
| | | @P(value = "æ°çå¼å§æ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String startDate, |
| | | @P(value = "æ°çç»ææ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String endDate, |
| | | @P(value = "æ°çéé¢ï¼å¯ä¸ä¼ ", required = false) BigDecimal price, |
| | | @P(value = "æ°çå°ç¹ï¼å¯ä¸ä¼ ", required = false) String location, |
| | | @P(value = "æ°ç审æ¹ç±»åï¼å¯ä¸ä¼ ", required = false) Integer approveType, |
| | | @P(value = "æ°ç夿³¨ï¼å¯ä¸ä¼ ", required = false) String approveRemark) { |
| | | |
| | | ApproveProcess process = getAccessibleProcess(memoryId, approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "update_action", "æªæ¾å°å¯¹åºå®¡æ¹ï¼æå½åç¨æ·æ æè®¿é®è¯¥æµç¨ã", approveId, null); |
| | | } |
| | | if (!Objects.equals(process.getApproveUser(), currentUserId(memoryId)) && !isAdmin(currentUserId(memoryId))) { |
| | | return actionResult(false, "update_action", "åªæç³è¯·äººæç®¡çåå¯ä»¥ä¿®æ¹å®¡æ¹åã", approveId, null); |
| | | } |
| | | if (process.getApproveStatus() != null && (process.getApproveStatus() == 1 || process.getApproveStatus() == 2)) { |
| | | return actionResult(false, "update_action", "审æ¹å¤ç䏿已宿æ¶ï¼ä¸å
许éè¿ AI ä¿®æ¹ã", approveId, null); |
| | | } |
| | | if (approveType != null && !Objects.equals(approveType, process.getApproveType())) { |
| | | return actionResult(false, "update_action", "AI 婿æä¸æ¯æç´æ¥åæ´å®¡æ¹ç±»åï¼é¿å
èç¹é
置失çã", approveId, null); |
| | | } |
| | | if (!StringUtils.hasText(approveReason) |
| | | && !StringUtils.hasText(startDate) |
| | | && !StringUtils.hasText(endDate) |
| | | && price == null |
| | | && !StringUtils.hasText(location) |
| | | && !StringUtils.hasText(approveRemark)) { |
| | | return actionResult(false, "update_action", "æ²¡ææ£æµå°å¯æ´æ°çåæ®µã", approveId, null); |
| | | } |
| | | |
| | | if (StringUtils.hasText(approveReason)) { |
| | | process.setApproveReason(approveReason); |
| | | } |
| | | if (StringUtils.hasText(startDate)) { |
| | | process.setStartDate(parseDate(startDate)); |
| | | } |
| | | if (StringUtils.hasText(endDate)) { |
| | | process.setEndDate(parseDate(endDate)); |
| | | } |
| | | if (price != null) { |
| | | process.setPrice(price); |
| | | } |
| | | if (StringUtils.hasText(location)) { |
| | | process.setLocation(location); |
| | | } |
| | | if (StringUtils.hasText(approveRemark)) { |
| | | process.setApproveRemark(approveRemark); |
| | | } |
| | | |
| | | approveProcessMapper.updateById(process); |
| | | return actionResult(true, "update_action", "审æ¹åå·²æ´æ°ã", approveId, Map.of( |
| | | "approveReason", safe(process.getApproveReason()), |
| | | "startDate", formatDate(process.getStartDate()), |
| | | "endDate", formatDate(process.getEndDate()), |
| | | "price", process.getPrice() == null ? "" : process.getPrice(), |
| | | "location", safe(process.getLocation()), |
| | | "approveType", approveTypeName(process.getApproveType()), |
| | | "approveRemark", safe(process.getApproveRemark()) |
| | | )); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Tool(name = "å é¤å®¡æ¹å¾
å", value = "å é¤å®¡æ¹æµç¨ï¼ä»
å
许ç³è¯·äººå 餿ªå®æçæµç¨ã") |
| | | public String deleteTodo(@ToolMemoryId String memoryId, |
| | | @P("æµç¨ç¼å· approveId") String approveId) { |
| | | ApproveProcess process = getAccessibleProcess(memoryId, approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "delete_action", "æªæ¾å°å¯¹åºå®¡æ¹ï¼æå½åç¨æ·æ æè®¿é®è¯¥æµç¨ã", approveId, null); |
| | | } |
| | | if (!Objects.equals(process.getApproveUser(), currentUserId(memoryId)) && !isAdmin(currentUserId(memoryId))) { |
| | | return actionResult(false, "delete_action", "åªæç³è¯·äººæç®¡çåå¯ä»¥å é¤å®¡æ¹åã", approveId, null); |
| | | } |
| | | if (process.getApproveStatus() != null && (process.getApproveStatus() == 1 || process.getApproveStatus() == 2)) { |
| | | return actionResult(false, "delete_action", "审æ¹å¤çä¸æå·²å®æçæµç¨ä¸å
许éè¿ AI å é¤ã", approveId, null); |
| | | } |
| | | |
| | | approveProcessService.delByIds(Collections.singletonList(process.getId())); |
| | | return actionResult(true, "delete_action", "å®¡æ¹æµç¨å·²å é¤ã", approveId, Map.of( |
| | | "deletedProcessId", process.getId(), |
| | | "approveStatus", approveStatusName(process.getApproveStatus()) |
| | | )); |
| | | } |
| | | |
| | | private ApproveProcess getAccessibleProcess(String memoryId, String approveId) { |
| | | ApproveProcess process = getProcessByApproveId(approveId); |
| | | if (process == null) { |
| | | return null; |
| | | } |
| | | return canView(process, currentUserId(memoryId)) ? process : null; |
| | | } |
| | | |
| | | private ApproveProcess getProcessByApproveId(String approveId) { |
| | | if (!StringUtils.hasText(approveId)) { |
| | | return null; |
| | | } |
| | | return approveProcessMapper.selectOne(new LambdaQueryWrapper<ApproveProcess>() |
| | | .eq(ApproveProcess::getApproveId, approveId) |
| | | .eq(ApproveProcess::getApproveDelete, 0) |
| | | .last("limit 1")); |
| | | } |
| | | |
| | | private List<ApproveNode> listNodes(ApproveProcess process) { |
| | | if (process == null) { |
| | | return List.of(); |
| | | } |
| | | List<ApproveNode> nodes = defaultList(approveNodeMapper.selectList(new LambdaQueryWrapper<ApproveNode>() |
| | | .eq(ApproveNode::getDeleteFlag, 0) |
| | | .eq(ApproveNode::getApproveProcessId, process.getApproveId()) |
| | | .orderByAsc(ApproveNode::getApproveNodeOrder))); |
| | | if (!nodes.isEmpty()) { |
| | | return nodes; |
| | | } |
| | | return defaultList(approveNodeMapper.selectList(new LambdaQueryWrapper<ApproveNode>() |
| | | .eq(ApproveNode::getDeleteFlag, 0) |
| | | .eq(ApproveNode::getApproveProcessId, String.valueOf(process.getId())) |
| | | .orderByAsc(ApproveNode::getApproveNodeOrder))); |
| | | } |
| | | |
| | | private List<ApproveLog> listLogs(Long processId) { |
| | | return defaultList(approveLogMapper.selectList(new LambdaQueryWrapper<ApproveLog>() |
| | | .eq(ApproveLog::getApproveId, processId) |
| | | .orderByAsc(ApproveLog::getApproveNodeOrder, ApproveLog::getApproveTime))); |
| | | } |
| | | |
| | | private ApproveNode findCurrentNode(List<ApproveNode> nodes) { |
| | | return nodes.stream() |
| | | .filter(node -> node.getApproveNodeStatus() != null && node.getApproveNodeStatus() == 0) |
| | | .min(Comparator.comparing(ApproveNode::getApproveNodeOrder)) |
| | | .orElse(null); |
| | | } |
| | | |
| | | private boolean isLastNode(List<ApproveNode> nodes, ApproveNode currentNode) { |
| | | Integer maxOrder = nodes.stream() |
| | | .map(ApproveNode::getApproveNodeOrder) |
| | | .filter(Objects::nonNull) |
| | | .max(Integer::compareTo) |
| | | .orElse(null); |
| | | return maxOrder != null && Objects.equals(maxOrder, currentNode.getApproveNodeOrder()); |
| | | } |
| | | |
| | | private void writeApproveLog(String memoryId, ApproveProcess process, ApproveNode currentNode, String remark) { |
| | | if (process == null || currentNode == null) { |
| | | return; |
| | | } |
| | | ApproveLog log = new ApproveLog(); |
| | | log.setApproveId(process.getId()); |
| | | log.setApproveNodeOrder(currentNode.getApproveNodeOrder()); |
| | | log.setApproveUser(currentUserId(memoryId)); |
| | | log.setApproveTime(new Date()); |
| | | log.setApproveStatus(process.getApproveStatus()); |
| | | log.setApproveRemark(remark); |
| | | approveLogMapper.insert(log); |
| | | } |
| | | |
| | | private boolean canView(ApproveProcess process, Long userId) { |
| | | if (process == null || userId == null) { |
| | | return false; |
| | | } |
| | | return isAdmin(userId) |
| | | || Objects.equals(process.getApproveUser(), userId) |
| | | || Objects.equals(process.getApproveUserCurrentId(), userId) |
| | | || containsUserId(process.getApproveUserIds(), userId); |
| | | } |
| | | |
| | | private boolean canOperate(ApproveProcess process, Long userId) { |
| | | return process != null && userId != null && Objects.equals(process.getApproveUserCurrentId(), userId); |
| | | } |
| | | |
| | | private boolean containsUserId(String csv, Long userId) { |
| | | if (!StringUtils.hasText(csv) || userId == null) { |
| | | return false; |
| | | } |
| | | String target = String.valueOf(userId); |
| | | for (String item : csv.split(",")) { |
| | | if (target.equals(item.trim())) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private String relationName(ApproveProcess process, Long userId) { |
| | | if (Objects.equals(process.getApproveUserCurrentId(), userId)) { |
| | | return "å½å审æ¹äºº"; |
| | | } |
| | | if (Objects.equals(process.getApproveUser(), userId)) { |
| | | return "ç³è¯·äºº"; |
| | | } |
| | | if (containsUserId(process.getApproveUserIds(), userId)) { |
| | | return "审æ¹é¾æå"; |
| | | } |
| | | return "å¯è§"; |
| | | } |
| | | |
| | | private List<String> todoColumns() { |
| | | return List.of("approveId", "approveType", "approveUserName", "approveUserCurrentName", |
| | | "approveReason", "approveStatus", "createTime", "relation"); |
| | | } |
| | | |
| | | private int normalizeLimit(Integer limit) { |
| | | if (limit == null || limit <= 0) { |
| | | return DEFAULT_LIMIT; |
| | | } |
| | | return Math.min(limit, MAX_LIMIT); |
| | | } |
| | | |
| | | private Integer parseStatus(String status) { |
| | | if (!StringUtils.hasText(status) || "all".equalsIgnoreCase(status)) { |
| | | return null; |
| | | } |
| | | return switch (status.trim().toLowerCase()) { |
| | | case "pending" -> 0; |
| | | case "processing" -> 1; |
| | | case "approved" -> 2; |
| | | case "rejected" -> 3; |
| | | case "resubmitted" -> 4; |
| | | default -> null; |
| | | }; |
| | | } |
| | | |
| | | private String normalizeScope(String scope) { |
| | | if (!StringUtils.hasText(scope)) { |
| | | return "related"; |
| | | } |
| | | return switch (scope.trim().toLowerCase()) { |
| | | case "applicant", "mine", "created", "initiated" -> "applicant"; |
| | | case "approver", "handler", "todo", "pending" -> "approver"; |
| | | default -> "related"; |
| | | }; |
| | | } |
| | | |
| | | private String approveStatusName(Integer status) { |
| | | if (status == null) { |
| | | return "æªç¥"; |
| | | } |
| | | return switch (status) { |
| | | case 0 -> "å¾
å®¡æ ¸"; |
| | | case 1 -> "å®¡æ ¸ä¸"; |
| | | case 2 -> "å®¡æ ¸å®æ"; |
| | | case 3 -> "å®¡æ ¸æªéè¿"; |
| | | case 4 -> "已鿰æäº¤"; |
| | | default -> "æªç¥"; |
| | | }; |
| | | } |
| | | |
| | | private String approveNodeStatusName(Integer status) { |
| | | if (status == null) { |
| | | return "æªç¥"; |
| | | } |
| | | return switch (status) { |
| | | case 0 -> "æªå®¡æ ¸"; |
| | | case 1 -> "åæ"; |
| | | case 2 -> "æç»"; |
| | | default -> "æªç¥"; |
| | | }; |
| | | } |
| | | |
| | | private String approveTypeName(Integer type) { |
| | | if (type == null) { |
| | | return "æªç¥"; |
| | | } |
| | | return switch (type) { |
| | | case 1 -> "å
¬åºç®¡ç"; |
| | | case 2 -> "请å管ç"; |
| | | case 3 -> "åºå·®ç®¡ç"; |
| | | case 4 -> "æ¥é管ç"; |
| | | case 5 -> "éè´å®¡æ¹"; |
| | | case 6 -> "æ¥ä»·å®¡æ¹"; |
| | | case 7 -> "å货审æ¹"; |
| | | case 8 -> "å±é©ä½ä¸å®¡æ¹"; |
| | | case 9 -> "åå
¬ç¨å审æ¹"; |
| | | default -> "ç±»å" + type; |
| | | }; |
| | | } |
| | | |
| | | private String safe(Object value) { |
| | | return value == null ? "" : String.valueOf(value).replace('\n', ' ').replace('\r', ' '); |
| | | } |
| | | |
| | | private String formatDateTime(Object value) { |
| | | if (value == null) { |
| | | return ""; |
| | | } |
| | | if (value instanceof LocalDateTime localDateTime) { |
| | | return localDateTime.format(DATE_TIME_FORMATTER); |
| | | } |
| | | return safe(value); |
| | | } |
| | | |
| | | private String formatDate(Date value) { |
| | | if (value == null) { |
| | | return ""; |
| | | } |
| | | return value.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DATE_FORMATTER); |
| | | } |
| | | |
| | | private long countByStatus(List<ApproveProcess> processes, int status) { |
| | | return processes.stream() |
| | | .filter(process -> process.getApproveStatus() != null) |
| | | .filter(process -> process.getApproveStatus() == status) |
| | | .count(); |
| | | } |
| | | |
| | | private String calculateRate(long part, int total) { |
| | | if (total <= 0) { |
| | | return "0.00%"; |
| | | } |
| | | return String.format("%.2f%%", part * 100.0 / total); |
| | | } |
| | | |
| | | private List<Map<String, Object>> toTrendItems(List<String> dates, List<Long> values) { |
| | | List<Map<String, Object>> items = new ArrayList<>(); |
| | | for (int i = 0; i < dates.size(); i++) { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("date", dates.get(i)); |
| | | item.put("count", values.get(i)); |
| | | items.add(item); |
| | | } |
| | | return items; |
| | | } |
| | | |
| | | private Map<String, Object> buildStatusBarOption(Map<String, Long> statusStats) { |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "审æ¹ç¶æåå¸", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", new ArrayList<>(statusStats.keySet()))); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of( |
| | | "name", "æ°é", |
| | | "type", "bar", |
| | | "data", new ArrayList<>(statusStats.values()), |
| | | "barWidth", "40%" |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildTypePieOption(Map<String, Long> typeStats) { |
| | | List<Map<String, Object>> data = typeStats.entrySet().stream() |
| | | .map(entry -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("name", entry.getKey()); |
| | | item.put("value", entry.getValue()); |
| | | return item; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "审æ¹ç±»åå æ¯", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "item")); |
| | | option.put("legend", Map.of("orient", "vertical", "left", "left")); |
| | | option.put("series", List.of(Map.of( |
| | | "name", "审æ¹ç±»å", |
| | | "type", "pie", |
| | | "radius", List.of("35%", "65%"), |
| | | "data", data |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildTrendLineOption(List<String> dates, List<Long> values, String label) { |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", label + "å®¡æ¹æ°å¢è¶å¿", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", dates)); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of( |
| | | "name", "æ°å¢å®¡æ¹", |
| | | "type", "line", |
| | | "smooth", true, |
| | | "data", values, |
| | | "areaStyle", Map.of() |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Date parseDate(String dateText) { |
| | | try { |
| | | LocalDate localDate = LocalDate.parse(dateText, DATE_FORMATTER); |
| | | return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | } catch (Exception e) { |
| | | throw new IllegalArgumentException("æ¥ææ ¼å¼å¿
é¡»æ¯ yyyy-MM-dd"); |
| | | } |
| | | } |
| | | |
| | | private DateRange resolveDateRange(String startDateText, String endDateText, String timeRange) { |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDate explicitStart = parseLocalDate(startDateText); |
| | | LocalDate explicitEnd = parseLocalDate(endDateText); |
| | | if (explicitStart != null || explicitEnd != null) { |
| | | LocalDate start = explicitStart != null ? explicitStart : explicitEnd; |
| | | LocalDate end = explicitEnd != null ? explicitEnd : explicitStart; |
| | | if (start.isAfter(end)) { |
| | | LocalDate temp = start; |
| | | start = end; |
| | | end = temp; |
| | | } |
| | | return new DateRange(start, end, start + "è³" + end); |
| | | } |
| | | if (!StringUtils.hasText(timeRange)) { |
| | | return new DateRange(today.minusDays(6), today, "è¿7天"); |
| | | } |
| | | |
| | | String text = timeRange.trim(); |
| | | if (text.contains("ä»å¤©")) { |
| | | return new DateRange(today, today, "ä»å¤©"); |
| | | } |
| | | if (text.contains("æ¨å¤©") || text.contains("æ¨æ¥")) { |
| | | LocalDate day = today.minusDays(1); |
| | | return new DateRange(day, day, "æ¨å¤©"); |
| | | } |
| | | if (text.contains("æ¬å¨")) { |
| | | LocalDate start = today.minusDays(today.getDayOfWeek().getValue() - 1L); |
| | | return new DateRange(start, today, "æ¬å¨"); |
| | | } |
| | | if (text.contains("ä¸å¨")) { |
| | | LocalDate thisWeekStart = today.minusDays(today.getDayOfWeek().getValue() - 1L); |
| | | LocalDate start = thisWeekStart.minusWeeks(1); |
| | | LocalDate end = thisWeekStart.minusDays(1); |
| | | return new DateRange(start, end, "ä¸å¨"); |
| | | } |
| | | if (text.contains("æ¬æ")) { |
| | | LocalDate start = today.withDayOfMonth(1); |
| | | return new DateRange(start, today, "æ¬æ"); |
| | | } |
| | | if (text.contains("䏿")) { |
| | | YearMonth lastMonth = YearMonth.from(today).minusMonths(1); |
| | | return new DateRange(lastMonth.atDay(1), lastMonth.atEndOfMonth(), "䏿"); |
| | | } |
| | | if (text.contains("æ¬å¹´") || text.contains("ä»å¹´")) { |
| | | LocalDate start = today.withDayOfYear(1); |
| | | return new DateRange(start, today, "æ¬å¹´"); |
| | | } |
| | | if (text.contains("å»å¹´")) { |
| | | LocalDate start = today.minusYears(1).withDayOfYear(1); |
| | | LocalDate end = today.minusYears(1).withMonth(12).withDayOfMonth(31); |
| | | return new DateRange(start, end, "å»å¹´"); |
| | | } |
| | | |
| | | Matcher relativeMatcher = java.util.regex.Pattern.compile("(è¿|æè¿)(\\d+)(天|å¨|个æ|æ|å¹´)").matcher(text); |
| | | if (relativeMatcher.find()) { |
| | | int amount = Integer.parseInt(relativeMatcher.group(2)); |
| | | String unit = relativeMatcher.group(3); |
| | | LocalDate start = switch (unit) { |
| | | case "天" -> today.minusDays(Math.max(amount - 1L, 0)); |
| | | case "å¨" -> today.minusWeeks(Math.max(amount, 1)).plusDays(1); |
| | | case "个æ", "æ" -> today.minusMonths(Math.max(amount, 1)).plusDays(1); |
| | | case "å¹´" -> today.minusYears(Math.max(amount, 1)).plusDays(1); |
| | | default -> today.minusDays(6); |
| | | }; |
| | | return new DateRange(start, today, "è¿" + amount + unit); |
| | | } |
| | | |
| | | Matcher dateMatcher = java.util.regex.Pattern.compile("(\\d{4}-\\d{2}-\\d{2})").matcher(text); |
| | | if (dateMatcher.find()) { |
| | | LocalDate start = LocalDate.parse(dateMatcher.group(1)); |
| | | LocalDate end = dateMatcher.find() ? LocalDate.parse(dateMatcher.group(1)) : start; |
| | | if (start.isAfter(end)) { |
| | | LocalDate temp = start; |
| | | start = end; |
| | | end = temp; |
| | | } |
| | | return new DateRange(start, end, start + "è³" + end); |
| | | } |
| | | |
| | | return new DateRange(today.minusDays(6), today, "è¿7天"); |
| | | } |
| | | |
| | | private boolean withinDateRange(LocalDateTime createTime, DateRange dateRange) { |
| | | if (createTime == null) { |
| | | return false; |
| | | } |
| | | LocalDate date = createTime.toLocalDate(); |
| | | return !date.isBefore(dateRange.start()) && !date.isAfter(dateRange.end()); |
| | | } |
| | | |
| | | private TrendRange buildTrendRange(LocalDate start, LocalDate end, List<ApproveProcess> processes) { |
| | | long days = ChronoUnit.DAYS.between(start, end) + 1; |
| | | if (days <= 31) { |
| | | List<String> labels = new ArrayList<>(); |
| | | List<Long> values = new ArrayList<>(); |
| | | for (LocalDate cursor = start; !cursor.isAfter(end); cursor = cursor.plusDays(1)) { |
| | | LocalDate current = cursor; |
| | | labels.add(current.toString()); |
| | | values.add(processes.stream() |
| | | .filter(process -> process.getCreateTime() != null) |
| | | .filter(process -> process.getCreateTime().toLocalDate().equals(current)) |
| | | .count()); |
| | | } |
| | | return new TrendRange(labels, values, "day", start + "è³" + end); |
| | | } |
| | | |
| | | List<String> labels = new ArrayList<>(); |
| | | List<Long> values = new ArrayList<>(); |
| | | YearMonth startMonth = YearMonth.from(start); |
| | | YearMonth endMonth = YearMonth.from(end); |
| | | for (YearMonth cursor = startMonth; !cursor.isAfter(endMonth); cursor = cursor.plusMonths(1)) { |
| | | YearMonth current = cursor; |
| | | labels.add(current.toString()); |
| | | values.add(processes.stream() |
| | | .filter(process -> process.getCreateTime() != null) |
| | | .filter(process -> YearMonth.from(process.getCreateTime()).equals(current)) |
| | | .count()); |
| | | } |
| | | return new TrendRange(labels, values, "month", start + "è³" + end); |
| | | } |
| | | |
| | | private LocalDate parseLocalDate(String text) { |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | return LocalDate.parse(text.trim()); |
| | | } |
| | | |
| | | private String actionResult(boolean success, String type, String description, String approveId, Map<String, Object> data) { |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("approveId", safe(approveId)); |
| | | return jsonResponse(success, type, description, summary, data == null ? Map.of() : data, Map.of()); |
| | | } |
| | | |
| | | private String jsonResponse(boolean success, |
| | | String type, |
| | | String description, |
| | | Map<String, Object> summary, |
| | | Map<String, Object> data, |
| | | Map<String, Object> charts) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("success", success); |
| | | result.put("type", type); |
| | | result.put("description", description); |
| | | result.put("summary", summary == null ? Map.of() : summary); |
| | | result.put("data", data == null ? Map.of() : data); |
| | | result.put("charts", charts == null ? Map.of() : charts); |
| | | return JSON.toJSONString(result); |
| | | } |
| | | |
| | | private LoginUser currentLoginUser(String memoryId) { |
| | | LoginUser loginUser = aiSessionUserContext.get(memoryId); |
| | | if (loginUser != null) { |
| | | return loginUser; |
| | | } |
| | | return SecurityUtils.getLoginUser(); |
| | | } |
| | | |
| | | private Long currentUserId(String memoryId) { |
| | | return currentLoginUser(memoryId).getUserId(); |
| | | } |
| | | |
| | | private boolean isAdmin(Long userId) { |
| | | return SecurityUtils.isAdmin(userId); |
| | | } |
| | | |
| | | private <T> List<T> defaultList(List<T> list) { |
| | | return list == null ? List.of() : list; |
| | | } |
| | | |
| | | private record DateRange(LocalDate start, LocalDate end, String label) { |
| | | } |
| | | |
| | | private record TrendRange(List<String> labels, List<Long> values, String granularity, String label) { |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.tools; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.support.SFunction; |
| | | import com.ruoyi.account.mapper.AccountStatementMapper; |
| | | import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper; |
| | | import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper; |
| | | import com.ruoyi.account.pojo.AccountStatement; |
| | | import com.ruoyi.account.pojo.purchase.AccountPurchasePayment; |
| | | import com.ruoyi.account.pojo.sales.AccountSalesCollection; |
| | | import com.ruoyi.account.service.impl.AccountingServiceImpl; |
| | | import com.ruoyi.ai.context.AiSessionUserContext; |
| | | import com.ruoyi.basic.mapper.CustomerMapper; |
| | | import com.ruoyi.basic.mapper.ProductMapper; |
| | | import com.ruoyi.basic.mapper.ProductModelMapper; |
| | | import com.ruoyi.basic.mapper.SupplierManageMapper; |
| | | import com.ruoyi.basic.pojo.Customer; |
| | | import com.ruoyi.basic.pojo.Product; |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.basic.pojo.SupplierManage; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.device.mapper.DeviceLedgerMapper; |
| | | import com.ruoyi.device.mapper.DeviceRepairMapper; |
| | | import com.ruoyi.device.pojo.DeviceLedger; |
| | | import com.ruoyi.device.pojo.DeviceRepair; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper; |
| | | import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper; |
| | | import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut; |
| | | import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage; |
| | | import com.ruoyi.production.mapper.ProductionAccountMapper; |
| | | import com.ruoyi.production.mapper.ProductionOperationTaskMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderMapper; |
| | | import com.ruoyi.production.mapper.ProductionPlanMapper; |
| | | import com.ruoyi.production.mapper.ProductionProductMainMapper; |
| | | import com.ruoyi.production.mapper.ProductionProductOutputMapper; |
| | | import com.ruoyi.production.pojo.ProductionAccount; |
| | | import com.ruoyi.production.pojo.ProductionOperationTask; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import com.ruoyi.production.pojo.ProductionPlan; |
| | | import com.ruoyi.production.pojo.ProductionProductMain; |
| | | import com.ruoyi.production.pojo.ProductionProductOutput; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedger; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.pojo.StockInventory; |
| | | import com.ruoyi.technology.mapper.TechnologyOperationMapper; |
| | | import com.ruoyi.technology.pojo.TechnologyOperation; |
| | | import dev.langchain4j.agent.tool.P; |
| | | import dev.langchain4j.agent.tool.Tool; |
| | | import dev.langchain4j.agent.tool.ToolMemoryId; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.YearMonth; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Component |
| | | public class FinancialAgentTools { |
| | | |
| | | private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | private static final Pattern RELATIVE_PATTERN = Pattern.compile("(è¿|æè¿)?\\s*(\\d+)\\s*(天|å¨|个æ|æ|å¹´)"); |
| | | private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4}-\\d{2}-\\d{2})"); |
| | | 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.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; |
| | | private final ProductionAccountMapper productionAccountMapper; |
| | | private final ProductionProductMainMapper productionProductMainMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionPlanMapper productionPlanMapper; |
| | | private final ProductionProductOutputMapper productionProductOutputMapper; |
| | | private final TechnologyOperationMapper technologyOperationMapper; |
| | | private final DeviceLedgerMapper deviceLedgerMapper; |
| | | private final DeviceRepairMapper deviceRepairMapper; |
| | | private final ProcurementRecordMapper procurementRecordMapper; |
| | | private final ProcurementRecordOutMapper procurementRecordOutMapper; |
| | | private final StockInventoryMapper stockInventoryMapper; |
| | | private final AccountSalesCollectionMapper accountSalesCollectionMapper; |
| | | private final AccountPurchasePaymentMapper accountPurchasePaymentMapper; |
| | | private final AccountStatementMapper accountStatementMapper; |
| | | private final CustomerMapper customerMapper; |
| | | private final SupplierManageMapper supplierManageMapper; |
| | | private final ProductModelMapper productModelMapper; |
| | | private final ProductMapper productMapper; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | |
| | | public FinancialAgentTools(SalesLedgerMapper salesLedgerMapper, |
| | | SalesLedgerProductMapper salesLedgerProductMapper, |
| | | ProductionAccountMapper productionAccountMapper, |
| | | ProductionProductMainMapper productionProductMainMapper, |
| | | ProductionOperationTaskMapper productionOperationTaskMapper, |
| | | ProductionOrderMapper productionOrderMapper, |
| | | ProductionPlanMapper productionPlanMapper, |
| | | ProductionProductOutputMapper productionProductOutputMapper, |
| | | TechnologyOperationMapper technologyOperationMapper, |
| | | DeviceLedgerMapper deviceLedgerMapper, |
| | | DeviceRepairMapper deviceRepairMapper, |
| | | ProcurementRecordMapper procurementRecordMapper, |
| | | ProcurementRecordOutMapper procurementRecordOutMapper, |
| | | StockInventoryMapper stockInventoryMapper, |
| | | AccountSalesCollectionMapper accountSalesCollectionMapper, |
| | | AccountPurchasePaymentMapper accountPurchasePaymentMapper, |
| | | AccountStatementMapper accountStatementMapper, |
| | | CustomerMapper customerMapper, |
| | | SupplierManageMapper supplierManageMapper, |
| | | ProductModelMapper productModelMapper, |
| | | ProductMapper productMapper, |
| | | AiSessionUserContext aiSessionUserContext) { |
| | | this.salesLedgerMapper = salesLedgerMapper; |
| | | this.salesLedgerProductMapper = salesLedgerProductMapper; |
| | | this.productionAccountMapper = productionAccountMapper; |
| | | this.productionProductMainMapper = productionProductMainMapper; |
| | | this.productionOperationTaskMapper = productionOperationTaskMapper; |
| | | this.productionOrderMapper = productionOrderMapper; |
| | | this.productionPlanMapper = productionPlanMapper; |
| | | this.productionProductOutputMapper = productionProductOutputMapper; |
| | | this.technologyOperationMapper = technologyOperationMapper; |
| | | this.deviceLedgerMapper = deviceLedgerMapper; |
| | | this.deviceRepairMapper = deviceRepairMapper; |
| | | this.procurementRecordMapper = procurementRecordMapper; |
| | | this.procurementRecordOutMapper = procurementRecordOutMapper; |
| | | this.stockInventoryMapper = stockInventoryMapper; |
| | | this.accountSalesCollectionMapper = accountSalesCollectionMapper; |
| | | this.accountPurchasePaymentMapper = accountPurchasePaymentMapper; |
| | | this.accountStatementMapper = accountStatementMapper; |
| | | this.customerMapper = customerMapper; |
| | | this.supplierManageMapper = supplierManageMapper; |
| | | this.productModelMapper = productModelMapper; |
| | | this.productMapper = productMapper; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | } |
| | | |
| | | @Tool(name = "è´¢å¡ç¥è¯æ£ç´¢", value = "æè´¢å¡ç»è¥é®é¢æ£ç´¢ä¸è´¢èåç¥è¯çæ®µä¸ææ å£å¾ï¼ä½ä¸ºRAGä¸ä¸æã") |
| | | public String retrieveFinancialKnowledge(@ToolMemoryId String memoryId, |
| | | @P(value = "é®é¢æå
³é®è¯ï¼ä¾å¦å©æ¶¦ä¸éãåºåå¨è½¬ãèµé缺å£") String question) { |
| | | List<KnowledgeDoc> knowledgeDocs = financeKnowledgeBase(); |
| | | String normalized = normalizeForMatch(question); |
| | | List<KnowledgeDoc> ranked = knowledgeDocs.stream() |
| | | .sorted(Comparator.comparingInt((KnowledgeDoc doc) -> keywordHitCount(doc.keywords(), normalized)).reversed()) |
| | | .filter(doc -> keywordHitCount(doc.keywords(), normalized) > 0 || !StringUtils.hasText(normalized)) |
| | | .limit(5) |
| | | .toList(); |
| | | |
| | | List<Map<String, Object>> items = ranked.stream().map(doc -> { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("topic", doc.topic()); |
| | | map.put("knowledge", doc.knowledge()); |
| | | map.put("relatedTables", doc.relatedTables()); |
| | | map.put("suggestedQuestions", doc.suggestedQuestions()); |
| | | return map; |
| | | }).toList(); |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("question", safe(question)); |
| | | summary.put("hitCount", items.size()); |
| | | summary.put("retrievalMode", "keyword_rag"); |
| | | return jsonResponse(true, "financial_rag_knowledge", "å·²è¿åè´¢å¡ç¥è¯æ£ç´¢ç»æ", summary, Map.of("items", items), Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "æºè½ææ¬æ ¸ç®", value = "èªå¨æ ¸ç®äº§åææ¬ãå·¥åºææ¬ãäººå·¥ææ¬ãè®¾å¤ææ§ãæææèä¸è®¢å婿¶¦ã") |
| | | public String calculateIntelligentCost(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼å¦æ¬æãè¿30天", required = false) String timeRange, |
| | | @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", 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()); |
| | | summary.put("totalLaborCost", bundle.totalLaborCost()); |
| | | summary.put("totalDepreciationCost", bundle.totalDepreciationCost()); |
| | | summary.put("totalScrapCost", bundle.totalScrapCost()); |
| | | summary.put("totalCost", bundle.totalCost()); |
| | | summary.put("totalProfit", bundle.totalProfit()); |
| | | summary.put("profitRate", toPercent(rate(bundle.totalProfit(), bundle.totalRevenue()))); |
| | | |
| | | List<Map<String, Object>> orderItems = bundle.orderMetrics().stream() |
| | | .map(this::toOrderCostItem) |
| | | .toList(); |
| | | List<Map<String, Object>> processItems = bundle.processCostRanking().entrySet().stream() |
| | | .map(entry -> { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("processName", entry.getKey()); |
| | | map.put("cost", entry.getValue()); |
| | | return map; |
| | | }).toList(); |
| | | |
| | | List<Map<String, Object>> topCustomerItems = buildCustomerProfitTop(bundle.orderMetrics(), 5); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("costCompositionPieOption", |
| | | buildCostCompositionPie(bundle.totalMaterialCost(), bundle.totalLaborCost(), bundle.totalDepreciationCost(), bundle.totalScrapCost())); |
| | | charts.put("orderProfitBarOption", buildOrderProfitBar(bundle.orderMetrics())); |
| | | charts.put("processCostBarOption", buildProcessCostBar(bundle.processCostRanking())); |
| | | |
| | | return jsonResponse(true, "financial_cost_accounting", "已宿æºè½ææ¬æ ¸ç®", summary, |
| | | Map.of( |
| | | "orders", orderItems, |
| | | "processCostRanking", processItems, |
| | | "topCustomers", topCustomerItems |
| | | ), |
| | | charts |
| | | ); |
| | | } |
| | | |
| | | @Tool(name = "订å婿¶¦åæ", value = "è¯å«ä½å©æ¶¦/äºæè®¢åï¼è¾åºåå åæåä¼å建议ã") |
| | | public String analyzeOrderProfit(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼å¦æ¬æãè¿30天", required = false) String timeRange, |
| | | @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(SME_PROFIT_WARNING_RATE) < 0) |
| | | .sorted(Comparator.comparing(OrderProfitMetric::profitRate) |
| | | .thenComparing(OrderProfitMetric::profit)) |
| | | .toList(); |
| | | |
| | | Map<String, BigDecimal> customerProfitMap = new LinkedHashMap<>(); |
| | | for (OrderProfitMetric metric : metrics) { |
| | | customerProfitMap.merge(metric.customerName(), metric.profit(), BigDecimal::add); |
| | | } |
| | | Map.Entry<String, BigDecimal> topCustomer = customerProfitMap.entrySet().stream() |
| | | .max(Map.Entry.comparingByValue()) |
| | | .orElse(Map.entry("ææ æ°æ®", BigDecimal.ZERO)); |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("timeRange", range.label()); |
| | | 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()); |
| | | summary.put("avgProfitRate", toPercent(avgRate(metrics))); |
| | | summary.put("topCustomerByProfit", topCustomer.getKey()); |
| | | summary.put("topCustomerProfit", topCustomer.getValue()); |
| | | |
| | | List<Map<String, Object>> riskyItems = riskyOrders.stream() |
| | | .limit(normalizeLimit(limit)) |
| | | .map(this::toRiskOrderItem) |
| | | .toList(); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("profitDistributionOption", buildProfitDistributionBar(metrics)); |
| | | charts.put("lossOrderTrendOption", buildLossOrderTrendLine(metrics)); |
| | | charts.put("customerProfitTopOption", buildCustomerProfitBar(customerProfitMap)); |
| | | |
| | | return jsonResponse(true, "financial_order_profit_analysis", "å·²å®æè®¢å婿¶¦åæ", summary, |
| | | Map.of( |
| | | "riskOrders", riskyItems, |
| | | "allOrders", metrics.stream().map(this::toOrderCostItem).toList(), |
| | | "customerProfitTop", buildCustomerProfitTop(metrics, 10) |
| | | ), |
| | | charts |
| | | ); |
| | | } |
| | | |
| | | @Tool(name = "åºåèµéåæ", value = "åæåºå积åãåæ»åºåãèµéå ç¨ä¸å¨è½¬çã") |
| | | public String analyzeInventoryCapital(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼å¦æ¬æãè¿30天", required = false) String timeRange, |
| | | @P(value = "å
³é®è¯ï¼å¯å¹é
产ååç§°/åå·", required = false) String keyword, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§50", required = false) Integer limit) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, timeRange, "è¿30天"); |
| | | int finalLimit = normalizeLimit(limit); |
| | | |
| | | List<StockInventory> inventoryRows = queryStockInventory(loginUser); |
| | | if (inventoryRows.isEmpty()) { |
| | | return jsonResponse(true, "financial_inventory_capital_analysis", "å½åæ åºåæ°æ®", |
| | | rangeSummary(range, 0, keyword), Map.of("items", List.of()), Map.of()); |
| | | } |
| | | |
| | | Set<Long> modelIds = inventoryRows.stream() |
| | | .map(StockInventory::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, ProductModel> productModelMap = queryProductModels(modelIds); |
| | | Map<Long, Product> productMap = queryProducts(productModelMap.values()); |
| | | Map<Long, BigDecimal> avgUnitCostByModelId = queryAverageUnitCostByModel(loginUser, modelIds); |
| | | OutboundStats outboundStats = queryOutboundStats(loginUser, modelIds, range); |
| | | |
| | | List<InventoryMetric> metrics = buildInventoryMetrics(inventoryRows, productModelMap, productMap, avgUnitCostByModelId, outboundStats) |
| | | .stream() |
| | | .filter(metric -> matchInventoryKeyword(metric, keyword)) |
| | | .sorted(Comparator.comparing(InventoryMetric::inventoryValue).reversed()) |
| | | .toList(); |
| | | |
| | | BigDecimal totalInventoryValue = metrics.stream().map(InventoryMetric::inventoryValue).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal stagnantValue = metrics.stream() |
| | | .filter(metric -> metric.stagnantDays() >= 90) |
| | | .map(InventoryMetric::inventoryValue) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | long stagnantCount = metrics.stream().filter(metric -> metric.stagnantDays() >= 90).count(); |
| | | long overstockCount = metrics.stream().filter(InventoryMetric::overstock).count(); |
| | | BigDecimal totalOutboundCost = outboundStats.totalOutboundCost(); |
| | | BigDecimal turnoverDays = totalOutboundCost.compareTo(BigDecimal.ZERO) > 0 |
| | | ? totalInventoryValue.multiply(BigDecimal.valueOf(daysBetween(range.start(), range.end()) + 1L)) |
| | | .divide(totalOutboundCost, 2, RoundingMode.HALF_UP) |
| | | : BigDecimal.ZERO; |
| | | |
| | | List<Map<String, Object>> items = metrics.stream() |
| | | .limit(finalLimit) |
| | | .map(this::toInventoryItem) |
| | | .toList(); |
| | | |
| | | Map<String, Object> summary = rangeSummary(range, metrics.size(), keyword); |
| | | summary.put("totalInventoryValue", totalInventoryValue); |
| | | summary.put("stagnantValue", stagnantValue); |
| | | summary.put("stagnantCount", stagnantCount); |
| | | summary.put("overstockCount", overstockCount); |
| | | summary.put("turnoverDays", turnoverDays); |
| | | summary.put("capitalOccupation", totalInventoryValue); |
| | | summary.put("totalOutboundCost", totalOutboundCost); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("inventoryValueTopOption", buildInventoryTopBar(metrics)); |
| | | charts.put("inventoryAgingPieOption", buildInventoryAgingPie(metrics)); |
| | | charts.put("inventoryTurnoverGauge", buildTurnoverGauge(turnoverDays)); |
| | | |
| | | return jsonResponse(true, "financial_inventory_capital_analysis", "已宿åºåèµéåæ", summary, Map.of("items", items), charts); |
| | | } |
| | | |
| | | @Tool(name = "åºæ¶åºä»ä¸ç°éæµé¢æµ", value = "颿µæªæ¥ç°éæµã忬¾é£é©ã仿¬¾ååä¸èµé缺å£ã") |
| | | public String forecastCashFlow(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼å¦è¿90å¤©ãæ¬å¹´", required = false) String timeRange, |
| | | @P(value = "颿µæä»½æ°ï¼é»è®¤3ï¼æå¤§6", required = false) Integer forecastMonths) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, timeRange, "è¿90天"); |
| | | int months = forecastMonths == null || forecastMonths <= 0 ? 3 : Math.min(forecastMonths, 6); |
| | | |
| | | List<AccountSalesCollection> collections = queryCollections(loginUser, range); |
| | | List<AccountPurchasePayment> payments = queryPayments(loginUser, range); |
| | | List<MonthlyCashFlow> monthlyActual = buildMonthlyCashFlow(range, collections, payments); |
| | | List<MonthlyCashFlow> monthlyForecast = forecastMonthlyCashFlow(monthlyActual, months); |
| | | |
| | | StatementSnapshot snapshot = buildStatementSnapshot(loginUser); |
| | | BigDecimal receivableTotal = snapshot.receivableTotal(); |
| | | BigDecimal payableTotal = snapshot.payableTotal(); |
| | | BigDecimal forecastNetSum = monthlyForecast.stream().map(MonthlyCashFlow::netFlow).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal coverage = receivableTotal.add(maxZero(forecastNetSum)); |
| | | BigDecimal fundGap = maxZero(payableTotal.subtract(coverage)); |
| | | |
| | | Map<String, String> customerNameMap = queryCustomerNameMap(snapshot.receivableTop().stream().map(StatementMetric::entityId).collect(Collectors.toSet())); |
| | | Map<String, String> supplierNameMap = querySupplierNameMap(snapshot.payableTop().stream().map(StatementMetric::entityId).collect(Collectors.toSet())); |
| | | |
| | | List<Map<String, Object>> receivableRiskItems = snapshot.receivableTop().stream().map(item -> toStatementRiskItem(item, customerNameMap, "customer")).toList(); |
| | | List<Map<String, Object>> payablePressureItems = snapshot.payableTop().stream().map(item -> toStatementRiskItem(item, supplierNameMap, "supplier")).toList(); |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("timeRange", range.label()); |
| | | 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); |
| | | summary.put("payableBalance", payableTotal); |
| | | summary.put("forecastNetSum", forecastNetSum); |
| | | summary.put("fundGap", fundGap); |
| | | summary.put("forecastMonths", months); |
| | | summary.put("collectionRiskLevel", riskLevelByAmount(receivableTotal)); |
| | | summary.put("paymentPressureLevel", riskLevelByAmount(payableTotal)); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("cashFlowTrendOption", buildCashflowTrend(monthlyActual, monthlyForecast)); |
| | | charts.put("receivablePayableBarOption", buildReceivablePayableBar(receivableTotal, payableTotal)); |
| | | charts.put("fundGapGaugeOption", buildFundGapGauge(fundGap)); |
| | | |
| | | return jsonResponse(true, "financial_cashflow_forecast", "å·²å®æåºæ¶åºä»ä¸ç°éæµé¢æµ", summary, |
| | | Map.of( |
| | | "actualMonthly", monthlyActual.stream().map(this::toMonthlyCashFlowItem).toList(), |
| | | "forecastMonthly", monthlyForecast.stream().map(this::toMonthlyCashFlowItem).toList(), |
| | | "receivableRiskTop", receivableRiskItems, |
| | | "payablePressureTop", payablePressureItems |
| | | ), |
| | | charts |
| | | ); |
| | | } |
| | | |
| | | @Tool(name = "ç»è¥å¼å¸¸é¢è¦", value = "è¯å«ææ¬å¼å¸¸ã婿¶¦å¼å¸¸ã忬¾å¼å¸¸ã订åé£é©ãåºåå¼å¸¸ã") |
| | | public String detectBusinessAnomalies(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼å¦è¿30天", required = false) String timeRange, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§50", required = false) Integer limit) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, timeRange, "è¿30天"); |
| | | int finalLimit = normalizeLimit(limit); |
| | | |
| | | AnalysisBundle currentBundle = buildOrderProfitBundle(loginUser, range, null, Math.max(finalLimit, 30)); |
| | | DateRange prevRange = previousSameLengthRange(range); |
| | | AnalysisBundle prevBundle = buildOrderProfitBundle(loginUser, prevRange, null, 50); |
| | | |
| | | BigDecimal currentCostRate = rate(currentBundle.totalCost(), currentBundle.totalRevenue()); |
| | | BigDecimal prevCostRate = rate(prevBundle.totalCost(), prevBundle.totalRevenue()); |
| | | BigDecimal costRateDiff = currentCostRate.subtract(prevCostRate); |
| | | |
| | | StatementSnapshot snapshot = buildStatementSnapshot(loginUser); |
| | | List<InventoryMetric> inventoryMetrics = buildInventoryMetrics( |
| | | queryStockInventory(loginUser), |
| | | queryProductModels(Collections.emptySet()), |
| | | Map.of(), |
| | | queryAverageUnitCostByModel(loginUser, Collections.emptySet()), |
| | | queryOutboundStats(loginUser, Collections.emptySet(), range) |
| | | ); |
| | | |
| | | List<Map<String, Object>> anomalyItems = new ArrayList<>(); |
| | | if (costRateDiff.compareTo(new BigDecimal("0.10")) > 0) { |
| | | anomalyItems.add(anomalyItem("high", "ææ¬å¼å¸¸", "å使¶å
¥ææ¬çè¾ä¸å¨æä¸åè¶
è¿10%", Map.of( |
| | | "currentCostRate", toPercent(currentCostRate), |
| | | "previousCostRate", toPercent(prevCostRate), |
| | | "delta", toPercent(costRateDiff) |
| | | ))); |
| | | } |
| | | long lossCount = currentBundle.orderMetrics().stream().filter(item -> item.profit().compareTo(BigDecimal.ZERO) < 0).count(); |
| | | if (lossCount > 0) { |
| | | anomalyItems.add(anomalyItem("high", "婿¶¦å¼å¸¸", "æ£æµå°äºæè®¢å", Map.of("lossOrderCount", lossCount))); |
| | | } |
| | | if (snapshot.receivableTotal().compareTo(snapshot.payableTotal().multiply(new BigDecimal("1.2"))) > 0) { |
| | | anomalyItems.add(anomalyItem("medium", "忬¾å¼å¸¸", "åºæ¶ä½é¢æ¾èé«äºåºä»ï¼å款ååå大", Map.of( |
| | | "receivableBalance", snapshot.receivableTotal(), |
| | | "payableBalance", snapshot.payableTotal() |
| | | ))); |
| | | } |
| | | long overdueOrderCount = currentBundle.orderMetrics().stream() |
| | | .filter(item -> item.deliveryDate() != null && item.deliveryDate().isBefore(LocalDate.now()) && item.profitRate().compareTo(new BigDecimal("0.10")) < 0) |
| | | .count(); |
| | | if (overdueOrderCount > 0) { |
| | | anomalyItems.add(anomalyItem("medium", "订åé£é©", "åå¨ä½å©æ¶¦ä¸äº¤ä»å·²é¾æè®¢å", Map.of("overdueRiskOrderCount", overdueOrderCount))); |
| | | } |
| | | long stagnantCount = inventoryMetrics.stream().filter(item -> item.stagnantDays() >= 90).count(); |
| | | if (stagnantCount > 0) { |
| | | anomalyItems.add(anomalyItem("medium", "åºåå¼å¸¸", "åå¨è¶
è¿90天æªå¨è½¬åºå", Map.of("stagnantCount", stagnantCount))); |
| | | } |
| | | |
| | | List<Map<String, Object>> topAnomalies = anomalyItems.stream().limit(finalLimit).toList(); |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("timeRange", range.label()); |
| | | 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()); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("anomalyLevelPieOption", buildAnomalyLevelPie(topAnomalies)); |
| | | charts.put("anomalyTypeBarOption", buildAnomalyTypeBar(topAnomalies)); |
| | | return jsonResponse(true, "financial_business_anomaly_warning", "已宿ç»è¥å¼å¸¸é¢è¦åæ", summary, |
| | | Map.of("items", topAnomalies), charts); |
| | | } |
| | | |
| | | @Tool(name = "AIç»è¥é©¾é©¶è±", value = "宿¶å±ç¤ºäº§å¼ã婿¶¦ãåºåã忬¾ã设å¤å©ç¨çã订å婿¶¦ççæ ¸å¿ç»è¥ææ ã") |
| | | public String getBusinessCockpit(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼å¦æ¬æãè¿30天", required = false) String timeRange) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, timeRange, "æ¬æ"); |
| | | |
| | | AnalysisBundle profitBundle = buildOrderProfitBundle(loginUser, range, null, 30); |
| | | StatementSnapshot snapshot = buildStatementSnapshot(loginUser); |
| | | List<StockInventory> inventories = queryStockInventory(loginUser); |
| | | BigDecimal inventoryValue = estimateInventoryValue(loginUser, inventories); |
| | | |
| | | long deviceTotal = countDevices(loginUser); |
| | | long repairingCount = countRepairingDevices(loginUser); |
| | | BigDecimal deviceUtilization = deviceTotal > 0 |
| | | ? new BigDecimal(deviceTotal - repairingCount).multiply(ONE_HUNDRED).divide(new BigDecimal(deviceTotal), 2, RoundingMode.HALF_UP) |
| | | : BigDecimal.ZERO; |
| | | |
| | | BigDecimal outputValue = profitBundle.totalRevenue(); |
| | | BigDecimal profitRate = rate(profitBundle.totalProfit(), profitBundle.totalRevenue()); |
| | | BigDecimal collectionRate = snapshot.receivableTotal().compareTo(BigDecimal.ZERO) > 0 |
| | | ? ONE_HUNDRED.subtract(rate(snapshot.receivableTotal(), snapshot.receivableTotal().add(snapshot.payableTotal())).multiply(ONE_HUNDRED)) |
| | | : BigDecimal.ZERO; |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("timeRange", range.label()); |
| | | 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)); |
| | | summary.put("inventoryValue", inventoryValue); |
| | | summary.put("receivableBalance", snapshot.receivableTotal()); |
| | | summary.put("payableBalance", snapshot.payableTotal()); |
| | | summary.put("collectionRate", toPercent(collectionRate.divide(ONE_HUNDRED, 4, RoundingMode.HALF_UP))); |
| | | summary.put("deviceUtilizationRate", deviceUtilization + "%"); |
| | | summary.put("orderProfitRate", toPercent(avgRate(profitBundle.orderMetrics()))); |
| | | |
| | | Map<String, Object> indicators = new LinkedHashMap<>(); |
| | | indicators.put("产å¼", outputValue); |
| | | indicators.put("婿¶¦", profitBundle.totalProfit()); |
| | | indicators.put("åºåèµéå ç¨", inventoryValue); |
| | | indicators.put("åºæ¶ä½é¢", snapshot.receivableTotal()); |
| | | indicators.put("设å¤å©ç¨ç", deviceUtilization + "%"); |
| | | indicators.put("订åå¹³å婿¶¦ç", toPercent(avgRate(profitBundle.orderMetrics()))); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("kpiCardData", indicators); |
| | | charts.put("profitTrendOption", buildOrderProfitBar(profitBundle.orderMetrics())); |
| | | charts.put("receivablePayableBarOption", buildReceivablePayableBar(snapshot.receivableTotal(), snapshot.payableTotal())); |
| | | charts.put("inventoryProfitGaugeOption", buildInventoryProfitGauge(inventoryValue, profitBundle.totalProfit())); |
| | | |
| | | return jsonResponse(true, "financial_business_cockpit", "å·²çæAIç»è¥é©¾é©¶è±æ°æ®", summary, |
| | | Map.of( |
| | | "orderProfitTop", profitBundle.orderMetrics().stream() |
| | | .sorted(Comparator.comparing(OrderProfitMetric::profit).reversed()) |
| | | .limit(10) |
| | | .map(this::toOrderCostItem) |
| | | .toList(), |
| | | "indicators", indicators |
| | | ), |
| | | charts |
| | | ); |
| | | } |
| | | |
| | | @Tool(name = "æ¥æ¥å¨æ¥èªå¨çæ", value = "èªå¨è¾åºç»è¥åææ¥æ¥/卿¥ä¸é£é©å»ºè®®ã") |
| | | public String generateOperationReport(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼å¦ä»å¤©ãæ¬å¨", required = false) String timeRange, |
| | | @P(value = "æ¥åç±»å daily/weekly", required = false) String reportType) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, timeRange, |
| | | "weekly".equalsIgnoreCase(reportType) ? "æ¬å¨" : "ä»å¤©"); |
| | | String type = "weekly".equalsIgnoreCase(reportType) ? "weekly" : "daily"; |
| | | |
| | | AnalysisBundle bundle = buildOrderProfitBundle(loginUser, range, null, 30); |
| | | StatementSnapshot snapshot = buildStatementSnapshot(loginUser); |
| | | BigDecimal inventoryValue = estimateInventoryValue(loginUser, queryStockInventory(loginUser)); |
| | | long lossCount = bundle.orderMetrics().stream().filter(item -> item.profit().compareTo(BigDecimal.ZERO) < 0).count(); |
| | | |
| | | List<String> conclusions = new ArrayList<>(); |
| | | conclusions.add("è¥æ¶" + bundle.totalRevenue() + "ï¼å©æ¶¦" + bundle.totalProfit() + "ï¼å©æ¶¦ç" + toPercent(rate(bundle.totalProfit(), bundle.totalRevenue())) + "ã"); |
| | | conclusions.add("åºæ¶ä½é¢" + snapshot.receivableTotal() + "ï¼åºä»ä½é¢" + snapshot.payableTotal() + "ï¼åºåèµéå ç¨" + inventoryValue + "ã"); |
| | | if (lossCount > 0) { |
| | | conclusions.add("åç°äºæè®¢å" + lossCount + "个ï¼å»ºè®®ä¼å
夿 ¸æææèåå·¥åºäººå·¥æçã"); |
| | | } else { |
| | | conclusions.add("å½åæªåç°äºæè®¢åï¼å»ºè®®æç»è·è¸ªä½äº8%婿¶¦ç订åã"); |
| | | } |
| | | if (snapshot.receivableTotal().compareTo(snapshot.payableTotal()) > 0) { |
| | | conclusions.add("忬¾åååé«ï¼å»ºè®®é对é«åºæ¶å®¢æ·æ§è¡åå±å¬æ¶ä¸è´¦æä¼åã"); |
| | | } else { |
| | | conclusions.add("èµéåå坿§ï¼å»ºè®®ä¿æä»æ¬¾è®¡åä¸éè´èå¥èå¨ã"); |
| | | } |
| | | |
| | | List<Map<String, Object>> riskSuggestions = new ArrayList<>(); |
| | | if (lossCount > 0) { |
| | | riskSuggestions.add(riskSuggestion("婿¶¦é£é©", "é«", "夿 ¸äºæè®¢åBOMåå·¥åºå·¥èµå®é¢ï¼å¿
è¦æ¶è°æ´æ¥ä»·ä¸äº¤ä»èå¥ã")); |
| | | } |
| | | if (snapshot.receivableTotal().compareTo(SME_RECEIVABLE_RISK_THRESHOLD) > 0) { |
| | | riskSuggestions.add(riskSuggestion("忬¾é£é©", "ä¸", "å¯¹åºæ¶TOP客æ·å»ºç«å¨åº¦å款计åï¼å¹¶è®¾ç½®é¢è¦éå¼ã")); |
| | | } |
| | | 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", displayDate(range.start())); |
| | | summary.put("endDate", displayDate(range.end())); |
| | | summary.put("orderCount", bundle.orderMetrics().size()); |
| | | summary.put("lossOrderCount", lossCount); |
| | | summary.put("riskSuggestionCount", riskSuggestions.size()); |
| | | |
| | | Map<String, Object> data = new LinkedHashMap<>(); |
| | | data.put("headline", "weekly".equals(type) ? "ç»è¥å¨æ¥" : "ç»è¥æ¥æ¥"); |
| | | data.put("conclusions", conclusions); |
| | | data.put("riskSuggestions", riskSuggestions); |
| | | data.put("orderProfitTop", bundle.orderMetrics().stream() |
| | | .sorted(Comparator.comparing(OrderProfitMetric::profitRate)) |
| | | .limit(10) |
| | | .map(this::toRiskOrderItem) |
| | | .toList()); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("reportProfitBarOption", buildOrderProfitBar(bundle.orderMetrics())); |
| | | charts.put("reportReceivablePayableOption", buildReceivablePayableBar(snapshot.receivableTotal(), snapshot.payableTotal())); |
| | | return jsonResponse(true, "financial_operation_report", "å·²èªå¨çæç»è¥åææ¥å", summary, data, charts); |
| | | } |
| | | |
| | | private AnalysisBundle buildOrderProfitBundle(LoginUser loginUser, DateRange range, String keyword, Integer limit) { |
| | | List<SalesLedger> ledgers = querySalesLedgers(loginUser, range, keyword, limit); |
| | | if (ledgers.isEmpty()) { |
| | | return AnalysisBundle.empty(); |
| | | } |
| | | |
| | | List<Long> ledgerIds = ledgers.stream().map(SalesLedger::getId).filter(Objects::nonNull).toList(); |
| | | List<SalesLedgerProduct> ledgerProducts = queryLedgerProducts(loginUser, ledgerIds); |
| | | Map<Long, List<SalesLedgerProduct>> productsByLedgerId = ledgerProducts.stream() |
| | | .collect(Collectors.groupingBy(SalesLedgerProduct::getSalesLedgerId)); |
| | | |
| | | MaterialCostResult materialCostResult = calculateMaterialCost(loginUser, range, ledgerProducts); |
| | | ProductionCostContext productionCostContext = calculateProductionCost(loginUser, range, ledgers, ledgerProducts, materialCostResult.avgUnitCostByModelId()); |
| | | BigDecimal totalDepreciation = calculateTotalDepreciation(loginUser); |
| | | |
| | | BigDecimal totalRevenue = ledgers.stream() |
| | | .map(SalesLedger::getContractAmount) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | Map<Long, BigDecimal> depreciationCostByLedger = allocateDepreciation(ledgers, totalDepreciation, totalRevenue); |
| | | |
| | | List<OrderProfitMetric> metrics = new ArrayList<>(); |
| | | for (SalesLedger ledger : ledgers) { |
| | | BigDecimal revenue = defaultDecimal(ledger.getContractAmount()); |
| | | BigDecimal materialCost = materialCostResult.materialCostByLedgerId().getOrDefault(ledger.getId(), fallbackMaterialCost(productsByLedgerId.get(ledger.getId()), revenue)); |
| | | BigDecimal laborCost = productionCostContext.laborCostByLedgerId().getOrDefault(ledger.getId(), BigDecimal.ZERO); |
| | | BigDecimal scrapCost = productionCostContext.scrapCostByLedgerId().getOrDefault(ledger.getId(), BigDecimal.ZERO); |
| | | BigDecimal depreciationCost = depreciationCostByLedger.getOrDefault(ledger.getId(), BigDecimal.ZERO); |
| | | BigDecimal totalCost = materialCost.add(laborCost).add(scrapCost).add(depreciationCost); |
| | | BigDecimal profit = revenue.subtract(totalCost); |
| | | BigDecimal profitRate = rate(profit, revenue); |
| | | String riskLevel = profit.compareTo(BigDecimal.ZERO) < 0 |
| | | ? "high" |
| | | : (profitRate.compareTo(new BigDecimal("0.08")) < 0 ? "medium" : "low"); |
| | | List<String> reasons = buildProfitReasons(revenue, materialCost, laborCost, scrapCost, profit, profitRate); |
| | | String suggestion = buildProfitSuggestion(riskLevel, reasons); |
| | | |
| | | metrics.add(new OrderProfitMetric( |
| | | ledger.getId(), |
| | | safe(ledger.getSalesContractNo()), |
| | | safe(ledger.getCustomerName()), |
| | | safe(ledger.getProjectName()), |
| | | toLocalDate(ledger.getEntryDate()), |
| | | ledger.getDeliveryDate(), |
| | | revenue, |
| | | materialCost, |
| | | laborCost, |
| | | depreciationCost, |
| | | scrapCost, |
| | | totalCost, |
| | | profit, |
| | | profitRate, |
| | | riskLevel, |
| | | reasons, |
| | | suggestion |
| | | )); |
| | | } |
| | | |
| | | metrics.sort(Comparator.comparing(OrderProfitMetric::entryDate, Comparator.nullsLast(Comparator.reverseOrder())) |
| | | .thenComparing(OrderProfitMetric::ledgerId, Comparator.nullsLast(Comparator.reverseOrder()))); |
| | | BigDecimal totalMaterialCost = metrics.stream().map(OrderProfitMetric::materialCost).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal totalLaborCost = metrics.stream().map(OrderProfitMetric::laborCost).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal totalScrapCost = metrics.stream().map(OrderProfitMetric::scrapCost).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal totalDepreciationCost = metrics.stream().map(OrderProfitMetric::depreciationCost).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal totalCost = metrics.stream().map(OrderProfitMetric::totalCost).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal totalProfit = metrics.stream().map(OrderProfitMetric::profit).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | return new AnalysisBundle( |
| | | metrics, |
| | | productionCostContext.processCostRanking(), |
| | | totalRevenue, |
| | | totalMaterialCost, |
| | | totalLaborCost, |
| | | totalDepreciationCost, |
| | | totalScrapCost, |
| | | totalCost, |
| | | totalProfit |
| | | ); |
| | | } |
| | | |
| | | private MaterialCostResult calculateMaterialCost(LoginUser loginUser, DateRange range, List<SalesLedgerProduct> ledgerProducts) { |
| | | if (ledgerProducts.isEmpty()) { |
| | | return new MaterialCostResult(Map.of(), Map.of()); |
| | | } |
| | | List<Long> ledgerProductIds = ledgerProducts.stream().map(SalesLedgerProduct::getId).filter(Objects::nonNull).toList(); |
| | | Set<Long> productModelIds = ledgerProducts.stream().map(SalesLedgerProduct::getProductModelId).filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Long, Long> productIdToLedgerId = ledgerProducts.stream() |
| | | .filter(item -> item.getId() != null && item.getSalesLedgerId() != null) |
| | | .collect(Collectors.toMap(SalesLedgerProduct::getId, SalesLedgerProduct::getSalesLedgerId, (a, b) -> a)); |
| | | |
| | | Map<Long, BigDecimal> avgUnitCostByModelId = queryAverageUnitCostByModel(loginUser, productModelIds); |
| | | LambdaQueryWrapper<ProcurementRecordOut> outWrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(outWrapper, loginUser.getTenantId(), ProcurementRecordOut::getTenantId); |
| | | applyDeptFilter(outWrapper, loginUser.getCurrentDeptId(), ProcurementRecordOut::getDeptId); |
| | | outWrapper.eq(ProcurementRecordOut::getType, 2) |
| | | .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() |
| | | .map(ProcurementRecordOut::getProcurementRecordStorageId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Integer, ProcurementRecordStorage> storageMap = storageIds.isEmpty() |
| | | ? Map.of() |
| | | : defaultList(procurementRecordMapper.selectBatchIds(storageIds)).stream() |
| | | .collect(Collectors.toMap(ProcurementRecordStorage::getId, item -> item, (a, b) -> a)); |
| | | |
| | | Map<Long, BigDecimal> materialCostByLedgerId = new HashMap<>(); |
| | | for (ProcurementRecordOut out : outList) { |
| | | Long ledgerId = productIdToLedgerId.get(out.getSalesLedgerProductId()); |
| | | if (ledgerId == null) { |
| | | continue; |
| | | } |
| | | ProcurementRecordStorage storage = storageMap.get(out.getProcurementRecordStorageId()); |
| | | BigDecimal unitPrice = storage == null ? BigDecimal.ZERO : defaultDecimal(storage.getUnitPrice()); |
| | | BigDecimal quantity = defaultDecimal(out.getInboundNum()); |
| | | BigDecimal cost = quantity.multiply(unitPrice); |
| | | materialCostByLedgerId.merge(ledgerId, cost, BigDecimal::add); |
| | | } |
| | | return new MaterialCostResult(materialCostByLedgerId, avgUnitCostByModelId); |
| | | } |
| | | |
| | | private ProductionCostContext calculateProductionCost(LoginUser loginUser, |
| | | DateRange range, |
| | | List<SalesLedger> ledgers, |
| | | List<SalesLedgerProduct> ledgerProducts, |
| | | Map<Long, BigDecimal> avgUnitCostByModelId) { |
| | | if (ledgers.isEmpty()) { |
| | | return ProductionCostContext.empty(); |
| | | } |
| | | |
| | | Set<Long> ledgerIds = ledgers.stream().map(SalesLedger::getId).filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Long, Set<Long>> productModelToLedgerIds = new HashMap<>(); |
| | | for (SalesLedgerProduct product : ledgerProducts) { |
| | | if (product.getProductModelId() == null || product.getSalesLedgerId() == null) { |
| | | continue; |
| | | } |
| | | productModelToLedgerIds.computeIfAbsent(product.getProductModelId(), key -> new HashSet<>()).add(product.getSalesLedgerId()); |
| | | } |
| | | |
| | | LambdaQueryWrapper<ProductionPlan> planWrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(planWrapper, loginUser.getCurrentDeptId(), ProductionPlan::getDeptId); |
| | | planWrapper.in(ProductionPlan::getSalesLedgerId, ledgerIds); |
| | | List<ProductionPlan> plans = defaultList(productionPlanMapper.selectList(planWrapper)); |
| | | Map<Long, Long> planIdToLedgerId = plans.stream() |
| | | .filter(item -> item.getId() != null && item.getSalesLedgerId() != null) |
| | | .collect(Collectors.toMap(ProductionPlan::getId, ProductionPlan::getSalesLedgerId, (a, b) -> a)); |
| | | |
| | | LambdaQueryWrapper<ProductionOrder> orderWrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(orderWrapper, loginUser.getCurrentDeptId(), ProductionOrder::getDeptId); |
| | | 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<>(); |
| | | for (ProductionOrder order : orders) { |
| | | Set<Long> orderLedgers = new HashSet<>(); |
| | | for (Long planId : parseIdList(order.getProductionPlanIds())) { |
| | | Long ledgerId = planIdToLedgerId.get(planId); |
| | | if (ledgerId != null) { |
| | | orderLedgers.add(ledgerId); |
| | | } |
| | | } |
| | | if (orderLedgers.isEmpty() && order.getProductModelId() != null) { |
| | | orderLedgers.addAll(productModelToLedgerIds.getOrDefault(order.getProductModelId(), Set.of())); |
| | | } |
| | | if (!orderLedgers.isEmpty()) { |
| | | orderIdToLedgerIds.put(order.getId(), orderLedgers); |
| | | } |
| | | } |
| | | if (orderIdToLedgerIds.isEmpty()) { |
| | | return ProductionCostContext.empty(); |
| | | } |
| | | |
| | | LambdaQueryWrapper<ProductionOperationTask> taskWrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(taskWrapper, loginUser.getCurrentDeptId(), ProductionOperationTask::getDeptId); |
| | | taskWrapper.in(ProductionOperationTask::getProductionOrderId, orderIdToLedgerIds.keySet()); |
| | | List<ProductionOperationTask> tasks = defaultList(productionOperationTaskMapper.selectList(taskWrapper)); |
| | | Map<Long, Long> taskIdToOrderId = tasks.stream() |
| | | .filter(item -> item.getId() != null && item.getProductionOrderId() != null) |
| | | .collect(Collectors.toMap(ProductionOperationTask::getId, ProductionOperationTask::getProductionOrderId, (a, b) -> a)); |
| | | if (taskIdToOrderId.isEmpty()) { |
| | | return ProductionCostContext.empty(); |
| | | } |
| | | |
| | | LambdaQueryWrapper<ProductionProductMain> mainWrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(mainWrapper, loginUser.getCurrentDeptId(), ProductionProductMain::getDeptId); |
| | | 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) { |
| | | Long orderId = taskIdToOrderId.get(main.getProductionOperationTaskId()); |
| | | if (orderId == null) { |
| | | continue; |
| | | } |
| | | Set<Long> ledgerSet = orderIdToLedgerIds.get(orderId); |
| | | if (ledgerSet == null || ledgerSet.isEmpty()) { |
| | | continue; |
| | | } |
| | | mainIdToLedgers.put(main.getId(), ledgerSet); |
| | | } |
| | | if (mainIdToLedgers.isEmpty()) { |
| | | return ProductionCostContext.empty(); |
| | | } |
| | | |
| | | LambdaQueryWrapper<ProductionAccount> accountWrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(accountWrapper, loginUser.getCurrentDeptId(), ProductionAccount::getDeptId); |
| | | 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>() |
| | | .select(TechnologyOperation::getName, TechnologyOperation::getSalaryQuota))) |
| | | .stream() |
| | | .filter(item -> StringUtils.hasText(item.getName())) |
| | | .collect(Collectors.toMap(TechnologyOperation::getName, item -> defaultDecimal(item.getSalaryQuota()), (a, b) -> a)); |
| | | |
| | | Map<Long, BigDecimal> laborCostByLedger = new HashMap<>(); |
| | | Map<String, BigDecimal> processCostMap = new HashMap<>(); |
| | | for (ProductionAccount account : accountList) { |
| | | Set<Long> ledgerSet = mainIdToLedgers.get(account.getProductionProductMainId()); |
| | | if (ledgerSet == null || ledgerSet.isEmpty()) { |
| | | continue; |
| | | } |
| | | BigDecimal cost = estimateLaborCost(account, salaryQuotaByOperation); |
| | | if (cost.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal split = cost.divide(new BigDecimal(ledgerSet.size()), 4, RoundingMode.HALF_UP); |
| | | for (Long ledgerId : ledgerSet) { |
| | | laborCostByLedger.merge(ledgerId, split, BigDecimal::add); |
| | | } |
| | | processCostMap.merge(safe(account.getTechnologyOperationName()), cost, BigDecimal::add); |
| | | } |
| | | |
| | | LambdaQueryWrapper<ProductionProductOutput> outputWrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(outputWrapper, loginUser.getCurrentDeptId(), ProductionProductOutput::getDeptId); |
| | | 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) { |
| | | Set<Long> ledgerSet = mainIdToLedgers.get(output.getProductionProductMainId()); |
| | | if (ledgerSet == null || ledgerSet.isEmpty()) { |
| | | continue; |
| | | } |
| | | BigDecimal scrapQty = defaultDecimal(output.getScrapQty()); |
| | | if (scrapQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal unitCost = avgUnitCostByModelId.getOrDefault(output.getProductModelId(), BigDecimal.ZERO); |
| | | BigDecimal scrapCost = scrapQty.multiply(unitCost); |
| | | BigDecimal split = scrapCost.divide(new BigDecimal(ledgerSet.size()), 4, RoundingMode.HALF_UP); |
| | | for (Long ledgerId : ledgerSet) { |
| | | scrapCostByLedger.merge(ledgerId, split, BigDecimal::add); |
| | | } |
| | | } |
| | | |
| | | Map<String, BigDecimal> processCostRanking = processCostMap.entrySet().stream() |
| | | .sorted(Map.Entry.<String, BigDecimal>comparingByValue().reversed()) |
| | | .limit(10) |
| | | .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new)); |
| | | |
| | | return new ProductionCostContext(laborCostByLedger, scrapCostByLedger, processCostRanking); |
| | | } |
| | | |
| | | private List<SalesLedger> querySalesLedgers(LoginUser loginUser, DateRange range, String keyword, Integer limit) { |
| | | LambdaQueryWrapper<SalesLedger> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), SalesLedger::getTenantId); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), SalesLedger::getDeptId); |
| | | if (StringUtils.hasText(keyword)) { |
| | | wrapper.and(w -> w.like(SalesLedger::getSalesContractNo, keyword) |
| | | .or().like(SalesLedger::getCustomerContractNo, keyword) |
| | | .or().like(SalesLedger::getCustomerName, keyword) |
| | | .or().like(SalesLedger::getProjectName, keyword) |
| | | .or().like(SalesLedger::getSalesman, keyword)); |
| | | } |
| | | 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)); |
| | | } |
| | | |
| | | private List<SalesLedgerProduct> queryLedgerProducts(LoginUser loginUser, List<Long> ledgerIds) { |
| | | if (ledgerIds == null || ledgerIds.isEmpty()) { |
| | | return List.of(); |
| | | } |
| | | LambdaQueryWrapper<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), SalesLedgerProduct::getDeptId); |
| | | wrapper.in(SalesLedgerProduct::getSalesLedgerId, ledgerIds) |
| | | .eq(SalesLedgerProduct::getType, 1); |
| | | return defaultList(salesLedgerProductMapper.selectList(wrapper)); |
| | | } |
| | | |
| | | private List<StockInventory> queryStockInventory(LoginUser loginUser) { |
| | | LambdaQueryWrapper<StockInventory> wrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), StockInventory::getDeptId); |
| | | return defaultList(stockInventoryMapper.selectList(wrapper)); |
| | | } |
| | | |
| | | private Map<Long, ProductModel> queryProductModels(Set<Long> modelIds) { |
| | | if (modelIds == null || modelIds.isEmpty()) { |
| | | return defaultList(productModelMapper.selectList(null)).stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(ProductModel::getId, item -> item, (a, b) -> a)); |
| | | } |
| | | LambdaQueryWrapper<ProductModel> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.in(ProductModel::getId, modelIds); |
| | | return defaultList(productModelMapper.selectList(wrapper)).stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(ProductModel::getId, item -> item, (a, b) -> a)); |
| | | } |
| | | |
| | | private Map<Long, Product> queryProducts(Collection<ProductModel> models) { |
| | | Set<Long> productIds = models == null ? Set.of() : models.stream() |
| | | .map(ProductModel::getProductId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | if (productIds.isEmpty()) { |
| | | return Map.of(); |
| | | } |
| | | LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.in(Product::getId, productIds); |
| | | return defaultList(productMapper.selectList(wrapper)).stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(Product::getId, item -> item, (a, b) -> a)); |
| | | } |
| | | |
| | | private Map<Long, BigDecimal> queryAverageUnitCostByModel(LoginUser loginUser, Set<Long> productModelIds) { |
| | | LambdaQueryWrapper<ProcurementRecordStorage> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), ProcurementRecordStorage::getTenantId); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProcurementRecordStorage::getDeptId); |
| | | wrapper.in(ProcurementRecordStorage::getType, List.of(1, 2)); |
| | | if (productModelIds != null && !productModelIds.isEmpty()) { |
| | | wrapper.in(ProcurementRecordStorage::getProductModelId, productModelIds); |
| | | } |
| | | List<ProcurementRecordStorage> rows = defaultList(procurementRecordMapper.selectList(wrapper)); |
| | | Map<Long, BigDecimal> amountByModel = new HashMap<>(); |
| | | Map<Long, BigDecimal> qtyByModel = new HashMap<>(); |
| | | for (ProcurementRecordStorage row : rows) { |
| | | if (row.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | BigDecimal qty = defaultDecimal(row.getInboundNum()); |
| | | if (qty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal amount = defaultDecimal(row.getUnitPrice()).multiply(qty); |
| | | amountByModel.merge(row.getProductModelId(), amount, BigDecimal::add); |
| | | qtyByModel.merge(row.getProductModelId(), qty, BigDecimal::add); |
| | | } |
| | | Map<Long, BigDecimal> result = new HashMap<>(); |
| | | for (Map.Entry<Long, BigDecimal> entry : amountByModel.entrySet()) { |
| | | BigDecimal qty = qtyByModel.get(entry.getKey()); |
| | | if (qty == null || qty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | result.put(entry.getKey(), entry.getValue().divide(qty, 6, RoundingMode.HALF_UP)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private OutboundStats queryOutboundStats(LoginUser loginUser, Set<Long> productModelIds, DateRange range) { |
| | | LambdaQueryWrapper<ProcurementRecordOut> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), ProcurementRecordOut::getTenantId); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ProcurementRecordOut::getDeptId); |
| | | if (productModelIds != null && !productModelIds.isEmpty()) { |
| | | wrapper.in(ProcurementRecordOut::getProductModelId, productModelIds); |
| | | } |
| | | 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(); |
| | | } |
| | | Set<Integer> storageIds = outList.stream() |
| | | .map(ProcurementRecordOut::getProcurementRecordStorageId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Integer, ProcurementRecordStorage> storageMap = storageIds.isEmpty() |
| | | ? Map.of() |
| | | : defaultList(procurementRecordMapper.selectBatchIds(storageIds)).stream() |
| | | .collect(Collectors.toMap(ProcurementRecordStorage::getId, item -> item, (a, b) -> a)); |
| | | |
| | | Map<Long, BigDecimal> outboundQtyByModel = new HashMap<>(); |
| | | Map<Long, LocalDateTime> lastOutboundTimeByModel = new HashMap<>(); |
| | | BigDecimal totalOutboundCost = BigDecimal.ZERO; |
| | | for (ProcurementRecordOut out : outList) { |
| | | Long modelId = out.getProductModelId(); |
| | | if (modelId == null) { |
| | | continue; |
| | | } |
| | | BigDecimal qty = defaultDecimal(out.getInboundNum()); |
| | | outboundQtyByModel.merge(modelId, qty, BigDecimal::add); |
| | | if (out.getCreateTime() != null) { |
| | | LocalDateTime existing = lastOutboundTimeByModel.get(modelId); |
| | | if (existing == null || out.getCreateTime().isAfter(existing)) { |
| | | lastOutboundTimeByModel.put(modelId, out.getCreateTime()); |
| | | } |
| | | } |
| | | ProcurementRecordStorage storage = storageMap.get(out.getProcurementRecordStorageId()); |
| | | BigDecimal unitPrice = storage == null ? BigDecimal.ZERO : defaultDecimal(storage.getUnitPrice()); |
| | | totalOutboundCost = totalOutboundCost.add(unitPrice.multiply(qty)); |
| | | } |
| | | return new OutboundStats(outboundQtyByModel, lastOutboundTimeByModel, totalOutboundCost); |
| | | } |
| | | |
| | | private List<InventoryMetric> buildInventoryMetrics(List<StockInventory> inventoryRows, |
| | | Map<Long, ProductModel> productModelMap, |
| | | Map<Long, Product> productMap, |
| | | Map<Long, BigDecimal> avgUnitCostByModelId, |
| | | OutboundStats outboundStats) { |
| | | Map<Long, InventoryMetricBuilder> metricBuilderByModel = new HashMap<>(); |
| | | for (StockInventory row : inventoryRows) { |
| | | if (row.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | InventoryMetricBuilder builder = metricBuilderByModel.computeIfAbsent(row.getProductModelId(), InventoryMetricBuilder::new); |
| | | builder.addQuantity(maxZero(defaultDecimal(row.getQualitity()).subtract(defaultDecimal(row.getLockedQuantity())))); |
| | | builder.addLockedQuantity(defaultDecimal(row.getLockedQuantity())); |
| | | builder.addWarnNum(defaultDecimal(row.getWarnNum())); |
| | | if (row.getCreateTime() != null) { |
| | | builder.updateFirstInTime(row.getCreateTime()); |
| | | } |
| | | } |
| | | |
| | | List<InventoryMetric> result = new ArrayList<>(); |
| | | LocalDate today = LocalDate.now(); |
| | | for (InventoryMetricBuilder builder : metricBuilderByModel.values()) { |
| | | Long modelId = builder.modelId(); |
| | | ProductModel model = productModelMap.get(modelId); |
| | | Product product = model == null ? null : productMap.get(model.getProductId()); |
| | | BigDecimal unitCost = avgUnitCostByModelId.getOrDefault(modelId, BigDecimal.ZERO); |
| | | BigDecimal value = builder.quantity().multiply(unitCost); |
| | | LocalDateTime lastOutTime = outboundStats.lastOutboundTimeByModel().get(modelId); |
| | | long stagnantDays; |
| | | if (lastOutTime != null) { |
| | | stagnantDays = daysBetween(lastOutTime.toLocalDate(), today); |
| | | } else if (builder.firstInTime() != null) { |
| | | stagnantDays = daysBetween(builder.firstInTime().toLocalDate(), today); |
| | | } else { |
| | | stagnantDays = 0; |
| | | } |
| | | BigDecimal outQty = outboundStats.outboundQtyByModel().getOrDefault(modelId, BigDecimal.ZERO); |
| | | boolean overstock = builder.warnNum().compareTo(BigDecimal.ZERO) > 0 |
| | | && builder.quantity().compareTo(builder.warnNum().multiply(new BigDecimal("3"))) > 0; |
| | | result.add(new InventoryMetric( |
| | | modelId, |
| | | product == null ? "æªç¥äº§å" : safe(product.getProductName()), |
| | | model == null ? "æªç¥åå·" : safe(model.getModel()), |
| | | builder.quantity(), |
| | | builder.lockedQuantity(), |
| | | unitCost, |
| | | value, |
| | | outQty, |
| | | stagnantDays, |
| | | overstock |
| | | )); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private List<AccountSalesCollection> queryCollections(LoginUser loginUser, DateRange range) { |
| | | LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId); |
| | | 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); |
| | | if (range.hasDateFilter()) { |
| | | wrapper.ge(AccountPurchasePayment::getPaymentDate, range.start()) |
| | | .le(AccountPurchasePayment::getPaymentDate, range.end()); |
| | | } |
| | | wrapper.orderByAsc(AccountPurchasePayment::getPaymentDate); |
| | | return defaultList(accountPurchasePaymentMapper.selectList(wrapper)); |
| | | } |
| | | |
| | | private List<MonthlyCashFlow> buildMonthlyCashFlow(DateRange range, |
| | | List<AccountSalesCollection> collections, |
| | | List<AccountPurchasePayment> payments) { |
| | | Map<YearMonth, BigDecimal> incomeByMonth = new LinkedHashMap<>(); |
| | | Map<YearMonth, BigDecimal> expenseByMonth = new LinkedHashMap<>(); |
| | | 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); |
| | | } |
| | | |
| | | for (AccountSalesCollection row : collections) { |
| | | if (row.getCollectionDate() == null) { |
| | | continue; |
| | | } |
| | | YearMonth month = YearMonth.from(row.getCollectionDate()); |
| | | if (incomeByMonth.containsKey(month)) { |
| | | incomeByMonth.put(month, incomeByMonth.get(month).add(defaultDecimal(row.getCollectionAmount()))); |
| | | } |
| | | } |
| | | for (AccountPurchasePayment row : payments) { |
| | | if (row.getPaymentDate() == null) { |
| | | continue; |
| | | } |
| | | YearMonth month = YearMonth.from(row.getPaymentDate()); |
| | | if (expenseByMonth.containsKey(month)) { |
| | | expenseByMonth.put(month, expenseByMonth.get(month).add(defaultDecimal(row.getPaymentAmount()))); |
| | | } |
| | | } |
| | | |
| | | List<MonthlyCashFlow> result = new ArrayList<>(); |
| | | for (YearMonth month : incomeByMonth.keySet()) { |
| | | BigDecimal income = incomeByMonth.get(month); |
| | | BigDecimal expense = expenseByMonth.getOrDefault(month, BigDecimal.ZERO); |
| | | 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) { |
| | | if (actual.isEmpty()) { |
| | | List<MonthlyCashFlow> defaults = new ArrayList<>(); |
| | | YearMonth now = YearMonth.now(); |
| | | for (int i = 1; i <= forecastMonths; i++) { |
| | | YearMonth month = now.plusMonths(i); |
| | | defaults.add(new MonthlyCashFlow(month.toString(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO)); |
| | | } |
| | | return defaults; |
| | | } |
| | | List<BigDecimal> series = actual.stream().map(MonthlyCashFlow::netFlow).toList(); |
| | | BigDecimal avg = series.stream().reduce(BigDecimal.ZERO, BigDecimal::add) |
| | | .divide(new BigDecimal(series.size()), 4, RoundingMode.HALF_UP); |
| | | BigDecimal slope = BigDecimal.ZERO; |
| | | if (series.size() > 1) { |
| | | slope = series.get(series.size() - 1).subtract(series.get(0)) |
| | | .divide(new BigDecimal(series.size() - 1), 4, RoundingMode.HALF_UP); |
| | | } |
| | | YearMonth lastMonth = YearMonth.parse(actual.get(actual.size() - 1).month()); |
| | | List<MonthlyCashFlow> forecast = new ArrayList<>(); |
| | | for (int i = 1; i <= forecastMonths; i++) { |
| | | YearMonth month = lastMonth.plusMonths(i); |
| | | BigDecimal net = avg.add(slope.multiply(new BigDecimal(i))).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal income = net.compareTo(BigDecimal.ZERO) >= 0 ? net : BigDecimal.ZERO; |
| | | BigDecimal expense = net.compareTo(BigDecimal.ZERO) >= 0 ? BigDecimal.ZERO : net.abs(); |
| | | forecast.add(new MonthlyCashFlow(month.toString(), income, expense, net)); |
| | | } |
| | | return forecast; |
| | | } |
| | | |
| | | private StatementSnapshot buildStatementSnapshot(LoginUser loginUser) { |
| | | LambdaQueryWrapper<AccountStatement> wrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountStatement::getDeptId); |
| | | wrapper.orderByDesc(AccountStatement::getStatementMonth); |
| | | List<AccountStatement> rows = defaultList(accountStatementMapper.selectList(wrapper)); |
| | | if (rows.isEmpty()) { |
| | | return StatementSnapshot.empty(); |
| | | } |
| | | |
| | | Map<String, AccountStatement> latestByEntity = new HashMap<>(); |
| | | for (AccountStatement row : rows) { |
| | | if (row.getAccountType() == null || row.getCustomerId() == null || !StringUtils.hasText(row.getStatementMonth())) { |
| | | continue; |
| | | } |
| | | String key = row.getAccountType() + "::" + row.getCustomerId(); |
| | | AccountStatement existing = latestByEntity.get(key); |
| | | if (existing == null || row.getStatementMonth().compareTo(existing.getStatementMonth()) > 0) { |
| | | latestByEntity.put(key, row); |
| | | } |
| | | } |
| | | |
| | | BigDecimal receivableTotal = BigDecimal.ZERO; |
| | | BigDecimal payableTotal = BigDecimal.ZERO; |
| | | List<StatementMetric> receivableMetrics = new ArrayList<>(); |
| | | List<StatementMetric> payableMetrics = new ArrayList<>(); |
| | | for (AccountStatement row : latestByEntity.values()) { |
| | | BigDecimal closing = defaultDecimal(row.getClosingBalance()); |
| | | if (Objects.equals(row.getAccountType(), 1)) { |
| | | receivableTotal = receivableTotal.add(closing); |
| | | receivableMetrics.add(new StatementMetric(String.valueOf(row.getCustomerId()), closing, |
| | | defaultDecimal(row.getCurrentPlan()), defaultDecimal(row.getCurrentActually()), safe(row.getStatementMonth()))); |
| | | } else if (Objects.equals(row.getAccountType(), 2)) { |
| | | payableTotal = payableTotal.add(closing); |
| | | payableMetrics.add(new StatementMetric(String.valueOf(row.getCustomerId()), closing, |
| | | defaultDecimal(row.getCurrentPlan()), defaultDecimal(row.getCurrentActually()), safe(row.getStatementMonth()))); |
| | | } |
| | | } |
| | | receivableMetrics.sort(Comparator.comparing(StatementMetric::closingBalance).reversed()); |
| | | payableMetrics.sort(Comparator.comparing(StatementMetric::closingBalance).reversed()); |
| | | |
| | | return new StatementSnapshot( |
| | | receivableTotal, |
| | | payableTotal, |
| | | receivableMetrics.stream().limit(10).toList(), |
| | | payableMetrics.stream().limit(10).toList() |
| | | ); |
| | | } |
| | | |
| | | private BigDecimal calculateTotalDepreciation(LoginUser loginUser) { |
| | | LambdaQueryWrapper<DeviceLedger> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceLedger::getTenantId); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), DeviceLedger::getDeptId); |
| | | wrapper.eq(DeviceLedger::getIsDepr, 1); |
| | | List<DeviceLedger> devices = defaultList(deviceLedgerMapper.selectList(wrapper)); |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | for (DeviceLedger device : devices) { |
| | | total = total.add(defaultDecimal(AccountingServiceImpl.calculatePreciseDepreciation(device))); |
| | | } |
| | | return total; |
| | | } |
| | | |
| | | private Map<Long, BigDecimal> allocateDepreciation(List<SalesLedger> ledgers, BigDecimal totalDepreciation, BigDecimal totalRevenue) { |
| | | if (ledgers.isEmpty() || totalDepreciation.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return Map.of(); |
| | | } |
| | | Map<Long, BigDecimal> result = new HashMap<>(); |
| | | if (totalRevenue.compareTo(BigDecimal.ZERO) <= 0) { |
| | | BigDecimal avg = totalDepreciation.divide(new BigDecimal(ledgers.size()), 4, RoundingMode.HALF_UP); |
| | | for (SalesLedger ledger : ledgers) { |
| | | result.put(ledger.getId(), avg); |
| | | } |
| | | return result; |
| | | } |
| | | for (SalesLedger ledger : ledgers) { |
| | | BigDecimal revenue = defaultDecimal(ledger.getContractAmount()); |
| | | BigDecimal ratio = revenue.divide(totalRevenue, 6, RoundingMode.HALF_UP); |
| | | result.put(ledger.getId(), totalDepreciation.multiply(ratio)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private BigDecimal fallbackMaterialCost(List<SalesLedgerProduct> products, BigDecimal revenue) { |
| | | if (products != null && !products.isEmpty()) { |
| | | BigDecimal productAmount = products.stream() |
| | | .map(SalesLedgerProduct::getTaxExclusiveTotalPrice) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | if (productAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | return productAmount; |
| | | } |
| | | } |
| | | |
| | | 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) { |
| | | if (idSet == null || idSet.isEmpty()) { |
| | | return Map.of(); |
| | | } |
| | | Set<Long> ids = idSet.stream() |
| | | .map(this::toLongOrNull) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | if (ids.isEmpty()) { |
| | | return Map.of(); |
| | | } |
| | | LambdaQueryWrapper<Customer> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.in(Customer::getId, ids); |
| | | return defaultList(customerMapper.selectList(wrapper)).stream() |
| | | .collect(Collectors.toMap(item -> String.valueOf(item.getId()), Customer::getCustomerName, (a, b) -> a)); |
| | | } |
| | | |
| | | private Map<String, String> querySupplierNameMap(Set<String> idSet) { |
| | | if (idSet == null || idSet.isEmpty()) { |
| | | return Map.of(); |
| | | } |
| | | Set<Long> ids = idSet.stream() |
| | | .map(this::toLongOrNull) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | if (ids.isEmpty()) { |
| | | return Map.of(); |
| | | } |
| | | LambdaQueryWrapper<SupplierManage> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.in(SupplierManage::getId, ids); |
| | | return defaultList(supplierManageMapper.selectList(wrapper)).stream() |
| | | .collect(Collectors.toMap(item -> String.valueOf(item.getId()), SupplierManage::getSupplierName, (a, b) -> a)); |
| | | } |
| | | |
| | | private String riskLevelByAmount(BigDecimal amount) { |
| | | if (amount.compareTo(new BigDecimal("5000000")) >= 0) { |
| | | return "high"; |
| | | } |
| | | if (amount.compareTo(new BigDecimal("1000000")) >= 0) { |
| | | return "medium"; |
| | | } |
| | | return "low"; |
| | | } |
| | | |
| | | private long countDevices(LoginUser loginUser) { |
| | | LambdaQueryWrapper<DeviceLedger> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceLedger::getTenantId); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), DeviceLedger::getDeptId); |
| | | return deviceLedgerMapper.selectCount(wrapper); |
| | | } |
| | | |
| | | private long countRepairingDevices(LoginUser loginUser) { |
| | | LambdaQueryWrapper<DeviceRepair> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), DeviceRepair::getTenantId); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), DeviceRepair::getDeptId); |
| | | wrapper.in(DeviceRepair::getStatus, List.of(0, 3)); |
| | | return deviceRepairMapper.selectCount(wrapper); |
| | | } |
| | | |
| | | private BigDecimal estimateInventoryValue(LoginUser loginUser, List<StockInventory> inventories) { |
| | | if (inventories == null || inventories.isEmpty()) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | Set<Long> modelIds = inventories.stream().map(StockInventory::getProductModelId).filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Long, BigDecimal> costMap = queryAverageUnitCostByModel(loginUser, modelIds); |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | for (StockInventory inventory : inventories) { |
| | | BigDecimal qty = maxZero(defaultDecimal(inventory.getQualitity()).subtract(defaultDecimal(inventory.getLockedQuantity()))); |
| | | BigDecimal unit = costMap.getOrDefault(inventory.getProductModelId(), BigDecimal.ZERO); |
| | | total = total.add(qty.multiply(unit)); |
| | | } |
| | | return total; |
| | | } |
| | | |
| | | 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); |
| | | return new DateRange(prevStart, prevEnd, prevStart + "è³" + prevEnd); |
| | | } |
| | | |
| | | private List<String> buildProfitReasons(BigDecimal revenue, |
| | | BigDecimal materialCost, |
| | | BigDecimal laborCost, |
| | | BigDecimal scrapCost, |
| | | BigDecimal profit, |
| | | BigDecimal profitRate) { |
| | | List<String> reasons = new ArrayList<>(); |
| | | BigDecimal materialRate = rate(materialCost, revenue); |
| | | if (materialRate.compareTo(new BigDecimal("0.70")) >= 0) { |
| | | reasons.add("ææææ¬å æ¯è¶
è¿70%"); |
| | | } else if (materialRate.compareTo(new BigDecimal("0.55")) >= 0) { |
| | | reasons.add("ææææ¬å æ¯åé«"); |
| | | } |
| | | BigDecimal laborRate = rate(laborCost, revenue); |
| | | if (laborRate.compareTo(new BigDecimal("0.20")) >= 0) { |
| | | reasons.add("äººå·¥ææ¬å æ¯è¶
è¿20%"); |
| | | } else if (laborRate.compareTo(new BigDecimal("0.12")) >= 0) { |
| | | reasons.add("äººå·¥ææ¬å¢é¿åå¿«"); |
| | | } |
| | | BigDecimal scrapRate = rate(scrapCost, revenue); |
| | | if (scrapRate.compareTo(new BigDecimal("0.05")) >= 0) { |
| | | reasons.add("æ¥åºæèå æ¯åé«"); |
| | | } |
| | | if (profit.compareTo(BigDecimal.ZERO) < 0) { |
| | | reasons.add("订åå¤äºäºæç¶æ"); |
| | | } else if (profitRate.compareTo(new BigDecimal("0.08")) < 0) { |
| | | reasons.add("婿¶¦çä½äº8%"); |
| | | } |
| | | if (reasons.isEmpty()) { |
| | | reasons.add("ææ¬ç»æå¤äºåçåºé´"); |
| | | } |
| | | return reasons; |
| | | } |
| | | |
| | | private String buildProfitSuggestion(String riskLevel, List<String> reasons) { |
| | | if ("high".equals(riskLevel)) { |
| | | return "ä¼å
夿 ¸BOMç¨éä¸å·¥åºå®é¢ï¼å¿
è¦æ¶è°æ´æ¥ä»·å仿¬¾æ¡æ¬¾ï¼å¹¶éå¶è¶
è´¦æäº¤ä»ã"; |
| | | } |
| | | if ("medium".equals(riskLevel)) { |
| | | return "建议ä¼åéè´æ¹æ¬¡åå·¥åºæäº§ï¼æå䏿¬¡åæ ¼ç并忥æ§è¡æ¯å©é¢è¦ã"; |
| | | } |
| | | if (reasons.stream().anyMatch(item -> item.contains("ææ"))) { |
| | | return "ä¿æææéè´ææ¬çæ¿ï¼æå¨è·è¸ªä¸»è¦ææåä»·æ³¢å¨ã"; |
| | | } |
| | | return "ç»´æå½åç»è¥èå¥ï¼ç»§ç»è·è¸ªè®¢å婿¶¦çå忬¾æçã"; |
| | | } |
| | | |
| | | private Map<String, Object> toOrderCostItem(OrderProfitMetric metric) { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("ledgerId", metric.ledgerId()); |
| | | item.put("salesContractNo", metric.salesContractNo()); |
| | | item.put("customerName", metric.customerName()); |
| | | item.put("projectName", metric.projectName()); |
| | | item.put("entryDate", formatDate(metric.entryDate())); |
| | | item.put("deliveryDate", formatDate(metric.deliveryDate())); |
| | | item.put("revenue", metric.revenue()); |
| | | item.put("materialCost", metric.materialCost()); |
| | | item.put("laborCost", metric.laborCost()); |
| | | item.put("depreciationCost", metric.depreciationCost()); |
| | | item.put("scrapCost", metric.scrapCost()); |
| | | item.put("totalCost", metric.totalCost()); |
| | | item.put("profit", metric.profit()); |
| | | item.put("profitRate", toPercent(metric.profitRate())); |
| | | item.put("riskLevel", metric.riskLevel()); |
| | | item.put("reasons", metric.reasons()); |
| | | item.put("suggestion", metric.suggestion()); |
| | | return item; |
| | | } |
| | | |
| | | private Map<String, Object> toRiskOrderItem(OrderProfitMetric metric) { |
| | | Map<String, Object> map = toOrderCostItem(metric); |
| | | map.put("priority", "high".equals(metric.riskLevel()) ? "high" : ("medium".equals(metric.riskLevel()) ? "medium" : "low")); |
| | | return map; |
| | | } |
| | | |
| | | private List<Map<String, Object>> buildCustomerProfitTop(List<OrderProfitMetric> metrics, int topN) { |
| | | Map<String, BigDecimal> customerProfitMap = new HashMap<>(); |
| | | Map<String, BigDecimal> customerRevenueMap = new HashMap<>(); |
| | | for (OrderProfitMetric metric : metrics) { |
| | | customerProfitMap.merge(metric.customerName(), metric.profit(), BigDecimal::add); |
| | | customerRevenueMap.merge(metric.customerName(), metric.revenue(), BigDecimal::add); |
| | | } |
| | | return customerProfitMap.entrySet().stream() |
| | | .sorted(Map.Entry.<String, BigDecimal>comparingByValue().reversed()) |
| | | .limit(topN) |
| | | .map(entry -> { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | BigDecimal revenue = customerRevenueMap.getOrDefault(entry.getKey(), BigDecimal.ZERO); |
| | | map.put("customerName", entry.getKey()); |
| | | map.put("profit", entry.getValue()); |
| | | map.put("revenue", revenue); |
| | | map.put("profitRate", toPercent(rate(entry.getValue(), revenue))); |
| | | return map; |
| | | }) |
| | | .toList(); |
| | | } |
| | | |
| | | private Map<String, Object> toInventoryItem(InventoryMetric metric) { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("productModelId", metric.modelId()); |
| | | map.put("productName", metric.productName()); |
| | | map.put("model", metric.modelName()); |
| | | map.put("quantity", metric.quantity()); |
| | | map.put("lockedQuantity", metric.lockedQuantity()); |
| | | map.put("avgUnitCost", metric.avgUnitCost()); |
| | | map.put("inventoryValue", metric.inventoryValue()); |
| | | map.put("outboundQuantity", metric.outboundQuantity()); |
| | | map.put("stagnantDays", metric.stagnantDays()); |
| | | map.put("overstock", metric.overstock()); |
| | | map.put("riskLevel", metric.stagnantDays() >= 90 ? "high" : (metric.stagnantDays() >= 30 ? "medium" : "low")); |
| | | return map; |
| | | } |
| | | |
| | | private boolean matchInventoryKeyword(InventoryMetric metric, String keyword) { |
| | | if (!StringUtils.hasText(keyword)) { |
| | | return true; |
| | | } |
| | | return metric.productName().contains(keyword.trim()) || metric.modelName().contains(keyword.trim()); |
| | | } |
| | | |
| | | private Map<String, Object> toMonthlyCashFlowItem(MonthlyCashFlow flow) { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("month", flow.month()); |
| | | map.put("income", flow.income()); |
| | | map.put("expense", flow.expense()); |
| | | map.put("netFlow", flow.netFlow()); |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, Object> toStatementRiskItem(StatementMetric metric, Map<String, String> nameMap, String type) { |
| | | BigDecimal actualRate = rate(metric.actualAmount(), metric.planAmount()); |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put(type + "Id", metric.entityId()); |
| | | map.put(type + "Name", safe(nameMap.get(metric.entityId()))); |
| | | map.put("statementMonth", metric.statementMonth()); |
| | | map.put("closingBalance", metric.closingBalance()); |
| | | map.put("planAmount", metric.planAmount()); |
| | | map.put("actualAmount", metric.actualAmount()); |
| | | map.put("actualRate", toPercent(actualRate)); |
| | | map.put("riskLevel", metric.closingBalance().compareTo(new BigDecimal("1000000")) > 0 || actualRate.compareTo(new BigDecimal("0.50")) < 0 ? "high" : "medium"); |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, Object> anomalyItem(String level, String type, String message, Map<String, Object> detail) { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("riskLevel", level); |
| | | map.put("type", type); |
| | | map.put("message", message); |
| | | map.put("detail", detail); |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, Object> riskSuggestion(String type, String level, String suggestion) { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("type", type); |
| | | map.put("level", level); |
| | | map.put("suggestion", suggestion); |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, Object> buildCostCompositionPie(BigDecimal material, BigDecimal labor, BigDecimal depreciation, BigDecimal scrap) { |
| | | List<Map<String, Object>> data = List.of( |
| | | Map.of("name", "ææææ¬", "value", material), |
| | | Map.of("name", "äººå·¥ææ¬", "value", labor), |
| | | Map.of("name", "ææ§ææ¬", "value", depreciation), |
| | | Map.of("name", "æèææ¬", "value", scrap) |
| | | ); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "ææ¬ææ", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "item")); |
| | | option.put("series", List.of(Map.of("name", "ææ¬ææ", "type", "pie", "radius", "60%", "data", data))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildOrderProfitBar(List<OrderProfitMetric> metrics) { |
| | | List<OrderProfitMetric> top = metrics.stream() |
| | | .sorted(Comparator.comparing(OrderProfitMetric::profit)) |
| | | .limit(10) |
| | | .toList(); |
| | | List<String> xData = top.stream().map(OrderProfitMetric::salesContractNo).toList(); |
| | | List<BigDecimal> yData = top.stream().map(OrderProfitMetric::profit).toList(); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "订å婿¶¦åå¸", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", xData)); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of("name", "婿¶¦", "type", "bar", "data", yData))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildProcessCostBar(Map<String, BigDecimal> processCosts) { |
| | | List<String> xData = new ArrayList<>(processCosts.keySet()); |
| | | List<BigDecimal> yData = new ArrayList<>(processCosts.values()); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "å·¥åºææ¬æå", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", xData)); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of("name", "ææ¬", "type", "bar", "data", yData))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildProfitDistributionBar(List<OrderProfitMetric> metrics) { |
| | | List<OrderProfitMetric> sorted = metrics.stream() |
| | | .sorted(Comparator.comparing(OrderProfitMetric::profitRate)) |
| | | .limit(15) |
| | | .toList(); |
| | | List<String> xData = sorted.stream().map(OrderProfitMetric::salesContractNo).toList(); |
| | | List<BigDecimal> yData = sorted.stream().map(metric -> metric.profitRate().multiply(ONE_HUNDRED).setScale(2, RoundingMode.HALF_UP)).toList(); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "订å婿¶¦çåå¸", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", xData)); |
| | | option.put("yAxis", Map.of("type", "value", "name", "%")); |
| | | option.put("series", List.of(Map.of("name", "婿¶¦ç", "type", "bar", "data", yData))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildLossOrderTrendLine(List<OrderProfitMetric> metrics) { |
| | | Map<String, Long> lossByDate = new LinkedHashMap<>(); |
| | | List<OrderProfitMetric> sorted = metrics.stream() |
| | | .filter(metric -> metric.entryDate() != null) |
| | | .sorted(Comparator.comparing(OrderProfitMetric::entryDate)) |
| | | .toList(); |
| | | for (OrderProfitMetric metric : sorted) { |
| | | String day = formatDate(metric.entryDate()); |
| | | long inc = metric.profit().compareTo(BigDecimal.ZERO) < 0 ? 1L : 0L; |
| | | lossByDate.merge(day, inc, Long::sum); |
| | | } |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "äºæè®¢åè¶å¿", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", new ArrayList<>(lossByDate.keySet()))); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of("name", "äºæè®¢åæ°", "type", "line", "smooth", true, "data", new ArrayList<>(lossByDate.values())))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildCustomerProfitBar(Map<String, BigDecimal> customerProfitMap) { |
| | | List<Map.Entry<String, BigDecimal>> top = customerProfitMap.entrySet().stream() |
| | | .sorted(Map.Entry.<String, BigDecimal>comparingByValue().reversed()) |
| | | .limit(10) |
| | | .toList(); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "客æ·å©æ¶¦è´¡ç®TOP10", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", top.stream().map(Map.Entry::getKey).toList())); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of("name", "婿¶¦", "type", "bar", "data", top.stream().map(Map.Entry::getValue).toList()))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildInventoryTopBar(List<InventoryMetric> metrics) { |
| | | List<InventoryMetric> top = metrics.stream() |
| | | .sorted(Comparator.comparing(InventoryMetric::inventoryValue).reversed()) |
| | | .limit(10) |
| | | .toList(); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "åºåèµéå ç¨TOP10", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", top.stream().map(item -> item.productName() + "/" + item.modelName()).toList())); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of("name", "èµéå ç¨", "type", "bar", "data", top.stream().map(InventoryMetric::inventoryValue).toList()))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildInventoryAgingPie(List<InventoryMetric> metrics) { |
| | | long normal = metrics.stream().filter(item -> item.stagnantDays() < 30).count(); |
| | | long slow = metrics.stream().filter(item -> item.stagnantDays() >= 30 && item.stagnantDays() < 90).count(); |
| | | long stagnant = metrics.stream().filter(item -> item.stagnantDays() >= 90).count(); |
| | | List<Map<String, Object>> data = List.of( |
| | | Map.of("name", "æ£å¸¸", "value", normal), |
| | | Map.of("name", "ç¼æ
¢", "value", slow), |
| | | Map.of("name", "åæ»", "value", stagnant) |
| | | ); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "åºååºé¾åå¸", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "item")); |
| | | option.put("series", List.of(Map.of("type", "pie", "radius", "60%", "data", data))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildTurnoverGauge(BigDecimal turnoverDays) { |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "åºåå¨è½¬å¤©æ°", "left", "center")); |
| | | option.put("series", List.of(Map.of( |
| | | "type", "gauge", |
| | | "min", 0, |
| | | "max", 180, |
| | | "detail", Map.of("formatter", "{value}天"), |
| | | "data", List.of(Map.of("value", turnoverDays, "name", "å¨è½¬å¤©æ°")) |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildCashflowTrend(List<MonthlyCashFlow> actual, List<MonthlyCashFlow> forecast) { |
| | | List<String> labels = new ArrayList<>(); |
| | | List<BigDecimal> netActual = new ArrayList<>(); |
| | | List<BigDecimal> netForecast = new ArrayList<>(); |
| | | for (MonthlyCashFlow point : actual) { |
| | | labels.add(point.month()); |
| | | netActual.add(point.netFlow()); |
| | | netForecast.add(null); |
| | | } |
| | | for (MonthlyCashFlow point : forecast) { |
| | | labels.add(point.month()); |
| | | netActual.add(null); |
| | | netForecast.add(point.netFlow()); |
| | | } |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "ç°éæµè¶å¿ï¼å®é
+颿µï¼", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", labels)); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of( |
| | | Map.of("name", "å®é
åç°éæµ", "type", "line", "smooth", true, "data", netActual), |
| | | Map.of("name", "颿µåç°éæµ", "type", "line", "smooth", true, "data", netForecast) |
| | | )); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildReceivablePayableBar(BigDecimal receivable, BigDecimal payable) { |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "åºæ¶åºä»ä½é¢å¯¹æ¯", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", List.of("åºæ¶", "åºä»"))); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of("name", "ä½é¢", "type", "bar", "data", List.of(receivable, payable)))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildFundGapGauge(BigDecimal fundGap) { |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "èµé缺å£", "left", "center")); |
| | | option.put("series", List.of(Map.of( |
| | | "type", "gauge", |
| | | "min", 0, |
| | | "max", 10000000, |
| | | "detail", Map.of("formatter", "{value}"), |
| | | "data", List.of(Map.of("value", fundGap, "name", "èµé缺å£")) |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildAnomalyLevelPie(List<Map<String, Object>> anomalies) { |
| | | long high = anomalies.stream().filter(item -> "high".equals(item.get("riskLevel"))).count(); |
| | | long medium = anomalies.stream().filter(item -> "medium".equals(item.get("riskLevel"))).count(); |
| | | long low = anomalies.stream().filter(item -> "low".equals(item.get("riskLevel"))).count(); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "å¼å¸¸ç级åå¸", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "item")); |
| | | option.put("series", List.of(Map.of("type", "pie", "radius", "60%", "data", List.of( |
| | | Map.of("name", "é«é£é©", "value", high), |
| | | Map.of("name", "ä¸é£é©", "value", medium), |
| | | Map.of("name", "ä½é£é©", "value", low) |
| | | )))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildAnomalyTypeBar(List<Map<String, Object>> anomalies) { |
| | | Map<String, Long> countByType = new LinkedHashMap<>(); |
| | | for (Map<String, Object> anomaly : anomalies) { |
| | | countByType.merge(String.valueOf(anomaly.get("type")), 1L, Long::sum); |
| | | } |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "å¼å¸¸ç±»ååå¸", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", new ArrayList<>(countByType.keySet()))); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of("name", "å¼å¸¸æ°", "type", "bar", "data", new ArrayList<>(countByType.values())))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildInventoryProfitGauge(BigDecimal inventoryValue, BigDecimal profit) { |
| | | BigDecimal ratio = inventoryValue.compareTo(BigDecimal.ZERO) <= 0 |
| | | ? BigDecimal.ZERO |
| | | : profit.divide(inventoryValue, 4, RoundingMode.HALF_UP).multiply(ONE_HUNDRED); |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "婿¶¦/åºåèµéæ¯", "left", "center")); |
| | | option.put("series", List.of(Map.of( |
| | | "type", "gauge", |
| | | "min", -100, |
| | | "max", 100, |
| | | "detail", Map.of("formatter", "{value}%"), |
| | | "data", List.of(Map.of("value", ratio.setScale(2, RoundingMode.HALF_UP), "name", "婿¶¦èµéæ¯")) |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private int normalizeLimit(Integer limit) { |
| | | if (limit == null || limit <= 0) { |
| | | return DEFAULT_LIMIT; |
| | | } |
| | | return Math.min(limit, MAX_LIMIT); |
| | | } |
| | | |
| | | private DateRange resolveDateRange(String startDate, String endDate, String timeRange, String defaultLabel) { |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDate explicitStart = parseLocalDate(startDate); |
| | | LocalDate explicitEnd = parseLocalDate(endDate); |
| | | if (explicitStart != null || explicitEnd != null) { |
| | | LocalDate start = explicitStart != null ? explicitStart : explicitEnd; |
| | | LocalDate end = explicitEnd != null ? explicitEnd : explicitStart; |
| | | if (start.isAfter(end)) { |
| | | LocalDate temp = start; |
| | | start = end; |
| | | end = temp; |
| | | } |
| | | return new DateRange(start, end, start + "è³" + end); |
| | | } |
| | | |
| | | if (!StringUtils.hasText(timeRange)) { |
| | | return new DateRange(null, null, "å
¨é¨"); |
| | | } |
| | | |
| | | String text = timeRange.trim(); |
| | | if (text.contains("ä»å¤©")) { |
| | | return new DateRange(today, today, "ä»å¤©"); |
| | | } |
| | | if (text.contains("æ¨å¤©") || text.contains("æ¨æ¥")) { |
| | | LocalDate day = today.minusDays(1); |
| | | return new DateRange(day, day, "æ¨å¤©"); |
| | | } |
| | | if (text.contains("æ¬å¨")) { |
| | | LocalDate start = today.minusDays(today.getDayOfWeek().getValue() - 1L); |
| | | return new DateRange(start, today, "æ¬å¨"); |
| | | } |
| | | if (text.contains("ä¸å¨")) { |
| | | LocalDate thisWeekStart = today.minusDays(today.getDayOfWeek().getValue() - 1L); |
| | | LocalDate start = thisWeekStart.minusWeeks(1); |
| | | LocalDate end = thisWeekStart.minusDays(1); |
| | | return new DateRange(start, end, "ä¸å¨"); |
| | | } |
| | | if (text.contains("æ¬æ")) { |
| | | return new DateRange(today.withDayOfMonth(1), today, "æ¬æ"); |
| | | } |
| | | if (text.contains("䏿")) { |
| | | YearMonth lastMonth = YearMonth.from(today).minusMonths(1); |
| | | return new DateRange(lastMonth.atDay(1), lastMonth.atEndOfMonth(), "䏿"); |
| | | } |
| | | if (text.contains("ä»å¹´") || text.contains("æ¬å¹´")) { |
| | | return new DateRange(today.withDayOfYear(1), today, "ä»å¹´"); |
| | | } |
| | | Matcher relativeMatcher = RELATIVE_PATTERN.matcher(text); |
| | | if (relativeMatcher.find()) { |
| | | int amount = Integer.parseInt(relativeMatcher.group(2)); |
| | | String unit = relativeMatcher.group(3); |
| | | LocalDate start = switch (unit) { |
| | | case "天" -> today.minusDays(Math.max(amount - 1L, 0)); |
| | | case "å¨" -> today.minusWeeks(Math.max(amount, 1)).plusDays(1); |
| | | case "个æ", "æ" -> today.minusMonths(Math.max(amount, 1)).plusDays(1); |
| | | case "å¹´" -> today.minusYears(Math.max(amount, 1)).plusDays(1); |
| | | default -> today.minusDays(29); |
| | | }; |
| | | return new DateRange(start, today, "è¿" + amount + unit); |
| | | } |
| | | Matcher dateMatcher = DATE_PATTERN.matcher(text); |
| | | if (dateMatcher.find()) { |
| | | LocalDate start = parseLocalDate(dateMatcher.group(1)); |
| | | LocalDate end = dateMatcher.find() ? parseLocalDate(dateMatcher.group(1)) : start; |
| | | if (start != null && end != null) { |
| | | if (start.isAfter(end)) { |
| | | LocalDate temp = start; |
| | | start = end; |
| | | end = temp; |
| | | } |
| | | return new DateRange(start, end, start + "è³" + end); |
| | | } |
| | | } |
| | | return new DateRange(null, null, "å
¨é¨"); |
| | | } |
| | | |
| | | private LocalDate parseLocalDate(String text) { |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return LocalDate.parse(text.trim(), DATE_FMT); |
| | | } catch (Exception ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private Date toDate(LocalDate localDate) { |
| | | return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | } |
| | | |
| | | private Date toExclusiveEndDate(LocalDate localDate) { |
| | | return Date.from(localDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | } |
| | | |
| | | private LocalDate toLocalDate(Date date) { |
| | | if (date == null) { |
| | | return null; |
| | | } |
| | | return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | } |
| | | |
| | | private String formatDate(LocalDate date) { |
| | | return date == null ? "" : date.format(DATE_FMT); |
| | | } |
| | | |
| | | private String displayDate(LocalDate date) { |
| | | return date == null ? "" : date.format(DATE_FMT); |
| | | } |
| | | |
| | | private long daysBetween(LocalDate start, LocalDate end) { |
| | | if (start == null || end == null || start.isAfter(end)) { |
| | | return 0; |
| | | } |
| | | return end.toEpochDay() - start.toEpochDay(); |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private BigDecimal maxZero(BigDecimal value) { |
| | | return value == null || value.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private BigDecimal rate(BigDecimal numerator, BigDecimal denominator) { |
| | | if (denominator == null || denominator.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return defaultDecimal(numerator).divide(denominator, 6, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private String toPercent(BigDecimal decimal) { |
| | | if (decimal == null) { |
| | | return "0.00%"; |
| | | } |
| | | BigDecimal rate = decimal.multiply(ONE_HUNDRED).setScale(2, RoundingMode.HALF_UP); |
| | | return rate.toPlainString() + "%"; |
| | | } |
| | | |
| | | private BigDecimal avgRate(List<OrderProfitMetric> metrics) { |
| | | if (metrics == null || metrics.isEmpty()) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal sum = metrics.stream().map(OrderProfitMetric::profitRate).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | return sum.divide(new BigDecimal(metrics.size()), 6, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal estimateLaborCost(ProductionAccount account, Map<String, BigDecimal> salaryQuotaByOperation) { |
| | | BigDecimal salaryQuota = salaryQuotaByOperation.getOrDefault(safe(account.getTechnologyOperationName()), BigDecimal.ZERO); |
| | | BigDecimal finishedNum = defaultDecimal(account.getFinishedNum()); |
| | | BigDecimal workHours = defaultDecimal(account.getWorkHours()); |
| | | if (salaryQuota.compareTo(BigDecimal.ZERO) > 0 && finishedNum.compareTo(BigDecimal.ZERO) > 0) { |
| | | return finishedNum.multiply(salaryQuota); |
| | | } |
| | | if (salaryQuota.compareTo(BigDecimal.ZERO) > 0 && workHours.compareTo(BigDecimal.ZERO) > 0) { |
| | | return workHours.multiply(salaryQuota); |
| | | } |
| | | if (workHours.compareTo(BigDecimal.ZERO) > 0) { |
| | | return workHours; |
| | | } |
| | | return finishedNum; |
| | | } |
| | | |
| | | private List<Long> parseIdList(String raw) { |
| | | if (!StringUtils.hasText(raw)) { |
| | | return List.of(); |
| | | } |
| | | String text = raw.replace("[", "").replace("]", "").replace(" ", ""); |
| | | if (!StringUtils.hasText(text)) { |
| | | return List.of(); |
| | | } |
| | | List<Long> result = new ArrayList<>(); |
| | | for (String part : text.split(",")) { |
| | | if (!StringUtils.hasText(part)) { |
| | | continue; |
| | | } |
| | | try { |
| | | result.add(Long.parseLong(part.trim())); |
| | | } catch (Exception ignored) { |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private int keywordHitCount(List<String> keywords, String question) { |
| | | if (!StringUtils.hasText(question) || keywords == null) { |
| | | return 0; |
| | | } |
| | | int count = 0; |
| | | for (String keyword : keywords) { |
| | | if (question.contains(keyword)) { |
| | | count++; |
| | | } |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | private String normalizeForMatch(String text) { |
| | | if (!StringUtils.hasText(text)) { |
| | | return ""; |
| | | } |
| | | return text.replace("ï¼", "") |
| | | .replace(",", "") |
| | | .replace("ã", "") |
| | | .replace(".", "") |
| | | .replace("ï¼", "") |
| | | .replace("!", "") |
| | | .replace("ï¼", "") |
| | | .replace("?", "") |
| | | .replace("ï¼", "") |
| | | .replace(":", "") |
| | | .replace("ï¼", "") |
| | | .replace(";", "") |
| | | .replace(" ", "") |
| | | .trim(); |
| | | } |
| | | |
| | | private String safe(Object value) { |
| | | return value == null ? "" : String.valueOf(value).replace('\n', ' ').replace('\r', ' ').trim(); |
| | | } |
| | | |
| | | private LoginUser currentLoginUser(String memoryId) { |
| | | LoginUser loginUser = aiSessionUserContext.get(memoryId); |
| | | if (loginUser != null) { |
| | | return loginUser; |
| | | } |
| | | return SecurityUtils.getLoginUser(); |
| | | } |
| | | |
| | | 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", displayDate(range.start())); |
| | | summary.put("endDate", displayDate(range.end())); |
| | | summary.put("count", count); |
| | | summary.put("keyword", safe(keyword)); |
| | | return summary; |
| | | } |
| | | |
| | | private Long toLongOrNull(String value) { |
| | | if (!StringUtils.hasText(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Long.valueOf(value.trim()); |
| | | } catch (Exception ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private <T> List<T> defaultList(List<T> list) { |
| | | return list == null ? List.of() : list; |
| | | } |
| | | |
| | | private <T> void applyTenantFilter(LambdaQueryWrapper<T> wrapper, Long tenantId, SFunction<T, Long> field) { |
| | | if (tenantId != null) { |
| | | wrapper.eq(field, tenantId); |
| | | } |
| | | } |
| | | |
| | | private <T> void applyDeptFilter(LambdaQueryWrapper<T> wrapper, Long deptId, SFunction<T, Long> field) { |
| | | if (deptId != null) { |
| | | wrapper.eq(field, deptId); |
| | | } |
| | | } |
| | | |
| | | private List<KnowledgeDoc> financeKnowledgeBase() { |
| | | return List.of( |
| | | new KnowledgeDoc( |
| | | "婿¶¦ä¸éåææ¡æ¶", |
| | | List.of("婿¶¦ä¸é", "äºæè®¢å", "æ¯å©ç", "åå©ç"), |
| | | "å
çæ¶å
¥ç«¯ï¼è®¢åç»æãåä»·ã交ä»å»¶è¿ï¼ï¼åçææ¬ç«¯ï¼ææãäººå·¥ãææ§ãæèï¼ï¼æåçç°é端ï¼å款ãè´¦æãåè´¦é£é©ï¼ã", |
| | | List.of("sales_ledger", "sales_ledger_product", "production_account", "device_ledger", "account_statement"), |
| | | List.of("为ä»ä¹æ¬æå©æ¶¦ä¸éï¼", "åªäºè®¢åäºææä¸¥éï¼", "ææ¬ä¸åæ¥èªåªä¸ªå·¥åºï¼") |
| | | ), |
| | | new KnowledgeDoc( |
| | | "åºåèµéå ç¨è¯æ", |
| | | List.of("åºå积å", "åæ»åºå", "å¨è½¬ç", "èµéå ç¨"), |
| | | "åºåèµéè¯æéç¹çï¼åºåä»·å¼ãè¿30天åºåºææ¬ã忻天æ°ãè¶
卿¯ä¾ï¼å½¢æå»åºåä¸éè´èå¥èå¨çç¥ã", |
| | | List.of("stock_inventory", "procurement_record_storage", "procurement_record_out"), |
| | | List.of("åªäºç©æèµéå ç¨æé«ï¼", "åªäºåºåè¶
è¿90天æªå¨è½¬ï¼", "åºåå¨è½¬å¤©æ°æ¯å¦å¼å¸¸ï¼") |
| | | ), |
| | | new KnowledgeDoc( |
| | | "ç°éæµä¸è´¦æ¬¾é£é©", |
| | | List.of("ç°éæµ", "åºæ¶", "åºä»", "忬¾", "èµé缺å£"), |
| | | "ç°éæµå¤æè¦ç»åæ¶æ¬¾ã仿¬¾ãåºæ¶åºä»ä½é¢ä¸é¢æµåæµéï¼éç¹å
³æ³¨é«ä½é¢å®¢æ·åé«éä¸ä»æ¬¾ä¾åºåã", |
| | | List.of("account_sales_collection", "account_purchase_payment", "account_statement"), |
| | | List.of("æªæ¥ä¸ä¸ªææ¯å¦æèµé缺å£ï¼", "åªä¸ªå®¢æ·å款é£é©æé«ï¼", "仿¬¾ååæå¤§çæ¯åªäºä¾åºåï¼") |
| | | ), |
| | | new KnowledgeDoc( |
| | | "ä¸è´¢ä¸ä½åå£å¾", |
| | | List.of("ä¸è´¢èå", "ä¸è´¢èå¨", "å£å¾", "驾驶è±"), |
| | | "订å婿¶¦å£å¾=é宿¶å
¥-ææææ¬-äººå·¥ææ¬-è®¾å¤ææ§-æèææ¬ï¼ç»è¥é©¾é©¶è±èå¨è®¢åãç产ãåºåã设å¤ãè´¦æ¬¾æ°æ®ã", |
| | | List.of("sales_ledger", "production_operation_task", "production_product_main", "device_ledger", "stock_inventory", "account_statement"), |
| | | List.of("订å婿¶¦çå¦ä½è®¡ç®ï¼", "ç»è¥é©¾é©¶è±æ ¸å¿ææ æåªäºï¼") |
| | | ) |
| | | ); |
| | | } |
| | | |
| | | private String jsonResponse(boolean success, |
| | | String type, |
| | | String description, |
| | | Map<String, Object> summary, |
| | | Map<String, Object> data, |
| | | Map<String, Object> charts) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("success", success); |
| | | result.put("type", type); |
| | | result.put("description", description); |
| | | result.put("summary", summary == null ? Map.of() : summary); |
| | | result.put("data", data == null ? Map.of() : data); |
| | | result.put("charts", charts == null ? Map.of() : charts); |
| | | return JSON.toJSONString(result); |
| | | } |
| | | |
| | | private record DateRange(LocalDate start, LocalDate end, String label) { |
| | | private boolean hasDateFilter() { |
| | | return start != null && end != null; |
| | | } |
| | | } |
| | | |
| | | private record OrderProfitMetric(Long ledgerId, |
| | | String salesContractNo, |
| | | String customerName, |
| | | String projectName, |
| | | LocalDate entryDate, |
| | | LocalDate deliveryDate, |
| | | BigDecimal revenue, |
| | | BigDecimal materialCost, |
| | | BigDecimal laborCost, |
| | | BigDecimal depreciationCost, |
| | | BigDecimal scrapCost, |
| | | BigDecimal totalCost, |
| | | BigDecimal profit, |
| | | BigDecimal profitRate, |
| | | String riskLevel, |
| | | List<String> reasons, |
| | | String suggestion) { |
| | | } |
| | | |
| | | private record AnalysisBundle(List<OrderProfitMetric> orderMetrics, |
| | | Map<String, BigDecimal> processCostRanking, |
| | | BigDecimal totalRevenue, |
| | | BigDecimal totalMaterialCost, |
| | | BigDecimal totalLaborCost, |
| | | BigDecimal totalDepreciationCost, |
| | | BigDecimal totalScrapCost, |
| | | BigDecimal totalCost, |
| | | BigDecimal totalProfit) { |
| | | private static AnalysisBundle empty() { |
| | | return new AnalysisBundle(List.of(), Map.of(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, |
| | | BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | } |
| | | |
| | | private record MaterialCostResult(Map<Long, BigDecimal> materialCostByLedgerId, |
| | | Map<Long, BigDecimal> avgUnitCostByModelId) { |
| | | } |
| | | |
| | | private record ProductionCostContext(Map<Long, BigDecimal> laborCostByLedgerId, |
| | | Map<Long, BigDecimal> scrapCostByLedgerId, |
| | | Map<String, BigDecimal> processCostRanking) { |
| | | private static ProductionCostContext empty() { |
| | | return new ProductionCostContext(Map.of(), Map.of(), Map.of()); |
| | | } |
| | | } |
| | | |
| | | private record InventoryMetric(Long modelId, |
| | | String productName, |
| | | String modelName, |
| | | BigDecimal quantity, |
| | | BigDecimal lockedQuantity, |
| | | BigDecimal avgUnitCost, |
| | | BigDecimal inventoryValue, |
| | | BigDecimal outboundQuantity, |
| | | long stagnantDays, |
| | | boolean overstock) { |
| | | } |
| | | |
| | | private static class InventoryMetricBuilder { |
| | | private final Long modelId; |
| | | private BigDecimal quantity = BigDecimal.ZERO; |
| | | private BigDecimal lockedQuantity = BigDecimal.ZERO; |
| | | private BigDecimal warnNum = BigDecimal.ZERO; |
| | | private LocalDateTime firstInTime; |
| | | |
| | | private InventoryMetricBuilder(Long modelId) { |
| | | this.modelId = modelId; |
| | | } |
| | | |
| | | private void addQuantity(BigDecimal quantity) { |
| | | this.quantity = this.quantity.add(quantity); |
| | | } |
| | | |
| | | private void addLockedQuantity(BigDecimal lockedQuantity) { |
| | | this.lockedQuantity = this.lockedQuantity.add(lockedQuantity); |
| | | } |
| | | |
| | | private void addWarnNum(BigDecimal warnNum) { |
| | | this.warnNum = this.warnNum.max(warnNum); |
| | | } |
| | | |
| | | private void updateFirstInTime(LocalDateTime createTime) { |
| | | if (this.firstInTime == null || createTime.isBefore(this.firstInTime)) { |
| | | this.firstInTime = createTime; |
| | | } |
| | | } |
| | | |
| | | private Long modelId() { |
| | | return modelId; |
| | | } |
| | | |
| | | private BigDecimal quantity() { |
| | | return quantity; |
| | | } |
| | | |
| | | private BigDecimal lockedQuantity() { |
| | | return lockedQuantity; |
| | | } |
| | | |
| | | private BigDecimal warnNum() { |
| | | return warnNum; |
| | | } |
| | | |
| | | private LocalDateTime firstInTime() { |
| | | return firstInTime; |
| | | } |
| | | } |
| | | |
| | | private record OutboundStats(Map<Long, BigDecimal> outboundQtyByModel, |
| | | Map<Long, LocalDateTime> lastOutboundTimeByModel, |
| | | BigDecimal totalOutboundCost) { |
| | | private static OutboundStats empty() { |
| | | return new OutboundStats(Map.of(), Map.of(), BigDecimal.ZERO); |
| | | } |
| | | } |
| | | |
| | | private record MonthlyCashFlow(String month, BigDecimal income, BigDecimal expense, BigDecimal netFlow) { |
| | | } |
| | | |
| | | private record StatementMetric(String entityId, |
| | | BigDecimal closingBalance, |
| | | BigDecimal planAmount, |
| | | BigDecimal actualAmount, |
| | | String statementMonth) { |
| | | } |
| | | |
| | | private record StatementSnapshot(BigDecimal receivableTotal, |
| | | BigDecimal payableTotal, |
| | | List<StatementMetric> receivableTop, |
| | | List<StatementMetric> payableTop) { |
| | | private static StatementSnapshot empty() { |
| | | return new StatementSnapshot(BigDecimal.ZERO, BigDecimal.ZERO, List.of(), List.of()); |
| | | } |
| | | } |
| | | |
| | | private record KnowledgeDoc(String topic, |
| | | List<String> keywords, |
| | | String knowledge, |
| | | List<String> relatedTables, |
| | | List<String> suggestedQuestions) { |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.dto; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.ruoyi.basic.dto.StorageBlobDTO; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class ApprovalInstanceDto extends ApprovalInstance { |
| | | |
| | | private String approveAction; |
| | | |
| | | private String approveComment; |
| | | |
| | | private String createTimeEnd; |
| | | |
| | | private String createTimeStart; |
| | | |
| | | private List<StorageBlobDTO> storageBlobDTOs; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.dto; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class ApprovalTemplateDto extends ApprovalTemplate { |
| | | |
| | | private List<ApprovalTemplateNodeDto> nodes; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.dto; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class ApprovalTemplateNodeApproverDto extends ApprovalTemplateNodeApprover { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.dto; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNode; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class ApprovalTemplateNodeDto extends ApprovalTemplateNode { |
| | | |
| | | private List<ApprovalTemplateNodeApproverDto> approvers; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.dto; |
| | | |
| | | import com.ruoyi.approve.pojo.FinReimbursement; |
| | | import com.ruoyi.approve.pojo.FinReimbursementDetail; |
| | | import com.ruoyi.approve.pojo.FinReimbursementTravel; |
| | | import com.ruoyi.basic.dto.StorageBlobDTO; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class FinReimbursementDto extends FinReimbursement { |
| | | |
| | | private String createTimeStart; |
| | | private String createTimeEnd; |
| | | |
| | | private FinReimbursementTravel travel; |
| | | private List<FinReimbursementDetail> details; |
| | | private List<ApprovalTemplateNodeDto> nodes; |
| | | private List<StorageBlobDTO> storageBlobDTOs; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.vo; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.ruoyi.approve.pojo.ApprovalRecord; |
| | | import com.ruoyi.approve.pojo.ApprovalTask; |
| | | import com.ruoyi.basic.dto.StorageBlobVO; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class ApprovalInstanceVo extends ApprovalInstance { |
| | | //å½åç¨æ·æ¯å¦å¯ä»¥å®¡æ¹ |
| | | @Schema(description = "å½åç¨æ·æ¯å¦å¯ä»¥å®¡æ¹") |
| | | private Boolean isApprove; |
| | | |
| | | //å®¡æ¹æµç¨ |
| | | private List<ApprovalTask> tasks; |
| | | |
| | | //审æ¹è®°å½ |
| | | private List<ApprovalRecord> records; |
| | | |
| | | @Schema(description = "ä¸å¡åç§°") |
| | | private String businessName; |
| | | |
| | | private List<StorageBlobVO> storageBlobVOList; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.vo; |
| | | |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class ApprovalTemplateNodeApproverVo extends ApprovalTemplateNodeApprover { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.vo; |
| | | |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNode; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class ApprovalTemplateNodeVo extends ApprovalTemplateNode { |
| | | |
| | | private List<ApprovalTemplateNodeApproverVo> approvers; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.vo; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class ApprovalTemplateVo extends ApprovalTemplate { |
| | | |
| | | private List<ApprovalTemplateNodeVo> nodes; |
| | | |
| | | private String createdUserName; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.bean.vo; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.approve.pojo.*; |
| | | import com.ruoyi.basic.dto.StorageBlobVO; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class FinReimbursementVo extends FinReimbursement { |
| | | |
| | | |
| | | private String createTimeStart; |
| | | private String createTimeEnd; |
| | | |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime startTime; |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime endTime; |
| | | |
| | | private FinReimbursementTravel travel; |
| | | private List<FinReimbursementDetail> details; |
| | | //å®¡æ¹æµç¨ |
| | | private List<ApprovalTask> tasks; |
| | | |
| | | //审æ¹è®°å½ |
| | | private List<ApprovalRecord> records; |
| | | private List<StorageBlobVO> storageBlobVOList; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalInstanceVo; |
| | | import com.ruoyi.approve.service.ApprovalInstanceService; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹å®ä¾è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:46 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/approvalInstance") |
| | | @Tag(name = "审æ¹å®ä¾è¡¨") |
| | | @AllArgsConstructor |
| | | public class ApprovalInstanceController extends BaseController { |
| | | |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | @GetMapping("/listPage") |
| | | @Operation(summary = "å页æ¥è¯¢") |
| | | @Log(title = "审æ¹å表å页æ¥è¯¢", businessType = BusinessType.OTHER) |
| | | public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) { |
| | | return approvalInstanceService.listPage(page, approvalInstanceDto); |
| | | } |
| | | |
| | | @PostMapping("/save") |
| | | @Operation(summary = "ä¿å") |
| | | @Log(title = "审æ¹å表ä¿å", businessType = BusinessType.INSERT) |
| | | public R save(@RequestBody ApprovalInstanceDto approvalInstanceDto) { |
| | | return approvalInstanceService.add(approvalInstanceDto) ? R.ok() : R.fail(); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°") |
| | | @Log(title = "审æ¹åè¡¨æ´æ°", businessType = BusinessType.UPDATE) |
| | | public R update(@RequestBody ApprovalInstanceDto approvalInstanceDto) { |
| | | return approvalInstanceService.update(approvalInstanceDto) ? R.ok() : R.fail(); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Log(title = "审æ¹å表å é¤", businessType = BusinessType.DELETE) |
| | | @Operation(summary = "å é¤") |
| | | public R delete(@RequestBody List<Long> ids) { |
| | | return approvalInstanceService.delete(ids) ? R.ok() : R.fail(); |
| | | } |
| | | |
| | | @Operation(summary = "审æ¹") |
| | | @PostMapping("/approve") |
| | | @Log(title = "审æ¹å表审æ¹", businessType = BusinessType.UPDATE) |
| | | public R approve(@RequestBody ApprovalInstanceDto approvalInstanceDto) { |
| | | return approvalInstanceService.approve(approvalInstanceDto); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹èç¹å®ä¾è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:54 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/approvalInstanceNode") |
| | | public class ApprovalInstanceNodeController extends BaseController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹è®°å½è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:28:21 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/approvalRecord") |
| | | public class ApprovalRecordController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹ä»»å¡è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:32:37 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/approvalTask") |
| | | public class ApprovalTaskController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateVo; |
| | | import com.ruoyi.approve.service.ApprovalTemplateService; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author èéè½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:08 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/approvalTemplate") |
| | | @Tag(name = "å®¡æ¹æ¨¡æ¿è¡¨") |
| | | @AllArgsConstructor |
| | | public class ApprovalTemplateController extends BaseController { |
| | | |
| | | private final ApprovalTemplateService approvalTemplateService; |
| | | |
| | | @GetMapping("/listPage") |
| | | @Operation(summary = "å页æ¥è¯¢") |
| | | @Log(title = "å®¡æ¹æ¨¡æ¿å页æ¥è¯¢", businessType = BusinessType.OTHER) |
| | | public R listPage(Page<ApprovalTemplateVo> page, ApprovalTemplateDto approvalTemplateDto) { |
| | | return R.ok(approvalTemplateService.listPage(page, approvalTemplateDto)); |
| | | } |
| | | |
| | | @PostMapping("/add") |
| | | @Operation(summary = "æ·»å ") |
| | | @Log(title = "æ·»å å®¡æ¹æ¨¡æ¿", businessType = BusinessType.INSERT) |
| | | public R add(@RequestBody ApprovalTemplateDto approvalTemplateDto) { |
| | | return R.ok(approvalTemplateService.saveApprovalTemplateDto(approvalTemplateDto)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "ä¿®æ¹") |
| | | @Log(title = "ä¿®æ¹å®¡æ¹æ¨¡æ¿", businessType = BusinessType.UPDATE) |
| | | public R update(@RequestBody ApprovalTemplateDto approvalTemplateDto) { |
| | | return R.ok(approvalTemplateService.updateApprovalTemplateDto(approvalTemplateDto)); |
| | | } |
| | | |
| | | @PostMapping("/delete") |
| | | @Operation(summary = "å é¤") |
| | | @Log(title = "å é¤å®¡æ¹æ¨¡æ¿", businessType = BusinessType.DELETE) |
| | | public R delete(@RequestBody List<Long> ids) { |
| | | return R.ok(approvalTemplateService.delete(ids)); |
| | | } |
| | | |
| | | @GetMapping("/list/{type}") |
| | | @Operation(summary = "æ¥è¯¢ææå®¡æ¹æ¨¡æ¿") |
| | | public R list(@PathVariable("type") Integer type) { |
| | | return R.ok(approvalTemplateService.listApprovalTemplateVo(type)); |
| | | } |
| | | |
| | | @GetMapping("/detail/{id}") |
| | | @Operation(summary = "æ¥è¯¢å®¡æ¹æ¨¡æ¿è¯¦æ
") |
| | | @Log(title = "æ¥è¯¢å®¡æ¹æ¨¡æ¿è¯¦æ
", businessType = BusinessType.OTHER) |
| | | public R detail(@PathVariable("id") Long id) { |
| | | return R.ok(approvalTemplateService.getApprovalTemplateVoById(id)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹å®¡æ¹äººè¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:30 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/approvalTemplateNodeApprover") |
| | | public class ApprovalTemplateNodeApproverController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:19 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/approvalTemplateNode") |
| | | public class ApprovalTemplateNodeController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.approve.bean.dto.FinReimbursementDto; |
| | | import com.ruoyi.approve.bean.vo.FinReimbursementVo; |
| | | import com.ruoyi.approve.service.FinReimbursementService; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éå主表 å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:15 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/finReimbursement") |
| | | @Tag(name = "æ¥éå主表", description = "æ¥éå主表") |
| | | @AllArgsConstructor |
| | | public class FinReimbursementController { |
| | | |
| | | private final FinReimbursementService finReimbursementService; |
| | | |
| | | @GetMapping("/listPage") |
| | | @Operation(summary = "å页æ¥è¯¢") |
| | | public R listPage(Page<FinReimbursementVo> page, FinReimbursementDto finReimbursementDto) { |
| | | return R.ok(finReimbursementService.listPage(finReimbursementDto, page)); |
| | | } |
| | | |
| | | @PostMapping("/save") |
| | | @Operation(summary = "ä¿å") |
| | | public R save(@RequestBody FinReimbursementDto finReimbursementDto) { |
| | | return R.ok(finReimbursementService.add(finReimbursementDto)); |
| | | } |
| | | |
| | | @GetMapping("/detail") |
| | | @Operation(summary = "详æ
") |
| | | public R detail(Long id) { |
| | | return R.ok(finReimbursementService.detail(id)); |
| | | } |
| | | |
| | | @PostMapping("/update") |
| | | @Operation(summary = "ä¿®æ¹") |
| | | public R update(@RequestBody FinReimbursementDto finReimbursementDto) { |
| | | return R.ok(finReimbursementService.update(finReimbursementDto)); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤") |
| | | public R delete(@RequestBody List<Long> ids) { |
| | | return R.ok(finReimbursementService.delete(ids)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éåæç»è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:38 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/finReimbursementDetail") |
| | | public class FinReimbursementDetailController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * å·®æ
æ¥éæ©å±è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:47 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/finReimbursementTravel") |
| | | public class FinReimbursementTravelController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | 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.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalInstanceVo; |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹å®ä¾è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:46 |
| | | */ |
| | | @Mapper |
| | | public interface ApprovalInstanceMapper extends BaseMapper<ApprovalInstance> { |
| | | |
| | | IPage<ApprovalInstanceVo> listPage(Page<ApprovalInstanceVo> page,@Param("ew") ApprovalInstanceDto approvalInstanceDto); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalInstanceNode; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹èç¹å®ä¾è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:54 |
| | | */ |
| | | @Mapper |
| | | public interface ApprovalInstanceNodeMapper extends BaseMapper<ApprovalInstanceNode> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalRecord; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹è®°å½è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:28:21 |
| | | */ |
| | | @Mapper |
| | | public interface ApprovalRecordMapper extends BaseMapper<ApprovalRecord> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalTask; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹ä»»å¡è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:32:37 |
| | | */ |
| | | @Mapper |
| | | public interface ApprovalTaskMapper extends BaseMapper<ApprovalTask> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | 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.approve.bean.dto.ApprovalTemplateDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateVo; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:08 |
| | | */ |
| | | @Mapper |
| | | public interface ApprovalTemplateMapper extends BaseMapper<ApprovalTemplate> { |
| | | |
| | | IPage<ApprovalTemplateVo> listPage(Page<ApprovalTemplateVo> page,@Param("ew") ApprovalTemplateDto approvalTemplateDto); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹å®¡æ¹äººè¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:30 |
| | | */ |
| | | @Mapper |
| | | public interface ApprovalTemplateNodeApproverMapper extends BaseMapper<ApprovalTemplateNodeApprover> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNode; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:19 |
| | | */ |
| | | @Mapper |
| | | public interface ApprovalTemplateNodeMapper extends BaseMapper<ApprovalTemplateNode> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.approve.pojo.FinReimbursementDetail; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éåæç»è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:38 |
| | | */ |
| | | @Mapper |
| | | public interface FinReimbursementDetailMapper extends BaseMapper<FinReimbursementDetail> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | 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.approve.bean.dto.FinReimbursementDto; |
| | | import com.ruoyi.approve.bean.vo.FinReimbursementVo; |
| | | import com.ruoyi.approve.pojo.FinReimbursement; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éå主表 Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:15 |
| | | */ |
| | | @Mapper |
| | | public interface FinReimbursementMapper extends BaseMapper<FinReimbursement> { |
| | | |
| | | IPage<FinReimbursementVo> listPage(@Param("ew") FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.approve.pojo.FinReimbursementTravel; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * å·®æ
æ¥éæ©å±è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:47 |
| | | */ |
| | | @Mapper |
| | | public interface FinReimbursementTravelMapper extends BaseMapper<FinReimbursementTravel> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹å®ä¾è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:46 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("approval_instance") |
| | | @ApiModel(value = "ApprovalInstance对象", description = "审æ¹å®ä¾è¡¨") |
| | | public class ApprovalInstance implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 审æ¹å®ä¾ID |
| | | */ |
| | | @Schema(description ="审æ¹å®ä¾ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 审æ¹ç¼å· |
| | | */ |
| | | @Schema(description ="审æ¹ç¼å·") |
| | | private String instanceNo; |
| | | |
| | | /** |
| | | * 模æ¿ID |
| | | */ |
| | | @Schema(description ="模æ¿ID") |
| | | private Long templateId; |
| | | |
| | | /** |
| | | * 模æ¿åç§° |
| | | */ |
| | | @Schema(description ="模æ¿åç§°") |
| | | private String templateName; |
| | | |
| | | /** |
| | | * ä¸å¡ID |
| | | */ |
| | | @Schema(description ="ä¸å¡ID") |
| | | private Long businessId; |
| | | |
| | | /** |
| | | * ä¸å¡ç±»å |
| | | */ |
| | | @Schema(description ="ä¸å¡ç±»å") |
| | | private Long businessType; |
| | | |
| | | /** |
| | | * å®¡æ¹æ é¢ |
| | | */ |
| | | @Schema(description ="å®¡æ¹æ é¢") |
| | | private String title; |
| | | |
| | | /** |
| | | * 审æ¹ç¶æ |
| | | */ |
| | | @Schema(description ="审æ¹ç¶æ PENDING - å¾
审æ¹/è¿è¡ä¸ APPROVED - å·²éè¿/已宿 REJECTED - 已驳å") |
| | | private String status; |
| | | |
| | | /** |
| | | * å½å审æ¹çº§å« |
| | | */ |
| | | @Schema(description ="å½å审æ¹çº§å«") |
| | | private Integer currentLevel; |
| | | |
| | | /** |
| | | * ç³è¯·äººID |
| | | */ |
| | | @Schema(description ="ç³è¯·äººID") |
| | | private Long applicantId; |
| | | |
| | | /** |
| | | * ç³è¯·äººåç§° |
| | | */ |
| | | @Schema(description ="ç³è¯·äººåç§°") |
| | | private String applicantName; |
| | | |
| | | /** |
| | | * ç³è¯·æ¶é´ |
| | | */ |
| | | @Schema(description ="ç³è¯·æ¶é´") |
| | | private LocalDateTime applyTime; |
| | | |
| | | /** |
| | | * 宿æ¶é´ |
| | | */ |
| | | @Schema(description ="宿æ¶é´") |
| | | private LocalDateTime finishTime; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description ="å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description ="å建æ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * æ´æ°äºº |
| | | */ |
| | | @Schema(description ="æ´æ°äºº") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | /** |
| | | * æ´æ°æ¶é´ |
| | | */ |
| | | @Schema(description ="æ´æ°æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | /** |
| | | * é»è¾å é¤ |
| | | */ |
| | | @Schema(description ="é»è¾å é¤") |
| | | private Byte deleted; |
| | | |
| | | @Schema(description = "è¡¨åæ°æ®") |
| | | private String formConfig; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹èç¹å®ä¾è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:54 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("approval_instance_node") |
| | | @ApiModel(value = "ApprovalInstanceNode对象", description = "审æ¹èç¹å®ä¾è¡¨") |
| | | public class ApprovalInstanceNode implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * èç¹å®ä¾ID |
| | | */ |
| | | @Schema(description ="èç¹å®ä¾ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 审æ¹å®ä¾ID |
| | | */ |
| | | @Schema(description ="审æ¹å®ä¾ID") |
| | | private Long instanceId; |
| | | |
| | | /** |
| | | * 审æ¹çº§å« |
| | | */ |
| | | @Schema(description ="审æ¹çº§å«") |
| | | private Integer levelNo; |
| | | |
| | | /** |
| | | * 审æ¹ç±»å |
| | | */ |
| | | @Schema(description ="审æ¹ç±»å") |
| | | private String approveType; |
| | | |
| | | /** |
| | | * èç¹ç¶æ |
| | | */ |
| | | @Schema(description ="èç¹ç¶æ PENDING - å¾
å¤ç APPROVED - å·²éè¿ REJECTED - 已驳å") |
| | | private String status; |
| | | |
| | | /** |
| | | * å¼å§æ¶é´ |
| | | */ |
| | | @Schema(description ="å¼å§æ¶é´") |
| | | private LocalDateTime startTime; |
| | | |
| | | /** |
| | | * 宿æ¶é´ |
| | | */ |
| | | @Schema(description ="宿æ¶é´") |
| | | private LocalDateTime finishTime; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description ="å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description ="å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * æ´æ°äºº |
| | | */ |
| | | @Schema(description ="æ´æ°äºº") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | /** |
| | | * æ´æ°æ¶é´ |
| | | */ |
| | | @Schema(description ="æ´æ°æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | /** |
| | | * é»è¾å é¤ |
| | | */ |
| | | @Schema(description ="é»è¾å é¤") |
| | | private Byte deleted; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹è®°å½è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:28:21 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("approval_record") |
| | | @ApiModel(value = "ApprovalRecord对象", description = "审æ¹è®°å½è¡¨") |
| | | public class ApprovalRecord implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 审æ¹è®°å½ID |
| | | */ |
| | | @Schema(description ="审æ¹è®°å½ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 审æ¹å®ä¾ID |
| | | */ |
| | | @Schema(description ="审æ¹å®ä¾ID") |
| | | private Long instanceId; |
| | | |
| | | /** |
| | | * èç¹å®ä¾ID |
| | | */ |
| | | @Schema(description ="èç¹å®ä¾ID") |
| | | private Long nodeId; |
| | | |
| | | /** |
| | | * 审æ¹ä»»å¡ID |
| | | */ |
| | | @Schema(description ="审æ¹ä»»å¡ID") |
| | | private Long taskId; |
| | | |
| | | /** |
| | | * æä½äººID |
| | | */ |
| | | @Schema(description ="æä½äººID") |
| | | private Long operatorId; |
| | | |
| | | /** |
| | | * æä½äººåç§° |
| | | */ |
| | | @Schema(description ="æä½äººåç§°") |
| | | private String operatorName; |
| | | |
| | | /** |
| | | * æä½ç±»å |
| | | */ |
| | | @Schema(description ="æä½ç±»å") |
| | | private String action; |
| | | |
| | | /** |
| | | * å®¡æ¹æè§ |
| | | */ |
| | | @Schema(description ="å®¡æ¹æè§") |
| | | private String comment; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description ="å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description ="å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * é»è¾å é¤ |
| | | */ |
| | | @Schema(description ="é»è¾å é¤") |
| | | private Byte deleted; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹ä»»å¡è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:32:37 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("approval_task") |
| | | @ApiModel(value = "ApprovalTask对象", description = "审æ¹ä»»å¡è¡¨") |
| | | public class ApprovalTask implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 审æ¹ä»»å¡ID |
| | | */ |
| | | @Schema(description ="审æ¹ä»»å¡ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 审æ¹å®ä¾ID |
| | | */ |
| | | @Schema(description ="审æ¹å®ä¾ID") |
| | | private Long instanceId; |
| | | |
| | | /** |
| | | * èç¹å®ä¾ID |
| | | */ |
| | | @Schema(description ="èç¹å®ä¾ID") |
| | | private Long nodeId; |
| | | |
| | | /** |
| | | * 审æ¹çº§å« |
| | | */ |
| | | @Schema(description ="审æ¹çº§å«") |
| | | private Integer levelNo; |
| | | |
| | | /** |
| | | * 审æ¹äººID |
| | | */ |
| | | @Schema(description ="审æ¹äººID") |
| | | private Long approverId; |
| | | |
| | | /** |
| | | * 审æ¹äººåç§° |
| | | */ |
| | | @Schema(description ="审æ¹äººåç§°") |
| | | private String approverName; |
| | | |
| | | /** |
| | | * ä»»å¡ç¶æ |
| | | */ |
| | | @Schema(description ="ä»»å¡ç¶æ PENDING - å¾
å®¡æ¹ APPROVED - å·²åæ REJECTED - å·²æç»") |
| | | private String taskStatus; |
| | | |
| | | /** |
| | | * å®¡æ¹æ¶é´ |
| | | */ |
| | | @Schema(description ="å®¡æ¹æ¶é´") |
| | | private LocalDateTime approveTime; |
| | | |
| | | /** |
| | | * å®¡æ¹æè§ |
| | | */ |
| | | @Schema(description ="å®¡æ¹æè§") |
| | | private String comment; |
| | | |
| | | /** |
| | | * æ¯å¦å·²è¯» |
| | | */ |
| | | @Schema(description ="æ¯å¦å·²è¯»") |
| | | private Byte isRead; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description ="å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description ="å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * æ´æ°äºº |
| | | */ |
| | | @Schema(description ="æ´æ°äºº") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | /** |
| | | * æ´æ°æ¶é´ |
| | | */ |
| | | @Schema(description ="æ´æ°æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | /** |
| | | * é»è¾å é¤ |
| | | */ |
| | | @Schema(description ="é»è¾å é¤") |
| | | private Byte deleted; |
| | | |
| | | @Schema(description ="é¨é¨ID") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:08 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("approval_template") |
| | | @ApiModel(value = "ApprovalTemplate对象", description = "å®¡æ¹æ¨¡æ¿è¡¨") |
| | | public class ApprovalTemplate implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 模æ¿ID |
| | | */ |
| | | @Schema(description ="模æ¿ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 模æ¿åç§° |
| | | */ |
| | | @Schema(description ="模æ¿åç§°") |
| | | private String templateName; |
| | | |
| | | /** |
| | | * å¯ç¨ç¶æï¼1å¯ç¨ï¼0åç¨ |
| | | */ |
| | | @Schema(description ="å¯ç¨ç¶æï¼1å¯ç¨ï¼0åç¨") |
| | | private Byte enabled; |
| | | |
| | | /** |
| | | * 模æ¿è¯´æ |
| | | */ |
| | | @Schema(description ="模æ¿è¯´æ") |
| | | private String description; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description ="å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description ="å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * æ´æ°äºº |
| | | */ |
| | | @Schema(description ="æ´æ°äºº") |
| | | @TableField(fill = FieldFill.UPDATE) |
| | | private Long updateUser; |
| | | |
| | | /** |
| | | * æ´æ°æ¶é´ |
| | | */ |
| | | @Schema(description ="æ´æ°æ¶é´") |
| | | @TableField(fill = FieldFill.UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime updateTime; |
| | | |
| | | /** |
| | | * é»è¾å é¤ï¼0å¦ï¼1æ¯ |
| | | */ |
| | | @Schema(description ="é»è¾å é¤ï¼0å¦ï¼1æ¯") |
| | | private Integer deleted; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | |
| | | @Schema(description = "表åé
ç½®") |
| | | private String formConfig; |
| | | |
| | | @Schema(description = "模æ¿ç±»åï¼0ç³»ç»å
ç½®ï¼1èªå®ä¹") |
| | | private Integer templateType; |
| | | |
| | | @Schema(description = "ä¸å¡ç±»å") |
| | | private Long businessType; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:19 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("approval_template_node") |
| | | @ApiModel(value = "ApprovalTemplateNode对象", description = "å®¡æ¹æ¨¡æ¿èç¹è¡¨") |
| | | public class ApprovalTemplateNode implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * èç¹ID |
| | | */ |
| | | @Schema(description ="èç¹ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * å®¡æ¹æ¨¡æ¿ID |
| | | */ |
| | | @Schema(description ="å®¡æ¹æ¨¡æ¿ID") |
| | | private Long templateId; |
| | | |
| | | /** |
| | | * 审æ¹çº§å«ï¼ä»1å¼å§ |
| | | */ |
| | | @Schema(description ="审æ¹çº§å«ï¼ä»1å¼å§") |
| | | private Integer levelNo; |
| | | |
| | | /** |
| | | * å®¡æ¹æ¹å¼ï¼ANDä¼ç¾ï¼ORæç¾ |
| | | */ |
| | | @Schema(description ="å®¡æ¹æ¹å¼ï¼ANDä¼ç¾ï¼ORæç¾") |
| | | private String approveType; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | @TableField(fill = FieldFill.UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹å®¡æ¹äººè¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:30 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("approval_template_node_approver") |
| | | @ApiModel(value = "ApprovalTemplateNodeApprover对象", description = "å®¡æ¹æ¨¡æ¿èç¹å®¡æ¹äººè¡¨") |
| | | public class ApprovalTemplateNodeApprover implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 主é®ID |
| | | */ |
| | | @Schema(description = "主é®ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 审æ¹èç¹ID |
| | | */ |
| | | @Schema(description ="审æ¹èç¹ID") |
| | | private Long nodeId; |
| | | |
| | | /** |
| | | * å®¡æ¹æ¨¡æ¿ID |
| | | */ |
| | | @Schema(description ="å®¡æ¹æ¨¡æ¿ID") |
| | | private Long templateId; |
| | | |
| | | /** |
| | | * 审æ¹äººID |
| | | */ |
| | | @Schema(description ="审æ¹äººID") |
| | | private Long approverId; |
| | | |
| | | /** |
| | | * 审æ¹äººåç§°åä½ |
| | | */ |
| | | @Schema(description ="审æ¹äººåç§°åä½") |
| | | private String approverName; |
| | | |
| | | /** |
| | | * 审æ¹äººæåº |
| | | */ |
| | | @Schema(description ="审æ¹äººæåº") |
| | | private Integer sortNo; |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deleted ; |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éå主表 |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:15 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("fin_reimbursement") |
| | | @ApiModel(value = "FinReimbursement对象", description = "æ¥éå主表") |
| | | public class FinReimbursement implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 主é®ID |
| | | */ |
| | | @Schema(description = "主é®ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * æ¥éåå· |
| | | */ |
| | | @Schema(description = "æ¥éåå·") |
| | | private String billNo; |
| | | |
| | | /** |
| | | * æ¥éç±»åï¼1-å·®æ
æ¥éï¼2-è´¹ç¨æ¥é |
| | | */ |
| | | @Schema(description = "æ¥éç±»åï¼1-å·®æ
æ¥éï¼2-è´¹ç¨æ¥é") |
| | | private Byte reimbursementType; |
| | | |
| | | /** |
| | | * è´¹ç¨ç±»åï¼å·®æ
è´¹/åå
¬éè´/ä¸å¡æå¾
/交éè´¹/é讯费/å
¶ä» |
| | | */ |
| | | @Schema(description = "è´¹ç¨ç±»åï¼å·®æ
è´¹/åå
¬éè´/ä¸å¡æå¾
/交éè´¹/é讯费/å
¶ä»") |
| | | private String expenseType; |
| | | |
| | | /** |
| | | * ç³è¯·äººID |
| | | */ |
| | | @Schema(description = "ç³è¯·äººID") |
| | | private Long applicantId; |
| | | |
| | | /** |
| | | * åå·¥ç¼å· |
| | | */ |
| | | @Schema(description = "åå·¥ç¼å·") |
| | | private String applicantCode; |
| | | |
| | | /** |
| | | * åå·¥å§å |
| | | */ |
| | | @Schema(description = "åå·¥å§å") |
| | | private String applicantName; |
| | | |
| | | /** |
| | | * ç³è¯·é¨é¨ID |
| | | */ |
| | | @Schema(description = "ç³è¯·é¨é¨ID") |
| | | private Long applicantDeptId; |
| | | |
| | | /** |
| | | * ç³è¯·é¨é¨åç§° |
| | | */ |
| | | @Schema(description = "ç³è¯·é¨é¨åç§°") |
| | | private String applicantDeptName; |
| | | |
| | | /** |
| | | * æ¥éåå |
| | | */ |
| | | @Schema(description = "æ¥éåå ") |
| | | private String reason; |
| | | |
| | | /** |
| | | * ç³è¯·éé¢ |
| | | */ |
| | | @Schema(description = "ç³è¯·éé¢") |
| | | private BigDecimal applyAmount; |
| | | |
| | | /** |
| | | * æç»æ±æ»éé¢ |
| | | */ |
| | | @Schema(description = "æç»æ±æ»éé¢") |
| | | private BigDecimal detailTotalAmount; |
| | | |
| | | /** |
| | | * æ¶æ¬¾äºº |
| | | */ |
| | | @Schema(description = "æ¶æ¬¾äºº") |
| | | private String payeeName; |
| | | |
| | | /** |
| | | * æ¶æ¬¾è´¦å· |
| | | */ |
| | | @Schema(description = "æ¶æ¬¾è´¦å·") |
| | | private String payeeAccount; |
| | | |
| | | /** |
| | | * 弿·æ¯è¡ |
| | | */ |
| | | @Schema(description = "弿·æ¯è¡") |
| | | private String payeeBank; |
| | | |
| | | /** |
| | | * 审æ¹å®ä¾IDï¼å¯¹åº approval_instance.id |
| | | */ |
| | | @Schema(description = "审æ¹å®ä¾IDï¼å¯¹åº approval_instance.id") |
| | | private Long approvalInstanceId; |
| | | |
| | | /** |
| | | * å®¡æ¹æµç¨IDï¼å¯¹åº approve_process.id |
| | | */ |
| | | @Schema(description = "å®¡æ¹æµç¨IDï¼å¯¹åº approve_process.id") |
| | | private Long approveProcessId; |
| | | |
| | | /** |
| | | * åæ®ç¶æï¼DRAFT-è稿ï¼IN_APPROVAL-审æ¹ä¸ï¼APPROVED-审æ¹éè¿ï¼REJECTED-审æ¹é©³åï¼WITHDRAWN-å·²æ¤åï¼PAID-已仿¬¾ |
| | | */ |
| | | @Schema(description = "åæ®ç¶æï¼DRAFT-è稿ï¼IN_APPROVAL-审æ¹ä¸ï¼APPROVED-审æ¹éè¿ï¼REJECTED-审æ¹é©³åï¼WITHDRAWN-å·²æ¤åï¼PAID-已仿¬¾") |
| | | private String billStatus; |
| | | |
| | | /** |
| | | * 审æ¹éè¿æ¶é´ |
| | | */ |
| | | @Schema(description = "审æ¹éè¿æ¶é´") |
| | | private LocalDateTime approvedTime; |
| | | |
| | | /** |
| | | * 仿¬¾æ¶é´ |
| | | */ |
| | | @Schema(description = "仿¬¾æ¶é´") |
| | | private LocalDateTime paidTime; |
| | | |
| | | /** |
| | | * çæçè´¢å¡æ¯åºè®°å½IDï¼å¯¹åº account_expense.id |
| | | */ |
| | | @Schema(description = "çæçè´¢å¡æ¯åºè®°å½IDï¼å¯¹åº account_expense.id") |
| | | private Long accountExpenseId; |
| | | |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | /** |
| | | * ç§æ·ID |
| | | */ |
| | | @Schema(description = "ç§æ·ID") |
| | | private Long tenantId; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description = "å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description = "å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * æ´æ°äºº |
| | | */ |
| | | @Schema(description = "æ´æ°äºº") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | /** |
| | | * æ´æ°æ¶é´ |
| | | */ |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | /** |
| | | * å½å±é¨é¨ID |
| | | */ |
| | | @Schema(description = "å½å±é¨é¨ID") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | |
| | | /** |
| | | * é»è¾å é¤ï¼0-å¦ï¼1-æ¯ |
| | | */ |
| | | @Schema(description = "é»è¾å é¤ï¼0-å¦ï¼1-æ¯") |
| | | private Byte deleted; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éåæç»è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:38 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("fin_reimbursement_detail") |
| | | @ApiModel(value = "FinReimbursementDetail对象", description = "æ¥éåæç»è¡¨") |
| | | public class FinReimbursementDetail implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 主é®ID |
| | | */ |
| | | @Schema(description = "主é®ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * æ¥éåIDï¼å¯¹åº fin_reimbursement.id |
| | | */ |
| | | @Schema(description = "æ¥éåIDï¼å¯¹åº fin_reimbursement.id") |
| | | private Long reimbursementId; |
| | | |
| | | /** |
| | | * æç»è¡å· |
| | | */ |
| | | @Schema(description = "æç»è¡å·") |
| | | private Integer rowNo; |
| | | |
| | | /** |
| | | * åç¥¨æ¥æ |
| | | */ |
| | | @Schema(description = "åç¥¨æ¥æ") |
| | | private LocalDate invoiceDate; |
| | | |
| | | /** |
| | | * è´¹ç¨ç§ç® |
| | | */ |
| | | @Schema(description = "è´¹ç¨ç§ç®") |
| | | private String expenseCategory; |
| | | |
| | | /** |
| | | * éé¢ |
| | | */ |
| | | @Schema(description = "éé¢") |
| | | private BigDecimal amount; |
| | | |
| | | /** |
| | | * æè¿° |
| | | */ |
| | | @Schema(description = "æè¿°") |
| | | private String description; |
| | | |
| | | /** |
| | | * å票å·ç |
| | | */ |
| | | @Schema(description = "å票å·ç ") |
| | | private String invoiceNo; |
| | | |
| | | /** |
| | | * å票类å |
| | | */ |
| | | @Schema(description = "å票类å") |
| | | private String invoiceType; |
| | | |
| | | /** |
| | | * 票é¢éé¢ |
| | | */ |
| | | @Schema(description = "票é¢éé¢") |
| | | private BigDecimal invoiceAmount; |
| | | |
| | | /** |
| | | * ç¨ç |
| | | */ |
| | | @Schema(description = "ç¨ç") |
| | | private BigDecimal taxRate; |
| | | |
| | | /** |
| | | * ç¨é¢ |
| | | */ |
| | | @Schema(description = "ç¨é¢") |
| | | private BigDecimal taxAmount; |
| | | |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | /** |
| | | * ç§æ·ID |
| | | */ |
| | | @Schema(description = "ç§æ·ID") |
| | | private Long tenantId; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description = "å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * æ´æ°äºº |
| | | */ |
| | | @Schema(description = "æ´æ°äºº") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | /** |
| | | * æ´æ°æ¶é´ |
| | | */ |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | /** |
| | | * å½å±é¨é¨ID |
| | | */ |
| | | @Schema(description = "å½å±é¨é¨ID") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | |
| | | /** |
| | | * é»è¾å é¤ï¼0-å¦ï¼1-æ¯ |
| | | */ |
| | | @Schema(description = "é»è¾å é¤ï¼0-å¦ï¼1-æ¯") |
| | | private Byte deleted; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | 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-21 09:56:47 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("fin_reimbursement_travel") |
| | | @ApiModel(value = "FinReimbursementTravel对象", description = "å·®æ
æ¥éæ©å±è¡¨") |
| | | public class FinReimbursementTravel implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 主é®ID |
| | | */ |
| | | @Schema(description = "主é®ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * æ¥éåIDï¼å¯¹åº fin_reimbursement.id |
| | | */ |
| | | @Schema(description = "æ¥éåIDï¼å¯¹åº fin_reimbursement.id") |
| | | private Long reimbursementId; |
| | | |
| | | /** |
| | | * åºå·®å¼å§æ¶é´ |
| | | */ |
| | | @Schema(description = "åºå·®å¼å§æ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime startTime; |
| | | |
| | | /** |
| | | * åºå·®ç»ææ¶é´ |
| | | */ |
| | | @Schema(description = "åºå·®ç»ææ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime endTime; |
| | | |
| | | /** |
| | | * åºå·®å¤©æ° |
| | | */ |
| | | @Schema(description = "åºå·®å¤©æ°") |
| | | private BigDecimal travelDays; |
| | | |
| | | /** |
| | | * åºå·®å°/åºååå¸ |
| | | */ |
| | | @Schema(description = "åºå·®å°/åºååå¸") |
| | | private String departureCity; |
| | | |
| | | /** |
| | | * ç®çå°/ç®çåå¸ |
| | | */ |
| | | @Schema(description = "ç®çå°/ç®çåå¸") |
| | | private String destinationCity; |
| | | |
| | | /** |
| | | * é
åºæ å |
| | | */ |
| | | @Schema(description = "é
åºæ å") |
| | | private BigDecimal hotelStandard; |
| | | |
| | | /** |
| | | * ä½å®¿å¤©æ° |
| | | */ |
| | | @Schema(description = "ä½å®¿å¤©æ°") |
| | | private BigDecimal lodgingDays; |
| | | |
| | | /** |
| | | * çæ´»è¡¥è´´ |
| | | */ |
| | | @Schema(description = "çæ´»è¡¥è´´") |
| | | private BigDecimal mealAllowance; |
| | | |
| | | /** |
| | | * 交é补贴 |
| | | */ |
| | | @Schema(description = "交é补贴") |
| | | private BigDecimal transportAllowance; |
| | | |
| | | /** |
| | | * ä½å®¿éé¢ |
| | | */ |
| | | @Schema(description = "ä½å®¿éé¢") |
| | | private BigDecimal lodgingLimit; |
| | | |
| | | /** |
| | | * ç¹æ¹æ è®°ææ¬ï¼å¦å¨æ åèå´å
/è¶
æ ç¹æ¹ |
| | | */ |
| | | @Schema(description = "ç¹æ¹æ è®°ææ¬ï¼å¦å¨æ åèå´å
/è¶
æ ç¹æ¹") |
| | | private String standardTag; |
| | | |
| | | /** |
| | | * æ¯å¦å¨æ åå
ï¼1-æ¯ï¼0-å¦ |
| | | */ |
| | | @Schema(description = "æ¯å¦å¨æ åå
ï¼1-æ¯ï¼0-å¦") |
| | | private Byte withinStandard; |
| | | |
| | | /** |
| | | * ç§æ·ID |
| | | */ |
| | | @Schema(description = "ç§æ·ID") |
| | | private Long tenantId; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @Schema(description = "å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * æ´æ°äºº |
| | | */ |
| | | @Schema(description = "æ´æ°äºº") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | /** |
| | | * æ´æ°æ¶é´ |
| | | */ |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | /** |
| | | * å½å±é¨é¨ID |
| | | */ |
| | | @Schema(description = "å½å±é¨é¨ID") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalInstanceNode; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹èç¹å®ä¾è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:54 |
| | | */ |
| | | public interface ApprovalInstanceNodeService extends IService<ApprovalInstanceNode> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalInstanceVo; |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹å®ä¾è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:46 |
| | | */ |
| | | public interface ApprovalInstanceService extends IService<ApprovalInstance> { |
| | | |
| | | R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto); |
| | | |
| | | Boolean add(ApprovalInstanceDto approvalInstanceDto); |
| | | |
| | | Boolean update(ApprovalInstanceDto approvalInstanceDto); |
| | | |
| | | Boolean delete(List<Long> ids); |
| | | |
| | | R approve(ApprovalInstanceDto approvalInstanceDto); |
| | | |
| | | R autoApprove(Long instanceId); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalRecord; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹è®°å½è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:28:21 |
| | | */ |
| | | public interface ApprovalRecordService extends IService<ApprovalRecord> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTask; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹ä»»å¡è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:32:37 |
| | | */ |
| | | public interface ApprovalTaskService extends IService<ApprovalTask> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹å®¡æ¹äººè¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:30 |
| | | */ |
| | | public interface ApprovalTemplateNodeApproverService extends IService<ApprovalTemplateNodeApprover> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeDto; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNode; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:19 |
| | | */ |
| | | public interface ApprovalTemplateNodeService extends IService<ApprovalTemplateNode> { |
| | | |
| | | Boolean saveApprovalTemplateNode(Long id, List<ApprovalTemplateNodeDto> nodes); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateVo; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:08 |
| | | */ |
| | | public interface ApprovalTemplateService extends IService<ApprovalTemplate> { |
| | | |
| | | IPage<ApprovalTemplateVo> listPage(Page<ApprovalTemplateVo> page, ApprovalTemplateDto approvalTemplateDto); |
| | | |
| | | Boolean saveApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto); |
| | | |
| | | Boolean updateApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto); |
| | | |
| | | Boolean delete(List<Long> ids); |
| | | |
| | | List<ApprovalTemplateVo> listApprovalTemplateVo(Integer type); |
| | | |
| | | ApprovalTemplateVo getApprovalTemplateVoById(Long id); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.ruoyi.approve.pojo.FinReimbursementDetail; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éåæç»è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:38 |
| | | */ |
| | | public interface FinReimbursementDetailService extends IService<FinReimbursementDetail> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.approve.bean.dto.FinReimbursementDto; |
| | | import com.ruoyi.approve.bean.vo.FinReimbursementVo; |
| | | import com.ruoyi.approve.pojo.FinReimbursement; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éå主表 æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:15 |
| | | */ |
| | | public interface FinReimbursementService extends IService<FinReimbursement> { |
| | | |
| | | IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page); |
| | | |
| | | Boolean add(FinReimbursementDto finReimbursementDto); |
| | | |
| | | FinReimbursementVo detail(Long id); |
| | | |
| | | Boolean update(FinReimbursementDto finReimbursementDto); |
| | | |
| | | Boolean delete(List<Long> ids); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service; |
| | | |
| | | import com.ruoyi.approve.pojo.FinReimbursementTravel; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * å·®æ
æ¥éæ©å±è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:47 |
| | | */ |
| | | public interface FinReimbursementTravelService extends IService<FinReimbursementTravel> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalInstanceNode; |
| | | import com.ruoyi.approve.mapper.ApprovalInstanceNodeMapper; |
| | | import com.ruoyi.approve.service.ApprovalInstanceNodeService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹èç¹å®ä¾è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:27:54 |
| | | */ |
| | | @Service |
| | | public class ApprovalInstanceNodeServiceImpl extends ServiceImpl<ApprovalInstanceNodeMapper, ApprovalInstanceNode> implements ApprovalInstanceNodeService { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalInstanceVo; |
| | | import com.ruoyi.approve.mapper.ApprovalInstanceMapper; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper; |
| | | import com.ruoyi.approve.mapper.FinReimbursementMapper; |
| | | import com.ruoyi.approve.pojo.*; |
| | | import com.ruoyi.approve.service.*; |
| | | import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper; |
| | | import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper; |
| | | import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser; |
| | | import com.ruoyi.common.enums.*; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | import com.ruoyi.project.system.mapper.SysDeptMapper; |
| | | import com.ruoyi.project.system.mapper.SysUserDeptMapper; |
| | | 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.quality.utils.QualityInspectHelper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.mapper.SalesQuotationMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.SalesQuotation; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import com.ruoyi.staff.mapper.HolidayApplicationMapper; |
| | | import com.ruoyi.staff.pojo.HolidayApplication; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹å®ä¾æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @since 2026-05-18 03:27:46 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class ApprovalInstanceServiceImpl extends ServiceImpl<ApprovalInstanceMapper, ApprovalInstance> implements ApprovalInstanceService { |
| | | |
| | | private static final String ENTERPRISE_NEWS_STATUS_PUBLISHED = "PUBLISHED"; |
| | | private static final String ENTERPRISE_NEWS_STATUS_REJECTED = "REJECTED"; |
| | | |
| | | private final ApprovalInstanceMapper approvalInstanceMapper; |
| | | private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils; |
| | | private final ApprovalInstanceNodeService approvalInstanceNodeService; |
| | | private final ApprovalTaskService approvalTaskService; |
| | | private final ApprovalRecordService approvalRecordService; |
| | | private final ApprovalTemplateNodeService approvalTemplateNodeService; |
| | | private final FinReimbursementMapper finReimbursementMapper; |
| | | private final FileUtil fileUtil; |
| | | private final ISysNoticeService sysNoticeService; |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final StockUtils stockUtils; |
| | | private final SalesQuotationMapper salesQuotationMapper; |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | private final QualityInspectHelper qualityInspectHelper; |
| | | private final EnterpriseNewsScopeUserMapper enterpriseNewsScopeUserMapper; |
| | | private final SysUserMapper sysUserMapper; |
| | | private final SysUserDeptMapper sysUserDeptMapper; |
| | | private final SysDeptMapper sysDeptMapper; |
| | | private final HolidayApplicationMapper holidayApplicationMapper; |
| | | private final EnterpriseNewsMapper enterpriseNewsMapper; |
| | | private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper; |
| | | private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper; |
| | | |
| | | @Override |
| | | public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) { |
| | | IPage<ApprovalInstanceVo> approvalInstanceVoIPage = approvalInstanceMapper.listPage(page, approvalInstanceDto); |
| | | |
| | | List<ApprovalInstanceVo> records = approvalInstanceVoIPage.getRecords(); |
| | | if (records == null || records.isEmpty()) { |
| | | return R.ok(approvalInstanceVoIPage); |
| | | } |
| | | records.forEach(vo -> { |
| | | vo.setBusinessName(TypeEnums.getLabelByValue(vo.getBusinessType())); |
| | | }); |
| | | Long currentUserId = SecurityUtils.getUserId(); |
| | | |
| | | List<Long> instanceIds = records.stream() |
| | | .map(ApprovalInstanceVo::getId) |
| | | .filter(id -> id != null) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!instanceIds.isEmpty()) { |
| | | Map<Long, List<ApprovalRecord>> recordMap = approvalRecordService.list( |
| | | Wrappers.<ApprovalRecord>lambdaQuery() |
| | | .in(ApprovalRecord::getInstanceId, instanceIds) |
| | | .eq(ApprovalRecord::getDeleted, 0) |
| | | ).stream().collect(Collectors.groupingBy(ApprovalRecord::getInstanceId)); |
| | | Map<Long, List<ApprovalTask>> taskMap = approvalTaskService.list( |
| | | Wrappers.<ApprovalTask>lambdaQuery() |
| | | .in(ApprovalTask::getInstanceId, instanceIds) |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | ).stream().collect(Collectors.groupingBy(ApprovalTask::getInstanceId)); |
| | | |
| | | for (ApprovalInstanceVo vo : records) { |
| | | vo.setIsApprove(approveProcessConfigNodeUtils.isCurrentApprover(vo.getId(), currentUserId)); |
| | | vo.setRecords(recordMap.getOrDefault(vo.getId(), new ArrayList<>())); |
| | | vo.setTasks(taskMap.getOrDefault(vo.getId(), new ArrayList<>())); |
| | | vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.APPROVAL_INSTANCE, vo.getId())); |
| | | } |
| | | |
| | | } |
| | | return R.ok(approvalInstanceVoIPage); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean add(ApprovalInstanceDto approvalInstanceDto) { |
| | | String instanceNo = OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no", approvalInstanceDto.getCreateTime() != null ? approvalInstanceDto.getCreateTime() : LocalDateTime.now()); |
| | | approvalInstanceDto.setInstanceNo(instanceNo); |
| | | approvalInstanceDto.setStatus("PENDING"); |
| | | approvalInstanceDto.setCurrentLevel(1); |
| | | boolean saved = this.save(approvalInstanceDto); |
| | | if (!saved) { |
| | | return false; |
| | | } |
| | | approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstanceDto); |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs()); |
| | | sendApproveNotice(approvalInstanceDto, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId())); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean update(ApprovalInstanceDto approvalInstanceDto) { |
| | | if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) { |
| | | return false; |
| | | } |
| | | // 夿æ¯å¦ææ£å¨è¿è¡ç审æ¹ä»»å¡ï¼æåä¸å
è®¸ä¿®æ¹ |
| | | long pendingTaskCount = approvalTaskService.count( |
| | | Wrappers.<ApprovalTask>lambdaQuery() |
| | | .eq(ApprovalTask::getInstanceId, approvalInstanceDto.getId()) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | ); |
| | | if (pendingTaskCount > 0) { |
| | | throw new ServiceException("该审æ¹åææ£å¨è¿è¡ç审æ¹ä»»å¡ï¼ä¸å
许修æ¹"); |
| | | } |
| | | boolean updated = this.updateById(approvalInstanceDto); |
| | | if (!updated) { |
| | | return false; |
| | | } |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs()); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean delete(List<Long> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | return false; |
| | | } |
| | | fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, ids); |
| | | |
| | | int instanceRows = approvalInstanceMapper.update( |
| | | null, |
| | | Wrappers.<ApprovalInstance>lambdaUpdate() |
| | | .in(ApprovalInstance::getId, ids) |
| | | .eq(ApprovalInstance::getDeleted, 0) |
| | | .set(ApprovalInstance::getDeleted, (byte) 1) |
| | | ); |
| | | |
| | | LambdaUpdateWrapper<ApprovalInstanceNode> nodeUpdateWrapper = Wrappers.lambdaUpdate(); |
| | | nodeUpdateWrapper.in(ApprovalInstanceNode::getInstanceId, ids) |
| | | .eq(ApprovalInstanceNode::getDeleted, 0) |
| | | .set(ApprovalInstanceNode::getDeleted, (byte) 1); |
| | | approvalInstanceNodeService.update(nodeUpdateWrapper); |
| | | |
| | | LambdaUpdateWrapper<ApprovalTask> taskUpdateWrapper = Wrappers.lambdaUpdate(); |
| | | taskUpdateWrapper.in(ApprovalTask::getInstanceId, ids) |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | .set(ApprovalTask::getDeleted, (byte) 1); |
| | | approvalTaskService.update(taskUpdateWrapper); |
| | | |
| | | LambdaUpdateWrapper<ApprovalRecord> recordUpdateWrapper = Wrappers.lambdaUpdate(); |
| | | recordUpdateWrapper.in(ApprovalRecord::getInstanceId, ids) |
| | | .eq(ApprovalRecord::getDeleted, 0) |
| | | .set(ApprovalRecord::getDeleted, (byte) 1); |
| | | approvalRecordService.update(recordUpdateWrapper); |
| | | |
| | | return instanceRows > 0; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R approve(ApprovalInstanceDto approvalInstanceDto) { |
| | | if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) { |
| | | return R.fail("审æ¹å®ä¾ ID ä¸è½ä¸ºç©º"); |
| | | } |
| | | String approveAction = normalizeApproveAction(approvalInstanceDto.getApproveAction()); |
| | | if (approveAction == null) { |
| | | return R.fail("审æ¹å¨ä½åªæ¯æ APPROVED æ REJECTED"); |
| | | } |
| | | |
| | | ApprovalInstance instance = getPendingApprovalInstance(approvalInstanceDto.getId()); |
| | | if (instance == null) { |
| | | return R.fail("审æ¹å®ä¾ä¸åå¨"); |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = approveProcessConfigNodeUtils.getCurrentNode(instance.getId()); |
| | | if (currentNode == null) { |
| | | return R.fail("å½å没æå¾
å¤çç审æ¹èç¹"); |
| | | } |
| | | |
| | | Long currentUserId = SecurityUtils.getUserId(); |
| | | ApprovalTask currentTask = approveProcessConfigNodeUtils.getCurrentUserTask(instance.getId(), currentUserId); |
| | | if (currentTask == null) { |
| | | return R.fail("å½åç¨æ·æ²¡æå¯å®¡æ¹ä»»å¡"); |
| | | } |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | String operatorName = loginUser.getUser() != null ? loginUser.getUser().getNickName() : SecurityUtils.getUsername(); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | |
| | | if (!updateCurrentTask(approvalInstanceDto, approveAction, currentTask, now)) { |
| | | return R.fail("å½åä»»å¡å·²è¢«å¤çï¼è¯·å·æ°åéè¯"); |
| | | } |
| | | |
| | | saveApprovalRecord( |
| | | instance.getId(), |
| | | currentNode.getId(), |
| | | currentTask.getId(), |
| | | currentUserId, |
| | | operatorName, |
| | | approveAction, |
| | | approvalInstanceDto.getApproveComment() |
| | | ); |
| | | //å®¡æ¹æç»çå¤ç |
| | | if ("REJECTED".equals(approveAction)) { |
| | | return rejectCurrentNode(instance, currentNode, now); |
| | | } |
| | | if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) { |
| | | return R.ok("å®¡æ¹æåï¼çå¾
å
¶ä»å®¡æ¹äººå¤ç"); |
| | | } |
| | | |
| | | return approveAndMoveNext(instance, currentNode, approvalInstanceDto, now); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R autoApprove(Long instanceId) { |
| | | if (instanceId == null) { |
| | | return R.fail("审æ¹å®ä¾ ID ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | ApprovalInstance instance = getPendingApprovalInstance(instanceId); |
| | | if (instance == null) { |
| | | return R.fail("审æ¹å®ä¾ä¸åå¨"); |
| | | } |
| | | if ("REJECTED".equals(instance.getStatus())) { |
| | | return R.fail("审æ¹å·²é©³åï¼æ æ³èªå¨éè¿"); |
| | | } |
| | | if ("APPROVED".equals(instance.getStatus())) { |
| | | return R.ok("审æ¹å·²å®æ"); |
| | | } |
| | | |
| | | ApprovalInstanceDto autoApproveDto = new ApprovalInstanceDto(); |
| | | autoApproveDto.setId(instanceId); |
| | | autoApproveDto.setApproveComment("ç³»ç»èªå¨å®¡æ¹"); |
| | | |
| | | int loopCount = 0; |
| | | while (loopCount++ < 20) { |
| | | ApprovalInstance currentInstance = getPendingApprovalInstance(instanceId); |
| | | if (currentInstance == null) { |
| | | return R.fail("审æ¹å®ä¾ä¸åå¨"); |
| | | } |
| | | if ("APPROVED".equals(currentInstance.getStatus())) { |
| | | return R.ok("审æ¹å·²å®æ"); |
| | | } |
| | | if ("REJECTED".equals(currentInstance.getStatus())) { |
| | | return R.fail("审æ¹å·²é©³åï¼æ æ³èªå¨éè¿"); |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = approveProcessConfigNodeUtils.getCurrentNode(currentInstance.getId()); |
| | | if (currentNode == null) { |
| | | currentInstance.setStatus("APPROVED"); |
| | | currentInstance.setFinishTime(LocalDateTime.now()); |
| | | this.updateById(currentInstance); |
| | | handleBusinessAfterApprovalFinished(currentInstance); |
| | | return R.ok("审æ¹å·²å®æ"); |
| | | } |
| | | |
| | | List<ApprovalTask> pendingTasks = approvalTaskService.list( |
| | | Wrappers.<ApprovalTask>lambdaQuery() |
| | | .eq(ApprovalTask::getInstanceId, currentInstance.getId()) |
| | | .eq(ApprovalTask::getNodeId, currentNode.getId()) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | ); |
| | | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | for (ApprovalTask currentTask : pendingTasks) { |
| | | if (!updateCurrentTask(autoApproveDto, "APPROVED", currentTask, now)) { |
| | | return R.fail("å½åä»»å¡å·²è¢«å¤çï¼è¯·å·æ°åéè¯"); |
| | | } |
| | | saveApprovalRecord( |
| | | currentInstance.getId(), |
| | | currentNode.getId(), |
| | | currentTask.getId(), |
| | | 0L, |
| | | "ç³»ç»èªå¨å®¡æ¹", |
| | | "APPROVED", |
| | | autoApproveDto.getApproveComment() |
| | | ); |
| | | } |
| | | |
| | | if (!approveProcessConfigNodeUtils.canProceedToNextLevel(currentInstance.getId(), currentNode.getApproveType())) { |
| | | return R.ok("å®¡æ¹æåï¼çå¾
å
¶ä»å®¡æ¹äººå¤ç"); |
| | | } |
| | | |
| | | R moveResult = moveToNextLevel(currentInstance, currentNode, autoApproveDto, now, false); |
| | | if (!R.isSuccess(moveResult)) { |
| | | return moveResult; |
| | | } |
| | | } |
| | | |
| | | return R.fail("èªå¨å®¡æ¹å¾ªç¯æ¬¡æ°è¶
é"); |
| | | } |
| | | |
| | | private String normalizeApproveAction(String approveAction) { |
| | | if (!StringUtils.hasText(approveAction)) { |
| | | return null; |
| | | } |
| | | String normalizedAction = approveAction.trim().toUpperCase(Locale.ROOT); |
| | | return "APPROVED".equals(normalizedAction) || "REJECTED".equals(normalizedAction) |
| | | ? normalizedAction |
| | | : null; |
| | | } |
| | | |
| | | private ApprovalInstance getPendingApprovalInstance(Long instanceId) { |
| | | return this.getOne( |
| | | new LambdaQueryWrapper<ApprovalInstance>() |
| | | .eq(ApprovalInstance::getId, instanceId) |
| | | .eq(ApprovalInstance::getDeleted, 0) |
| | | .last("LIMIT 1") |
| | | ); |
| | | } |
| | | |
| | | private boolean updateCurrentTask(ApprovalInstanceDto approvalInstanceDto, |
| | | String approveAction, |
| | | ApprovalTask currentTask, |
| | | LocalDateTime now) { |
| | | // ä»
å
许å¾
审æ¹ä»»å¡è¢«æåå¤ç䏿¬¡ï¼é¿å
å¹¶åä¸éå¤å®¡æ¹æåã |
| | | return approvalTaskService.update( |
| | | Wrappers.<ApprovalTask>lambdaUpdate() |
| | | .eq(ApprovalTask::getId, currentTask.getId()) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | .set(ApprovalTask::getTaskStatus, approveAction) |
| | | .set(ApprovalTask::getComment, approvalInstanceDto.getApproveComment()) |
| | | .set(ApprovalTask::getApproveTime, now) |
| | | .set(ApprovalTask::getIsRead, (byte) 1) |
| | | ); |
| | | } |
| | | |
| | | private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) { |
| | | if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) { |
| | | return R.ok("å½åèç¹å·²å¤ç宿"); |
| | | } |
| | | |
| | | closePendingTasks(instance.getId(), currentNode.getId()); |
| | | instance.setStatus("REJECTED"); |
| | | instance.setFinishTime(now); |
| | | this.updateById(instance); |
| | | // 驳å对åºçä¼ä¸æ°é»ï¼ å·®æ
æ¥é |
| | | if (instance.getBusinessType().equals(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode())) { |
| | | enterpriseNewsMapper.update( |
| | | new LambdaUpdateWrapper<EnterpriseNews>() |
| | | .eq(EnterpriseNews::getId, instance.getBusinessId()) |
| | | .set(EnterpriseNews::getStatus, "REJECTED") |
| | | ); |
| | | }else if (instance.getBusinessType().equals(TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode())||instance.getBusinessType().equals(TypeEnums.EXPENSE_APPROVAL.getCode())) { |
| | | finReimbursementMapper.update( |
| | | new LambdaUpdateWrapper<FinReimbursement>() |
| | | .eq(FinReimbursement::getId, instance.getBusinessId()) |
| | | .set(FinReimbursement::getBillStatus, "REJECTED") |
| | | ); |
| | | } |
| | | return R.ok("审æ¹å·²é©³å"); |
| | | } |
| | | |
| | | private R approveAndMoveNext(ApprovalInstance instance, |
| | | ApprovalInstanceNode currentNode, |
| | | ApprovalInstanceDto approvalInstanceDto, |
| | | LocalDateTime now) { |
| | | return moveToNextLevel(instance, currentNode, approvalInstanceDto, now, true); |
| | | } |
| | | |
| | | private R moveToNextLevel(ApprovalInstance instance, |
| | | ApprovalInstanceNode currentNode, |
| | | ApprovalInstanceDto approvalInstanceDto, |
| | | LocalDateTime now, |
| | | boolean notifyNextNode) { |
| | | if (!updateCurrentNodeStatus(currentNode.getId(), "APPROVED", now)) { |
| | | return R.ok("å½åèç¹å·²å¤ç宿"); |
| | | } |
| | | |
| | | closePendingTasks(instance.getId(), currentNode.getId()); |
| | | |
| | | int nextLevel = currentNode.getLevelNo() + 1; |
| | | ApprovalInstanceNode nextInstanceNode = approvalInstanceNodeService.getOne( |
| | | new LambdaQueryWrapper<ApprovalInstanceNode>() |
| | | .eq(ApprovalInstanceNode::getInstanceId, instance.getId()) |
| | | .eq(ApprovalInstanceNode::getLevelNo, nextLevel) |
| | | .eq(ApprovalInstanceNode::getDeleted, 0) |
| | | .orderByAsc(ApprovalInstanceNode::getId) |
| | | .last("LIMIT 1") |
| | | ); |
| | | |
| | | if (nextInstanceNode != null) { |
| | | if (!activateNextInstanceNode(nextInstanceNode.getId(), now)) { |
| | | return R.ok("ä¸ä¸å®¡æ¹èç¹å·²è¢«æ¿æ´»ï¼è¯·å·æ°åéè¯"); |
| | | } |
| | | instance.setCurrentLevel(nextLevel); |
| | | instance.setStatus("PENDING"); |
| | | this.updateById(instance); |
| | | if (notifyNextNode) { |
| | | List<ApprovalTask> nextTasks = approvalTaskService.list( |
| | | Wrappers.<ApprovalTask>lambdaQuery() |
| | | .eq(ApprovalTask::getInstanceId, instance.getId()) |
| | | .eq(ApprovalTask::getNodeId, nextInstanceNode.getId()) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | ); |
| | | sendApproveNotice(instance, nextTasks); |
| | | } |
| | | return R.ok("å®¡æ¹æåï¼å·²æµè½¬å°ä¸ä¸èç¹"); |
| | | } |
| | | |
| | | ApprovalTemplateNode nextTemplateNode = approvalTemplateNodeService.getOne( |
| | | new LambdaQueryWrapper<ApprovalTemplateNode>() |
| | | .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId()) |
| | | .eq(ApprovalTemplateNode::getLevelNo, nextLevel) |
| | | .orderByAsc(ApprovalTemplateNode::getId) |
| | | .last("LIMIT 1") |
| | | ); |
| | | |
| | | if (nextTemplateNode == null) { |
| | | instance.setStatus("APPROVED"); |
| | | instance.setFinishTime(now); |
| | | this.updateById(instance); |
| | | handleBusinessAfterApprovalFinished(instance); |
| | | return R.ok("审æ¹å·²å®æ"); |
| | | } |
| | | |
| | | instance.setCurrentLevel(nextLevel); |
| | | instance.setStatus("PENDING"); |
| | | this.updateById(instance); |
| | | approveProcessConfigNodeUtils.createCurrentNodeAndTasks(instance, false); |
| | | if (notifyNextNode) { |
| | | sendApproveNotice(instance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId())); |
| | | } |
| | | return R.ok("å®¡æ¹æåï¼å·²æµè½¬å°ä¸ä¸èç¹"); |
| | | } |
| | | |
| | | private boolean activateNextInstanceNode(Long nodeId, LocalDateTime now) { |
| | | return approvalInstanceNodeService.update( |
| | | Wrappers.<ApprovalInstanceNode>lambdaUpdate() |
| | | .eq(ApprovalInstanceNode::getId, nodeId) |
| | | .eq(ApprovalInstanceNode::getStatus, "WAITING") |
| | | .eq(ApprovalInstanceNode::getDeleted, 0) |
| | | .set(ApprovalInstanceNode::getStatus, "PENDING") |
| | | .set(ApprovalInstanceNode::getStartTime, now) |
| | | ); |
| | | } |
| | | |
| | | private boolean updateCurrentNodeStatus(Long nodeId, String targetStatus, LocalDateTime now) { |
| | | // ä»
å
许ä¸ä¸ªè¯·æ±å°å½åèç¹ä»å¾
å¤çæ¨è¿å°ç®æ ç¶æï¼é¿å
é夿µè½¬ã |
| | | return approvalInstanceNodeService.update( |
| | | Wrappers.<ApprovalInstanceNode>lambdaUpdate() |
| | | .eq(ApprovalInstanceNode::getId, nodeId) |
| | | .eq(ApprovalInstanceNode::getStatus, "PENDING") |
| | | .eq(ApprovalInstanceNode::getDeleted, 0) |
| | | .set(ApprovalInstanceNode::getStatus, targetStatus) |
| | | .set(ApprovalInstanceNode::getFinishTime, now) |
| | | ); |
| | | } |
| | | |
| | | private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) { |
| | | String status = instance.getStatus(); |
| | | Long businessType = instance.getBusinessType(); |
| | | if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) { |
| | | handlePurchaseApprovalFinished(instance, status); |
| | | return; |
| | | } |
| | | if (TypeEnums.QUOTATION_APPROVAL.getCode().equals(businessType)) { |
| | | handleSalesQuotationApprovalFinished(instance, status); |
| | | return; |
| | | } |
| | | if (TypeEnums.SHIPPING_APPROVAL.getCode().equals(businessType)) { |
| | | handleShippingApprovalFinished(instance, status); |
| | | return; |
| | | } |
| | | if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType) |
| | | || TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) { |
| | | handleReimbursementApprovalFinished(instance, status); |
| | | return; |
| | | } |
| | | // 驳å对åºçä¼ä¸æ°é»ãå çç³è¯·ã请åç³è¯·å¯ä»¥éæ°åæäº¤ |
| | | if (TypeEnums.LEAVE_APPROVAL.getCode().equals(businessType) |
| | | || TypeEnums.OVERTIME_APPROVAL.getCode().equals(businessType)) { |
| | | handleHolidayApplicationApprovalFinished(instance, status); |
| | | return; |
| | | } |
| | | if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) { |
| | | handleNewsApprovalFinished(instance, status); |
| | | } |
| | | } |
| | | |
| | | private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) { |
| | | if (instance == null || instance.getBusinessId() == null) { |
| | | return; |
| | | } |
| | | |
| | | FinReimbursement reimbursement = new FinReimbursement(); |
| | | reimbursement.setId(instance.getBusinessId()); |
| | | if ("APPROVED".equals(status)) { |
| | | reimbursement.setBillStatus("APPROVED"); |
| | | reimbursement.setApprovedTime(instance.getFinishTime()); |
| | | } else if ("REJECTED".equals(status)) { |
| | | reimbursement.setBillStatus("REJECTED"); |
| | | } else if ("PENDING".equals(status)) { |
| | | reimbursement.setBillStatus("IN_APPROVAL"); |
| | | } else { |
| | | return; |
| | | } |
| | | finReimbursementMapper.updateById(reimbursement); |
| | | } |
| | | |
| | | private void handleNewsApprovalFinished(ApprovalInstance instance, String status) { |
| | | if (instance == null || instance.getBusinessId() == null) { |
| | | return; |
| | | } |
| | | EnterpriseNews enterpriseNews = new EnterpriseNews(); |
| | | enterpriseNews.setId(instance.getBusinessId()); |
| | | if ("APPROVED".equals(status)) { |
| | | enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_PUBLISHED); |
| | | enterpriseNewsMapper.updateById(enterpriseNews); |
| | | sendEnterpriseNewsNotice(instance.getBusinessId()); |
| | | return; |
| | | } |
| | | if ("REJECTED".equals(status)) { |
| | | enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_REJECTED); |
| | | enterpriseNewsMapper.updateById(enterpriseNews); |
| | | } |
| | | } |
| | | |
| | | private void handleHolidayApplicationApprovalFinished(ApprovalInstance instance, String status) { |
| | | if (instance == null || instance.getBusinessId() == null) { |
| | | return; |
| | | } |
| | | HolidayApplication holidayApplication = new HolidayApplication(); |
| | | holidayApplication.setId(instance.getBusinessId()); |
| | | if ("APPROVED".equals(status)) { |
| | | holidayApplication.setStatus("APPROVED"); |
| | | } else if ("REJECTED".equals(status)) { |
| | | holidayApplication.setStatus("REJECTED"); |
| | | } else if ("PENDING".equals(status)) { |
| | | holidayApplication.setStatus("PENDING"); |
| | | } else { |
| | | return; |
| | | } |
| | | holidayApplicationMapper.updateById(holidayApplication); |
| | | } |
| | | |
| | | private void handlePurchaseApprovalFinished(ApprovalInstance instance, String status) { |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne( |
| | | new LambdaQueryWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getId, instance.getBusinessId()) |
| | | .last("limit 1") |
| | | ); |
| | | if (purchaseLedger == null) { |
| | | return; |
| | | } |
| | | |
| | | if ("APPROVED".equals(status)) { |
| | | purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode()); |
| | | 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()) { |
| | | qualityInspectHelper.addQualityInspect(purchaseLedger, salesLedgerProduct); |
| | | } else { |
| | | stockUtils.addStockWithBatchNo( |
| | | salesLedgerProduct.getProductModelId(), |
| | | salesLedgerProduct.getQuantity(), |
| | | StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), |
| | | purchaseLedger.getId(), |
| | | purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId() |
| | | ); |
| | | } |
| | | } |
| | | } else if ("REJECTED".equals(status)) { |
| | | purchaseLedger.setApprovalStatus(ApprovalStatusEnum.REJECTED.getCode()); |
| | | } else if ("PENDING".equals(status)) { |
| | | purchaseLedger.setApprovalStatus(ApprovalStatusEnum.IN_PROGRESS.getCode()); |
| | | } |
| | | purchaseLedgerMapper.updateById(purchaseLedger); |
| | | } |
| | | |
| | | private void handleSalesQuotationApprovalFinished(ApprovalInstance instance, String status) { |
| | | SalesQuotation salesQuote = salesQuotationMapper.selectOne( |
| | | new LambdaQueryWrapper<SalesQuotation>() |
| | | .eq(SalesQuotation::getId, instance.getBusinessId()) |
| | | .last("limit 1") |
| | | ); |
| | | if (salesQuote == null) { |
| | | return; |
| | | } |
| | | |
| | | if ("APPROVED".equals(status)) { |
| | | salesQuote.setStatus(SalesQuotationStatusEnum.APPROVED.getCode()); |
| | | } else if ("REJECTED".equals(status)) { |
| | | salesQuote.setStatus(SalesQuotationStatusEnum.REJECTED.getCode()); |
| | | } else if ("PENDING".equals(status)) { |
| | | salesQuote.setStatus(SalesQuotationStatusEnum.IN_PROGRESS.getCode()); |
| | | } |
| | | salesQuotationMapper.updateById(salesQuote); |
| | | } |
| | | |
| | | private void handleShippingApprovalFinished(ApprovalInstance instance, String status) { |
| | | ShippingInfo shippingInfo = shippingInfoMapper.selectOne( |
| | | new LambdaQueryWrapper<ShippingInfo>() |
| | | .eq(ShippingInfo::getId, instance.getTitle()) |
| | | .orderByDesc(ShippingInfo::getCreateTime) |
| | | .last("limit 1") |
| | | ); |
| | | if (shippingInfo == null) { |
| | | return; |
| | | } |
| | | |
| | | if ("APPROVED".equals(status)) { |
| | | shippingInfo.setStatus(ShippingStatusEnum.APPROVED.getCode()); |
| | | shippingInfo.setShippingDate(new Date()); |
| | | stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId()); |
| | | } else if ("REJECTED".equals(status)) { |
| | | stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode()); |
| | | shippingInfo.setStatus(ShippingStatusEnum.REJECTED.getCode()); |
| | | } else if ("PENDING".equals(status)) { |
| | | shippingInfo.setStatus(ShippingStatusEnum.IN_PROGRESS.getCode()); |
| | | } |
| | | shippingInfoMapper.updateById(shippingInfo); |
| | | } |
| | | |
| | | private List<ApprovalTask> createNodeAndTasks(ApprovalInstance instance, ApprovalTemplateNode templateNode) { |
| | | List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverMapper.selectList( |
| | | new LambdaQueryWrapper<ApprovalTemplateNodeApprover>() |
| | | .eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId()) |
| | | .eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId()) |
| | | .eq(ApprovalTemplateNodeApprover::getDeleted, 0L) |
| | | .orderByAsc(ApprovalTemplateNodeApprover::getSortNo) |
| | | ); |
| | | if (approvers == null || approvers.isEmpty()) { |
| | | throw new RuntimeException("ä¸ä¸å®¡æ¹èç¹æªé
置审æ¹äºº"); |
| | | } |
| | | |
| | | ApprovalInstanceNode instanceNode = new ApprovalInstanceNode(); |
| | | instanceNode.setInstanceId(instance.getId()); |
| | | instanceNode.setLevelNo(templateNode.getLevelNo()); |
| | | instanceNode.setApproveType(templateNode.getApproveType()); |
| | | instanceNode.setStatus("PENDING"); |
| | | instanceNode.setStartTime(LocalDateTime.now()); |
| | | instanceNode.setDeleted((byte) 0); |
| | | approvalInstanceNodeService.save(instanceNode); |
| | | |
| | | List<ApprovalTask> taskList = new ArrayList<>(approvers.size()); |
| | | for (ApprovalTemplateNodeApprover approver : approvers) { |
| | | ApprovalTask task = new ApprovalTask(); |
| | | task.setInstanceId(instance.getId()); |
| | | task.setNodeId(instanceNode.getId()); |
| | | task.setLevelNo(instanceNode.getLevelNo()); |
| | | task.setApproverId(approver.getApproverId()); |
| | | task.setApproverName(approver.getApproverName()); |
| | | task.setTaskStatus("PENDING"); |
| | | task.setIsRead((byte) 0); |
| | | task.setDeleted((byte) 0); |
| | | taskList.add(task); |
| | | } |
| | | approvalTaskService.saveBatch(taskList); |
| | | return taskList; |
| | | } |
| | | |
| | | private void sendApproveNotice(ApprovalInstance instance, List<ApprovalTask> tasks) { |
| | | if (instance == null || tasks == null || tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | List<Long> approverIds = tasks.stream() |
| | | .map(ApprovalTask::getApproverId) |
| | | .filter(id -> id != null && id > 0) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (approverIds.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | String title = StringUtils.hasText(instance.getTemplateName()) ? instance.getTemplateName() : "å®¡æ¹æé"; |
| | | String message = "审æ¹åå· " + instance.getInstanceNo() + " éè¦æ¨å®¡æ¹"; |
| | | String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list?id=" + instance.getId(); |
| | | sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath); |
| | | } |
| | | |
| | | private void sendEnterpriseNewsNotice(Long newsId) { |
| | | EnterpriseNews enterpriseNews = enterpriseNewsMapper.selectById(newsId); |
| | | if (enterpriseNews == null) { |
| | | return; |
| | | } |
| | | List<Long> userIds = getEnterpriseNewsNoticeUserIds(enterpriseNews); |
| | | if (userIds == null || userIds.isEmpty()) { |
| | | return; |
| | | } |
| | | String title = "ä¼ä¸æ°é»"; |
| | | String message = "æ¨ææ°çä¼ä¸æ°é»ã" + enterpriseNews.getTitle() + "ãè¯·åæ¶æ¥é
"; |
| | | String jumpPath = "/officeProcessAutomation/EnterpriseNews?id=" + newsId; |
| | | sysNoticeService.simpleNoticeByUser(title, message, userIds, jumpPath); |
| | | } |
| | | |
| | | private List<Long> getEnterpriseNewsNoticeUserIds(EnterpriseNews enterpriseNews) { |
| | | if (enterpriseNews == null || !org.springframework.util.StringUtils.hasText(enterpriseNews.getReadScope())) { |
| | | return Collections.emptyList(); |
| | | } |
| | | String readScope = enterpriseNews.getReadScope().trim(); |
| | | if ("all".equals(readScope)) { |
| | | return sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>() |
| | | .select(SysUser::getUserId) |
| | | .eq(SysUser::getDelFlag, "0")) |
| | | .stream() |
| | | .map(SysUser::getUserId) |
| | | .filter(id -> id != null && id > 0) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | if ("dept".equals(readScope)) { |
| | | List<Long> deptIds = enterpriseNewsScopeDeptMapper.selectList( |
| | | new LambdaQueryWrapper<EnterpriseNewsScopeDept>() |
| | | .eq(EnterpriseNewsScopeDept::getNewsId, enterpriseNews.getId())) |
| | | .stream() |
| | | .map(EnterpriseNewsScopeDept::getDeptId) |
| | | .filter(id -> id != null && id > 0) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (deptIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return sysUserDeptMapper.selectDistinctUserIdsByDeptIds(collectDeptIdsWithChildren(deptIds)); |
| | | } |
| | | if ("custom".equals(readScope)) { |
| | | return enterpriseNewsScopeUserMapper.selectList( |
| | | new LambdaQueryWrapper<EnterpriseNewsScopeUser>() |
| | | .eq(EnterpriseNewsScopeUser::getNewsId, enterpriseNews.getId())) |
| | | .stream() |
| | | .map(EnterpriseNewsScopeUser::getUserId) |
| | | .filter(id -> id != null && id > 0) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | private List<Long> collectDeptIdsWithChildren(List<Long> deptIds) { |
| | | Set<Long> allDeptIds = new LinkedHashSet<>(); |
| | | for (Long deptId : deptIds) { |
| | | if (deptId == null) { |
| | | continue; |
| | | } |
| | | allDeptIds.add(deptId); |
| | | List<SysDept> children = sysDeptMapper.selectChildrenDeptById(deptId); |
| | | if (children != null && !children.isEmpty()) { |
| | | for (SysDept child : children) { |
| | | if (child != null && child.getDeptId() != null) { |
| | | allDeptIds.add(child.getDeptId()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return new ArrayList<>(allDeptIds); |
| | | } |
| | | |
| | | private void closePendingTasks(Long instanceId, Long nodeId) { |
| | | LambdaUpdateWrapper<ApprovalTask> updateWrapper = Wrappers.lambdaUpdate(); |
| | | updateWrapper.eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getNodeId, nodeId) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | .set(ApprovalTask::getDeleted, (byte) 1); |
| | | approvalTaskService.update(updateWrapper); |
| | | } |
| | | |
| | | private void saveApprovalRecord(Long instanceId, |
| | | Long nodeId, |
| | | Long taskId, |
| | | Long operatorId, |
| | | String operatorName, |
| | | String action, |
| | | String comment) { |
| | | ApprovalRecord record = new ApprovalRecord(); |
| | | record.setInstanceId(instanceId); |
| | | record.setNodeId(nodeId); |
| | | record.setTaskId(taskId); |
| | | record.setOperatorId(operatorId); |
| | | record.setOperatorName(operatorName); |
| | | record.setAction(action); |
| | | record.setComment(comment); |
| | | record.setDeleted((byte) 0); |
| | | approvalRecordService.save(record); |
| | | } |
| | | |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalRecord; |
| | | import com.ruoyi.approve.mapper.ApprovalRecordMapper; |
| | | import com.ruoyi.approve.service.ApprovalRecordService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹è®°å½è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:28:21 |
| | | */ |
| | | @Service |
| | | public class ApprovalRecordServiceImpl extends ServiceImpl<ApprovalRecordMapper, ApprovalRecord> implements ApprovalRecordService { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTask; |
| | | import com.ruoyi.approve.mapper.ApprovalTaskMapper; |
| | | import com.ruoyi.approve.service.ApprovalTaskService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审æ¹ä»»å¡è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 03:32:37 |
| | | */ |
| | | @Service |
| | | public class ApprovalTaskServiceImpl extends ServiceImpl<ApprovalTaskMapper, ApprovalTask> implements ApprovalTaskService { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper; |
| | | import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹å®¡æ¹äººè¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:30 |
| | | */ |
| | | @Service |
| | | public class ApprovalTemplateNodeApproverServiceImpl extends ServiceImpl<ApprovalTemplateNodeApproverMapper, ApprovalTemplateNodeApprover> implements ApprovalTemplateNodeApproverService { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeApproverDto; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeDto; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateNodeMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNode; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService; |
| | | import com.ruoyi.approve.service.ApprovalTemplateNodeService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿èç¹è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-18 11:20:19 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class ApprovalTemplateNodeServiceImpl extends ServiceImpl<ApprovalTemplateNodeMapper, ApprovalTemplateNode> implements ApprovalTemplateNodeService { |
| | | |
| | | private final ApprovalTemplateNodeMapper approvalTemplateNodeMapper; |
| | | private final ApprovalTemplateNodeApproverService approvalTemplateNodeApproverService; |
| | | @Override |
| | | public Boolean saveApprovalTemplateNode(Long templateId, List<ApprovalTemplateNodeDto> nodes) { |
| | | if (nodes == null || nodes.isEmpty()) { |
| | | throw new RuntimeException("èç¹å表ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | List<ApprovalTemplateNodeApprover> approverList = new ArrayList<>(); |
| | | |
| | | for (ApprovalTemplateNodeDto nodeDto : nodes) { |
| | | ApprovalTemplateNode node = new ApprovalTemplateNode(); |
| | | BeanUtils.copyProperties(nodeDto, node); |
| | | node.setTemplateId(templateId); |
| | | approvalTemplateNodeMapper.insert(node); |
| | | |
| | | List<ApprovalTemplateNodeApproverDto> approvers = nodeDto.getApprovers(); |
| | | if (approvers == null || approvers.isEmpty()) { |
| | | throw new RuntimeException("èç¹å®¡æ¹äººä¸è½ä¸ºç©º"); |
| | | } |
| | | for (ApprovalTemplateNodeApproverDto approverDto : approvers) { |
| | | ApprovalTemplateNodeApprover approver = new ApprovalTemplateNodeApprover(); |
| | | BeanUtils.copyProperties(approverDto, approver); |
| | | approver.setNodeId(node.getId()); |
| | | approver.setTemplateId(templateId); |
| | | approver.setDeleted(0L); |
| | | approverList.add(approver); |
| | | } |
| | | } |
| | | approvalTemplateNodeApproverService.saveBatch(approverList); |
| | | return true; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateNodeApproverVo; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateNodeVo; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateVo; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateMapper; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNode; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import com.ruoyi.approve.service.ApprovalTemplateNodeService; |
| | | import com.ruoyi.approve.service.ApprovalTemplateService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * å®¡æ¹æ¨¡æ¿æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @since 2026-05-18 11:20:08 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class ApprovalTemplateServiceImpl extends ServiceImpl<ApprovalTemplateMapper, ApprovalTemplate> implements ApprovalTemplateService { |
| | | |
| | | private final ApprovalTemplateMapper approvalTemplateMapper; |
| | | private final ApprovalTemplateNodeService approvalTemplateNodeService; |
| | | private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper; |
| | | |
| | | @Override |
| | | public IPage<ApprovalTemplateVo> listPage(Page<ApprovalTemplateVo> page, ApprovalTemplateDto approvalTemplateDto) { |
| | | IPage<ApprovalTemplateVo> approvalTemplateVoIPage = approvalTemplateMapper.listPage(page, approvalTemplateDto); |
| | | fillTemplateVoNodes(approvalTemplateVoIPage.getRecords()); |
| | | return approvalTemplateVoIPage; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean saveApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto) { |
| | | approvalTemplateMapper.insert(approvalTemplateDto); |
| | | approvalTemplateNodeService.remove( |
| | | new LambdaQueryWrapper<ApprovalTemplateNode>() |
| | | .eq(ApprovalTemplateNode::getTemplateId, approvalTemplateDto.getId()) |
| | | ); |
| | | approvalTemplateNodeApproverMapper.delete( |
| | | new LambdaQueryWrapper<ApprovalTemplateNodeApprover>() |
| | | .eq(ApprovalTemplateNodeApprover::getTemplateId, approvalTemplateDto.getId()) |
| | | ); |
| | | return approvalTemplateNodeService.saveApprovalTemplateNode( |
| | | approvalTemplateDto.getId(), |
| | | approvalTemplateDto.getNodes() |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean updateApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto) { |
| | | approvalTemplateMapper.updateById(approvalTemplateDto); |
| | | approvalTemplateNodeService.remove( |
| | | new LambdaQueryWrapper<ApprovalTemplateNode>() |
| | | .eq(ApprovalTemplateNode::getTemplateId, approvalTemplateDto.getId()) |
| | | ); |
| | | approvalTemplateNodeApproverMapper.delete( |
| | | new LambdaQueryWrapper<ApprovalTemplateNodeApprover>() |
| | | .eq(ApprovalTemplateNodeApprover::getTemplateId, approvalTemplateDto.getId()) |
| | | ); |
| | | return approvalTemplateNodeService.saveApprovalTemplateNode( |
| | | approvalTemplateDto.getId(), |
| | | approvalTemplateDto.getNodes() |
| | | ); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean delete(List<Long> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | return false; |
| | | } |
| | | ApprovalTemplate updateEntity = new ApprovalTemplate(); |
| | | updateEntity.setDeleted(1); |
| | | LambdaUpdateWrapper<ApprovalTemplate> updateWrapper = Wrappers.lambdaUpdate(); |
| | | updateWrapper.in(ApprovalTemplate::getId, ids) |
| | | .eq(ApprovalTemplate::getDeleted, 0); |
| | | |
| | | int rows = approvalTemplateMapper.update(updateEntity, updateWrapper); |
| | | return rows == ids.size(); |
| | | } |
| | | |
| | | @Override |
| | | public List<ApprovalTemplateVo> listApprovalTemplateVo(Integer type) { |
| | | List<ApprovalTemplate> templateList = this.list( |
| | | new LambdaQueryWrapper<ApprovalTemplate>() |
| | | .eq(ApprovalTemplate::getDeleted, 0) |
| | | .eq(ApprovalTemplate::getEnabled, 1) |
| | | .orderByDesc(ApprovalTemplate::getTemplateType) |
| | | .orderByDesc(ApprovalTemplate::getId) |
| | | ); |
| | | if (CollUtil.isEmpty(templateList)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | List<ApprovalTemplateVo> templateVos = templateList.stream() |
| | | .map(template -> { |
| | | ApprovalTemplateVo templateVo = new ApprovalTemplateVo(); |
| | | BeanUtils.copyProperties(template, templateVo); |
| | | return templateVo; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | fillTemplateVoNodes(templateVos); |
| | | return templateVos; |
| | | } |
| | | |
| | | @Override |
| | | public ApprovalTemplateVo getApprovalTemplateVoById(Long id) { |
| | | if (id == null) { |
| | | throw new IllegalArgumentException("åæ° id ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | ApprovalTemplate template = this.getOne( |
| | | new LambdaQueryWrapper<ApprovalTemplate>() |
| | | .eq(ApprovalTemplate::getId, id) |
| | | .eq(ApprovalTemplate::getDeleted, 0) |
| | | ); |
| | | if (template == null) { |
| | | throw new IllegalArgumentException("模æ¿ä¸åå¨"); |
| | | } |
| | | |
| | | List<ApprovalTemplateNode> nodeList = approvalTemplateNodeService.list( |
| | | new LambdaQueryWrapper<ApprovalTemplateNode>() |
| | | .eq(ApprovalTemplateNode::getTemplateId, id) |
| | | .orderByAsc(ApprovalTemplateNode::getLevelNo) |
| | | ); |
| | | |
| | | List<ApprovalTemplateNodeApprover> approverList = approvalTemplateNodeApproverMapper.selectList( |
| | | new LambdaQueryWrapper<ApprovalTemplateNodeApprover>() |
| | | .eq(ApprovalTemplateNodeApprover::getTemplateId, id) |
| | | .eq(ApprovalTemplateNodeApprover::getDeleted, 0L) |
| | | ); |
| | | |
| | | Map<Long, List<ApprovalTemplateNode>> nodeMap = nodeList.stream() |
| | | .collect(Collectors.groupingBy(ApprovalTemplateNode::getTemplateId)); |
| | | |
| | | Map<Long, List<ApprovalTemplateNodeApprover>> approverMap = approverList.stream() |
| | | .collect(Collectors.groupingBy(ApprovalTemplateNodeApprover::getNodeId)); |
| | | |
| | | return buildTemplateVo(template, nodeMap, approverMap); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éå¡«å
模æ¿èç¹åèç¹å®¡æ¹äººï¼é¿å
å¾ªç¯æ¥åºã |
| | | */ |
| | | private void fillTemplateVoNodes(List<ApprovalTemplateVo> templateVos) { |
| | | if (CollUtil.isEmpty(templateVos)) { |
| | | return; |
| | | } |
| | | |
| | | List<Long> templateIds = templateVos.stream() |
| | | .map(ApprovalTemplateVo::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | List<ApprovalTemplateNode> nodeList = approvalTemplateNodeService.list( |
| | | new LambdaQueryWrapper<ApprovalTemplateNode>() |
| | | .in(ApprovalTemplateNode::getTemplateId, templateIds) |
| | | .orderByAsc(ApprovalTemplateNode::getLevelNo) |
| | | ); |
| | | |
| | | List<ApprovalTemplateNodeApprover> approverList = approvalTemplateNodeApproverMapper.selectList( |
| | | new LambdaQueryWrapper<ApprovalTemplateNodeApprover>() |
| | | .in(ApprovalTemplateNodeApprover::getTemplateId, templateIds) |
| | | .eq(ApprovalTemplateNodeApprover::getDeleted, 0L) |
| | | ); |
| | | |
| | | Map<Long, List<ApprovalTemplateNode>> nodeMap = nodeList.stream() |
| | | .collect(Collectors.groupingBy(ApprovalTemplateNode::getTemplateId)); |
| | | |
| | | Map<Long, List<ApprovalTemplateNodeApprover>> approverMap = approverList.stream() |
| | | .collect(Collectors.groupingBy(ApprovalTemplateNodeApprover::getNodeId)); |
| | | |
| | | templateVos.forEach(templateVo -> templateVo.setNodes( |
| | | nodeMap.getOrDefault(templateVo.getId(), Collections.emptyList()) |
| | | .stream() |
| | | .sorted(Comparator.comparing( |
| | | ApprovalTemplateNode::getLevelNo, |
| | | Comparator.nullsLast(Integer::compareTo) |
| | | )) |
| | | .map(node -> buildNodeVo(node, approverMap)) |
| | | .collect(Collectors.toList()) |
| | | )); |
| | | } |
| | | |
| | | private ApprovalTemplateVo buildTemplateVo(ApprovalTemplate template, |
| | | Map<Long, List<ApprovalTemplateNode>> nodeMap, |
| | | Map<Long, List<ApprovalTemplateNodeApprover>> approverMap) { |
| | | ApprovalTemplateVo templateVo = new ApprovalTemplateVo(); |
| | | BeanUtils.copyProperties(template, templateVo); |
| | | |
| | | List<ApprovalTemplateNodeVo> nodeVos = nodeMap |
| | | .getOrDefault(template.getId(), Collections.emptyList()) |
| | | .stream() |
| | | .sorted(Comparator.comparing( |
| | | ApprovalTemplateNode::getLevelNo, |
| | | Comparator.nullsLast(Integer::compareTo) |
| | | )) |
| | | .map(node -> buildNodeVo(node, approverMap)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | templateVo.setNodes(nodeVos); |
| | | return templateVo; |
| | | } |
| | | |
| | | private ApprovalTemplateNodeVo buildNodeVo(ApprovalTemplateNode node, |
| | | Map<Long, List<ApprovalTemplateNodeApprover>> approverMap) { |
| | | ApprovalTemplateNodeVo nodeVo = new ApprovalTemplateNodeVo(); |
| | | BeanUtils.copyProperties(node, nodeVo); |
| | | |
| | | List<ApprovalTemplateNodeApproverVo> approverVos = approverMap |
| | | .getOrDefault(node.getId(), Collections.emptyList()) |
| | | .stream() |
| | | .sorted(Comparator.comparing( |
| | | ApprovalTemplateNodeApprover::getSortNo, |
| | | Comparator.nullsLast(Integer::compareTo) |
| | | )) |
| | | .map(this::buildApproverVo) |
| | | .collect(Collectors.toList()); |
| | | |
| | | nodeVo.setApprovers(approverVos); |
| | | return nodeVo; |
| | | } |
| | | |
| | | private ApprovalTemplateNodeApproverVo buildApproverVo(ApprovalTemplateNodeApprover approver) { |
| | | ApprovalTemplateNodeApproverVo approverVo = new ApprovalTemplateNodeApproverVo(); |
| | | BeanUtils.copyProperties(approver, approverVo); |
| | | return approverVo; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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.toolkit.Wrappers; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.quality.mapper.QualityInspectMapper; |
| | | import com.ruoyi.quality.mapper.QualityInspectParamMapper; |
| | | import com.ruoyi.quality.mapper.QualityTestStandardMapper; |
| | | import com.ruoyi.quality.mapper.QualityTestStandardParamMapper; |
| | | import com.ruoyi.quality.pojo.QualityInspect; |
| | | import com.ruoyi.quality.pojo.QualityInspectParam; |
| | | import com.ruoyi.quality.pojo.QualityTestStandard; |
| | | import com.ruoyi.quality.pojo.QualityTestStandardParam; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.mapper.SalesQuotationMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.SalesQuotation; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class ApproveBusinessStatusService { |
| | | |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesQuotationMapper salesQuotationMapper; |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final StockUtils stockUtils; |
| | | private final QualityInspectMapper qualityInspectMapper; |
| | | private final QualityTestStandardMapper qualityTestStandardMapper; |
| | | private final QualityTestStandardParamMapper qualityTestStandardParamMapper; |
| | | private final QualityInspectParamMapper qualityInspectParamMapper; |
| | | |
| | | /** |
| | | * ç»ä¸åæ¥å®¡æ¹ç»æå¯¹åºçä¸å¡åæ®ç¶æã |
| | | * statusï¼1-å®¡æ ¸ä¸ï¼2-å®¡æ ¸å®æï¼3-å®¡æ ¸æªéè¿ã |
| | | */ |
| | | public void syncBusinessStatus(Integer approveType, String approveReason, Integer status) { |
| | | if (approveType == null || status == null || !StringUtils.hasText(approveReason)) { |
| | | return; |
| | | } |
| | | switch (approveType) { |
| | | case 5: |
| | | syncPurchaseStatus(approveReason, status); |
| | | break; |
| | | case 6: |
| | | syncSalesQuotationStatus(approveReason, status); |
| | | break; |
| | | case 7: |
| | | syncShippingStatus(approveReason, status); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // éè´å®¡æ¹éè¿æ¶ï¼æäº§åè´¨æ£é
ç½®å³å®çæè´¨æ£åæç´æ¥å
¥åºã |
| | | private void syncPurchaseStatus(String approveReason, Integer status) { |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, approveReason) |
| | | .last("limit 1")); |
| | | if (purchaseLedger == null) { |
| | | return; |
| | | } |
| | | 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 (Boolean.TRUE.equals(salesLedgerProduct.getIsChecked())) { |
| | | addQualityInspect(purchaseLedger, salesLedgerProduct); |
| | | } else { |
| | | stockUtils.addStockWithBatchNo( |
| | | salesLedgerProduct.getProductModelId(), |
| | | salesLedgerProduct.getQuantity(), |
| | | StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), |
| | | purchaseLedger.getId(), |
| | | purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId()); |
| | | } |
| | | } |
| | | } else if (status.equals(3)) { |
| | | purchaseLedger.setApprovalStatus(4); |
| | | } else if (status.equals(1)) { |
| | | purchaseLedger.setApprovalStatus(2); |
| | | } else { |
| | | return; |
| | | } |
| | | purchaseLedgerMapper.updateById(purchaseLedger); |
| | | } |
| | | |
| | | // æ¥ä»·å®¡æ¹ç¶æååå°é宿¥ä»·åç¶æã |
| | | private void syncSalesQuotationStatus(String approveReason, Integer status) { |
| | | SalesQuotation salesQuote = salesQuotationMapper.selectOne(new LambdaQueryWrapper<SalesQuotation>() |
| | | .eq(SalesQuotation::getQuotationNo, approveReason) |
| | | .last("limit 1")); |
| | | if (salesQuote == null) { |
| | | return; |
| | | } |
| | | if (status.equals(2)) { |
| | | salesQuote.setStatus("éè¿"); |
| | | } else if (status.equals(3)) { |
| | | salesQuote.setStatus("æç»"); |
| | | } else if (status.equals(1)) { |
| | | salesQuote.setStatus("å®¡æ ¸ä¸"); |
| | | } else { |
| | | return; |
| | | } |
| | | salesQuotationMapper.updateById(salesQuote); |
| | | } |
| | | |
| | | // å货审æ¹éè¿æ¶åæ¥åè´§ç¶æååºåºå®¡æ¹ç¶æï¼æç»æ¶å é¤å¾
确认åºåºè®°å½ã |
| | | private void syncShippingStatus(String approveReason, Integer status) { |
| | | ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>() |
| | | .eq(ShippingInfo::getShippingNo, approveReason) |
| | | .orderByDesc(ShippingInfo::getCreateTime) |
| | | .last("limit 1")); |
| | | if (shippingInfo == null) { |
| | | return; |
| | | } |
| | | if (status.equals(2)) { |
| | | shippingInfo.setStatus("å®¡æ ¸éè¿"); |
| | | shippingInfo.setShippingDate(new Date()); |
| | | stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId()); |
| | | } else if (status.equals(3)) { |
| | | stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode()); |
| | | shippingInfo.setStatus("å®¡æ ¸æç»"); |
| | | } else if (status.equals(1)) { |
| | | shippingInfo.setStatus("å®¡æ ¸ä¸"); |
| | | } else { |
| | | return; |
| | | } |
| | | shippingInfoMapper.updateById(shippingInfo); |
| | | } |
| | | |
| | | // çæéè´è´¨æ£åï¼å¹¶æäº§åè´¨æ£æ ååå§åè´¨æ£åæ°ã |
| | | private void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) { |
| | | QualityInspect qualityInspect = new QualityInspect(); |
| | | qualityInspect.setInspectType(0); |
| | | qualityInspect.setSupplier(purchaseLedger.getSupplierName()); |
| | | qualityInspect.setPurchaseLedgerId(purchaseLedger.getId()); |
| | | qualityInspect.setProductId(saleProduct.getProductId()); |
| | | qualityInspect.setProductName(saleProduct.getProductCategory()); |
| | | qualityInspect.setModel(saleProduct.getSpecificationModel()); |
| | | qualityInspect.setProductModelId(saleProduct.getProductModelId()); |
| | | qualityInspect.setUnit(saleProduct.getUnit()); |
| | | qualityInspect.setQuantity(saleProduct.getQuantity()); |
| | | qualityInspectMapper.insert(qualityInspect); |
| | | List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0, null); |
| | | if (qualityTestStandard.size() > 0) { |
| | | qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId()); |
| | | qualityInspectMapper.updateById(qualityInspect); |
| | | qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery() |
| | | .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId())) |
| | | .forEach(qualityTestStandardParam -> { |
| | | QualityInspectParam param = new QualityInspectParam(); |
| | | com.ruoyi.common.utils.bean.BeanUtils.copyProperties(qualityTestStandardParam, param); |
| | | param.setId(null); |
| | | param.setInspectId(qualityInspect.getId()); |
| | | qualityInspectParamMapper.insert(param); |
| | | }); |
| | | } |
| | | } |
| | | } |
| | |
| | | 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.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.device.mapper.DeviceRepairMapper; |
| | | 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.quality.mapper.QualityInspectMapper; |
| | | import com.ruoyi.quality.mapper.QualityInspectParamMapper; |
| | | import com.ruoyi.quality.mapper.QualityTestStandardMapper; |
| | | import com.ruoyi.quality.mapper.QualityTestStandardParamMapper; |
| | | import com.ruoyi.quality.pojo.QualityInspect; |
| | | import com.ruoyi.quality.pojo.QualityInspectParam; |
| | | import com.ruoyi.quality.pojo.QualityTestStandard; |
| | | import com.ruoyi.quality.pojo.QualityTestStandardParam; |
| | | import com.ruoyi.sales.mapper.*; |
| | | import com.ruoyi.sales.pojo.*; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import com.ruoyi.sales.service.impl.CommonFileServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | private final SysUserMapper sysUserMapper; |
| | | private final ISysNoticeService sysNoticeService; |
| | | private final CommonFileMapper fileMapper; |
| | | private final DeviceRepairMapper deviceRepairMapper; |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesQuotationMapper salesQuotationMapper; |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | private final ShippingProductDetailMapper shippingProductDetailMapper; |
| | | private final CommonFileServiceImpl commonFileService; |
| | | private final StockUtils stockUtils; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final QualityInspectMapper qualityInspectMapper; |
| | | private final QualityTestStandardMapper qualityTestStandardMapper; |
| | | private final QualityTestStandardParamMapper qualityTestStandardParamMapper; |
| | | private final QualityInspectParamMapper qualityInspectParamMapper; |
| | | private final ApproveBusinessStatusService approveBusinessStatusService; |
| | | private final FileUtil fileUtil; |
| | | |
| | | |
| | |
| | | } |
| | | approveProcessMapper.updateById(approveProcess); |
| | | |
| | | //éè´å®¡æ ¸ |
| | | if (approveProcess.getApproveType().equals(5)) { |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, approveProcess.getApproveReason()) |
| | | .last("limit 1")); |
| | | if (purchaseLedger != null) { |
| | | 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()) { |
| | | addQualityInspect(purchaseLedger, salesLedgerProduct); |
| | | } else { |
| | | //ç´æ¥å
¥åº |
| | | stockUtils.addStockWithBatchNo(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId()); |
| | | } |
| | | } |
| | | } else if (status.equals(3)) { |
| | | // æç» |
| | | purchaseLedger.setApprovalStatus(4); |
| | | } else if (status.equals(1)) { |
| | | // å®¡æ ¸ä¸ |
| | | purchaseLedger.setApprovalStatus(2); |
| | | } |
| | | purchaseLedgerMapper.updateById(purchaseLedger); |
| | | } |
| | | } |
| | | // é宿¥ä»·ç¶æä¿®æ¹ |
| | | if (approveProcess.getApproveType().equals(6)) { |
| | | SalesQuotation salesQuote = salesQuotationMapper.selectOne(new LambdaQueryWrapper<SalesQuotation>() |
| | | .eq(SalesQuotation::getQuotationNo, approveProcess.getApproveReason()) |
| | | .last("limit 1")); |
| | | // åæ |
| | | if (status.equals(2) && salesQuote != null) { |
| | | salesQuote.setStatus("éè¿"); |
| | | } else if (status.equals(3) && salesQuote != null) { |
| | | salesQuote.setStatus("æç»"); |
| | | } else if (status.equals(1) && salesQuote != null) { |
| | | salesQuote.setStatus("å®¡æ ¸ä¸"); |
| | | } |
| | | salesQuotationMapper.updateById(salesQuote); |
| | | } |
| | | // åºåºå®¡æ¹ä¿®æ¹=åè´§å®¡æ¹ |
| | | if (approveProcess.getApproveType().equals(7)) { |
| | | ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>() |
| | | .eq(ShippingInfo::getShippingNo, approveProcess.getApproveReason()) |
| | | .orderByDesc(ShippingInfo::getCreateTime) |
| | | .last("limit 1")); |
| | | if (shippingInfo != null) { |
| | | if (status.equals(2)) { |
| | | shippingInfo.setStatus("å®¡æ ¸éè¿"); |
| | | shippingInfo.setShippingDate(new Date()); |
| | | //æ´æ¹åºåºå®¡æ ¸ç¶æï¼å¾
ç¡®è®¤æ¹æå¾
å®¡æ ¸ï¼ |
| | | stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId()); |
| | | } else if (status.equals(3)) { |
| | | //å é¤åæ¬ï¼å¾
确认ï¼çåºåºå®¡æ ¸ç¶æ |
| | | stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode()); |
| | | shippingInfo.setStatus("å®¡æ ¸æç»"); |
| | | } else if (status.equals(1)) { |
| | | shippingInfo.setStatus("å®¡æ ¸ä¸"); |
| | | } |
| | | shippingInfoMapper.updateById(shippingInfo); |
| | | } |
| | | |
| | | } |
| | | approveBusinessStatusService.syncBusinessStatus(approveProcess.getApproveType(), approveProcess.getApproveReason(), status); |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVE_NODE, approveNode.getId(), approveNode.getStorageBlobDTOS()); |
| | | } |
| | | |
| | |
| | | return "åå
¬ç¨å审æ¹"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) { |
| | | QualityInspect qualityInspect = new QualityInspect(); |
| | | qualityInspect.setInspectType(0); |
| | | qualityInspect.setSupplier(purchaseLedger.getSupplierName()); |
| | | qualityInspect.setPurchaseLedgerId(purchaseLedger.getId()); |
| | | qualityInspect.setProductId(saleProduct.getProductId()); |
| | | qualityInspect.setProductName(saleProduct.getProductCategory()); |
| | | qualityInspect.setModel(saleProduct.getSpecificationModel()); |
| | | qualityInspect.setProductModelId(saleProduct.getProductModelId()); |
| | | qualityInspect.setUnit(saleProduct.getUnit()); |
| | | qualityInspect.setQuantity(saleProduct.getQuantity()); |
| | | qualityInspectMapper.insert(qualityInspect); |
| | | List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0, null); |
| | | if (qualityTestStandard.size() > 0) { |
| | | qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId()); |
| | | qualityInspectMapper.updateById(qualityInspect); |
| | | qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery() |
| | | .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId())) |
| | | .forEach(qualityTestStandardParam -> { |
| | | QualityInspectParam param = new QualityInspectParam(); |
| | | com.ruoyi.common.utils.bean.BeanUtils.copyProperties(qualityTestStandardParam, param); |
| | | param.setId(null); |
| | | param.setInspectId(qualityInspect.getId()); |
| | | qualityInspectParamMapper.insert(param); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | 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.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.domain.SysNotice; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | |
| | | import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import com.ruoyi.sales.service.impl.CommonFileServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | |
| | | import java.io.IOException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class ApproveProcessServiceImpl extends ServiceImpl<ApproveProcessMapper, ApproveProcess> implements IApproveProcessService { |
| | | private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd"); |
| | | |
| | | private final SysDeptMapper sysDeptMapper; |
| | | private final IApproveNodeService approveNodeService; |
| | | private final SysUserMapper sysUserMapper; |
| | |
| | | private final CommonFileServiceImpl commonFileService; |
| | | private final ISysNoticeService sysNoticeService; |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final StockUtils stockUtils; |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | private final ApproveNodeMapper approveNodeMapper; |
| | | private final ApproveProcessConfigNodeService approveProcessConfigNodeService; |
| | | private final ApproveBusinessStatusService approveBusinessStatusService; |
| | | private final FileUtil fileUtil; |
| | | private final ApproveProcessConfigNodeMapper approveProcessConfigNodeMapper; |
| | | |
| | |
| | | public void addApprove(ApproveProcessVO approveProcessVO) throws Exception { |
| | | SysUser sysUser = SecurityUtils.getLoginUser().getUser(); |
| | | SysDept sysDept = sysDeptMapper.selectDeptById(SecurityUtils.getLoginUser().getCurrentDeptId()); |
| | | List<ApproveProcessConfigNodeVo> list = approveProcessConfigNodeService.listNode(approveProcessVO.getApproveType()); |
| | | if (sysDept == null) throw new RuntimeException("é¨é¨ä¸åå¨"); |
| | | if (sysUser == null) throw new RuntimeException("ç³è¯·äººä¸åå¨"); |
| | | |
| | | List<ApproveProcessConfigNodeVo> list = Optional.ofNullable(approveProcessConfigNodeService.listNode(approveProcessVO.getApproveType())) |
| | | .orElse(Collections.emptyList()); |
| | | List<Long> nodeIds = list.stream() |
| | | .map(ApproveProcessConfigNodeVo::getApproverId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | // æ å®¡æ ¸äººé»è¾æ·»å |
| | | |
| | | // 审æ¹é
ç½®æ²¡æææå®¡æ ¸äººæ¶ï¼ä¸æ°å¢ååå®¡æ¹æµç¨ï¼ç´æ¥æ§è¡ä¸å¡å®¡æ ¸éè¿é»è¾ã |
| | | if (CollectionUtils.isEmpty(nodeIds)) { |
| | | autoPassPurchaseApproveIfNoApprover(approveProcessVO); // éè´åæ å®¡æ ¸äººé»è¾ |
| | | approveBusinessStatusService.syncBusinessStatus(approveProcessVO.getApproveType(), approveProcessVO.getApproveReason(), 2); |
| | | return; |
| | | } |
| | | |
| | | List<SysUser> sysUsers = sysUserMapper.selectUserByIds(nodeIds); |
| | | if (CollectionUtils.isEmpty(sysUsers)) throw new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨"); |
| | | if (sysDept == null) throw new RuntimeException("é¨é¨ä¸åå¨"); |
| | | if (sysUser == null) throw new RuntimeException("ç³è¯·äººä¸åå¨"); |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
| | | ApproveProcess approveProcess = new ApproveProcess(); |
| | | String no = OrderUtils.countTodayByCreateTime(approveProcessMapper, "", "approve_id"); |
| | | approveProcess.setApproveId(no); |
| | | approveProcess.setApproveUser(sysUser.getUserId()); |
| | | approveProcess.setApproveUserName(sysUser.getNickName()); |
| | | approveProcess.setApproveDeptId(sysDept.getDeptId()); |
| | | approveProcess.setApproveUserIds(nodeIds.stream().map(String::valueOf).collect(Collectors.joining(","))); |
| | | approveProcess.setApproveDeptName(sysDept.getDeptName()); |
| | | approveProcess.setApproveUserNames(sysUsers.stream().map(SysUser::getNickName).collect(Collectors.joining(","))); |
| | | approveProcess.setApproveTime(StringUtils.isEmpty(approveProcessVO.getApproveTime()) ? new Date() : dateFormat.parse(approveProcessVO.getApproveTime())); |
| | | approveProcess.setApproveReason(approveProcessVO.getApproveReason()); |
| | | approveProcess.setDeviceRepairId(approveProcessVO.getDeviceRepairId()); |
| | | approveProcess.setMaintenancePrice(approveProcessVO.getMaintenancePrice()); |
| | | approveProcess.setPrice(approveProcessVO.getPrice()); |
| | | approveProcess.setStartDate(approveProcessVO.getStartDate()); |
| | | approveProcess.setEndDate(approveProcessVO.getEndDate()); |
| | | approveProcess.setStartDateTime(approveProcessVO.getStartDateTime()); |
| | | approveProcess.setEndDateTime(approveProcessVO.getEndDateTime()); |
| | | approveProcess.setApproveStatus(0); |
| | | approveProcess.setApproveDelete(0); |
| | | approveProcess.setApproveType(approveProcessVO.getApproveType()); |
| | | approveProcess.setCreateTime(LocalDateTime.now()); |
| | | approveProcess.setTenantId(approveProcessVO.getApproveDeptId()); |
| | | approveProcess.setApproveUserCurrentId(nodeIds.get(0)); |
| | | approveProcess.setApproveUserCurrentName(sysUsers |
| | | .stream() |
| | | .filter(SysUser -> SysUser.getUserId().equals(nodeIds.get(0))) |
| | | .collect(Collectors.toList()) |
| | | .get(0) |
| | | .getNickName()); |
| | | // è®¾ç½®ç¶æä¸ºéæ°æäº¤ |
| | | if (approveProcessVO.getId() != null) { |
| | | ApproveProcess approveProcess1 = approveProcessMapper.selectById(approveProcessVO.getId()); |
| | | approveProcess1.setApproveStatus(4); |
| | | approveProcessMapper.updateById(approveProcess1); |
| | | } |
| | | |
| | | // æå®¡æ ¸äººæ¶ï¼ææ£å¸¸ååå®¡æ¹æµç¨å建审æ¹ä¸»è¡¨ã审æ¹èç¹å¹¶éç¥é¦ä¸ªå®¡æ ¸äººã |
| | | ApproveProcess approveProcess = buildApproveProcess(approveProcessVO, sysUser, sysDept, nodeIds, sysUsers, 0); |
| | | markResubmitted(approveProcessVO); |
| | | save(approveProcess); |
| | | //åå§å审æ¹èç¹ |
| | | String nodeIdStr = nodeIds.stream() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(",")); |
| | | approveNodeService.initApproveNodes(nodeIdStr, no, approveProcessVO.getApproveDeptId()); |
| | | approveNodeService.initApproveNodes(nodeIdStr, approveProcess.getApproveId(), approveProcessVO.getApproveDeptId()); |
| | | // éä»¶ç»å® |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVE_PROCESS, approveProcess.getId(), approveProcessVO.getStorageBlobDTOS()); |
| | | /*æ¶æ¯éç¥*/ |
| | |
| | | } |
| | | } |
| | | |
| | | private void autoPassPurchaseApproveIfNoApprover(ApproveProcessVO approveProcessVO) { |
| | | if (!Objects.equals(approveProcessVO.getApproveType(), 5) |
| | | || !StringUtils.hasText(approveProcessVO.getApproveReason())) { |
| | | throw new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨"); |
| | | private ApproveProcess buildApproveProcess(ApproveProcessVO approveProcessVO, SysUser sysUser, SysDept sysDept, |
| | | List<Long> nodeIds, List<SysUser> sysUsers, Integer approveStatus) throws Exception { |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
| | | ApproveProcess approveProcess = new ApproveProcess(); |
| | | String no = OrderUtils.countTodayByCreateTime(approveProcessMapper, "", "approve_id", approveProcess.getCreateTime() != null ? approveProcess.getCreateTime() : LocalDateTime.now()); |
| | | approveProcess.setApproveId(no); |
| | | approveProcess.setApproveUser(sysUser.getUserId()); |
| | | approveProcess.setApproveUserName(sysUser.getNickName()); |
| | | approveProcess.setApproveDeptId(sysDept.getDeptId()); |
| | | approveProcess.setApproveDeptName(sysDept.getDeptName()); |
| | | approveProcess.setApproveUserIds(nodeIds.stream().map(String::valueOf).collect(Collectors.joining(","))); |
| | | approveProcess.setApproveUserNames(sysUsers.stream().map(SysUser::getNickName).collect(Collectors.joining(","))); |
| | | approveProcess.setApproveTime(StringUtils.isEmpty(approveProcessVO.getApproveTime()) ? new Date() : dateFormat.parse(approveProcessVO.getApproveTime())); |
| | | approveProcess.setApproveReason(approveProcessVO.getApproveReason()); |
| | | approveProcess.setDeviceRepairId(approveProcessVO.getDeviceRepairId()); |
| | | approveProcess.setMaintenancePrice(approveProcessVO.getMaintenancePrice()); |
| | | approveProcess.setPrice(approveProcessVO.getPrice()); |
| | | approveProcess.setStartDate(approveProcessVO.getStartDate()); |
| | | approveProcess.setEndDate(approveProcessVO.getEndDate()); |
| | | approveProcess.setStartDateTime(approveProcessVO.getStartDateTime()); |
| | | approveProcess.setEndDateTime(approveProcessVO.getEndDateTime()); |
| | | approveProcess.setApproveStatus(approveStatus); |
| | | approveProcess.setApproveDelete(0); |
| | | approveProcess.setApproveType(approveProcessVO.getApproveType()); |
| | | approveProcess.setCreateTime(LocalDateTime.now()); |
| | | approveProcess.setTenantId(approveProcessVO.getApproveDeptId()); |
| | | if (!CollectionUtils.isEmpty(nodeIds)) { |
| | | SysUser currentUser = sysUsers.stream() |
| | | .filter(user -> user.getUserId().equals(nodeIds.get(0))) |
| | | .findFirst() |
| | | .orElseThrow(() -> new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨")); |
| | | approveProcess.setApproveUserCurrentId(currentUser.getUserId()); |
| | | approveProcess.setApproveUserCurrentName(currentUser.getNickName()); |
| | | } |
| | | if (approveStatus.equals(2) || approveStatus.equals(3) || approveStatus.equals(4)) { |
| | | approveProcess.setApproveOverTime(new Date()); |
| | | } |
| | | return approveProcess; |
| | | } |
| | | |
| | | purchaseLedgerMapper.update(null, new LambdaUpdateWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, approveProcessVO.getApproveReason()) |
| | | .set(PurchaseLedger::getApprovalStatus, 3)); |
| | | //éè´å
¥åº |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, approveProcessVO.getApproveReason()) |
| | | .last("limit 1")); |
| | | List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new QueryWrapper<SalesLedgerProduct>() |
| | | .lambda().eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()).eq(SalesLedgerProduct::getType, 2)); |
| | | for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) { |
| | | stockUtils.addStockWithBatchNo(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(),purchaseLedger.getPurchaseContractNumber()+"-"+salesLedgerProduct.getId()); |
| | | private void markResubmitted(ApproveProcessVO approveProcessVO) { |
| | | if (approveProcessVO.getId() == null) { |
| | | return; |
| | | } |
| | | ApproveProcess approveProcess = approveProcessMapper.selectById(approveProcessVO.getId()); |
| | | if (approveProcess == null) { |
| | | return; |
| | | } |
| | | approveProcess.setApproveStatus(4); |
| | | approveProcessMapper.updateById(approveProcess); |
| | | } |
| | | |
| | | @Override |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.ruoyi.approve.pojo.FinReimbursementDetail; |
| | | import com.ruoyi.approve.mapper.FinReimbursementDetailMapper; |
| | | import com.ruoyi.approve.service.FinReimbursementDetailService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éåæç»è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:38 |
| | | */ |
| | | @Service |
| | | public class FinReimbursementDetailServiceImpl extends ServiceImpl<FinReimbursementDetailMapper, FinReimbursementDetail> implements FinReimbursementDetailService { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeApproverDto; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeDto; |
| | | import com.ruoyi.approve.bean.dto.FinReimbursementDto; |
| | | import com.ruoyi.approve.bean.vo.FinReimbursementVo; |
| | | import com.ruoyi.approve.mapper.ApprovalInstanceMapper; |
| | | import com.ruoyi.approve.mapper.FinReimbursementDetailMapper; |
| | | import com.ruoyi.approve.mapper.FinReimbursementMapper; |
| | | import com.ruoyi.approve.mapper.FinReimbursementTravelMapper; |
| | | import com.ruoyi.approve.pojo.*; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.approve.service.*; |
| | | import com.ruoyi.common.enums.TypeEnums; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.project.system.service.ISysNoticeService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éå主表 æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:15 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class FinReimbursementServiceImpl extends ServiceImpl<FinReimbursementMapper, FinReimbursement> implements FinReimbursementService { |
| | | |
| | | private static final String BILL_STATUS_DRAFT = "DRAFT"; |
| | | private static final String BILL_STATUS_IN_APPROVAL = "IN_APPROVAL"; |
| | | private static final String NODE_STATUS_WAITING = "WAITING"; |
| | | |
| | | private final ApprovalInstanceMapper approvalInstanceMapper; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | private final ApprovalInstanceNodeService approvalInstanceNodeService; |
| | | private final ApprovalTaskService approvalTaskService; |
| | | private final ApprovalRecordService approvalRecordService; |
| | | private final FinReimbursementMapper finReimbursementMapper; |
| | | private final FinReimbursementTravelMapper finReimbursementTravelMapper; |
| | | private final FinReimbursementDetailMapper finReimbursementDetailMapper; |
| | | private final FileUtil fileUtil; |
| | | private final ISysNoticeService sysNoticeService; |
| | | @Override |
| | | public IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page) { |
| | | IPage<FinReimbursementVo> finReimbursementVoIPage = finReimbursementMapper.listPage(finReimbursementDto, page); |
| | | finReimbursementVoIPage.getRecords().forEach(vo -> { |
| | | vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_REIMBURSEMENT, vo.getId())); |
| | | }); |
| | | |
| | | return finReimbursementVoIPage; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean add(FinReimbursementDto finReimbursementDto) { |
| | | String billStatus = validateAddParam(finReimbursementDto); |
| | | |
| | | // çææ¥éåå· |
| | | String billNo = OrderUtils.countTodayByCreateTime(finReimbursementMapper, "BXD", "bill_no", finReimbursementDto.getCreateTime() != null ? finReimbursementDto.getCreateTime() : LocalDateTime.now()); |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | BigDecimal totalAmount = details.stream() |
| | | .map(FinReimbursementDetail::getAmount) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | FinReimbursement reimbursement = buildReimbursement(finReimbursementDto, billNo, totalAmount, billStatus); |
| | | // ä¿åæ¥éå主表 |
| | | boolean saved = this.save(reimbursement); |
| | | if (!saved || reimbursement.getId() == null) { |
| | | throw new ServiceException("æ°å¢æ¥éå失败"); |
| | | } |
| | | Long reimbursementId = reimbursement.getId(); |
| | | |
| | | // ä¿åå·®æ
æ¥éæ©å±ä¿¡æ¯ï¼æ¥éç±»å为差æ
æ¥éæ¶ï¼ |
| | | FinReimbursementTravel travel = finReimbursementDto.getTravel(); |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType())) { |
| | | travel.setReimbursementId(reimbursementId); |
| | | int travelRows = finReimbursementTravelMapper.insert(travel); |
| | | if (travelRows != 1) { |
| | | throw new ServiceException("æ°å¢å·®æ
æ¥éæ©å±ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | // ä¿åæ¥éåæç» |
| | | for (int i = 0; i < details.size(); i++) { |
| | | FinReimbursementDetail detail = details.get(i); |
| | | detail.setId(null); |
| | | detail.setReimbursementId(reimbursementId); |
| | | detail.setRowNo(i + 1); |
| | | int detailRows = finReimbursementDetailMapper.insert(detail); |
| | | if (detailRows != 1) { |
| | | throw new ServiceException("æ°å¢æ¥éåæç»å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | startApproval(reimbursement, finReimbursementDto); |
| | | } |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, reimbursementId, finReimbursementDto.getStorageBlobDTOs()); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean update(FinReimbursementDto finReimbursementDto) { |
| | | String billStatus = validateUpdateParam(finReimbursementDto); |
| | | |
| | | Long reimbursementId = finReimbursementDto.getId(); |
| | | FinReimbursement existing = finReimbursementMapper.selectById(reimbursementId); |
| | | if (existing == null) { |
| | | throw new ServiceException("æ¥éåä¸åå¨"); |
| | | } |
| | | |
| | | // è®¡ç®æç»æ±æ»éé¢ |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | BigDecimal totalAmount = details.stream() |
| | | .map(FinReimbursementDetail::getAmount) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | // æ´æ°ä¸»è¡¨ |
| | | FinReimbursement reimbursement = buildReimbursement( |
| | | finReimbursementDto, |
| | | existing.getBillNo(), |
| | | totalAmount, |
| | | billStatus |
| | | ); |
| | | reimbursement.setId(reimbursementId); |
| | | int mainRows = finReimbursementMapper.updateById(reimbursement); |
| | | if (mainRows != 1) { |
| | | throw new ServiceException("æ´æ°æ¥éå主表失败"); |
| | | } |
| | | |
| | | // æ¥è¯¢æ°æ®åºä¸å·²æçæç» |
| | | List<FinReimbursementDetail> existingDetails = finReimbursementDetailMapper.selectList( |
| | | new LambdaQueryWrapper<FinReimbursementDetail>() |
| | | .eq(FinReimbursementDetail::getReimbursementId, reimbursementId)); |
| | | Set<Long> existingDetailIds = existingDetails.stream() |
| | | .map(FinReimbursementDetail::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | |
| | | // æ°æç»ä¸æIDç â æ´æ°ï¼æ IDç â æ°å¢ |
| | | Set<Long> submittedDetailIds = new HashSet<>(); |
| | | for (int i = 0; i < details.size(); i++) { |
| | | FinReimbursementDetail detail = details.get(i); |
| | | detail.setReimbursementId(reimbursementId); |
| | | detail.setRowNo(i + 1); |
| | | if (detail.getId() != null && existingDetailIds.contains(detail.getId())) { |
| | | finReimbursementDetailMapper.updateById(detail); |
| | | submittedDetailIds.add(detail.getId()); |
| | | } else { |
| | | detail.setId(null); |
| | | finReimbursementDetailMapper.insert(detail); |
| | | } |
| | | } |
| | | |
| | | // æ°æ®åºä¸å·²æä½æ°æç»ä¸æ²¡æç â å é¤ |
| | | for (Long existingId : existingDetailIds) { |
| | | if (!submittedDetailIds.contains(existingId)) { |
| | | finReimbursementDetailMapper.deleteById(existingId); |
| | | } |
| | | } |
| | | |
| | | // å·®æ
æ©å±ï¼æåæ´æ°ï¼æ åæ°å¢ |
| | | FinReimbursementTravel existingTravel = finReimbursementTravelMapper.selectOne( |
| | | new LambdaQueryWrapper<FinReimbursementTravel>() |
| | | .eq(FinReimbursementTravel::getReimbursementId, reimbursementId) |
| | | .last("LIMIT 1")); |
| | | FinReimbursementTravel travel = finReimbursementDto.getTravel(); |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && travel != null) { |
| | | travel.setReimbursementId(reimbursementId); |
| | | if (existingTravel != null) { |
| | | travel.setId(existingTravel.getId()); |
| | | finReimbursementTravelMapper.updateById(travel); |
| | | } else { |
| | | travel.setId(null); |
| | | finReimbursementTravelMapper.insert(travel); |
| | | } |
| | | } |
| | | |
| | | resetApprovalFlow(existing, reimbursementId); |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | reimbursement.setApprovalInstanceId(null); |
| | | startApproval(reimbursement, finReimbursementDto); |
| | | } |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, reimbursementId, finReimbursementDto.getStorageBlobDTOs()); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public FinReimbursementVo detail(Long id) { |
| | | if (id == null ) { |
| | | throw new ServiceException("æ¥éåIDä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | FinReimbursement reimbursement = finReimbursementMapper.selectById(id); |
| | | if (reimbursement == null) { |
| | | throw new ServiceException("æ¥éåä¸åå¨"); |
| | | } |
| | | |
| | | FinReimbursementVo vo = new FinReimbursementVo(); |
| | | vo.setId(reimbursement.getId()); |
| | | vo.setBillNo(reimbursement.getBillNo()); |
| | | vo.setReimbursementType(reimbursement.getReimbursementType()); |
| | | vo.setExpenseType(reimbursement.getExpenseType()); |
| | | vo.setApplicantId(reimbursement.getApplicantId()); |
| | | vo.setApplicantCode(reimbursement.getApplicantCode()); |
| | | vo.setApplicantName(reimbursement.getApplicantName()); |
| | | vo.setApplicantDeptId(reimbursement.getApplicantDeptId()); |
| | | vo.setApplicantDeptName(reimbursement.getApplicantDeptName()); |
| | | vo.setReason(reimbursement.getReason()); |
| | | vo.setApplyAmount(reimbursement.getApplyAmount()); |
| | | vo.setDetailTotalAmount(reimbursement.getDetailTotalAmount()); |
| | | vo.setPayeeName(reimbursement.getPayeeName()); |
| | | vo.setPayeeAccount(reimbursement.getPayeeAccount()); |
| | | vo.setPayeeBank(reimbursement.getPayeeBank()); |
| | | vo.setApprovalInstanceId(reimbursement.getApprovalInstanceId()); |
| | | vo.setApproveProcessId(reimbursement.getApproveProcessId()); |
| | | vo.setBillStatus(reimbursement.getBillStatus()); |
| | | vo.setApprovedTime(reimbursement.getApprovedTime()); |
| | | vo.setPaidTime(reimbursement.getPaidTime()); |
| | | vo.setAccountExpenseId(reimbursement.getAccountExpenseId()); |
| | | vo.setRemark(reimbursement.getRemark()); |
| | | vo.setTenantId(reimbursement.getTenantId()); |
| | | vo.setCreateUser(reimbursement.getCreateUser()); |
| | | vo.setCreateTime(reimbursement.getCreateTime()); |
| | | vo.setUpdateUser(reimbursement.getUpdateUser()); |
| | | vo.setUpdateTime(reimbursement.getUpdateTime()); |
| | | vo.setDeptId(reimbursement.getDeptId()); |
| | | vo.setDeleted(reimbursement.getDeleted()); |
| | | |
| | | vo.setDetails(finReimbursementDetailMapper.selectList( |
| | | new LambdaQueryWrapper<FinReimbursementDetail>() |
| | | .eq(FinReimbursementDetail::getReimbursementId, reimbursement.getId()) |
| | | .orderByAsc(FinReimbursementDetail::getRowNo) |
| | | )); |
| | | |
| | | if (isTravelReimbursement(reimbursement.getReimbursementType())) { |
| | | vo.setTravel(finReimbursementTravelMapper.selectOne( |
| | | new LambdaQueryWrapper<FinReimbursementTravel>() |
| | | .eq(FinReimbursementTravel::getReimbursementId, reimbursement.getId()) |
| | | .last("LIMIT 1") |
| | | )); |
| | | } |
| | | vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_REIMBURSEMENT, reimbursement.getId())); |
| | | //审æ¹è®°å½è¿å |
| | | vo.setTasks(approvalTaskService.list(new LambdaQueryWrapper<ApprovalTask>().eq(ApprovalTask::getInstanceId, reimbursement.getApprovalInstanceId()))); |
| | | vo.setRecords(approvalRecordService.list(new LambdaQueryWrapper<ApprovalRecord>().eq(ApprovalRecord::getInstanceId, reimbursement.getApprovalInstanceId()))); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean delete(List<Long> ids) { |
| | | fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, ids); |
| | | //å
å é¤æç» |
| | | finReimbursementDetailMapper.delete(new LambdaQueryWrapper<FinReimbursementDetail>().in(FinReimbursementDetail::getReimbursementId, ids)); |
| | | //å é¤å·®æ
|
| | | finReimbursementTravelMapper.delete(new LambdaQueryWrapper<FinReimbursementTravel>().in(FinReimbursementTravel::getReimbursementId, ids)); |
| | | //å é¤ä¸»è¡¨ |
| | | int rows = finReimbursementMapper.delete(new LambdaQueryWrapper<FinReimbursement>().in(FinReimbursement::getId, ids)); |
| | | return rows == ids.size(); |
| | | } |
| | | |
| | | private String validateUpdateParam(FinReimbursementDto finReimbursementDto) { |
| | | if (finReimbursementDto == null || finReimbursementDto.getId() == null) { |
| | | throw new ServiceException("æ¥éåIDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (finReimbursementDto.getReimbursementType() == null) { |
| | | throw new ServiceException("æ¥éç±»åä¸è½ä¸ºç©º"); |
| | | } |
| | | String billStatus = normalizeBillStatus(finReimbursementDto.getBillStatus()); |
| | | if (billStatus == null) { |
| | | throw new ServiceException("åæ®ç¶æåªæ¯æ DRAFT æ IN_APPROVAL"); |
| | | } |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | validateApprovalNodes(finReimbursementDto.getNodes()); |
| | | } |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | if (details == null || details.isEmpty()) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | for (FinReimbursementDetail detail : details) { |
| | | if (detail == null) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (detail.getAmount() == null || detail.getAmount().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("æ¥éåæç»éé¢å¿
须大äº0"); |
| | | } |
| | | } |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() == null) { |
| | | throw new ServiceException("å·®æ
æ¥éå¿
须填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | if (!isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() != null) { |
| | | throw new ServiceException("éå·®æ
æ¥éä¸å
许填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | return billStatus; |
| | | } |
| | | |
| | | private String validateAddParam(FinReimbursementDto finReimbursementDto) { |
| | | if (finReimbursementDto == null) { |
| | | throw new ServiceException("æ¥éåæ°æ®ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (finReimbursementDto.getReimbursementType() == null) { |
| | | throw new ServiceException("æ¥éç±»åä¸è½ä¸ºç©º"); |
| | | } |
| | | String billStatus = normalizeBillStatus(finReimbursementDto.getBillStatus()); |
| | | if (billStatus == null) { |
| | | throw new ServiceException("åæ®ç¶æåªæ¯æ DRAFT æ IN_APPROVAL"); |
| | | } |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | validateApprovalNodes(finReimbursementDto.getNodes()); |
| | | } |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | if (details == null || details.isEmpty()) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | for (FinReimbursementDetail detail : details) { |
| | | if (detail == null) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (detail.getAmount() == null || detail.getAmount().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("æ¥éåæç»éé¢å¿
须大äº0"); |
| | | } |
| | | } |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() == null) { |
| | | throw new ServiceException("å·®æ
æ¥éå¿
须填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | if (!isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() != null) { |
| | | throw new ServiceException("éå·®æ
æ¥éä¸å
许填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | return billStatus; |
| | | } |
| | | |
| | | private FinReimbursement buildReimbursement(FinReimbursementDto finReimbursementDto, String billNo, BigDecimal totalAmount, String billStatus) { |
| | | FinReimbursement reimbursement = new FinReimbursement(); |
| | | reimbursement.setId(null); |
| | | reimbursement.setBillNo(billNo); |
| | | reimbursement.setReimbursementType(finReimbursementDto.getReimbursementType()); |
| | | reimbursement.setExpenseType(finReimbursementDto.getExpenseType()); |
| | | reimbursement.setApplicantId(finReimbursementDto.getApplicantId()); |
| | | reimbursement.setApplicantCode(finReimbursementDto.getApplicantCode()); |
| | | reimbursement.setApplicantName(finReimbursementDto.getApplicantName()); |
| | | reimbursement.setApplicantDeptId(finReimbursementDto.getApplicantDeptId()); |
| | | reimbursement.setApplicantDeptName(finReimbursementDto.getApplicantDeptName()); |
| | | reimbursement.setReason(finReimbursementDto.getReason()); |
| | | reimbursement.setApplyAmount(finReimbursementDto.getApplyAmount()); |
| | | reimbursement.setDetailTotalAmount(totalAmount); |
| | | reimbursement.setPayeeName(finReimbursementDto.getPayeeName()); |
| | | reimbursement.setPayeeAccount(finReimbursementDto.getPayeeAccount()); |
| | | reimbursement.setPayeeBank(finReimbursementDto.getPayeeBank()); |
| | | reimbursement.setRemark(finReimbursementDto.getRemark()); |
| | | reimbursement.setTenantId(finReimbursementDto.getTenantId()); |
| | | reimbursement.setApproveProcessId(null); |
| | | reimbursement.setBillStatus(billStatus); |
| | | return reimbursement; |
| | | } |
| | | |
| | | private void startApproval(FinReimbursement reimbursement, FinReimbursementDto finReimbursementDto) { |
| | | Long businessType = resolveBusinessType(finReimbursementDto.getReimbursementType()); |
| | | ApprovalInstanceDto approvalInstanceDto = new ApprovalInstanceDto(); |
| | | approvalInstanceDto.setInstanceNo(OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no", approvalInstanceDto.getCreateTime() != null ? approvalInstanceDto.getCreateTime() : LocalDateTime.now())); |
| | | approvalInstanceDto.setBusinessId(reimbursement.getId()); |
| | | approvalInstanceDto.setTemplateId(null); |
| | | approvalInstanceDto.setTemplateName(TypeEnums.getLabelByValue(businessType) + "审æ¹"); |
| | | approvalInstanceDto.setBusinessType(businessType); |
| | | approvalInstanceDto.setTitle("æ¥éåå·ï¼" + reimbursement.getBillNo()); |
| | | approvalInstanceDto.setApplicantId(reimbursement.getApplicantId() != null ? reimbursement.getApplicantId() : SecurityUtils.getUserId()); |
| | | approvalInstanceDto.setApplicantName(reimbursement.getApplicantName() != null ? reimbursement.getApplicantName() : SecurityUtils.getLoginUser().getNickName()); |
| | | approvalInstanceDto.setApplyTime(LocalDateTime.now()); |
| | | approvalInstanceDto.setStatus("PENDING"); |
| | | approvalInstanceDto.setCurrentLevel(1); |
| | | |
| | | boolean approvalSaved = approvalInstanceService.save(approvalInstanceDto); |
| | | if (!approvalSaved || approvalInstanceDto.getId() == null) { |
| | | throw new ServiceException("å起审æ¹å¤±è´¥"); |
| | | } |
| | | List<ApprovalTask> firstTasks = createApprovalNodes(approvalInstanceDto, finReimbursementDto.getNodes()); |
| | | sendApproveNotice(approvalInstanceDto, firstTasks); |
| | | |
| | | FinReimbursement update = new FinReimbursement(); |
| | | update.setId(reimbursement.getId()); |
| | | update.setApprovalInstanceId(approvalInstanceDto.getId()); |
| | | update.setBillStatus(BILL_STATUS_IN_APPROVAL); |
| | | int rows = finReimbursementMapper.updateById(update); |
| | | if (rows != 1) { |
| | | throw new ServiceException("å填审æ¹å®ä¾å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | private List<ApprovalTask> createApprovalNodes(ApprovalInstanceDto approvalInstanceDto, List<ApprovalTemplateNodeDto> nodes) { |
| | | List<ApprovalTask> firstTasks = Collections.emptyList(); |
| | | for (int i = 0; i < nodes.size(); i++) { |
| | | ApprovalTemplateNodeDto nodeDto = nodes.get(i); |
| | | ApprovalInstanceNode instanceNode = new ApprovalInstanceNode(); |
| | | instanceNode.setInstanceId(approvalInstanceDto.getId()); |
| | | instanceNode.setLevelNo(nodeDto.getLevelNo()); |
| | | instanceNode.setApproveType(nodeDto.getApproveType()); |
| | | instanceNode.setStatus(i == 0 ? "PENDING" : NODE_STATUS_WAITING); |
| | | instanceNode.setStartTime(i == 0 ? LocalDateTime.now() : null); |
| | | instanceNode.setDeleted((byte) 0); |
| | | approvalInstanceNodeService.save(instanceNode); |
| | | |
| | | List<ApprovalTask> tasks = nodeDto.getApprovers().stream().map(approver -> { |
| | | ApprovalTask task = new ApprovalTask(); |
| | | task.setInstanceId(approvalInstanceDto.getId()); |
| | | task.setNodeId(instanceNode.getId()); |
| | | task.setLevelNo(instanceNode.getLevelNo()); |
| | | task.setApproverId(approver.getApproverId()); |
| | | task.setApproverName(approver.getApproverName()); |
| | | task.setTaskStatus("PENDING"); |
| | | task.setIsRead((byte) 0); |
| | | task.setDeleted((byte) 0); |
| | | return task; |
| | | }).collect(Collectors.toList()); |
| | | approvalTaskService.saveBatch(tasks); |
| | | |
| | | if (i == 0) { |
| | | firstTasks = tasks; |
| | | ApprovalRecord record = new ApprovalRecord(); |
| | | record.setInstanceId(approvalInstanceDto.getId()); |
| | | record.setNodeId(instanceNode.getId()); |
| | | record.setOperatorId(approvalInstanceDto.getApplicantId()); |
| | | record.setOperatorName(approvalInstanceDto.getApplicantName()); |
| | | record.setAction("SUBMIT"); |
| | | record.setComment("å起审æ¹"); |
| | | record.setDeleted((byte) 0); |
| | | approvalRecordService.save(record); |
| | | } |
| | | } |
| | | return firstTasks; |
| | | } |
| | | |
| | | private void validateApprovalNodes(List<ApprovalTemplateNodeDto> nodes) { |
| | | if (nodes == null || nodes.isEmpty()) { |
| | | throw new ServiceException("æäº¤å®¡æ¹æ¶å®¡æ¹èç¹ä¸è½ä¸ºç©º"); |
| | | } |
| | | for (int i = 0; i < nodes.size(); i++) { |
| | | ApprovalTemplateNodeDto node = nodes.get(i); |
| | | if (node == null) { |
| | | throw new ServiceException("审æ¹èç¹ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (node.getLevelNo() == null) { |
| | | node.setLevelNo(i + 1); |
| | | } |
| | | if (!StringUtils.hasText(node.getApproveType())) { |
| | | throw new ServiceException("审æ¹èç¹å®¡æ¹æ¹å¼ä¸è½ä¸ºç©º"); |
| | | } |
| | | List<ApprovalTemplateNodeApproverDto> approvers = node.getApprovers(); |
| | | if (approvers == null || approvers.isEmpty()) { |
| | | throw new ServiceException("审æ¹èç¹å®¡æ¹äººä¸è½ä¸ºç©º"); |
| | | } |
| | | for (ApprovalTemplateNodeApproverDto approver : approvers) { |
| | | if (approver == null || approver.getApproverId() == null) { |
| | | throw new ServiceException("审æ¹äººä¸è½ä¸ºç©º"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void sendApproveNotice(ApprovalInstanceDto instance, List<ApprovalTask> tasks) { |
| | | if (instance == null || tasks == null || tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> approverIds = tasks.stream() |
| | | .map(ApprovalTask::getApproverId) |
| | | .filter(id -> id != null && id > 0) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (approverIds.isEmpty()) { |
| | | return; |
| | | } |
| | | String title = "æ¥é审æ¹"; |
| | | String message = "审æ¹åå· " + instance.getInstanceNo() + " éè¦æ¨å®¡æ¹"; |
| | | String jumpPath = "/approvalInstance?id=" + instance.getId(); |
| | | sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath); |
| | | } |
| | | |
| | | private void resetApprovalFlow(FinReimbursement existing, Long reimbursementId) { |
| | | if (existing == null || existing.getApprovalInstanceId() == null) { |
| | | return; |
| | | } |
| | | Long approvalInstanceId = existing.getApprovalInstanceId(); |
| | | if (!"REJECTED".equals(existing.getBillStatus())) { |
| | | approvalInstanceService.delete(Collections.singletonList(approvalInstanceId)); |
| | | } |
| | | clearApprovalBinding(reimbursementId); |
| | | } |
| | | |
| | | private void clearApprovalBinding(Long reimbursementId) { |
| | | int rows = finReimbursementMapper.update( |
| | | null, |
| | | Wrappers.<FinReimbursement>lambdaUpdate() |
| | | .eq(FinReimbursement::getId, reimbursementId) |
| | | .set(FinReimbursement::getApprovalInstanceId, null) |
| | | ); |
| | | if (rows != 1) { |
| | | throw new ServiceException("éç½®å®¡æ¹æµç¨å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | private Long resolveBusinessType(Byte reimbursementType) { |
| | | return isTravelReimbursement(reimbursementType) |
| | | ? TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode() |
| | | : TypeEnums.EXPENSE_APPROVAL.getCode(); |
| | | } |
| | | |
| | | private String normalizeBillStatus(String billStatus) { |
| | | if (billStatus == null) { |
| | | return BILL_STATUS_DRAFT; |
| | | } |
| | | String normalized = billStatus.trim().toUpperCase(); |
| | | if (BILL_STATUS_DRAFT.equals(normalized) || BILL_STATUS_IN_APPROVAL.equals(normalized)) { |
| | | return normalized; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean isTravelReimbursement(Byte reimbursementType) { |
| | | return Byte.valueOf((byte) 1).equals(reimbursementType); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.ruoyi.approve.pojo.FinReimbursementTravel; |
| | | import com.ruoyi.approve.mapper.FinReimbursementTravelMapper; |
| | | import com.ruoyi.approve.service.FinReimbursementTravelService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * å·®æ
æ¥éæ©å±è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:47 |
| | | */ |
| | | @Service |
| | | public class FinReimbursementTravelServiceImpl extends ServiceImpl<FinReimbursementTravelMapper, FinReimbursementTravel> implements FinReimbursementTravelService { |
| | | |
| | | } |
| | |
| | | package com.ruoyi.approve.utils; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.ruoyi.approve.pojo.ApprovalInstanceNode; |
| | | import com.ruoyi.approve.pojo.ApprovalRecord; |
| | | import com.ruoyi.approve.pojo.ApprovalTask; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNode; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover; |
| | | import com.ruoyi.approve.service.ApprovalInstanceNodeService; |
| | | import com.ruoyi.approve.service.ApprovalRecordService; |
| | | import com.ruoyi.approve.service.ApprovalTaskService; |
| | | import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService; |
| | | import com.ruoyi.approve.service.ApprovalTemplateNodeService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * å®¡æ¹æµç¨èç¹å·¥å
·ç±» |
| | | */ |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class ApproveProcessConfigNodeUtils { |
| | | |
| | | private final ApprovalInstanceNodeService instanceNodeService; |
| | | private final ApprovalTaskService approvalTaskService; |
| | | private final ApprovalRecordService approvalRecordService; |
| | | private final ApprovalTemplateNodeService approvalTemplateNodeService; |
| | | private final ApprovalTemplateNodeApproverService approvalTemplateNodeApproverService; |
| | | |
| | | /** |
| | | * æå½åå±çº§å建审æ¹èç¹å审æ¹ä»»å¡ã |
| | | * 该éè½½ä¼åæ¶åå
¥ä¸æ¡å起审æ¹è®°å½ã |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance) { |
| | | return createCurrentNodeAndTasks(instance, true); |
| | | } |
| | | |
| | | /** |
| | | * æå½åå±çº§å建审æ¹èç¹å审æ¹ä»»å¡ã |
| | | * |
| | | * @param instance 审æ¹å®ä¾ |
| | | * @param createSubmitRecord æ¯å¦å建å起审æ¹è®°å½ |
| | | * @return å建åºçå½åèç¹å®ä¾ |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance, boolean createSubmitRecord) { |
| | | if (instance == null || instance.getId() == null) { |
| | | throw new RuntimeException("审æ¹å®ä¾ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (instance.getTemplateId() == null) { |
| | | throw new RuntimeException("å®¡æ¹æ¨¡æ¿ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | Integer currentLevel = instance.getCurrentLevel() == null ? 1 : instance.getCurrentLevel(); |
| | | |
| | | ApprovalInstanceNode existsNode = instanceNodeService.getOne( |
| | | new LambdaQueryWrapper<ApprovalInstanceNode>() |
| | | .eq(ApprovalInstanceNode::getInstanceId, instance.getId()) |
| | | .eq(ApprovalInstanceNode::getLevelNo, currentLevel) |
| | | .eq(ApprovalInstanceNode::getDeleted, 0) |
| | | .last("LIMIT 1") |
| | | ); |
| | | if (existsNode != null) { |
| | | return existsNode; |
| | | } |
| | | |
| | | ApprovalTemplateNode templateNode = approvalTemplateNodeService.getOne( |
| | | new LambdaQueryWrapper<ApprovalTemplateNode>() |
| | | .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId()) |
| | | .eq(ApprovalTemplateNode::getLevelNo, currentLevel) |
| | | .orderByAsc(ApprovalTemplateNode::getId) |
| | | .last("LIMIT 1") |
| | | ); |
| | | if (templateNode == null) { |
| | | throw new RuntimeException("æªæ¾å°å½åå±çº§å¯¹åºçå®¡æ¹æ¨¡æ¿èç¹"); |
| | | } |
| | | |
| | | List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverService.list( |
| | | new LambdaQueryWrapper<ApprovalTemplateNodeApprover>() |
| | | .eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId()) |
| | | .eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId()) |
| | | .eq(ApprovalTemplateNodeApprover::getDeleted, 0L) |
| | | .orderByAsc(ApprovalTemplateNodeApprover::getSortNo) |
| | | ); |
| | | if (approvers == null || approvers.isEmpty()) { |
| | | throw new RuntimeException("å½å审æ¹èç¹æªé
置审æ¹äºº"); |
| | | } |
| | | |
| | | ApprovalInstanceNode instanceNode = new ApprovalInstanceNode(); |
| | | instanceNode.setInstanceId(instance.getId()); |
| | | instanceNode.setLevelNo(templateNode.getLevelNo()); |
| | | instanceNode.setApproveType(templateNode.getApproveType()); |
| | | instanceNode.setStatus("PENDING"); |
| | | instanceNode.setStartTime(LocalDateTime.now()); |
| | | instanceNode.setDeleted((byte) 0); |
| | | instanceNodeService.save(instanceNode); |
| | | |
| | | List<ApprovalTask> taskList = new ArrayList<>(approvers.size()); |
| | | for (ApprovalTemplateNodeApprover approver : approvers) { |
| | | ApprovalTask task = new ApprovalTask(); |
| | | task.setInstanceId(instance.getId()); |
| | | task.setNodeId(instanceNode.getId()); |
| | | task.setLevelNo(instanceNode.getLevelNo()); |
| | | task.setApproverId(approver.getApproverId()); |
| | | task.setApproverName(approver.getApproverName()); |
| | | task.setTaskStatus("PENDING"); |
| | | task.setIsRead((byte) 0); |
| | | task.setDeleted((byte) 0); |
| | | taskList.add(task); |
| | | } |
| | | approvalTaskService.saveBatch(taskList); |
| | | |
| | | if (createSubmitRecord) { |
| | | ApprovalRecord record = new ApprovalRecord(); |
| | | record.setInstanceId(instance.getId()); |
| | | record.setNodeId(instanceNode.getId()); |
| | | record.setOperatorId(instance.getApplicantId()); |
| | | record.setOperatorName(instance.getApplicantName()); |
| | | record.setAction("SUBMIT"); |
| | | record.setComment("å起审æ¹"); |
| | | record.setDeleted((byte) 0); |
| | | approvalRecordService.save(record); |
| | | } |
| | | |
| | | return instanceNode; |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åå¾
å¤çèç¹ã |
| | | */ |
| | | public ApprovalInstanceNode getCurrentNode(Long instanceId) { |
| | | if (instanceId == null) { |
| | | return null; |
| | | } |
| | | |
| | | return instanceNodeService.getOne( |
| | | new LambdaQueryWrapper<ApprovalInstanceNode>() |
| | | .eq(ApprovalInstanceNode::getInstanceId, instanceId) |
| | | .eq(ApprovalInstanceNode::getStatus, "PENDING") |
| | | .eq(ApprovalInstanceNode::getDeleted, 0) |
| | | .orderByAsc(ApprovalInstanceNode::getLevelNo) |
| | | .last("LIMIT 1") |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½å审æ¹å±çº§ã |
| | | */ |
| | | public Integer getCurrentLevel(Long instanceId) { |
| | | ApprovalInstanceNode currentNode = getCurrentNode(instanceId); |
| | | return currentNode != null ? currentNode.getLevelNo() : null; |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åèç¹ä¸çå¾
审æ¹ä»»å¡ã |
| | | */ |
| | | public List<ApprovalTask> getCurrentPendingTasks(Long instanceId) { |
| | | if (instanceId == null) { |
| | | return List.of(); |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = getCurrentNode(instanceId); |
| | | if (currentNode == null) { |
| | | return List.of(); |
| | | } |
| | | |
| | | return approvalTaskService.list( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getNodeId, currentNode.getId()) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | .orderByAsc(ApprovalTask::getLevelNo) |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åç¨æ·å¨å½åèç¹ä¸çå¾
审æ¹ä»»å¡ã |
| | | */ |
| | | public ApprovalTask getCurrentUserTask(Long instanceId, Long userId) { |
| | | if (instanceId == null || userId == null) { |
| | | return null; |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = getCurrentNode(instanceId); |
| | | if (currentNode == null) { |
| | | return null; |
| | | } |
| | | |
| | | return approvalTaskService.getOne( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getNodeId, currentNode.getId()) |
| | | .eq(ApprovalTask::getApproverId, userId) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | .last("LIMIT 1") |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 夿å½åç¨æ·æ¯å¦æ¯å½å审æ¹äººã |
| | | */ |
| | | public boolean isCurrentApprover(Long instanceId, Long userId) { |
| | | return getCurrentUserTask(instanceId, userId) != null; |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åèç¹ç审æ¹äºº ID å表ã |
| | | */ |
| | | public List<Long> getCurrentNodeApproverIds(Long instanceId) { |
| | | return getCurrentPendingTasks(instanceId).stream() |
| | | .map(ApprovalTask::getApproverId) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åèç¹å©ä½å¾
审æ¹äººæ°ã |
| | | */ |
| | | public int getRemainingApproverCount(Long instanceId) { |
| | | return getCurrentPendingTasks(instanceId).size(); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åèç¹å·²åæäººæ°ã |
| | | */ |
| | | public int getApprovedCount(Long instanceId) { |
| | | if (instanceId == null) { |
| | | return 0; |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = getCurrentNode(instanceId); |
| | | if (currentNode == null) { |
| | | return 0; |
| | | } |
| | | |
| | | return Math.toIntExact(approvalTaskService.count( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getNodeId, currentNode.getId()) |
| | | .eq(ApprovalTask::getTaskStatus, "APPROVED") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | )); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åèç¹å·²æç»äººæ°ã |
| | | */ |
| | | public int getRejectedCount(Long instanceId) { |
| | | if (instanceId == null) { |
| | | return 0; |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = getCurrentNode(instanceId); |
| | | if (currentNode == null) { |
| | | return 0; |
| | | } |
| | | |
| | | return Math.toIntExact(approvalTaskService.count( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getNodeId, currentNode.getId()) |
| | | .eq(ApprovalTask::getTaskStatus, "REJECTED") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | )); |
| | | } |
| | | |
| | | /** |
| | | * 夿å½åèç¹æ¯å¦å¯ä»¥æµè½¬å°ä¸ä¸å±ã |
| | | */ |
| | | public boolean canProceedToNextLevel(Long instanceId, String approveType) { |
| | | if (instanceId == null || approveType == null) { |
| | | return false; |
| | | } |
| | | |
| | | if (getRejectedCount(instanceId) > 0) { |
| | | return false; |
| | | } |
| | | |
| | | int totalApproverCount = getCurrentPendingTasks(instanceId).size() + getApprovedCount(instanceId); |
| | | int approvedCount = getApprovedCount(instanceId); |
| | | |
| | | if ("AND".equalsIgnoreCase(approveType)) { |
| | | return approvedCount > 0 && approvedCount == totalApproverCount; |
| | | } |
| | | if ("OR".equalsIgnoreCase(approveType)) { |
| | | return approvedCount > 0; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½åç¨æ·å¨å½åèç¹ä¸çä»»å¡ç¶æã |
| | | */ |
| | | public String getUserTaskStatus(Long instanceId, Long userId) { |
| | | if (instanceId == null || userId == null) { |
| | | return null; |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = getCurrentNode(instanceId); |
| | | if (currentNode == null) { |
| | | return null; |
| | | } |
| | | |
| | | ApprovalTask task = approvalTaskService.getOne( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .eq(ApprovalTask::getNodeId, currentNode.getId()) |
| | | .eq(ApprovalTask::getApproverId, userId) |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | .last("LIMIT 1") |
| | | ); |
| | | |
| | | return task != null ? task.getTaskStatus() : null; |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢æå®ç¨æ·çå
¨é¨å¾
审æ¹ä»»å¡ã |
| | | */ |
| | | public List<ApprovalTask> getUserAllPendingTasks(Long userId) { |
| | | if (userId == null) { |
| | | return List.of(); |
| | | } |
| | | |
| | | return approvalTaskService.list( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getApproverId, userId) |
| | | .eq(ApprovalTask::getTaskStatus, "PENDING") |
| | | .eq(ApprovalTask::getDeleted, 0) |
| | | .orderByDesc(ApprovalTask::getCreateTime) |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å®¡æ¹å®ä¾çè¿åº¦æè¦ã |
| | | */ |
| | | public String getApprovalProgress(Long instanceId) { |
| | | if (instanceId == null) { |
| | | return "æ æç审æ¹å®ä¾"; |
| | | } |
| | | |
| | | ApprovalInstanceNode currentNode = getCurrentNode(instanceId); |
| | | if (currentNode == null) { |
| | | return "审æ¹å·²å®ææå°æªå¼å§"; |
| | | } |
| | | |
| | | int approvedCount = getApprovedCount(instanceId); |
| | | int rejectedCount = getRejectedCount(instanceId); |
| | | int pendingCount = getRemainingApproverCount(instanceId); |
| | | int totalCount = approvedCount + rejectedCount + pendingCount; |
| | | |
| | | return String.format( |
| | | "第%d级审æ¹ï¼æ»äººæ°=%dï¼å·²åæ=%dï¼å·²æç»=%dï¼å¾
审æ¹=%d", |
| | | currentNode.getLevelNo(), |
| | | totalCount, |
| | | approvedCount, |
| | | rejectedCount, |
| | | pendingCount |
| | | ); |
| | | } |
| | | } |
| | |
| | | // Account |
| | | SALES_REFUND_AMOUNT_ORDER("sales_refund_amount_order"), |
| | | SALES_RECEIPT_RETURN("sales_receipt_return"), |
| | | ACCOUNT_EXPENSE("account_expense"), |
| | | FIN_REIMBURSEMENT("fin_reimbursement"), |
| | | FIN_VOUCHER("fin_voucher"), |
| | | ACCOUNT_FILE("account_file"), |
| | | ENTERPRISE_NEWS("enterprise_news"), |
| | | APPROVAL_INSTANCE("approval_instance"), |
| | | ACCOUNT_INVOICE_APPLICATION("account_invoice_application"), |
| | | ACCOUNT_PURCHASE_INVOICE("account_purchase_invoice"); |
| | | |
| | |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper; |
| | | import com.ruoyi.procurementrecord.pojo.ReturnManagement; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.sales.mapper.SalesQuotationMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedger; |
| | | import com.ruoyi.sales.pojo.SalesQuotation; |
| | | import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo; |
| | | import com.ruoyi.sales.vo.CustomerTransactionsVo; |
| | | import lombok.AllArgsConstructor; |
| | |
| | | public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements ICustomerService { |
| | | @Autowired |
| | | private SalesLedgerMapper salesLedgerMapper; |
| | | @Autowired |
| | | private SalesQuotationMapper salesQuotationMapper; |
| | | @Autowired |
| | | private ReturnManagementMapper returnManagementMapper; |
| | | @Autowired |
| | | private CustomerMapper customerMapper; |
| | | |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int deleteCustomerByIds(Long[] ids) { |
| | | List<Long> idList = Arrays.asList(ids); |
| | | // æ£æ¥æ¯å¦æéå®å°è´¦å
³è |
| | | List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(new QueryWrapper<SalesLedger>().lambda().in(SalesLedger::getCustomerId, idList)); |
| | | if (!salesLedgers.isEmpty()) { |
| | | throw new RuntimeException("å®¢æ·æ¡£æ¡ä¸æéå®ååï¼è¯·å
å é¤éå®åå"); |
| | | throw new RuntimeException("å®¢æ·æ¡£æ¡ä¸æéå®å°è´¦ï¼è¯·å
å é¤éå®å°è´¦"); |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦æéå®éè´§å
³è |
| | | List<ReturnManagement> returnManagements = returnManagementMapper.selectList(new QueryWrapper<ReturnManagement>().lambda().in(ReturnManagement::getCustomerId, idList)); |
| | | if (!returnManagements.isEmpty()) { |
| | | throw new RuntimeException("å®¢æ·æ¡£æ¡ä¸æéå®éè´§ï¼è¯·å
å é¤éå®éè´§"); |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦æé宿¥ä»·å
³è |
| | | List<SalesQuotation> salesQuotations = salesQuotationMapper.selectList(new QueryWrapper<SalesQuotation>().lambda().in(SalesQuotation::getCustomerId, idList)); |
| | | if (!salesQuotations.isEmpty()) { |
| | | throw new RuntimeException("å®¢æ·æ¡£æ¡ä¸æé宿¥ä»·ï¼è¯·å
å é¤é宿¥ä»·"); |
| | | } |
| | | |
| | | // æ¥è¯¢æ¯å¦æå·²åé
çå
¬æµ·å®¢æ· |
| | | List<Customer> assignedPools = customerMapper.selectList( |
| | | new QueryWrapper<Customer>().lambda() |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto; |
| | | import com.ruoyi.collaborativeApproval.service.EnterpriseNewsService; |
| | | import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:50:59 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/enterpriseNews") |
| | | @Tag(name = "ä¼ä¸æ°é»è¡¨") |
| | | @AllArgsConstructor |
| | | public class EnterpriseNewsController { |
| | | |
| | | private final EnterpriseNewsService enterpriseNewsService; |
| | | |
| | | @Operation(summary = "å页æ¥è¯¢") |
| | | @GetMapping("/listPage") |
| | | @Log(title = "ä¼ä¸æ°é»å页æ¥è¯¢", businessType = BusinessType.OTHER) |
| | | public R listPage(Page<EnterpriseNewsVo> page , EnterpriseNewsDto enterpriseNewsDto) { |
| | | return R.ok(enterpriseNewsService.listPage(page, enterpriseNewsDto)); |
| | | } |
| | | |
| | | @PostMapping("/save") |
| | | @Operation(summary = "ä¿å") |
| | | @Log(title = "ä¿åä¼ä¸æ°é»", businessType = BusinessType.INSERT) |
| | | public R save(@RequestBody EnterpriseNewsDto enterpriseNewsDto) { |
| | | return R.ok(enterpriseNewsService.add(enterpriseNewsDto)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°") |
| | | @Log(title = "æ´æ°ä¼ä¸æ°é»", businessType = BusinessType.UPDATE) |
| | | public R update(@RequestBody EnterpriseNewsDto enterpriseNewsDto) { |
| | | return R.ok(enterpriseNewsService.updateEnterpriseNewsDto(enterpriseNewsDto)); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤") |
| | | @Log(title = "å é¤ä¼ä¸æ°é»", businessType = BusinessType.DELETE) |
| | | public R delete(@RequestBody List<Long> ids) { |
| | | return R.ok(enterpriseNewsService.delete(ids)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´é¨é¨è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:12 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/enterpriseNewsScopeDept") |
| | | public class EnterpriseNewsScopeDeptController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.controller; |
| | | |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´ç¨æ·è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:23 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/enterpriseNewsScopeUser") |
| | | public class EnterpriseNewsScopeUserController { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.dto; |
| | | |
| | | import com.ruoyi.basic.dto.StorageBlobDTO; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class EnterpriseNewsDto extends EnterpriseNews { |
| | | |
| | | private String createUserName; |
| | | |
| | | private List<Long> deptIds; |
| | | |
| | | private List<Long> userIds; |
| | | |
| | | private Long templateId; |
| | | private String templateName; |
| | | |
| | | private String createTimeStart; |
| | | private String createTimeEnd; |
| | | |
| | | private List<StorageBlobDTO> storageBlobDTOs; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.mapper; |
| | | |
| | | 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.collaborativeApproval.dto.EnterpriseNewsDto; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; |
| | | import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:50:59 |
| | | */ |
| | | @Mapper |
| | | public interface EnterpriseNewsMapper extends BaseMapper<EnterpriseNews> { |
| | | |
| | | IPage<EnterpriseNewsVo> listPage(Page<EnterpriseNewsVo> page,@Param("enterpriseNewsDto") EnterpriseNewsDto enterpriseNewsDto); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´é¨é¨è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:12 |
| | | */ |
| | | @Mapper |
| | | public interface EnterpriseNewsScopeDeptMapper extends BaseMapper<EnterpriseNewsScopeDept> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´ç¨æ·è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:23 |
| | | */ |
| | | @Mapper |
| | | public interface EnterpriseNewsScopeUserMapper extends BaseMapper<EnterpriseNewsScopeUser> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.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.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * ä¼ä¸æ°é»è¡¨ |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:50:59 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("enterprise_news") |
| | | @ApiModel(value = "EnterpriseNews对象", description = "ä¼ä¸æ°é»è¡¨") |
| | | public class EnterpriseNews implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Schema(description = "ç¼å· ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @Schema(description = "æ é¢ Title") |
| | | private String title; |
| | | |
| | | @Schema(description = "æè¦ Summary") |
| | | private String summary; |
| | | |
| | | @Schema(description = "æ£æ Content") |
| | | private String content; |
| | | |
| | | @Schema(description = "åç±» Category") |
| | | private String category; |
| | | |
| | | @Schema(description = "é
读èå´ Read scope: all å
¨å, dept é¨é¨, custom èªå®ä¹") |
| | | private String readScope; |
| | | |
| | | @Schema(description = "æ¯å¦å¿
读 Required flag: 0 å¦, 1 æ¯") |
| | | private Byte isRequired; |
| | | |
| | | @Schema(description = "ç¶æ Status: DRAFT è稿, PENDING å¾
审æ¹, PUBLISHED å·²åå¸, REJECTED 驳å, OFFLINE å·²ä¸çº¿") |
| | | private String status; |
| | | |
| | | @Schema(description = "åºè¯»äººæ° Required read count") |
| | | private Integer requiredReadCount; |
| | | |
| | | @Schema(description = "å·²è¯»äººæ° Read count") |
| | | private Integer readCount; |
| | | |
| | | @Schema(description = "å建人 Create user") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long createUser; |
| | | |
| | | @Schema(description = "å建æ¶é´ Create time") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME) |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "æ´æ°äºº Update user") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private Long updateUser; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´ Update time") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME) |
| | | private LocalDateTime updateTime; |
| | | |
| | | @Schema(description = "é¨é¨ID Dept ID") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.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 io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´é¨é¨è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:12 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("enterprise_news_scope_dept") |
| | | @ApiModel(value = "EnterpriseNewsScopeDept对象", description = "ä¼ä¸æ°é»é
读èå´é¨é¨è¡¨") |
| | | public class EnterpriseNewsScopeDept implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * ç¼å· |
| | | */ |
| | | @ApiModelProperty("ç¼å·") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * ä¼ä¸æ°é»ID |
| | | */ |
| | | @ApiModelProperty("ä¼ä¸æ°é»ID") |
| | | private Long newsId; |
| | | |
| | | /** |
| | | * é¨é¨ID |
| | | */ |
| | | @ApiModelProperty("é¨é¨ID") |
| | | private Long deptId; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @ApiModelProperty("å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.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 io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´ç¨æ·è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:23 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("enterprise_news_scope_user") |
| | | @ApiModel(value = "EnterpriseNewsScopeUser对象", description = "ä¼ä¸æ°é»é
读èå´ç¨æ·è¡¨") |
| | | public class EnterpriseNewsScopeUser implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * ç¼å· |
| | | */ |
| | | @ApiModelProperty("ç¼å·") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * ä¼ä¸æ°é»ID |
| | | */ |
| | | @ApiModelProperty("ä¼ä¸æ°é»ID") |
| | | private Long newsId; |
| | | |
| | | /** |
| | | * ç¨æ·ID |
| | | */ |
| | | @ApiModelProperty("ç¨æ·ID") |
| | | private Long userId; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @ApiModelProperty("å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.service; |
| | | |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´é¨é¨è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:12 |
| | | */ |
| | | public interface EnterpriseNewsScopeDeptService extends IService<EnterpriseNewsScopeDept> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.service; |
| | | |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´ç¨æ·è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:23 |
| | | */ |
| | | public interface EnterpriseNewsScopeUserService extends IService<EnterpriseNewsScopeUser> { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:50:59 |
| | | */ |
| | | public interface EnterpriseNewsService extends IService<EnterpriseNews> { |
| | | |
| | | IPage<EnterpriseNewsVo> listPage(Page<EnterpriseNewsVo> page, EnterpriseNewsDto enterpriseNewsDto); |
| | | |
| | | Boolean add(EnterpriseNewsDto enterpriseNewsDto); |
| | | |
| | | Boolean updateEnterpriseNewsDto(EnterpriseNewsDto enterpriseNewsDto); |
| | | |
| | | Boolean delete(List<Long> ids); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.service.impl; |
| | | |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept; |
| | | import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper; |
| | | import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeDeptService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´é¨é¨è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:12 |
| | | */ |
| | | @Service |
| | | public class EnterpriseNewsScopeDeptServiceImpl extends ServiceImpl<EnterpriseNewsScopeDeptMapper, EnterpriseNewsScopeDept> implements EnterpriseNewsScopeDeptService { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.service.impl; |
| | | |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser; |
| | | import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper; |
| | | import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeUserService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * <p> |
| | | * ä¼ä¸æ°é»é
读èå´ç¨æ·è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:51:23 |
| | | */ |
| | | @Service |
| | | public class EnterpriseNewsScopeUserServiceImpl extends ServiceImpl<EnterpriseNewsScopeUserMapper, EnterpriseNewsScopeUser> implements EnterpriseNewsScopeUserService { |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.mapper.ApprovalInstanceMapper; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.ruoyi.approve.pojo.ApprovalTask; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import com.ruoyi.approve.service.ApprovalInstanceService; |
| | | import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto; |
| | | import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser; |
| | | import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeDeptService; |
| | | import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeUserService; |
| | | import com.ruoyi.collaborativeApproval.service.EnterpriseNewsService; |
| | | import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo; |
| | | import com.ruoyi.common.enums.TypeEnums; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | import com.ruoyi.project.system.mapper.SysDeptMapper; |
| | | import com.ruoyi.project.system.mapper.SysUserDeptMapper; |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.project.system.service.ISysNoticeService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * ä¼ä¸æ°é»è¡¨æå¡å®ç°ç±» |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-20 11:50:59 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class EnterpriseNewsServiceImpl extends ServiceImpl<EnterpriseNewsMapper, EnterpriseNews> implements EnterpriseNewsService { |
| | | |
| | | private static final String READ_SCOPE_ALL = "all"; |
| | | private static final String READ_SCOPE_DEPT = "dept"; |
| | | private static final String READ_SCOPE_CUSTOM = "custom"; |
| | | |
| | | private static final String STATUS_DRAFT = "DRAFT"; |
| | | private static final String STATUS_PENDING = "PENDING"; |
| | | private static final String STATUS_PUBLISHED = "PUBLISHED"; |
| | | private static final String STATUS_REJECTED = "REJECTED"; |
| | | private static final String STATUS_OFFLINE = "OFFLINE"; |
| | | |
| | | private final EnterpriseNewsMapper enterpriseNewsMapper; |
| | | private final EnterpriseNewsScopeDeptService enterpriseNewsScopeDeptService; |
| | | private final EnterpriseNewsScopeUserService enterpriseNewsScopeUserService; |
| | | private final SysUserMapper sysUserMapper; |
| | | private final SysDeptMapper sysDeptMapper; |
| | | private final SysUserDeptMapper sysUserDeptMapper; |
| | | private final ApprovalInstanceMapper approvalInstanceMapper; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | private final ApprovalTemplateMapper approvalTemplateMapper; |
| | | private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils; |
| | | private final ISysNoticeService sysNoticeService; |
| | | private final FileUtil fileUtil; |
| | | |
| | | @Override |
| | | public IPage<EnterpriseNewsVo> listPage(Page<EnterpriseNewsVo> page, EnterpriseNewsDto enterpriseNewsDto) { |
| | | IPage<EnterpriseNewsVo> enterpriseNewsVoIPage = enterpriseNewsMapper.listPage(page, enterpriseNewsDto); |
| | | enterpriseNewsVoIPage.getRecords().forEach(enterpriseNewsVo -> { |
| | | enterpriseNewsVo.setStorageBlobDTOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNewsVo.getId())); |
| | | }); |
| | | return enterpriseNewsVoIPage; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean add(EnterpriseNewsDto enterpriseNewsDto) { |
| | | validateForSave(enterpriseNewsDto); |
| | | String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope()); |
| | | List<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); |
| | | List<Long> userIds = distinctIds(enterpriseNewsDto.getUserIds()); |
| | | |
| | | EnterpriseNews enterpriseNews = new EnterpriseNews(); |
| | | BeanUtils.copyProperties(enterpriseNewsDto, enterpriseNews); |
| | | enterpriseNews.setReadScope(readScope); |
| | | enterpriseNews.setIsRequired(enterpriseNewsDto.getIsRequired() == null ? (byte) 0 : enterpriseNewsDto.getIsRequired()); |
| | | enterpriseNews.setStatus(normalizeStatus(enterpriseNewsDto.getStatus(), STATUS_DRAFT)); |
| | | enterpriseNews.setReadCount(0); |
| | | enterpriseNews.setRequiredReadCount(calculateRequiredReadCount(readScope, deptIds, userIds)); |
| | | |
| | | Long[] loginDeptIds = SecurityUtils.getDeptId(); |
| | | if (StringUtils.isNotEmpty(loginDeptIds)) { |
| | | enterpriseNews.setDeptId(loginDeptIds[0]); |
| | | } |
| | | |
| | | if (!save(enterpriseNews) || enterpriseNews.getId() == null) { |
| | | throw new ServiceException("æ°å¢ä¼ä¸æ°é»å¤±è´¥"); |
| | | } |
| | | |
| | | saveReadScopeRelations(enterpriseNews.getId(), readScope, deptIds, userIds); |
| | | if (STATUS_PENDING.equals(enterpriseNews.getStatus())) { |
| | | startEnterpriseNewsApproval(enterpriseNews, enterpriseNewsDto); |
| | | } |
| | | //æ·»å éä»¶ |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNews.getId(), enterpriseNewsDto.getStorageBlobDTOs()); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean updateEnterpriseNewsDto(EnterpriseNewsDto enterpriseNewsDto) { |
| | | if (enterpriseNewsDto == null || enterpriseNewsDto.getId() == null) { |
| | | throw new ServiceException("ä¼ä¸æ°é»IDä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | EnterpriseNews oldEnterpriseNews = getById(enterpriseNewsDto.getId()); |
| | | if (oldEnterpriseNews == null) { |
| | | throw new ServiceException("ä¼ä¸æ°é»ä¸åå¨"); |
| | | } |
| | | if (!STATUS_DRAFT.equals(oldEnterpriseNews.getStatus()) |
| | | && !STATUS_REJECTED.equals(oldEnterpriseNews.getStatus())) { |
| | | throw new ServiceException("å¾
å®¡æ¹æå·²åå¸çä¼ä¸æ°é»ä¸å
许修æ¹"); |
| | | } |
| | | |
| | | validateForSave(enterpriseNewsDto); |
| | | String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope()); |
| | | List<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); |
| | | List<Long> userIds = distinctIds(enterpriseNewsDto.getUserIds()); |
| | | |
| | | EnterpriseNews enterpriseNews = new EnterpriseNews(); |
| | | BeanUtils.copyProperties(enterpriseNewsDto, enterpriseNews); |
| | | enterpriseNews.setReadScope(readScope); |
| | | enterpriseNews.setIsRequired(enterpriseNewsDto.getIsRequired() == null ? oldEnterpriseNews.getIsRequired() : enterpriseNewsDto.getIsRequired()); |
| | | enterpriseNews.setStatus(normalizeStatus(enterpriseNewsDto.getStatus(), oldEnterpriseNews.getStatus())); |
| | | enterpriseNews.setReadCount(oldEnterpriseNews.getReadCount()); |
| | | enterpriseNews.setRequiredReadCount(calculateRequiredReadCount(readScope, deptIds, userIds)); |
| | | enterpriseNews.setCreateUser(oldEnterpriseNews.getCreateUser()); |
| | | enterpriseNews.setCreateTime(oldEnterpriseNews.getCreateTime()); |
| | | enterpriseNews.setDeptId(oldEnterpriseNews.getDeptId()); |
| | | |
| | | if (!updateById(enterpriseNews)) { |
| | | throw new ServiceException("ä¿®æ¹ä¼ä¸æ°é»å¤±è´¥"); |
| | | } |
| | | |
| | | clearReadScopeRelations(enterpriseNews.getId()); |
| | | saveReadScopeRelations(enterpriseNews.getId(), readScope, deptIds, userIds); |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNews.getId(), enterpriseNewsDto.getStorageBlobDTOs()); |
| | | if (STATUS_PENDING.equals(enterpriseNews.getStatus())) { |
| | | resetEnterpriseNewsApprovalFlow(oldEnterpriseNews); |
| | | startEnterpriseNewsApproval(enterpriseNews, enterpriseNewsDto); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delete(List<Long> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | return false; |
| | | } |
| | | if (!removeByIds(ids)) { |
| | | throw new ServiceException("å é¤ä¼ä¸æ°é»å¤±è´¥"); |
| | | } |
| | | ids.forEach(this::clearReadScopeRelations); |
| | | return true; |
| | | } |
| | | |
| | | private void validateForSave(EnterpriseNewsDto enterpriseNewsDto) { |
| | | if (enterpriseNewsDto == null) { |
| | | throw new ServiceException("ä¼ä¸æ°é»æ°æ®ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isEmpty(enterpriseNewsDto.getTitle())) { |
| | | throw new ServiceException("æ é¢ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isEmpty(enterpriseNewsDto.getContent())) { |
| | | throw new ServiceException("æ£æä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | normalizeStatus(enterpriseNewsDto.getStatus(), STATUS_DRAFT); |
| | | String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope()); |
| | | List<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); |
| | | List<Long> userIds = distinctIds(enterpriseNewsDto.getUserIds()); |
| | | |
| | | if (READ_SCOPE_DEPT.equals(readScope) && StringUtils.isEmpty(deptIds)) { |
| | | throw new ServiceException("è¯·éæ©é
读èå´é¨é¨"); |
| | | } |
| | | if (READ_SCOPE_CUSTOM.equals(readScope) && StringUtils.isEmpty(userIds)) { |
| | | throw new ServiceException("è¯·éæ©èªå®ä¹é
读人å"); |
| | | } |
| | | |
| | | validateDeptIds(deptIds); |
| | | validateUserIds(userIds); |
| | | } |
| | | |
| | | private String normalizeReadScope(String readScope) { |
| | | String normalized = StringUtils.isEmpty(readScope) ? READ_SCOPE_ALL : readScope.trim(); |
| | | if (!READ_SCOPE_ALL.equals(normalized) |
| | | && !READ_SCOPE_DEPT.equals(normalized) |
| | | && !READ_SCOPE_CUSTOM.equals(normalized)) { |
| | | throw new ServiceException("é
读èå´ä¸åæ³"); |
| | | } |
| | | return normalized; |
| | | } |
| | | |
| | | private String normalizeStatus(String status, String defaultStatus) { |
| | | String normalized = StringUtils.isEmpty(status) ? defaultStatus : status.trim().toUpperCase(); |
| | | if (!STATUS_DRAFT.equals(normalized) |
| | | && !STATUS_PENDING.equals(normalized) |
| | | && !STATUS_PUBLISHED.equals(normalized) |
| | | && !STATUS_REJECTED.equals(normalized) |
| | | && !STATUS_OFFLINE.equals(normalized)) { |
| | | throw new ServiceException("ä¼ä¸æ°é»ç¶æä¸åæ³"); |
| | | } |
| | | return normalized; |
| | | } |
| | | |
| | | private void validateDeptIds(List<Long> deptIds) { |
| | | if (StringUtils.isEmpty(deptIds)) { |
| | | return; |
| | | } |
| | | for (Long deptId : deptIds) { |
| | | SysDept sysDept = sysDeptMapper.selectDeptById(deptId); |
| | | if (deptId == null || sysDept == null) { |
| | | throw new ServiceException("é
读èå´é¨é¨ä¸åå¨"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void validateUserIds(List<Long> userIds) { |
| | | if (StringUtils.isEmpty(userIds)) { |
| | | return; |
| | | } |
| | | List<SysUser> users = sysUserMapper.selectUsersByIds(userIds); |
| | | if (users.size() != userIds.size()) { |
| | | throw new ServiceException("èªå®ä¹é
读人åå
嫿 æç¨æ·"); |
| | | } |
| | | } |
| | | |
| | | private Integer calculateRequiredReadCount(String readScope, List<Long> deptIds, List<Long> userIds) { |
| | | if (READ_SCOPE_ALL.equals(readScope)) { |
| | | Long count = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUser>() |
| | | .eq(SysUser::getDelFlag, "0")); |
| | | return count == null ? 0 : count.intValue(); |
| | | } |
| | | if (READ_SCOPE_DEPT.equals(readScope)) { |
| | | List<Long> allDeptIds = collectDeptIdsWithChildren(deptIds); |
| | | if (StringUtils.isEmpty(allDeptIds)) { |
| | | return 0; |
| | | } |
| | | Long count = sysUserDeptMapper.countDistinctUserIdsByDeptIds(allDeptIds); |
| | | return count == null ? 0 : count.intValue(); |
| | | } |
| | | return userIds.size(); |
| | | } |
| | | |
| | | private List<Long> collectDeptIdsWithChildren(List<Long> deptIds) { |
| | | Set<Long> allDeptIds = new LinkedHashSet<>(); |
| | | for (Long deptId : deptIds) { |
| | | if (deptId == null) { |
| | | continue; |
| | | } |
| | | allDeptIds.add(deptId); |
| | | List<SysDept> children = sysDeptMapper.selectChildrenDeptById(deptId); |
| | | if (StringUtils.isNotEmpty(children)) { |
| | | for (SysDept child : children) { |
| | | allDeptIds.add(child.getDeptId()); |
| | | } |
| | | } |
| | | } |
| | | return new ArrayList<>(allDeptIds); |
| | | } |
| | | |
| | | private void saveReadScopeRelations(Long newsId, String readScope, List<Long> deptIds, List<Long> userIds) { |
| | | if (READ_SCOPE_DEPT.equals(readScope)) { |
| | | List<EnterpriseNewsScopeDept> scopeDeptList = new ArrayList<>(); |
| | | for (Long deptId : deptIds) { |
| | | EnterpriseNewsScopeDept scopeDept = new EnterpriseNewsScopeDept(); |
| | | scopeDept.setNewsId(newsId); |
| | | scopeDept.setDeptId(deptId); |
| | | scopeDeptList.add(scopeDept); |
| | | } |
| | | if (StringUtils.isNotEmpty(scopeDeptList)) { |
| | | enterpriseNewsScopeDeptService.saveBatch(scopeDeptList); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | if (READ_SCOPE_CUSTOM.equals(readScope)) { |
| | | List<EnterpriseNewsScopeUser> scopeUserList = new ArrayList<>(); |
| | | for (Long userId : userIds) { |
| | | EnterpriseNewsScopeUser scopeUser = new EnterpriseNewsScopeUser(); |
| | | scopeUser.setNewsId(newsId); |
| | | scopeUser.setUserId(userId); |
| | | scopeUserList.add(scopeUser); |
| | | } |
| | | if (StringUtils.isNotEmpty(scopeUserList)) { |
| | | enterpriseNewsScopeUserService.saveBatch(scopeUserList); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void clearReadScopeRelations(Long newsId) { |
| | | enterpriseNewsScopeDeptService.remove(new LambdaQueryWrapper<EnterpriseNewsScopeDept>() |
| | | .eq(EnterpriseNewsScopeDept::getNewsId, newsId)); |
| | | enterpriseNewsScopeUserService.remove(new LambdaQueryWrapper<EnterpriseNewsScopeUser>() |
| | | .eq(EnterpriseNewsScopeUser::getNewsId, newsId)); |
| | | } |
| | | |
| | | private void resetEnterpriseNewsApprovalFlow(EnterpriseNews oldEnterpriseNews) { |
| | | if (oldEnterpriseNews == null || !STATUS_DRAFT.equals(oldEnterpriseNews.getStatus())) { |
| | | return; |
| | | } |
| | | List<Long> approvalInstanceIds = approvalInstanceMapper.selectList(new LambdaQueryWrapper<ApprovalInstance>() |
| | | .eq(ApprovalInstance::getBusinessId, oldEnterpriseNews.getId()) |
| | | .eq(ApprovalInstance::getBusinessType, TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode()) |
| | | .eq(ApprovalInstance::getDeleted, (byte) 0)) |
| | | .stream() |
| | | .map(ApprovalInstance::getId) |
| | | .filter(id -> id != null && id > 0) |
| | | .collect(Collectors.toList()); |
| | | if (StringUtils.isEmpty(approvalInstanceIds)) { |
| | | return; |
| | | } |
| | | approvalInstanceService.delete(approvalInstanceIds); |
| | | } |
| | | |
| | | private List<Long> distinctIds(List<Long> ids) { |
| | | if (StringUtils.isEmpty(ids)) { |
| | | return new ArrayList<>(); |
| | | } |
| | | Set<Long> distinctSet = new LinkedHashSet<>(); |
| | | for (Long id : ids) { |
| | | if (id != null) { |
| | | distinctSet.add(id); |
| | | } |
| | | } |
| | | return new ArrayList<>(distinctSet); |
| | | } |
| | | |
| | | private void startEnterpriseNewsApproval(EnterpriseNews enterpriseNews, EnterpriseNewsDto enterpriseNewsDto) { |
| | | if (enterpriseNewsDto.getTemplateId() == null) { |
| | | throw new ServiceException("å®¡æ¹æ¨¡æ¿ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | String templateName = enterpriseNewsDto.getTemplateName(); |
| | | if (StringUtils.isEmpty(templateName)) { |
| | | ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectById(enterpriseNewsDto.getTemplateId()); |
| | | if (approvalTemplate == null) { |
| | | throw new ServiceException("å®¡æ¹æ¨¡æ¿ä¸åå¨"); |
| | | } |
| | | templateName = approvalTemplate.getTemplateName(); |
| | | } |
| | | |
| | | ApprovalInstance approvalInstance = new ApprovalInstance(); |
| | | approvalInstance.setInstanceNo(OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no", enterpriseNews.getCreateTime() != null ? enterpriseNews.getCreateTime() : LocalDateTime.now())); |
| | | approvalInstance.setTemplateId(enterpriseNewsDto.getTemplateId()); |
| | | approvalInstance.setTemplateName(templateName); |
| | | approvalInstance.setBusinessId(enterpriseNews.getId()); |
| | | approvalInstance.setBusinessType(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode()); |
| | | approvalInstance.setTitle(enterpriseNews.getTitle()); |
| | | approvalInstance.setStatus("PENDING"); |
| | | approvalInstance.setCurrentLevel(1); |
| | | approvalInstance.setApplicantId(SecurityUtils.getUserId()); |
| | | approvalInstance.setApplicantName(SecurityUtils.getLoginUser().getNickName()); |
| | | approvalInstance.setApplyTime(LocalDateTime.now()); |
| | | approvalInstance.setDeleted((byte) 0); |
| | | approvalInstance.setCreateUser(SecurityUtils.getUserId()); |
| | | approvalInstance.setUpdateUser(SecurityUtils.getUserId()); |
| | | approvalInstance.setDeptId(enterpriseNews.getDeptId()); |
| | | approvalInstanceMapper.insert(approvalInstance); |
| | | |
| | | approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstance); |
| | | sendApproveNotice(approvalInstance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstance.getId())); |
| | | } |
| | | |
| | | private void sendApproveNotice(ApprovalInstance instance, List<ApprovalTask> tasks) { |
| | | if (instance == null || tasks == null || tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> approverIds = tasks.stream() |
| | | .map(ApprovalTask::getApproverId) |
| | | .filter(id -> id != null && id > 0) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (approverIds.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | String title = StringUtils.isNotEmpty(instance.getTemplateName()) ? instance.getTemplateName() : "å®¡æ¹æé"; |
| | | String message = "审æ¹åå· " + instance.getInstanceNo() + " éè¦æ¨å®¡æ¹"; |
| | | String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list/?id=" + instance.getId(); |
| | | sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.collaborativeApproval.vo; |
| | | |
| | | import com.ruoyi.basic.dto.StorageBlobVO; |
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class EnterpriseNewsVo extends EnterpriseNews { |
| | | |
| | | private String createUserName; |
| | | |
| | | private List<StorageBlobVO> storageBlobDTOs; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.common.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * 审æ¹ç¶ææä¸¾ |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum ApprovalStatusEnum implements BaseEnum<Integer> { |
| | | |
| | | DRAFT(0, "è稿"), |
| | | PENDING(1, "å¾
审æ¹"), |
| | | IN_PROGRESS(2, "审æ¹ä¸"), |
| | | APPROVED(3, "å·²éè¿"), |
| | | REJECTED(4, "已驳å"); |
| | | |
| | | |
| | | private final Integer value; |
| | | private final String label; |
| | | |
| | | @Override |
| | | public Integer getCode() { |
| | | return value; |
| | | } |
| | | |
| | | @Override |
| | | public String getValue() { |
| | | return label; |
| | | } |
| | | |
| | | public static ApprovalStatusEnum fromValue(Integer value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | for (ApprovalStatusEnum status : values()) { |
| | | if (status.getCode().equals(value)) { |
| | | return status; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | public static String getLabelByValue(Integer value) { |
| | | ApprovalStatusEnum statusEnum = fromValue(value); |
| | | return statusEnum != null ? statusEnum.getValue() : "æªç¥ç¶æ"; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.common.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * ä¼ä¸æ°é»ç¶ææä¸¾ç±» |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-05-20 |
| | | */ |
| | | @Schema(description = "ä¼ä¸æ°é»ç¶ææä¸¾") |
| | | public enum EnterpriseNewsStatusEnum implements BaseEnum<String> { |
| | | |
| | | /** |
| | | * è稿 |
| | | */ |
| | | @Schema(description = "è稿") |
| | | DRAFT("DRAFT", "è稿"), |
| | | |
| | | /** |
| | | * å¾
å®¡æ¹ |
| | | */ |
| | | @Schema(description = "å¾
审æ¹") |
| | | PENDING("PENDING", "å¾
审æ¹"), |
| | | |
| | | /** |
| | | * å·²åå¸ |
| | | */ |
| | | @Schema(description = "å·²åå¸") |
| | | PUBLISHED("PUBLISHED", "å·²åå¸"), |
| | | |
| | | /** |
| | | * 驳å |
| | | */ |
| | | @Schema(description = "驳å") |
| | | REJECTED("REJECTED", "驳å"), |
| | | |
| | | /** |
| | | * å·²ä¸çº¿ |
| | | */ |
| | | @Schema(description = "å·²ä¸çº¿") |
| | | OFFLINE("OFFLINE", "å·²ä¸çº¿"); |
| | | |
| | | /** |
| | | * ç¶æç |
| | | */ |
| | | private final String code; |
| | | |
| | | /** |
| | | * ç¶ææè¿° |
| | | * -- GETTER -- |
| | | * è·åç¶ææè¿° |
| | | * |
| | | * @return ç¶ææè¿° |
| | | |
| | | */ |
| | | @Getter |
| | | private final String description; |
| | | |
| | | EnterpriseNewsStatusEnum(String code, String description) { |
| | | this.code = code; |
| | | this.description = description; |
| | | } |
| | | |
| | | /** |
| | | * è·åç¶æç |
| | | * |
| | | * @return ç¶æç |
| | | */ |
| | | @JsonValue |
| | | public String getCode() { |
| | | return code; |
| | | } |
| | | |
| | | @Override |
| | | public String getValue() { |
| | | return ""; |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®ç¶æç è·åæä¸¾ |
| | | * |
| | | * @param code ç¶æç |
| | | * @return æä¸¾å¼ |
| | | */ |
| | | @JsonCreator |
| | | public static EnterpriseNewsStatusEnum getByCode(String code) { |
| | | for (EnterpriseNewsStatusEnum status : values()) { |
| | | if (status.code.equals(code)) { |
| | | return status; |
| | | } |
| | | } |
| | | throw new IllegalArgumentException("Invalid enterprise news status code: " + code); |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦ä¸ºèç¨¿ç¶æ |
| | | * |
| | | * @return æ¯å¦ä¸ºèç¨¿ç¶æ |
| | | */ |
| | | public boolean isDraft() { |
| | | return DRAFT.equals(this); |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦ä¸ºå¾
审æ¹ç¶æ |
| | | * |
| | | * @return æ¯å¦ä¸ºå¾
审æ¹ç¶æ |
| | | */ |
| | | public boolean isPending() { |
| | | return PENDING.equals(this); |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦ä¸ºå·²åå¸ç¶æ |
| | | * |
| | | * @return æ¯å¦ä¸ºå·²åå¸ç¶æ |
| | | */ |
| | | public boolean isPublished() { |
| | | return PUBLISHED.equals(this); |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦ä¸ºé©³åç¶æ |
| | | * |
| | | * @return æ¯å¦ä¸ºé©³åç¶æ |
| | | */ |
| | | public boolean isRejected() { |
| | | return REJECTED.equals(this); |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦ä¸ºå·²ä¸çº¿ç¶æ |
| | | * |
| | | * @return æ¯å¦ä¸ºå·²ä¸çº¿ç¶æ |
| | | */ |
| | | public boolean isOffline() { |
| | | return OFFLINE.equals(this); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.common.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * é宿¥ä»·ç¶ææä¸¾ |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum SalesQuotationStatusEnum implements BaseEnum<String> { |
| | | |
| | | DRAFT("è稿", "è稿"), |
| | | PENDING("å¾
审æ¹", "å¾
审æ¹"), |
| | | IN_PROGRESS("å®¡æ ¸ä¸", "å®¡æ ¸ä¸"), |
| | | APPROVED("éè¿", "éè¿"), |
| | | REJECTED("æç»", "æç»"); |
| | | |
| | | private final String value; |
| | | private final String label; |
| | | |
| | | @Override |
| | | public String getCode() { |
| | | return value; |
| | | } |
| | | |
| | | @Override |
| | | public String getValue() { |
| | | return label; |
| | | } |
| | | |
| | | public static SalesQuotationStatusEnum fromValue(String value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | for (SalesQuotationStatusEnum status : values()) { |
| | | if (status.getCode().equals(value)) { |
| | | return status; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.common.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * å货审æ¹ç¶ææä¸¾ |
| | | */ |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum ShippingStatusEnum implements BaseEnum<String> { |
| | | |
| | | PENDING("å¾
确认", "å¾
确认"), |
| | | IN_PROGRESS("å®¡æ ¸ä¸", "å®¡æ ¸ä¸"), |
| | | APPROVED("å®¡æ ¸éè¿", "å®¡æ ¸éè¿"), |
| | | REJECTED("å®¡æ ¸æç»", "å®¡æ ¸æç»"); |
| | | |
| | | private final String value; |
| | | private final String label; |
| | | |
| | | @Override |
| | | public String getCode() { |
| | | return value; |
| | | } |
| | | |
| | | @Override |
| | | public String getValue() { |
| | | return label; |
| | | } |
| | | |
| | | public static ShippingStatusEnum fromValue(String value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | for (ShippingStatusEnum status : values()) { |
| | | if (status.getCode().equals(value)) { |
| | | return status; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.common.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum TypeEnums implements BaseEnum<Long> { |
| | | |
| | | PUBLIC_OUT(1L, "å
¬åºç®¡ç"), |
| | | LEAVE(2L, "请å管ç"), |
| | | BUSINESS_TRIP(3L, "åºå·®ç®¡ç"), |
| | | REIMBURSEMENT(4L, "æ¥é管ç"), |
| | | PURCHASE_APPROVAL(5L, "éè´å®¡æ¹"), |
| | | QUOTATION_APPROVAL(6L, "æ¥ä»·å®¡æ¹"), |
| | | SHIPPING_APPROVAL(7L, "å货审æ¹"), |
| | | DANGEROUS_OPERATION(8L, "å±é©ä½ä¸å®¡æ¹"), |
| | | OFFICE_SUPPLIES(9L, "åå
¬ç¨å审æ¹"), |
| | | REGULARIZATION_APPROVAL(10L, "转æ£å®¡æ¹"), |
| | | TRANSFER_APPROVAL(11L, "è°å¨å®¡æ¹"), |
| | | RESIGNATION_APPROVAL(12L, "离è审æ¹"), |
| | | WORK_HANDOVER_APPROVAL(13L, "å·¥ä½äº¤æ¥å®¡æ¹"), |
| | | LEAVE_APPROVAL(14L, "请å审æ¹"), |
| | | OVERTIME_APPROVAL(15L, "å ç审æ¹"), |
| | | TRAVEL_REIMBURSEMENT_APPROVAL(16L, "åºå·®æ¥é审æ¹"), |
| | | EXPENSE_APPROVAL(17L, "è´¹ç¨å®¡æ¹"), |
| | | ENTERPRISE_NEWS_APPROVAL(18L, "ä¼ä¸æ°é»å®¡æ¹"); |
| | | |
| | | |
| | | |
| | | private final Long value; |
| | | private final String label; |
| | | |
| | | @Override |
| | | public Long getCode() { |
| | | return value; |
| | | } |
| | | |
| | | @Override |
| | | public String getValue() { |
| | | return label; |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®å¼è·å对åºçæä¸¾ |
| | | * @param value ä¸å¡ç±»åå¼ |
| | | * @return 对åºçæä¸¾ï¼æªå¹é
è¿ånull |
| | | */ |
| | | public static TypeEnums fromValue(Long value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | for (TypeEnums type : values()) { |
| | | if (type.getCode().equals(value)) { |
| | | return type; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®å¼è·åæè¿° |
| | | * @param value ä¸å¡ç±»åå¼ |
| | | * @return ä¸å¡ç±»åæè¿°ï¼æªå¹é
è¿å"èªå®ä¹å®¡æ¹" |
| | | */ |
| | | public static String getLabelByValue(Long value) { |
| | | TypeEnums typeEnum = fromValue(value); |
| | | return typeEnum != null ? typeEnum.getValue() : "èªå®ä¹å®¡æ¹"; |
| | | } |
| | | } |
| | |
| | | * @param <T> å®ä½ç±»å |
| | | * @return 订åç¼å· |
| | | */ |
| | | public static <T> String countTodayByCreateTime(BaseMapper<T> mapper,String preFix,String code) { |
| | | LocalDate today = LocalDate.now(); |
| | | 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(); |
| | | String dateStr = today.format(DateTimeFormatter.BASIC_ISO_DATE); |
| | |
| | | * @param <T> å®ä½ç±»æ³å |
| | | * @return å½å¤©è®°å½æ°é |
| | | */ |
| | | public static <T> String countAfterServiceTodayByCreateTime(BaseMapper<T> mapper,String preFix) { |
| | | 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( |
| | | LocalDateTime.now().toLocalDate(), |
| | | localDate, |
| | | LocalTime.MIN |
| | | ); |
| | | LocalDateTime todayEnd = LocalDateTime.of( |
| | | LocalDateTime.now().toLocalDate(), |
| | | localDate, |
| | | LocalTime.MAX |
| | | ); |
| | | |
| | |
| | | .lt("create_time", endDate); |
| | | |
| | | Long aLong = mapper.selectCount(queryWrapper); |
| | | return preFix + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", "") + String.format("%03d", (aLong + 1)); |
| | | return preFix + localDate.format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", "") + String.format("%03d", (aLong + 1)); |
| | | } |
| | | } |
| | |
| | | wrapper.like(CustomerVisits::getVisitingPeople, customerVisits.getVisitingPeople()); |
| | | } |
| | | } |
| | | |
| | | wrapper.orderByDesc(CustomerVisits::getId); |
| | | return customerVisitsMapper.selectPage(page, wrapper); |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | |
| | | @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); |
| | |
| | | if (StringUtils.isNotBlank(inspectionTaskDto.getInspectionProject())) { |
| | | queryWrapper.like(InspectionTask::getInspectionProject, inspectionTaskDto.getInspectionProject()); |
| | | } |
| | | queryWrapper.orderByDesc(InspectionTask::getCreateTime); |
| | | IPage<InspectionTask> entityPage = inspectionTaskMapper.selectPage(page, queryWrapper); |
| | | |
| | | // æ æ°æ®æåè¿å |
| | |
| | | if (timingTask.getIsEnabled() != null) { |
| | | queryWrapper.eq(TimingTask::getIsEnabled, timingTask.getIsEnabled()); |
| | | } |
| | | queryWrapper.orderByDesc(TimingTask::getCreateTime); |
| | | IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, queryWrapper); |
| | | |
| | | // 2. å¦ææ²¡ææ°æ®ï¼ç´æ¥è¿å空å页 |
| | |
| | | /** |
| | | * å
¥åºæ¶é´ |
| | | */ |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDateTime createTime; |
| | |
| | | @Schema(description = "å建æ¶é´") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | |
| | | Long aLong = customStorageMapper.selectCount(null); |
| | | item.setInboundBatches(aLong.equals(0L) ? "第1æ¹æ¬¡(èªå®ä¹å
¥åº)" : "第"+ (aLong + 1) + "æ¹æ¬¡(èªå®ä¹å
¥åº)" ); |
| | | item.setCreateBy(loginUser.getNickName()); |
| | | item.setCode(OrderUtils.countTodayByCreateTime(customStorageMapper, "", "code")); |
| | | item.setCode(OrderUtils.countTodayByCreateTime(customStorageMapper, "", "code", item.getCreateTime() != null ? item.getCreateTime() : LocalDateTime.now())); |
| | | customStorageMapper.insert(item); |
| | | }); |
| | | return AjaxResult.success("èªå®ä¹å
¥åºæå"); |
| | |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto; |
| | | import com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto; |
| | | import com.ruoyi.procurementrecord.bean.vo.ShippingInfoVo; |
| | |
| | | @Override |
| | | public boolean addReturnManagementDto(ReturnManagementDto returnManagementDto) { |
| | | if (ObjectUtils.isEmpty(returnManagementDto.getReturnNo())){ |
| | | String rt = OrderUtils.countTodayByCreateTime(returnManagementMapper, "RT","return_no"); |
| | | String rt = OrderUtils.countTodayByCreateTime(returnManagementMapper, "RT","return_no", returnManagementDto.getCreateTime() != null ? returnManagementDto.getCreateTime() : LocalDateTime.now()); |
| | | returnManagementDto.setReturnNo(rt); |
| | | } |
| | | save(returnManagementDto); |
| | |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Collections; |
| | | |
| | | @Component |
| | |
| | | * @param recordId |
| | | */ |
| | | public void addStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId) { |
| | | addStock(productModelId, quantity, recordType, recordId, null); |
| | | } |
| | | |
| | | /** |
| | | * åæ ¼å
¥åº |
| | | * @param recordType |
| | | * @param recordId |
| | | */ |
| | | public void addStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId, LocalDateTime createTime) { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(recordId); |
| | | stockInventoryDto.setRecordType(String.valueOf(recordType)); |
| | | stockInventoryDto.setQualitity(quantity); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setCreateTime(createTime); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } |
| | | |
| | |
| | | * @param recordId |
| | | */ |
| | | public void addStockWithBatchNo(Long productModelId, BigDecimal quantity, String recordType, Long recordId, String batchNo) { |
| | | addStockWithBatchNo(productModelId, quantity, recordType, recordId, batchNo, null); |
| | | } |
| | | |
| | | /** |
| | | * åæ ¼å
¥åºå¸¦æ¹æ¬¡å· |
| | | * @param recordType |
| | | * @param recordId |
| | | */ |
| | | public void addStockWithBatchNo(Long productModelId, BigDecimal quantity, String recordType, Long recordId, String batchNo, LocalDateTime createTime) { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(recordId); |
| | | stockInventoryDto.setRecordType(String.valueOf(recordType)); |
| | | stockInventoryDto.setQualitity(quantity); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setCreateTime(createTime); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } |
| | | |
| | |
| | | @PostMapping("/updateRouteItem") |
| | | @Operation(summary = "ä¿®æ¹ç产订åçå·¥èºè·¯çº¿è¯¦æ
") |
| | | public R updateRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) { |
| | | return R.ok(productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation)); |
| | | return productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation); |
| | | } |
| | | |
| | | @Log(title = "ç产工åºè·¯ç±", businessType = BusinessType.DELETE) |
| | |
| | | private String npsNo; |
| | | |
| | | @Schema(description = "å½å
¥æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | |
| | | R deleteRouteItem(Long id); |
| | | |
| | | int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation); |
| | | |
| | | } |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | |
| | | // ä¸åå
¥å£ç»ä¸è¡¥é½æ¥æºåæ®ã计ååå·¥èºä¿¡æ¯ï¼é¿å
å端åå«ä¼ å¤å¥å段ã |
| | | validateAndFillOrder(productionOrder, oldOrder); |
| | | if (productionOrder.getNpsNo() == null || productionOrder.getNpsNo().trim().isEmpty()) { |
| | | productionOrder.setNpsNo(generateNextOrderNo()); |
| | | productionOrder.setNpsNo(generateNextOrderNo(productionOrder.getCreateTime() != null ? productionOrder.getCreateTime() : LocalDateTime.now())); |
| | | } |
| | | if (productionOrder.getCompleteQuantity() == null) { |
| | | productionOrder.setCompleteQuantity(BigDecimal.ZERO); |
| | |
| | | .orderByDesc(ProductionOrder::getId); |
| | | } |
| | | |
| | | private String generateNextOrderNo() { |
| | | private String generateNextOrderNo(LocalDateTime createTime) { |
| | | // çæä¸ä¸ä¸ªç产订åå· |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | LocalDate localDate = createTime.toLocalDate(); |
| | | String datePrefix = localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String prefix = "SC" + datePrefix; |
| | | ProductionOrder latestOrder = this.getOne(Wrappers.<ProductionOrder>lambdaQuery() |
| | | .likeRight(ProductionOrder::getNpsNo, prefix) |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.project.system.domain.SysUserDept; |
| | | import com.ruoyi.project.system.domain.vo.SysUserDeptVo; |
| | | import org.apache.ibatis.annotations.Select; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | |
| | | List<SysUserDeptVo> userLoginFacotryList(@Param("userDeptVo") SysUserDeptVo userDeptVo); |
| | | |
| | | List<Map<String, Object>> setSchemeApplicableStaffUserInfo(@Param("ids") List<Long> ids); |
| | | |
| | | @Select("<script>" + |
| | | "select count(distinct sud.user_id) " + |
| | | "from sys_user_dept sud " + |
| | | "inner join sys_user su on su.user_id = sud.user_id " + |
| | | "where su.del_flag = '0' " + |
| | | "and sud.dept_id in " + |
| | | "<foreach collection='deptIds' item='deptId' open='(' separator=',' close=')'>" + |
| | | "#{deptId}" + |
| | | "</foreach>" + |
| | | "</script>") |
| | | Long countDistinctUserIdsByDeptIds(@Param("deptIds") List<Long> deptIds); |
| | | |
| | | @Select("<script>" + |
| | | "select distinct sud.user_id " + |
| | | "from sys_user_dept sud " + |
| | | "inner join sys_user su on su.user_id = sud.user_id " + |
| | | "where su.del_flag = '0' " + |
| | | "and sud.dept_id in " + |
| | | "<foreach collection='deptIds' item='deptId' open='(' separator=',' close=')'>" + |
| | | "#{deptId}" + |
| | | "</foreach>" + |
| | | "</script>") |
| | | List<Long> selectDistinctUserIdsByDeptIds(@Param("deptIds") List<Long> deptIds); |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
| | | import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
|
| | | import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
|
| | | import com.ruoyi.common.utils.SecurityUtils;
|
| | | import com.ruoyi.project.system.domain.SysDept;
|
| | | import com.ruoyi.project.system.domain.SysNotice;
|
| | |
| | | import org.springframework.stereotype.Service;
|
| | | import org.springframework.transaction.annotation.Transactional;
|
| | |
|
| | | import java.util.HashSet;
|
| | | import java.util.List;
|
| | | import java.util.Set;
|
| | | import java.util.stream.Collectors;
|
| | | import java.util.regex.Matcher;
|
| | | import java.util.regex.Pattern;
|
| | |
|
| | | /**
|
| | | * å
¬å æå¡å±å®ç°
|
| | |
| | | @RequiredArgsConstructor
|
| | | public class SysNoticeServiceImpl extends ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
|
| | |
|
| | | private static final Pattern ENTERPRISE_NEWS_ID_PATTERN = Pattern.compile("[?&]id=(\\d+)");
|
| | |
|
| | | private final SysNoticeMapper noticeMapper;
|
| | | private final SysUserMapper userMapper;
|
| | | private final SysDeptMapper deptMapper;
|
| | | private final SysUserDeptMapper userDeptMapper;
|
| | | private final UnipushService unipushService;
|
| | | private final EnterpriseNewsMapper enterpriseNewsMapper;
|
| | |
|
| | | /**
|
| | | * æ¥è¯¢å
¬åä¿¡æ¯
|
| | |
| | | * @return ç»æ
|
| | | */
|
| | | @Override
|
| | | @Transactional(rollbackFor = Exception.class)
|
| | | public int updateNotice(SysNotice notice)
|
| | | {
|
| | | return noticeMapper.updateNotice(notice);
|
| | | if (notice == null || notice.getNoticeId() == null) {
|
| | | return 0;
|
| | | }
|
| | | SysNotice dbNotice = noticeMapper.selectNoticeById(notice.getNoticeId());
|
| | | if (dbNotice == null) {
|
| | | return 0;
|
| | | }
|
| | | boolean needSyncNewsReadCount = isEnterpriseNewsNotice(dbNotice)
|
| | | && notice.getStatus() != null
|
| | | && !notice.getStatus().equals(dbNotice.getStatus());
|
| | | int rows = noticeMapper.updateNotice(notice);
|
| | | if (rows > 0 && needSyncNewsReadCount) {
|
| | | syncEnterpriseNewsReadCount(dbNotice.getJumpPath());
|
| | | }
|
| | | return rows;
|
| | | }
|
| | |
|
| | | /**
|
| | |
| | | @Override
|
| | | public int readAll() {
|
| | | Long userId = SecurityUtils.getUserId();
|
| | | return noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
|
| | | List<SysNotice> unreadNotices = noticeMapper.selectList(Wrappers.<SysNotice>lambdaQuery()
|
| | | .eq(SysNotice::getConsigneeId, userId)
|
| | | .eq(SysNotice::getStatus, "0"));
|
| | | int rows = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
|
| | | .eq(SysNotice::getConsigneeId, userId)
|
| | | .eq(SysNotice::getStatus, "0")
|
| | | .set(SysNotice::getStatus, "1"));
|
| | | if (rows > 0) {
|
| | | syncEnterpriseNewsReadCount(unreadNotices);
|
| | | }
|
| | | return rows;
|
| | | }
|
| | |
|
| | | @Override
|
| | |
| | | return false;
|
| | | }
|
| | | sysNotice.setStatus("1");
|
| | | return noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
|
| | | boolean updated = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
|
| | | .eq(SysNotice::getNoticeId, noticeId)
|
| | | .eq(SysNotice::getStatus, "0")
|
| | | .set(SysNotice::getStatus, "1")) > 0;
|
| | | if (updated) {
|
| | | syncEnterpriseNewsReadCount(sysNotice.getJumpPath());
|
| | | }
|
| | | return updated;
|
| | | }
|
| | |
|
| | | private boolean isEnterpriseNewsNotice(SysNotice sysNotice) {
|
| | | return sysNotice != null
|
| | | && sysNotice.getJumpPath() != null
|
| | | && sysNotice.getJumpPath().contains("/enterpriseNews?id=");
|
| | | }
|
| | |
|
| | | private void syncEnterpriseNewsReadCount(List<SysNotice> notices) {
|
| | | if (notices == null || notices.isEmpty()) {
|
| | | return;
|
| | | }
|
| | | Set<String> jumpPaths = new HashSet<>();
|
| | | for (SysNotice notice : notices) {
|
| | | if (isEnterpriseNewsNotice(notice)) {
|
| | | jumpPaths.add(notice.getJumpPath());
|
| | | }
|
| | | }
|
| | | for (String jumpPath : jumpPaths) {
|
| | | syncEnterpriseNewsReadCount(jumpPath);
|
| | | }
|
| | | }
|
| | |
|
| | | private void syncEnterpriseNewsReadCount(String jumpPath) {
|
| | | Long newsId = parseEnterpriseNewsId(jumpPath);
|
| | | if (newsId == null) {
|
| | | return;
|
| | | }
|
| | | long readCount = noticeMapper.selectCount(Wrappers.<SysNotice>lambdaQuery()
|
| | | .eq(SysNotice::getStatus, "1")
|
| | | .eq(SysNotice::getJumpPath, jumpPath));
|
| | | EnterpriseNews enterpriseNews = new EnterpriseNews();
|
| | | enterpriseNews.setId(newsId);
|
| | | enterpriseNews.setReadCount((int) readCount);
|
| | | enterpriseNewsMapper.updateById(enterpriseNews);
|
| | | }
|
| | |
|
| | | private Long parseEnterpriseNewsId(String jumpPath) {
|
| | | if (jumpPath == null || !jumpPath.startsWith("/enterpriseNews")) {
|
| | | return null;
|
| | | }
|
| | | Matcher matcher = ENTERPRISE_NEWS_ID_PATTERN.matcher(jumpPath);
|
| | | if (!matcher.find()) {
|
| | | return null;
|
| | | }
|
| | | try {
|
| | | return Long.parseLong(matcher.group(1));
|
| | | } catch (NumberFormatException e) {
|
| | | return null;
|
| | | }
|
| | | }
|
| | | }
|
| | |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import com.ruoyi.projectManagement.dto.RoleDto; |
| | | import com.ruoyi.projectManagement.mapper.RolesMapper; |
| | | import com.ruoyi.projectManagement.pojo.Roles; |
| | |
| | | @Log(title = "项ç®è§è²-æ°å¢", businessType = BusinessType.INSERT) |
| | | public AjaxResult add(@RequestBody RoleDto roleDto) { |
| | | if (roleDto.getIsDefaultNo()) { |
| | | roleDto.setNo(OrderUtils.countTodayByCreateTime(rolesMapper, "XMJS","no")); |
| | | roleDto.setNo(OrderUtils.countTodayByCreateTime(rolesMapper, "XMJS","no", roleDto.getCreateTime() != null ? roleDto.getCreateTime() : LocalDateTime.now())); |
| | | } |
| | | return AjaxResult.success(rolesservice.save(roleDto)); |
| | | } |
| | |
| | | private Integer status; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "åå»ºç¨æ·") |
| | |
| | | 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.framework.web.page.TableDataInfo; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | | import com.ruoyi.purchase.mapper.PurchaseLedgerTemplateMapper; |
| | |
| | | import java.io.InputStream; |
| | | import java.io.OutputStream; |
| | | import java.net.URLEncoder; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | @PostMapping("/addOrEditPurchase") |
| | | public AjaxResult addOrEditPurchase(@RequestBody PurchaseLedgerDto purchaseLedgerDto) throws Exception { |
| | | return toAjax(purchaseLedgerService.addOrEditPurchase(purchaseLedgerDto)); |
| | | } |
| | | |
| | | @Operation(summary = "æ¹éæ¨è¿éè´å°è´¦å°å
¥åº") |
| | | @Log(title = "æ¹éæ¨è¿éè´å°è´¦å°å
¥åº", businessType = BusinessType.OTHER) |
| | | @PostMapping("/batchInsertPurchaseSteps") |
| | | public R batchInsertPurchaseSteps(@RequestBody PurchaseLedgerDto purchaseLedgerDto) { |
| | | return purchaseLedgerService.batchInsertPurchaseSteps(purchaseLedgerDto == null ? null : purchaseLedgerDto.getIds()); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Operation(summary = "çæéè´åºåå·") |
| | | @GetMapping("/createPurchaseNo") |
| | | @Log(title = "çæéè´åºåå·", businessType = BusinessType.OTHER) |
| | | public AjaxResult createPurchaseNo() { |
| | | return AjaxResult.success("çææå",purchaseLedgerService.getPurchaseNo()); |
| | | public AjaxResult createPurchaseNo(@RequestParam Date entryDate) { |
| | | return AjaxResult.success("çææå",purchaseLedgerService.getPurchaseNo(entryDate)); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderDto; |
| | | import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper; |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrders; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import com.ruoyi.purchase.service.PurchaseReturnOrdersService; |
| | | import com.ruoyi.purchase.vo.PurchaseStockInProductVo; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | |
| | | @PostMapping("/add") |
| | | public AjaxResult add(@RequestBody PurchaseReturnOrderDto purchaseReturnOrderDto) throws Exception { |
| | | if (purchaseReturnOrderDto.getIsDefaultNo()) { |
| | | purchaseReturnOrderDto.setNo(OrderUtils.countTodayByCreateTime(purchaseReturnOrdersMapper, "CGTL", "no")); |
| | | purchaseReturnOrderDto.setNo(OrderUtils.countTodayByCreateTime(purchaseReturnOrdersMapper, "CGTL", "no", purchaseReturnOrderDto.getCreateTime() != null ? purchaseReturnOrderDto.getCreateTime() : LocalDateTime.now())); |
| | | } |
| | | return AjaxResult.success(purchaseReturnOrdersService.add(purchaseReturnOrderDto)); |
| | | } |
| | |
| | | |
| | | private List<SalesLedgerProduct> productData; |
| | | |
| | | @Schema(description = "æ¹éå¤çéè´å°è´¦IDå表") |
| | | private List<Long> ids; |
| | | |
| | | private List<String> tempFileIds; |
| | | |
| | | private List<CommonFile> SalesLedgerFiles; |
| | |
| | | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | |
| | | } |
| | |
| | | private BigDecimal totalAmount; |
| | | |
| | | @Schema(description = "å½å
¥æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger); |
| | | |
| | | int addOrEditPurchase(PurchaseLedgerDto purchaseLedgerDto) throws Exception; |
| | | |
| | | R batchInsertPurchaseSteps(List<Long> ids); |
| | | |
| | | void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct); |
| | | |
| | |
| | | |
| | | IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, PurchaseLedgerDto purchaseLedger); |
| | | |
| | | String getPurchaseNo(); |
| | | String getPurchaseNo(Date entryDate); |
| | | |
| | | AjaxResult importData(MultipartFile file); |
| | | |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessVO; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import com.ruoyi.approve.service.ApprovalInstanceService; |
| | | import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.basic.pojo.SupplierManage; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.exception.base.BaseException; |
| | | import com.ruoyi.common.enums.ApprovalStatusEnum; |
| | | import com.ruoyi.common.enums.ReviewStatusEnum; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper; |
| | | import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | |
| | | import com.ruoyi.quality.pojo.QualityInspectParam; |
| | | import com.ruoyi.quality.pojo.QualityTestStandard; |
| | | import com.ruoyi.quality.pojo.QualityTestStandardParam; |
| | | import com.ruoyi.quality.service.IQualityInspectService; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import com.ruoyi.sales.pojo.SalesLedger; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.service.impl.CommonFileServiceImpl; |
| | | import com.ruoyi.stock.pojo.StockInRecord; |
| | | import com.ruoyi.stock.service.StockInRecordService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.BeanUtils; |
| | |
| | | private final ApproveProcessServiceImpl approveProcessService; |
| | | private final ProcurementRecordMapper procurementRecordStorageMapper; |
| | | private final FileUtil fileUtil; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | private final IQualityInspectService qualityInspectService; |
| | | private final StockInRecordService stockInRecordService; |
| | | private final StockUtils stockUtils; |
| | | private final ApprovalTemplateMapper approvalTemplateMapper; |
| | | |
| | | @Override |
| | | public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) { |
| | |
| | | return 1; |
| | | } |
| | | |
| | | @Override |
| | | public R batchInsertPurchaseSteps(List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | return R.fail("è¯·éæ©éè´å°è´¦"); |
| | | } |
| | | |
| | | List<Long> distinctIds = ids.stream() |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (CollectionUtils.isEmpty(distinctIds)) { |
| | | return R.fail("è¯·éæ©éè´å°è´¦"); |
| | | } |
| | | |
| | | PurchaseLedgerDto queryDto = new PurchaseLedgerDto(); |
| | | queryDto.setIds(distinctIds); |
| | | IPage<PurchaseLedgerDto> pageResult = this.selectPurchaseLedgerListPage( |
| | | new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(1, distinctIds.size()), |
| | | queryDto |
| | | ); |
| | | List<PurchaseLedgerDto> ledgerDtos = pageResult == null || pageResult.getRecords() == null |
| | | ? Collections.emptyList() |
| | | : pageResult.getRecords(); |
| | | |
| | | Map<Long, PurchaseLedgerDto> ledgerDtoMap = ledgerDtos.stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(PurchaseLedgerDto::getId, item -> item, (left, right) -> left, LinkedHashMap::new)); |
| | | |
| | | List<Map<String, Object>> details = new ArrayList<>(); |
| | | int successCount = 0; |
| | | int skipCount = 0; |
| | | int failCount = 0; |
| | | int autoApprovedCount = 0; |
| | | |
| | | for (Long id : distinctIds) { |
| | | Map<String, Object> detail = new LinkedHashMap<>(); |
| | | detail.put("purchaseLedgerId", id); |
| | | PurchaseLedgerDto purchaseLedgerDto = ledgerDtoMap.get(id); |
| | | if (purchaseLedgerDto == null) { |
| | | failCount++; |
| | | detail.put("status", "FAIL"); |
| | | detail.put("message", "éè´å°è´¦ä¸åå¨ææªæ¥è¯¢å°"); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | |
| | | detail.put("purchaseContractNumber", purchaseLedgerDto.getPurchaseContractNumber()); |
| | | detail.put("approvalStatus", purchaseLedgerDto.getApprovalStatus()); |
| | | detail.put("stockInStatus", purchaseLedgerDto.getStockInStatus()); |
| | | |
| | | if (ApprovalStatusEnum.REJECTED.getCode().equals(purchaseLedgerDto.getApprovalStatus())) { |
| | | skipCount++; |
| | | detail.put("status", "SKIP"); |
| | | detail.put("message", "éè´å已驳å"); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | |
| | | if ("å®å
¨å
¥åº".equals(purchaseLedgerDto.getStockInStatus())) { |
| | | skipCount++; |
| | | detail.put("status", "SKIP"); |
| | | detail.put("message", "éè´åå·²å®å
¨å
¥åº"); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | |
| | | try { |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id); |
| | | if (purchaseLedger == null) { |
| | | failCount++; |
| | | detail.put("status", "FAIL"); |
| | | detail.put("message", "éè´å°è´¦ä¸åå¨"); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | |
| | | if (!ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) { |
| | | ApprovalInstance approvalInstance = approvalInstanceService.getOne( |
| | | Wrappers.<ApprovalInstance>lambdaQuery() |
| | | .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId()) |
| | | .eq(ApprovalInstance::getBusinessType, 5L) |
| | | .eq(ApprovalInstance::getDeleted, 0) |
| | | .orderByDesc(ApprovalInstance::getId) |
| | | .last("limit 1") |
| | | ); |
| | | if (approvalInstance == null) { |
| | | failCount++; |
| | | detail.put("status", "FAIL"); |
| | | detail.put("message", "æªæ¾å°å¯¹åºçéè´å®¡æ¹å®ä¾"); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | |
| | | if ("APPROVED".equals(approvalInstance.getStatus()) |
| | | && !ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) { |
| | | purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode()); |
| | | purchaseLedgerMapper.updateById(purchaseLedger); |
| | | } else if (!"APPROVED".equals(approvalInstance.getStatus())) { |
| | | R autoApproveResult = approvalInstanceService.autoApprove(approvalInstance.getId()); |
| | | if (autoApproveResult == null || !R.isSuccess(autoApproveResult)) { |
| | | failCount++; |
| | | detail.put("status", "FAIL"); |
| | | detail.put("message", autoApproveResult == null ? "éè´å®¡æ¹èªå¨éè¿å¤±è´¥" : autoApproveResult.getMsg()); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | autoApprovedCount++; |
| | | } |
| | | |
| | | purchaseLedger = purchaseLedgerMapper.selectById(id); |
| | | if (purchaseLedger == null || !ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) { |
| | | failCount++; |
| | | detail.put("status", "FAIL"); |
| | | detail.put("message", "éè´å审æ¹ç¶ææªæ´æ°ä¸ºå·²éè¿"); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList( |
| | | Wrappers.<SalesLedgerProduct>lambdaQuery() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()) |
| | | .eq(SalesLedgerProduct::getType, 2) |
| | | .orderByAsc(SalesLedgerProduct::getId) |
| | | ); |
| | | if (CollectionUtils.isEmpty(products)) { |
| | | skipCount++; |
| | | detail.put("status", "SKIP"); |
| | | detail.put("message", "éè´å没æäº§åæç»"); |
| | | details.add(detail); |
| | | continue; |
| | | } |
| | | |
| | | int processedProductCount = 0; |
| | | int skippedProductCount = 0; |
| | | int failedProductCount = 0; |
| | | for (SalesLedgerProduct product : products) { |
| | | try { |
| | | boolean processed; |
| | | if (Boolean.TRUE.equals(product.getIsChecked())) { |
| | | processed = processPurchaseQualityProduct(purchaseLedger, product); |
| | | } else { |
| | | processed = processPurchaseDirectProduct(purchaseLedger, product); |
| | | } |
| | | if (processed) { |
| | | processedProductCount++; |
| | | } else { |
| | | skippedProductCount++; |
| | | } |
| | | } catch (Exception ex) { |
| | | failedProductCount++; |
| | | log.error("æ¹éæ¨è¿éè´å°è´¦å¤±è´¥, purchaseLedgerId={}, productId={}, productModelId={}", |
| | | purchaseLedger.getId(), product.getId(), product.getProductModelId(), ex); |
| | | } |
| | | } |
| | | |
| | | successCount++; |
| | | detail.put("status", failedProductCount > 0 ? "PARTIAL" : "SUCCESS"); |
| | | detail.put("processedProductCount", processedProductCount); |
| | | detail.put("skippedProductCount", skippedProductCount); |
| | | detail.put("failedProductCount", failedProductCount); |
| | | detail.put("message", failedProductCount > 0 ? "é¨å产åå¤ç失败" : "å¤ç宿"); |
| | | details.add(detail); |
| | | } catch (Exception ex) { |
| | | failCount++; |
| | | detail.put("status", "FAIL"); |
| | | detail.put("message", ex.getMessage()); |
| | | details.add(detail); |
| | | log.error("æ¹éæ¨è¿éè´å°è´¦å¤±è´¥, purchaseLedgerId={}", id, ex); |
| | | } |
| | | } |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("totalCount", distinctIds.size()); |
| | | summary.put("successCount", successCount); |
| | | summary.put("skipCount", skipCount); |
| | | summary.put("failCount", failCount); |
| | | summary.put("autoApprovedCount", autoApprovedCount); |
| | | summary.put("details", details); |
| | | return R.ok(summary, "æ¹éæ¨è¿éè´æ¥éª¤å®æ"); |
| | | } |
| | | |
| | | |
| | | public void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) { |
| | | QualityInspect qualityInspect = new QualityInspect(); |
| | |
| | | param.setInspectId(qualityInspect.getId()); |
| | | qualityInspectParamMapper.insert(param); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | private boolean processPurchaseQualityProduct(PurchaseLedger purchaseLedger, SalesLedgerProduct product) { |
| | | if (purchaseLedger == null || product == null || product.getProductModelId() == null) { |
| | | return false; |
| | | } |
| | | |
| | | QualityInspect qualityInspect = findLatestPurchaseQualityInspect(purchaseLedger.getId(), product.getProductModelId()); |
| | | if (qualityInspect == null) { |
| | | addQualityInspect(purchaseLedger, product); |
| | | qualityInspect = findLatestPurchaseQualityInspect(purchaseLedger.getId(), product.getProductModelId()); |
| | | } |
| | | if (qualityInspect == null) { |
| | | return false; |
| | | } |
| | | |
| | | LocalDateTime purchaseInspectTime = toStartOfDayPlusDays(purchaseLedger.getEntryDate(), 1); |
| | | if (purchaseInspectTime != null && (qualityInspect.getCheckTime() == null |
| | | || !DateUtils.toLocalDate(qualityInspect.getCheckTime()).equals(purchaseInspectTime.toLocalDate()))) { |
| | | qualityInspect.setCheckTime(DateUtils.toDate(purchaseInspectTime.toLocalDate())); |
| | | qualityInspectMapper.updateById(qualityInspect); |
| | | } |
| | | |
| | | List<StockInRecord> stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId()); |
| | | if (hasApprovedStockRecord(stockRecords)) { |
| | | return true; |
| | | } |
| | | |
| | | if (!Integer.valueOf(1).equals(qualityInspect.getInspectState())) { |
| | | R autoSubmitResult = qualityInspectService.autoSubmit(qualityInspect.getId()); |
| | | if (autoSubmitResult == null || !R.isSuccess(autoSubmitResult)) { |
| | | return false; |
| | | } |
| | | qualityInspect = qualityInspectMapper.selectById(qualityInspect.getId()); |
| | | } |
| | | |
| | | stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId()); |
| | | if (CollectionUtils.isEmpty(stockRecords) |
| | | && qualityInspect.getQualifiedQuantity() != null |
| | | && qualityInspect.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) { |
| | | stockUtils.addStockWithBatchNo( |
| | | product.getProductModelId(), |
| | | qualityInspect.getQualifiedQuantity(), |
| | | StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode(), |
| | | qualityInspect.getId(), |
| | | null, |
| | | purchaseInspectTime == null ? null : purchaseInspectTime.plusDays(1) |
| | | ); |
| | | stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId()); |
| | | } |
| | | |
| | | StockInRecord targetStockRecord = findLatestUnapprovedStockRecord(stockRecords); |
| | | if (targetStockRecord == null) { |
| | | return false; |
| | | } |
| | | |
| | | LocalDateTime qualityStockCreateTime = resolveQualityStockCreateTime(qualityInspect); |
| | | if (qualityStockCreateTime != null && (targetStockRecord.getCreateTime() == null |
| | | || !qualityStockCreateTime.equals(targetStockRecord.getCreateTime()))) { |
| | | targetStockRecord.setCreateTime(qualityStockCreateTime); |
| | | stockInRecordService.updateById(targetStockRecord); |
| | | } |
| | | |
| | | approveStockRecords(Collections.singletonList(targetStockRecord)); |
| | | stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId()); |
| | | return hasApprovedStockRecord(stockRecords); |
| | | } |
| | | |
| | | private boolean processPurchaseDirectProduct(PurchaseLedger purchaseLedger, SalesLedgerProduct product) { |
| | | if (purchaseLedger == null || product == null || product.getProductModelId() == null) { |
| | | return false; |
| | | } |
| | | if (product.getQuantity() == null) { |
| | | return false; |
| | | } |
| | | if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) { |
| | | return false; |
| | | } |
| | | |
| | | LocalDateTime stockCreateTime = toStartOfDayPlusDays(purchaseLedger.getEntryDate(), 1); |
| | | |
| | | List<StockInRecord> stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId()); |
| | | if (hasApprovedStockRecord(stockRecords)) { |
| | | return true; |
| | | } |
| | | |
| | | if (CollectionUtils.isEmpty(stockRecords)) { |
| | | stockUtils.addStockWithBatchNo( |
| | | product.getProductModelId(), |
| | | product.getQuantity(), |
| | | StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), |
| | | purchaseLedger.getId(), |
| | | purchaseLedger.getPurchaseContractNumber() + "-" + product.getId(), |
| | | stockCreateTime |
| | | ); |
| | | stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId()); |
| | | } |
| | | |
| | | if (CollectionUtils.isEmpty(stockRecords)) { |
| | | return false; |
| | | } |
| | | |
| | | StockInRecord targetStockRecord = findLatestUnapprovedStockRecord(stockRecords); |
| | | if (targetStockRecord == null) { |
| | | return false; |
| | | } |
| | | |
| | | if (stockCreateTime != null && (targetStockRecord.getCreateTime() == null |
| | | || !stockCreateTime.equals(targetStockRecord.getCreateTime()))) { |
| | | targetStockRecord.setCreateTime(stockCreateTime); |
| | | stockInRecordService.updateById(targetStockRecord); |
| | | } |
| | | |
| | | approveStockRecords(Collections.singletonList(targetStockRecord)); |
| | | stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId()); |
| | | return hasApprovedStockRecord(stockRecords); |
| | | } |
| | | |
| | | private LocalDateTime toStartOfDayPlusDays(Date date, int days) { |
| | | if (date == null) { |
| | | return null; |
| | | } |
| | | return DateUtils.toLocalDate(date).plusDays(days).atStartOfDay(); |
| | | } |
| | | |
| | | private LocalDateTime resolveQualityStockCreateTime(QualityInspect qualityInspect) { |
| | | if (qualityInspect == null || qualityInspect.getCheckTime() == null) { |
| | | return null; |
| | | } |
| | | return DateUtils.toLocalDate(qualityInspect.getCheckTime()).plusDays(1).atStartOfDay(); |
| | | } |
| | | |
| | | private QualityInspect findLatestPurchaseQualityInspect(Long purchaseLedgerId, Long productModelId) { |
| | | if (purchaseLedgerId == null || productModelId == null) { |
| | | return null; |
| | | } |
| | | return qualityInspectMapper.selectOne( |
| | | Wrappers.<QualityInspect>lambdaQuery() |
| | | .eq(QualityInspect::getInspectType, 0) |
| | | .eq(QualityInspect::getPurchaseLedgerId, purchaseLedgerId) |
| | | .eq(QualityInspect::getProductModelId, productModelId) |
| | | .orderByDesc(QualityInspect::getId) |
| | | .last("limit 1") |
| | | ); |
| | | } |
| | | |
| | | private List<StockInRecord> findQualityStockRecords(Long qualityInspectId, Long productModelId) { |
| | | if (qualityInspectId == null || productModelId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return stockInRecordService.list( |
| | | Wrappers.<StockInRecord>lambdaQuery() |
| | | .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode()) |
| | | .eq(StockInRecord::getRecordId, qualityInspectId) |
| | | .eq(StockInRecord::getProductModelId, productModelId) |
| | | .orderByDesc(StockInRecord::getId) |
| | | ); |
| | | } |
| | | |
| | | private List<StockInRecord> findDirectStockRecords(Long purchaseLedgerId, String purchaseContractNumber, Long productModelId, Long purchaseProductId) { |
| | | if (purchaseLedgerId == null || productModelId == null || purchaseProductId == null || !StringUtils.hasText(purchaseContractNumber)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | String batchNo = purchaseContractNumber + "-" + purchaseProductId; |
| | | return stockInRecordService.list( |
| | | Wrappers.<StockInRecord>lambdaQuery() |
| | | .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode()) |
| | | .eq(StockInRecord::getRecordId, purchaseLedgerId) |
| | | .eq(StockInRecord::getProductModelId, productModelId) |
| | | .eq(StockInRecord::getBatchNo, batchNo) |
| | | .orderByDesc(StockInRecord::getId) |
| | | ); |
| | | } |
| | | |
| | | private boolean hasApprovedStockRecord(List<StockInRecord> stockRecords) { |
| | | return stockRecords != null && stockRecords.stream() |
| | | .anyMatch(item -> ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus())); |
| | | } |
| | | |
| | | private StockInRecord findLatestUnapprovedStockRecord(List<StockInRecord> stockRecords) { |
| | | if (CollectionUtils.isEmpty(stockRecords)) { |
| | | return null; |
| | | } |
| | | return stockRecords.stream() |
| | | .filter(item -> !ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus())) |
| | | .findFirst() |
| | | .orElse(null); |
| | | } |
| | | |
| | | private void approveStockRecords(List<StockInRecord> stockRecords) { |
| | | if (CollectionUtils.isEmpty(stockRecords)) { |
| | | return; |
| | | } |
| | | List<Long> rejectedIds = stockRecords.stream() |
| | | .filter(item -> ReviewStatusEnum.REJECTED.getCode().equals(item.getApprovalStatus())) |
| | | .map(StockInRecord::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (!rejectedIds.isEmpty()) { |
| | | stockInRecordService.batchReAudit(rejectedIds); |
| | | } |
| | | |
| | | List<Long> pendingIds = stockRecords.stream() |
| | | .filter(item -> item.getApprovalStatus() == null |
| | | || ReviewStatusEnum.PENDING_REVIEW.getCode().equals(item.getApprovalStatus()) |
| | | || ReviewStatusEnum.REJECTED.getCode().equals(item.getApprovalStatus())) |
| | | .map(StockInRecord::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (!pendingIds.isEmpty()) { |
| | | stockInRecordService.batchApprove(pendingIds, ReviewStatusEnum.APPROVED.getCode()); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public String getPurchaseNo() { |
| | | public String getPurchaseNo(Date entryDate) { |
| | | LocalDate localDate = entryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | // çææ¥æåç¼ï¼ä¾å¦ï¼CG20250405ï¼ |
| | | String purchaseNo = "CG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String purchaseNo = "CG" + localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | |
| | | // æå»º Redis Keyï¼æå¤©åéï¼ |
| | | String redisKey = "purchase_no:" + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | String redisKey = "purchase_no:" + localDate.format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | // è·åå½ååºå·å¹¶éå¢ï¼ååæä½ï¼ |
| | | Long sequence = redisTemplate.opsForValue().increment(redisKey); |
| | |
| | | if (loginUser == null) { |
| | | return; |
| | | } |
| | | ApproveProcessVO approveProcessVO = new ApproveProcessVO(); |
| | | approveProcessVO.setApproveType(5); |
| | | approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); |
| | | approveProcessVO.setApproveReason(purchaseLedger.getPurchaseContractNumber()); |
| | | approveProcessVO.setApproveUserIds(purchaseLedger.getApproveUserIds()); |
| | | approveProcessVO.setApproveUser(loginUser.getUserId()); |
| | | approveProcessVO.setApproveTime(LocalDate.now().toString()); |
| | | approveProcessService.addApprove(approveProcessVO); |
| | | ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne( |
| | | new LambdaQueryWrapper<ApprovalTemplate>() |
| | | .eq(ApprovalTemplate::getBusinessType, 5L) |
| | | .orderByDesc(ApprovalTemplate::getId) |
| | | .last("LIMIT 1") |
| | | ); |
| | | if (approvalTemplate == null) { |
| | | throw new BaseException("请å
é
ç½®éè´å®¡æ¹æ¨¡æ¿"); |
| | | } |
| | | |
| | | ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto(); |
| | | approvalInstance.setTemplateId(approvalTemplate.getId()); |
| | | approvalInstance.setTemplateName(approvalTemplate.getTemplateName()); |
| | | approvalInstance.setBusinessId(purchaseLedger.getId()); |
| | | approvalInstance.setBusinessType(5L); |
| | | approvalInstance.setCurrentLevel(1); |
| | | approvalInstance.setApplicantId(loginUser.getUserId()); |
| | | approvalInstance.setTitle(purchaseLedger.getPurchaseContractNumber()+"审æ¹"); |
| | | approvalInstance.setApplicantName(loginUser.getNickName()); |
| | | approvalInstance.setApplyTime(LocalDateTime.now()); |
| | | approvalInstanceService.add(approvalInstance); |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | @Operation(summary = "æ£æµææ ç»´æ¤æ¥è¯¢") |
| | | @Log(title = "æ£æµææ ç»´æ¤æ¥è¯¢", businessType = BusinessType.OTHER) |
| | | public R<?> list(Long testStandardId) { |
| | | return R.ok(qualityTestStandardParamService.list(Wrappers.<QualityTestStandardParam>lambdaQuery().eq(QualityTestStandardParam::getTestStandardId,testStandardId))); |
| | | return R.ok(qualityTestStandardParamService.list(Wrappers.<QualityTestStandardParam>lambdaQuery() |
| | | .eq(QualityTestStandardParam::getTestStandardId, testStandardId) |
| | | .orderByDesc(QualityTestStandardParam::getCreateTime))); |
| | | } |
| | | |
| | | } |
| | |
| | | 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.framework.web.domain.R; |
| | | import com.ruoyi.quality.dto.QualityInspectDto; |
| | | import com.ruoyi.quality.pojo.QualityInspect; |
| | | |
| | |
| | | |
| | | int submit(QualityInspect qualityInspect); |
| | | |
| | | R autoSubmit(Long id); |
| | | |
| | | void down(HttpServletResponse response, QualityInspect qualityInspect); |
| | | } |
| | |
| | | import com.deepoove.poi.config.Configure; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import com.ruoyi.common.utils.HackLoopTableRenderPolicy; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import com.ruoyi.procurementrecord.service.ProcurementRecordService; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.quality.dto.QualityInspectDto; |
| | |
| | | import java.io.OutputStream; |
| | | import java.math.BigDecimal; |
| | | import java.net.URLEncoder; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Arrays; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | |
| | | stockInventoryDto.setRecordId(qualityInspect.getId()); |
| | | stockInventoryDto.setProductModelId(qualityInspect.getProductModelId()); |
| | | stockInventoryDto.setQualitity(qualityInspect.getQualifiedQuantity()); |
| | | if (qualityInspect.getCheckTime() != null) { |
| | | LocalDate stockCreateDate = DateUtils.toLocalDate(qualityInspect.getCheckTime()).plusDays(1); |
| | | stockInventoryDto.setCreateTime(LocalDateTime.of(stockCreateDate, java.time.LocalTime.MIDNIGHT)); |
| | | } |
| | | stockInventoryDto.setBatchNo(resolveProductionBatchNo( |
| | | qualityInspect.getProductMainId(), |
| | | qualityInspect.getId(), |
| | |
| | | return qualityInspectMapper.updateById(qualityInspect); |
| | | } |
| | | |
| | | @Override |
| | | public R autoSubmit(Long id) { |
| | | if (id == null) { |
| | | return R.fail("æ£éªåIDä¸è½ä¸ºç©º"); |
| | | } |
| | | QualityInspect qualityInspect = qualityInspectMapper.selectById(id); |
| | | if (qualityInspect == null) { |
| | | return R.fail("æ£éªåä¸åå¨"); |
| | | } |
| | | if (Integer.valueOf(1).equals(qualityInspect.getInspectState())) { |
| | | return R.ok("æ£éªåå·²æäº¤"); |
| | | } |
| | | |
| | | if (ObjectUtils.isNull(qualityInspect.getCheckResult())) { |
| | | qualityInspect.setCheckResult("åæ ¼"); |
| | | } |
| | | if (ObjectUtils.isNull(qualityInspect.getQualifiedQuantity())) { |
| | | qualityInspect.setQualifiedQuantity(qualityInspect.getQuantity() == null ? BigDecimal.ZERO : qualityInspect.getQuantity()); |
| | | } |
| | | if (ObjectUtils.isNull(qualityInspect.getUnqualifiedQuantity())) { |
| | | qualityInspect.setUnqualifiedQuantity(BigDecimal.ZERO); |
| | | } |
| | | qualityInspectMapper.updateById(qualityInspect); |
| | | int rows = submit(qualityInspect); |
| | | return rows > 0 ? R.ok("æ£éªåæäº¤æå") : R.fail("æ£éªåæäº¤å¤±è´¥"); |
| | | } |
| | | |
| | | private String resolveProductionBatchNo(Long productionProductMainId, |
| | | Long qualityInspectId, |
| | | Long productModelId) { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.quality.utils; |
| | | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.ruoyi.common.utils.bean.BeanUtils; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.quality.mapper.QualityInspectMapper; |
| | | import com.ruoyi.quality.mapper.QualityInspectParamMapper; |
| | | import com.ruoyi.quality.mapper.QualityTestStandardMapper; |
| | | import com.ruoyi.quality.mapper.QualityTestStandardParamMapper; |
| | | import com.ruoyi.quality.pojo.QualityInspect; |
| | | import com.ruoyi.quality.pojo.QualityInspectParam; |
| | | import com.ruoyi.quality.pojo.QualityTestStandard; |
| | | import com.ruoyi.quality.pojo.QualityTestStandardParam; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * è´¨æ£åå建工å
·ç±» |
| | | */ |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class QualityInspectHelper { |
| | | |
| | | private final QualityInspectMapper qualityInspectMapper; |
| | | private final QualityTestStandardMapper qualityTestStandardMapper; |
| | | private final QualityTestStandardParamMapper qualityTestStandardParamMapper; |
| | | private final QualityInspectParamMapper qualityInspectParamMapper; |
| | | |
| | | /** |
| | | * å建质æ£å |
| | | * @param purchaseLedger éè´å°è´¦ |
| | | * @param saleProduct éè´äº§å |
| | | */ |
| | | public void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) { |
| | | QualityInspect qualityInspect = new QualityInspect(); |
| | | qualityInspect.setInspectType(0); |
| | | qualityInspect.setSupplier(purchaseLedger.getSupplierName()); |
| | | qualityInspect.setPurchaseLedgerId(purchaseLedger.getId()); |
| | | qualityInspect.setProductId(saleProduct.getProductId()); |
| | | qualityInspect.setProductName(saleProduct.getProductCategory()); |
| | | qualityInspect.setModel(saleProduct.getSpecificationModel()); |
| | | qualityInspect.setProductModelId(saleProduct.getProductModelId()); |
| | | qualityInspect.setUnit(saleProduct.getUnit()); |
| | | qualityInspect.setQuantity(saleProduct.getQuantity()); |
| | | qualityInspectMapper.insert(qualityInspect); |
| | | |
| | | List<QualityTestStandard> qualityTestStandardList = qualityTestStandardMapper |
| | | .getQualityTestStandardByProductId(saleProduct.getProductId(), 0, null); |
| | | |
| | | if (qualityTestStandardList.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | QualityTestStandard firstStandard = qualityTestStandardList.get(0); |
| | | qualityInspect.setTestStandardId(firstStandard.getId()); |
| | | qualityInspectMapper.updateById(qualityInspect); |
| | | |
| | | List<QualityTestStandardParam> standardParams = qualityTestStandardParamMapper.selectList( |
| | | Wrappers.<QualityTestStandardParam>lambdaQuery() |
| | | .eq(QualityTestStandardParam::getTestStandardId, firstStandard.getId())); |
| | | |
| | | for (QualityTestStandardParam standardParam : standardParams) { |
| | | QualityInspectParam param = new QualityInspectParam(); |
| | | BeanUtils.copyProperties(standardParam, param); |
| | | param.setId(null); |
| | | param.setInspectId(qualityInspect.getId()); |
| | | qualityInspectParamMapper.insert(param); |
| | | } |
| | | } |
| | | } |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | @Operation(summary = "æ·»å æ¯ä»ä¸åè´§ä¿¡æ¯") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public AjaxResult add(@RequestBody PaymentShipping paymentShipping) { |
| | | String ord = OrderUtils.countTodayByCreateTime(paymentShippingMapper, "ORD","order_no"); |
| | | String ord = OrderUtils.countTodayByCreateTime(paymentShippingMapper, "ORD","order_no", paymentShipping.getCreateTime() != null ? paymentShipping.getCreateTime() : LocalDateTime.now()); |
| | | paymentShipping.setOrderNo(ord); |
| | | boolean save = paymentShippingService.save(paymentShipping); |
| | | return save ? success() : error(); |
| | |
| | | |
| | | // å¦æå·²ç»æè¿å¼ç¥¨æå款æä½,åä¸å
许ç¼è¾ |
| | | boolean hasReceiptOperation = receiptPaymentAmountTotal.compareTo(BigDecimal.ZERO) > 0; |
| | | salesLedgerVo.setIsEdit(hasReceiptOperation); |
| | | salesLedgerVo.setIsEdit(!hasReceiptOperation); |
| | | |
| | | salesLedgerVo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, ledgerId)); |
| | | } |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessVO; |
| | | import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.framework.web.domain.R; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @Log(title = "åè´§ä¿¡æ¯ç®¡ç", businessType = BusinessType.INSERT) |
| | | public AjaxResult add(@RequestBody ShippingInfoDto req) throws Exception { |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | String sh = OrderUtils.countTodayByCreateTime(shippingInfoMapper, "SH","shipping_no"); |
| | | // åè´§å®¡æ¹ |
| | | ApproveProcessVO approveProcessVO = new ApproveProcessVO(); |
| | | approveProcessVO.setApproveType(7); |
| | | approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); |
| | | approveProcessVO.setApproveReason(sh);//åè´§ç¼å· |
| | | approveProcessVO.setApproveUserIds(req.getApproveUserIds()); |
| | | approveProcessVO.setApproveUser(loginUser.getUserId()); |
| | | approveProcessVO.setApproveTime(LocalDate.now().toString()); |
| | | approveProcessService.addApprove(approveProcessVO); |
| | | // æ·»å åè´§æ¶æ¯ |
| | | req.setShippingNo(sh); |
| | | req.setStatus("å¾
å®¡æ ¸"); |
| | | boolean save = shippingInfoService.add(req); |
| | | return save ? AjaxResult.success() : AjaxResult.error(); |
| | | return AjaxResult.success(shippingInfoService.addReq(req) ? "æ·»å æå" : "æ·»å 失败"); |
| | | } |
| | | |
| | | @Operation(summary = "åè´§æ£åºå") |
| | |
| | | */ |
| | | // 审æ¹äºº |
| | | private String approveUserIds; |
| | | |
| | | private Long templateId; |
| | | } |
| | |
| | | //åè´§æ°é |
| | | private BigDecimal totalQuantity; |
| | | |
| | | private Long templateId; |
| | | |
| | | private String templateName; |
| | | |
| | | |
| | | } |
| | |
| | | private String remark; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "åå»ºç¨æ·") |
| | |
| | | @ApiModelProperty(value = "夿³¨") |
| | | private String remark; |
| | | @ApiModelProperty(value = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | @ApiModelProperty(value = "ä¿®æ¹æ¶é´") |
| | |
| | | private String shippingCarNumber; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "ä¿®æ¹æ¶é´") |
| | |
| | | List<ShippingProductDetailDto> getDetail(Long id); |
| | | |
| | | ShippingApproveDto getDateilByShippingNo(String shippingNo); |
| | | |
| | | boolean addReq(ShippingInfoDto req); |
| | | } |
| | |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | |
| | | productionPlan.setPromisedDeliveryDate(salesLedger.getDeliveryDate());//æ¿è¯ºæ¥æ=äº¤è´§æ¥æ |
| | | productionPlanMapper.insert(productionPlan); |
| | | } |
| | | SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerProduct.getSalesLedgerId()); |
| | | ProductionPlan productionPlan = new ProductionPlan(); |
| | | productionPlan.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId()); |
| | | productionPlan.setSalesLedgerProductId(salesLedgerProduct.getId()); |
| | | productionPlan.setMpsNo(generateNextPlanNo(salesLedger.getEntryDate().toInstant() |
| | | .atZone(ZoneId.systemDefault()) |
| | | .toLocalDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")))); |
| | | productionPlan.setProductModelId(salesLedgerProduct.getProductModelId()); |
| | | productionPlan.setQtyRequired(salesLedgerProduct.getQuantity()); |
| | | productionPlan.setSource("éå®"); |
| | | productionPlan.setStatus(0); |
| | | productionPlan.setRequiredDate(salesLedger.getDeliveryDate());//éæ±æ¥æ=äº¤è´§æ¥æ |
| | | productionPlan.setPromisedDeliveryDate(salesLedger.getDeliveryDate());//æ¿è¯ºæ¥æ=äº¤è´§æ¥æ |
| | | productionPlanMapper.insert(productionPlan); |
| | | |
| | | } |
| | | |
| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | |
| | | import java.io.InputStream; |
| | | import java.lang.reflect.Field; |
| | |
| | | if (CollectionUtils.isEmpty(salesLedgerImportDtoList)) return AjaxResult.error("éå®å°è´¦æ°æ®ä¸ºç©ºï¼"); |
| | | List<SalesLedgerImportDto> salesLedgerProductImportDtoList = stringListMap.get("éå®äº§åæ°æ®"); |
| | | if (CollectionUtils.isEmpty(salesLedgerProductImportDtoList)) return AjaxResult.error("éå®äº§åæ°æ®ä¸ºç©ºï¼"); |
| | | // å®¢æ·æ°æ® |
| | | List<Customer> customers = customerMapper.selectList(new LambdaQueryWrapper<Customer>().in(Customer::getCustomerName, |
| | | salesLedgerImportDtoList.stream().map(SalesLedgerImportDto::getCustomerName).collect(Collectors.toList()))); |
| | | // å®¢æ·æ°æ® - åè listPage æ¥è¯¢ç§æµ·å®¢æ·ï¼type = 0ï¼ |
| | | // type = 0ï¼ç§æµ·å®¢æ·ï¼æè
type = 1ï¼å
¬æµ·å®¢æ·ï¼ä¸å·²è¢«åé
ï¼å¹¶ä¸æ¯èªå·±é¢ç¨ãèªå·±å建æè
å
±äº«ç»èªå·±çå®¢æ· |
| | | Long loginUserId = loginUser.getUser().getUserId(); |
| | | List<Customer> customers = customerMapper.selectList(new QueryWrapper<Customer>() |
| | | .in("customer_name", salesLedgerImportDtoList.stream() |
| | | .map(SalesLedgerImportDto::getCustomerName).collect(Collectors.toList())) |
| | | .and(wrapper -> wrapper.eq("type", 0) |
| | | .or(wrapper2 -> wrapper2.eq("type", 1).eq("is_assigned", 1))) |
| | | .and(wrapper -> wrapper.eq("usage_user", loginUserId) |
| | | .or(wrapper2 -> wrapper2.eq("create_user", loginUserId) |
| | | .or(wrapper3 -> wrapper3.exists("select 1 from customer_user cu where cu.customer_id = customer.id and cu.user_id = " + loginUserId))))); |
| | | // // è§æ ¼åå·æ°æ® |
| | | // List<ProductModel> productModels = productModelMapper.selectList(new LambdaQueryWrapper<ProductModel>().in(ProductModel::getModel, |
| | | // salesLedgerProductImportDtoList.stream().map(SalesLedgerImportDto::getSpecificationModel).collect(Collectors.toList()))); |
| | |
| | | if (salesLedger1 != null) { |
| | | continue; |
| | | } |
| | | |
| | | // 夿ä¸å¡åæ¯å¦åå¨ |
| | | SysUser salesman = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>() |
| | | .eq(SysUser::getNickName, salesLedgerImportDto.getSalesman())); |
| | | if (salesman == null) { |
| | | throw new RuntimeException("ä¸å¡å:" + salesLedgerImportDto.getSalesman() + "ä¸åå¨ï¼"); |
| | | } |
| | | SalesLedger salesLedger = new SalesLedger(); |
| | | BeanUtils.copyProperties(salesLedgerImportDto, salesLedger); |
| | | salesLedger.setExecutionDate(DateUtils.toLocalDate(salesLedgerImportDto.getExecutionDate())); |
| | | salesLedger.setDeliveryDate(DateUtils.toLocalDate(salesLedgerImportDto.getDeliveryDate())); |
| | | // éè¿å®¢æ·åç§°æ¥è¯¢å®¢æ·IDï¼å®¢æ·ååå· |
| | | salesLedger.setCustomerId(customers.stream() |
| | | Optional<Customer> customerOptional = customers.stream() |
| | | .filter(customer -> customer.getCustomerName().equals(salesLedger.getCustomerName())) |
| | | .findFirst() |
| | | .map(Customer::getId) |
| | | .orElse(null)); |
| | | salesLedger.setCustomerContractNo(customers.stream() |
| | | .filter(customer -> customer.getCustomerName().equals(salesLedger.getCustomerName())) |
| | | .findFirst() |
| | | .map(Customer::getTaxpayerIdentificationNumber) |
| | | .orElse(null)); |
| | | .findFirst(); |
| | | if (customerOptional.isEmpty()) { |
| | | throw new RuntimeException("客æ·:" + salesLedger.getCustomerName() + "ä¸åå¨ï¼æè
éç§æµ·ç¨æ·"); |
| | | } |
| | | salesLedger.setCustomerId(customerOptional.get().getId()); |
| | | salesLedger.setCustomerContractNo(customerOptional.get().getTaxpayerIdentificationNumber()); |
| | | Long aLong = sysUsers.stream() |
| | | .filter(sysUser -> sysUser.getNickName().equals(salesLedger.getEntryPerson())) |
| | | .findFirst() |
| | |
| | | salesLedgerProduct.setType(1); |
| | | // 计ç®ä¸å«ç¨æ»ä»· |
| | | salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP)); |
| | | list.stream() |
| | | // æ ¡éªäº§åè§æ ¼æ¯å¦åå¨ |
| | | Optional<Map<String, Object>> productModelOptional = list.stream() |
| | | .filter(map -> Objects.equals(map.get("productName"), salesLedgerProduct.getProductCategory()) && Objects.equals(map.get("model"), salesLedgerProduct.getSpecificationModel())) |
| | | .findFirst() |
| | | .ifPresent(map -> { |
| | | salesLedgerProduct.setProductModelId(Long.parseLong(map.get("modelId").toString())); |
| | | salesLedgerProduct.setProductId(Long.parseLong(map.get("id").toString())); |
| | | }); |
| | | .findFirst(); |
| | | if (productModelOptional.isEmpty()) { |
| | | throw new RuntimeException("产å大类:" + salesLedgerProduct.getProductCategory() + ",è§æ ¼åå·:" + salesLedgerProduct.getSpecificationModel() + "ä¸åå¨ï¼"); |
| | | } |
| | | Map<String, Object> productModelMap = productModelOptional.get(); |
| | | salesLedgerProduct.setProductModelId(Long.parseLong(productModelMap.get("modelId").toString())); |
| | | salesLedgerProduct.setProductId(Long.parseLong(productModelMap.get("id").toString())); |
| | | // salesLedgerProduct.setProductId(productList.stream() |
| | | // .filter(product -> product.getProductName().equals(salesLedgerProduct.getProductCategory())) |
| | | // .findFirst() |
| | |
| | | if (salesLedger.getId() == null) { |
| | | String contractNo = salesLedger.getSalesContractNo(); |
| | | if (StringUtils.isEmpty(contractNo)) { |
| | | contractNo = generateSalesContractNo(); |
| | | contractNo = generateSalesContractNo(salesLedgerDto.getEntryDate()); |
| | | } |
| | | salesLedger.setSalesContractNo(contractNo); |
| | | salesLedgerMapper.insert(salesLedger); |
| | |
| | | } |
| | | |
| | | @Transactional(readOnly = true) |
| | | public String generateSalesContractNo() { |
| | | LocalDate currentDate = LocalDate.now(); |
| | | public String generateSalesContractNo(Date entryDate) { |
| | | LocalDate currentDate = entryDate != null ? DateUtils.toLocalDate(entryDate) : LocalDate.now(); |
| | | String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE); |
| | | String lockKey = LOCK_PREFIX + datePart; |
| | | String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime(); // å¯ä¸æ è¯éææè
|
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.vo.ApproveGetAndUpdateVo; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessVO; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalInstance; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import com.ruoyi.approve.service.ApprovalInstanceService; |
| | | import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; |
| | | import com.ruoyi.basic.mapper.CustomerMapper; |
| | | import com.ruoyi.basic.pojo.Customer; |
| | | import com.ruoyi.common.enums.IsDeleteEnum; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | |
| | | |
| | | private final ApproveProcessServiceImpl approveProcessService; |
| | | private final CustomerMapper customerMapper; |
| | | private final ApprovalTemplateMapper approvalTemplateMapper; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | |
| | | @Override |
| | | public IPage<SalesQuotationDto> listPage(Page page, SalesQuotationDto salesQuotationDto) { |
| | |
| | | if(CollectionUtils.isEmpty(salesQuotationDtoIPage.getRecords())){ |
| | | return salesQuotationDtoIPage; |
| | | } |
| | | salesQuotationDtoIPage.getRecords().forEach(record -> { |
| | | List<SalesQuotationProduct> products = salesQuotationProductMapper.selectBySalesQuotationId(record.getId()); |
| | | record.setProducts(products); |
| | | }); |
| | | |
| | | // æ¹éæ¥è¯¢äº§åï¼é¿å
N+1 é®é¢ |
| | | List<Long> quotationIds = salesQuotationDtoIPage.getRecords().stream() |
| | | .map(SalesQuotationDto::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!quotationIds.isEmpty()) { |
| | | List<SalesQuotationProduct> allProducts = salesQuotationProductMapper.selectList( |
| | | new LambdaQueryWrapper<SalesQuotationProduct>() |
| | | .in(SalesQuotationProduct::getSalesQuotationId, quotationIds) |
| | | ); |
| | | |
| | | Map<Long, List<SalesQuotationProduct>> productMap = allProducts.stream() |
| | | .collect(Collectors.groupingBy(SalesQuotationProduct::getSalesQuotationId)); |
| | | |
| | | salesQuotationDtoIPage.getRecords().forEach(record -> |
| | | record.setProducts(productMap.getOrDefault(record.getId(), new ArrayList<>())) |
| | | ); |
| | | } |
| | | return salesQuotationDtoIPage; |
| | | } |
| | | |
| | |
| | | if (ObjectUtils.isNotEmpty(customer)) { |
| | | salesQuotation.setCustomer(customer.getCustomerName()); |
| | | } |
| | | String quotationNo = OrderUtils.countTodayByCreateTime(salesQuotationMapper, "QT","quotation_no"); |
| | | String quotationNo = OrderUtils.countTodayByCreateTime(salesQuotationMapper, "QT","quotation_no", salesQuotationDto.getCreateTime() != null ? salesQuotationDto.getCreateTime() : LocalDateTime.now()); |
| | | salesQuotation.setQuotationNo(quotationNo); |
| | | salesQuotation.setStatus("å¾
审æ¹"); |
| | | salesQuotationMapper.insert(salesQuotation); |
| | |
| | | }).collect(Collectors.toList()); |
| | | salesQuotationProductService.saveBatch(products); |
| | | // æ¥ä»·å®¡æ¹ |
| | | ApproveProcessVO approveProcessVO = new ApproveProcessVO(); |
| | | approveProcessVO.setApproveType(6); |
| | | approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); |
| | | approveProcessVO.setApproveReason(quotationNo); |
| | | approveProcessVO.setApproveUserIds(salesQuotationDto.getApproveUserIds()); |
| | | approveProcessVO.setApproveUser(loginUser.getUserId()); |
| | | approveProcessVO.setApproveTime(LocalDate.now().toString()); |
| | | approveProcessVO.setPrice(salesQuotationDto.getTotalAmount()); |
| | | ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto(); |
| | | approvalInstance.setTemplateId(approvalTemplateMapper.selectOne(new LambdaQueryWrapper<ApprovalTemplate>().eq(ApprovalTemplate::getBusinessType,6L).orderByDesc(ApprovalTemplate::getId).last("LIMIT 1")).getId()); |
| | | approvalInstance.setTemplateName(approvalTemplateMapper.selectOne(new LambdaQueryWrapper<ApprovalTemplate>().eq(ApprovalTemplate::getBusinessType,6L).orderByDesc(ApprovalTemplate::getId).last("LIMIT 1")).getTemplateName()); |
| | | approvalInstance.setBusinessId(salesQuotation.getId()); |
| | | approvalInstance.setBusinessType(6L); |
| | | approvalInstance.setCurrentLevel(1); |
| | | approvalInstance.setTitle(quotationNo+"审æ¹"); |
| | | approvalInstance.setApplicantId(loginUser.getUserId()); |
| | | approvalInstance.setApplicantName(loginUser.getNickName()); |
| | | approvalInstance.setApplyTime(LocalDateTime.now()); |
| | | try { |
| | | approveProcessService.addApprove(approveProcessVO); |
| | | approvalInstanceService.add(approvalInstance); |
| | | }catch (Exception e){ |
| | | log.error("SalesQuotationServiceImpl error:{}", e); |
| | | throw new RuntimeException("审æ¹å¤±è´¥"); |
| | | log.error("SalesQuotationServiceImpl approve error for quotationNo: {}", e); |
| | | throw new RuntimeException("审æ¹å¤±è´¥: " + e.getMessage(), e); |
| | | } |
| | | return true; |
| | | } |
| | |
| | | |
| | | salesQuotationProductService.saveBatch(products); |
| | | // ä¿®æ¹æ¥ä»·å®¡æ¹ |
| | | vo.setApproveUserIds(salesQuotationDto.getApproveUserIds()); |
| | | vo.setApproveType(6); |
| | | vo.setApproveReason(salesQuotationDto.getQuotationNo()); |
| | | approveProcessService.updateApproveUser(vo); |
| | | // å
ç»æä¹åæªç»æçæ¥ä»·å®¡æ¹ |
| | | approvalInstanceService.lambdaUpdate().set(ApprovalInstance::getStatus,"REJECTED").eq(ApprovalInstance::getBusinessId,salesQuotation.getId()).eq(ApprovalInstance::getBusinessType,6L).update(); |
| | | |
| | | ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto(); |
| | | approvalInstance.setTemplateId(approvalTemplateMapper.selectOne(new LambdaQueryWrapper<ApprovalTemplate>().eq(ApprovalTemplate::getBusinessType,6L).orderByDesc(ApprovalTemplate::getId).last("LIMIT 1")).getId()); |
| | | approvalInstance.setTemplateName(approvalTemplateMapper.selectOne(new LambdaQueryWrapper<ApprovalTemplate>().eq(ApprovalTemplate::getBusinessType,6L).orderByDesc(ApprovalTemplate::getId).last("LIMIT 1")).getTemplateName()); |
| | | approvalInstance.setBusinessId(salesQuotation.getId()); |
| | | approvalInstance.setBusinessType(6L); |
| | | approvalInstance.setCurrentLevel(1); |
| | | approvalInstance.setTitle(salesQuotation.getQuotationNo()+"审æ¹"); |
| | | approvalInstance.setApplicantId(SecurityUtils.getUserId()); |
| | | approvalInstance.setApplicantName(SecurityUtils.getLoginUser().getNickName()); |
| | | approvalInstance.setApplyTime(LocalDateTime.now()); |
| | | try { |
| | | approvalInstanceService.add(approvalInstance); |
| | | } catch (Exception e) { |
| | | log.error("SalesQuotationServiceImpl approve error for quotationNo: {}", e); |
| | | throw new RuntimeException("审æ¹å¤±è´¥: " + e.getMessage(), e); |
| | | } |
| | | return true; |
| | | } |
| | | @Override |
| | |
| | | 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.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.mapper.ApprovalTemplateMapper; |
| | | import com.ruoyi.approve.pojo.ApprovalTemplate; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import com.ruoyi.approve.service.ApprovalInstanceService; |
| | | import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.procurementrecord.bean.vo.ShippingProductVo; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.sales.dto.ShippingApproveDto; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | private final ApproveProcessServiceImpl approveProcessService; |
| | | private final FileUtil fileUtil; |
| | | private final ShippingProductDetailMapper shippingProductDetailMapper; |
| | | private final ApprovalTemplateMapper approvalTemplateMapper; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | |
| | | @Override |
| | | public IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req) { |
| | |
| | | shippingApproveDto.setShippingProductDetailDtoList(dateilByShippingNo); |
| | | return shippingApproveDto; |
| | | } |
| | | |
| | | @Override |
| | | public boolean addReq(ShippingInfoDto req) { |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | String sh = OrderUtils.countTodayByCreateTime(shippingInfoMapper, "SH","shipping_no",req.getCreateTime()); |
| | | // å
ä¿ååè´§åï¼åå起审æ¹ï¼æ å®¡æ ¸äººèªå¨éè¿æ¶éè¦æåè´§ç¼å·åååè´§ç¶æã |
| | | req.setShippingNo(sh); |
| | | req.setStatus("å¾
å®¡æ ¸"); |
| | | boolean save = this.add(req); |
| | | // åè´§å®¡æ¹ |
| | | ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto(); |
| | | approvalInstance.setTemplateId(approvalTemplateMapper.selectOne(new LambdaQueryWrapper<ApprovalTemplate>().eq(ApprovalTemplate::getBusinessType,6L).orderByDesc(ApprovalTemplate::getId).last("LIMIT 1")).getId()); |
| | | approvalInstance.setTemplateName(approvalTemplateMapper.selectOne(new LambdaQueryWrapper<ApprovalTemplate>().eq(ApprovalTemplate::getBusinessType,6L).orderByDesc(ApprovalTemplate::getId).last("LIMIT 1")).getTemplateName()); |
| | | approvalInstance.setBusinessId(req.getId()); |
| | | approvalInstance.setBusinessType(7L); |
| | | approvalInstance.setCurrentLevel(1); |
| | | approvalInstance.setTitle(sh+"审æ¹"); |
| | | approvalInstance.setApplicantId(loginUser.getUserId()); |
| | | approvalInstance.setApplicantName(loginUser.getNickName()); |
| | | approvalInstance.setApplyTime(LocalDateTime.now()); |
| | | approvalInstanceService.add(approvalInstance); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.staff.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.OrderItem; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | |
| | | @Operation(summary = "å页æ¥è¯¢äººåæå¡è§åé
ç½®") |
| | | @GetMapping("/listPage") |
| | | public R listPage(Page page){ |
| | | page.addOrder(OrderItem.desc("id")); |
| | | return R.ok(personalAttendanceLocationConfigService.page(page)); |
| | | } |
| | | |
| | |
| | | * @return |
| | | */ |
| | | @GetMapping("/listPage") |
| | | public AjaxResult staffOnJobListPage(Page page, StaffOnJob staffOnJob) { |
| | | public AjaxResult staffOnJobListPage(Page page, StaffOnJobDto staffOnJob) { |
| | | return AjaxResult.success(staffOnJobService.staffOnJobListPage(page, staffOnJob)); |
| | | } |
| | | |
| | |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private Date contractEndTime; |
| | | |
| | | private Long sysDeptId; |
| | | } |
| | |
| | | @Mapper |
| | | public interface StaffOnJobMapper extends BaseMapper<StaffOnJob> { |
| | | |
| | | IPage<StaffOnJobDto> staffOnJobListPage(Page page, @Param("staffOnJob") StaffOnJob staffOnJob); |
| | | IPage<StaffOnJobDto> staffOnJobListPage(Page page, @Param("staffOnJob") StaffOnJobDto staffOnJob); |
| | | |
| | | List<StaffOnJobDto> staffOnJobList(@Param("staffOnJob") StaffOnJob staffOnJob); |
| | | |
| | |
| | | public interface IStaffOnJobService extends IService<StaffOnJob> { |
| | | |
| | | |
| | | IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJob staffOnJob); |
| | | IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJobDto staffOnJob); |
| | | |
| | | StaffOnJobDto staffOnJobDetail(Long id); |
| | | |
| | |
| | | schemeApplicableStaffLambdaQueryWrapper.like(SchemeApplicableStaff::getTitle, schemeApplicableStaff.getTitle()); |
| | | } |
| | | } |
| | | schemeApplicableStaffLambdaQueryWrapper.orderByDesc(SchemeApplicableStaff::getId); |
| | | Page<SchemeApplicableStaff> page1 = schemeApplicableStaffMapper.selectPage(page, schemeApplicableStaffLambdaQueryWrapper); |
| | | List<Long> collect = page1.getRecords().stream().map(SchemeApplicableStaff::getId).collect(Collectors.toList()); |
| | | if(CollectionUtils.isEmpty(collect)){ |
| | |
| | | StaffLeave staffLeave = new StaffLeave(); |
| | | staffLeave.setStaffOnJobId(staffLeaveDto.getStaffOnJobId()); |
| | | staffLeave.setReason(staffLeaveDto.getReason()); |
| | | staffLeave.setLeaveDate(staffLeaveDto.getLeaveDate()); |
| | | String reason = staffLeaveDto.getReason(); |
| | | if (StaffLeaveReasonOther.getCode().equals(reason)){ |
| | | staffLeave.setRemark(staffLeaveDto.getRemark()); |
| | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.exception.base.BaseException; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.dto.WordDateDto; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | |
| | | |
| | | //å¨èåå·¥å°è´¦å页æ¥è¯¢ |
| | | @Override |
| | | public IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJob staffOnJob) { |
| | | return staffOnJobMapper.staffOnJobListPage(page,staffOnJob); |
| | | public IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJobDto staffOnJob) { |
| | | IPage<StaffOnJobDto> staffOnJobDtoIPage = staffOnJobMapper.staffOnJobListPage(page, staffOnJob); |
| | | return staffOnJobDtoIPage; |
| | | } |
| | | |
| | | //æ°å¢å
¥è |
| | |
| | | } |
| | | |
| | | // å建å
¥èæ°æ® |
| | | syncStudyInfoFromEducation(staffOnJobPrams); |
| | | staffOnJobPrams.setContractExpireTime(staffOnJobPrams.getContractEndTime()); |
| | | staffOnJobPrams.setStaffState(1); |
| | | staffOnJobMapper.insert(staffOnJobPrams); |
| | |
| | | // ç»å®åè¡¨æ°æ® |
| | | bingingStaffOnJobExtra(id,staffOnJobParams); |
| | | // æ´æ°åå·¥æ°æ® |
| | | syncStudyInfoFromEducation(staffOnJobParams); |
| | | staffOnJobParams.setContractExpireTime(staffOnJobParams.getContractEndTime()); |
| | | return staffOnJobMapper.updateById(staffOnJobParams); |
| | | } |
| | |
| | | .forEach(staff -> staff.setStaffOnJobId(id)); // èµå¼ |
| | | staffEducationService.saveBatch(staffOnJobPrams.getStaffEducationList()); |
| | | } |
| | | |
| | | // æ°å¢å·¥ä½ç»å |
| | | if(CollectionUtils.isNotEmpty(staffOnJobPrams.getStaffWorkExperienceList())){ |
| | | staffOnJobPrams.getStaffWorkExperienceList().stream() |
| | |
| | | } |
| | | } |
| | | |
| | | private void syncStudyInfoFromEducation(StaffOnJob staffOnJobPrams) { |
| | | if (staffOnJobPrams == null || CollectionUtils.isEmpty(staffOnJobPrams.getStaffEducationList())) { |
| | | if (staffOnJobPrams != null) { |
| | | staffOnJobPrams.setFirstStudy("/"); |
| | | staffOnJobPrams.setProfession("/"); |
| | | } |
| | | return; |
| | | } |
| | | Optional<StaffEducation> matchedEducation = staffOnJobPrams.getStaffEducationList().stream() |
| | | .filter(Objects::nonNull) |
| | | .filter(education -> StringUtils.isNotEmpty(education.getMajor())) |
| | | .findFirst(); |
| | | if (matchedEducation.isPresent()) { |
| | | StaffEducation education = matchedEducation.get(); |
| | | staffOnJobPrams.setFirstStudy(education.getEducation()); |
| | | staffOnJobPrams.setProfession(education.getMajor()); |
| | | return; |
| | | } |
| | | staffOnJobPrams.setFirstStudy("/"); |
| | | staffOnJobPrams.setProfession("/"); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * éè¿åå·¥idå 餿è²ç»åï¼å·¥ä½ç»åï¼ç´§æ¥è系人 |
| | |
| | | staffSalaryMainLambdaQueryWrapper.eq(StaffSalaryMain::getStatus, staffSalaryMain.getStatus()); |
| | | } |
| | | } |
| | | staffSalaryMainLambdaQueryWrapper.orderByDesc(StaffSalaryMain::getId); |
| | | Page<StaffSalaryMain> page1 = staffSalaryMainMapper.selectPage(page, staffSalaryMainLambdaQueryWrapper); |
| | | page1.getRecords().forEach(main -> { |
| | | List<StaffSalaryDetail> staffSalaryDetailList = staffSalaryDetailMapper.selectList(new LambdaQueryWrapper<StaffSalaryDetail>().eq(StaffSalaryDetail::getMainId, main.getId())); |
| | |
| | | private String remark; |
| | | |
| | | @Schema(description = "é¢è¦æ°é") |
| | | @TableField(exist = false) |
| | | private BigDecimal warnNum; |
| | | |
| | | @Schema(description = "ç±»å 0åæ ¼å
¥åº 1ä¸åæ ¼å
¥åº") |
| | | private String type; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | |
| | | private String remark; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.bean.BeanUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import com.ruoyi.stock.dto.StockInRecordDto; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.dto.StockUninventoryDto; |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int add(StockInRecordDto stockInRecordDto) { |
| | | String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK","inbound_batches"); |
| | | String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK","inbound_batches", stockInRecordDto.getCreateTime() != null ? stockInRecordDto.getCreateTime() : LocalDateTime.now()); |
| | | stockInRecordDto.setInboundBatches(no); |
| | | StockInRecord stockInRecord = new StockInRecord(); |
| | | BeanUtils.copyProperties(stockInRecordDto, stockInRecord); |
| | |
| | | setWarnNum(stockInRecord.getWarnNum()); |
| | | setBatchNo(stockInRecord.getBatchNo()); |
| | | setRemark(stockInRecord.getRemark()); |
| | | setWarnNum(stockInRecord.getWarnNum()); |
| | | setVersion(1); |
| | | }}); |
| | | } else { |
| | |
| | | stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum()); |
| | | stockInRecordDto.setType("0"); |
| | | stockInRecordDto.setRemark(stockInventoryDto.getRemark()); |
| | | stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum()); |
| | | stockInRecordService.add(stockInRecordDto); |
| | | return true; |
| | | } |
| | |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.bean.BeanUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.dto.StockOutRecordDto; |
| | | import com.ruoyi.stock.dto.StockUninventoryDto; |
| | |
| | | |
| | | @Override |
| | | public int add(StockOutRecordDto stockOutRecordDto) { |
| | | String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK","outbound_batches"); |
| | | String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK","outbound_batches", stockOutRecordDto.getCreateTime() != null ? stockOutRecordDto.getCreateTime() : LocalDateTime.now()); |
| | | stockOutRecordDto.setOutboundBatches(no); |
| | | if (StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode().equals(stockOutRecordDto.getRecordType())){ |
| | | stockOutRecordDto.setApprovalStatus(3); |
| | |
| | | private final TechnologyOperationService technologyOperationService; |
| | | |
| | | @GetMapping("/listPage") |
| | | @Log(title = "Technology operation page", businessType = BusinessType.OTHER) |
| | | @Log(title = "å·¥åºå页æ¥è¯¢", businessType = BusinessType.OTHER) |
| | | @Operation(summary = "å·¥åºå页æ¥è¯¢") |
| | | public R<IPage<TechnologyOperationVo>> listPage(Page<TechnologyOperationDto> page, TechnologyOperationDto technologyOperationDto) { |
| | | return R.ok(technologyOperationService.listPage(page, technologyOperationDto)); |
| | | } |
| | | |
| | | @PostMapping("/add") |
| | | @Log(title = "Add technology operation", businessType = BusinessType.INSERT) |
| | | @Log(title = "å·¥åºæ°å¢", businessType = BusinessType.INSERT) |
| | | @Operation(summary = "æ°å¢å·¥åº") |
| | | public R add(@RequestBody TechnologyOperationDto technologyOperationDto) { |
| | | return technologyOperationService.add(technologyOperationDto); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Log(title = "Update technology operation", businessType = BusinessType.UPDATE) |
| | | @Log(title = "å·¥åºæ´æ°", businessType = BusinessType.UPDATE) |
| | | @Operation(summary = "ä¿®æ¹å·¥åº") |
| | | public R update(@RequestBody com.ruoyi.technology.pojo.TechnologyOperation technologyOperation) { |
| | | return R.ok(technologyOperationService.updateById(technologyOperation)); |
| | | } |
| | | |
| | | @DeleteMapping("/batchDelete") |
| | | @Log(title = "Delete technology operation", businessType = BusinessType.DELETE) |
| | | @Log(title = "å é¤å·¥åº", businessType = BusinessType.DELETE) |
| | | @Operation(summary = "æ¹éå é¤å·¥åº") |
| | | public R batchDelete(@RequestBody List<Long> ids) { |
| | | return R.ok(technologyOperationService.batchDelete(ids)); |
| | |
| | | } |
| | | |
| | | @DeleteMapping("/batchDelete/{id}") |
| | | @Log(title = "Delete technology operation param", businessType = BusinessType.DELETE) |
| | | @Log(title = "å é¤å·¥åºåæ°", businessType = BusinessType.DELETE) |
| | | @Operation(summary = "å é¤å·¥åºåæ°") |
| | | public AjaxResult batchDelete(@PathVariable("id") Long id) { |
| | | return AjaxResult.success(technologyOperationParamService.batchDelete(id)); |
| | |
| | | package com.ruoyi.technology.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "ä¿®æ¹äºº") |
| | |
| | | |
| | | @Schema(description = "ä¿®æ¹æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private LocalDateTime updateTime; |
| | | |
| | | @Schema(description = "é¨é¨ID") |
| | |
| | | private String description; |
| | | |
| | | @Schema(description = "å½å
¥æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import com.ruoyi.technology.bean.dto.TechnologyRoutingDto; |
| | | import com.ruoyi.technology.bean.vo.TechnologyRoutingVo; |
| | | import com.ruoyi.production.mapper.ProductionOrderRoutingMapper; |
| | |
| | | |
| | | @Override |
| | | public Long saveTechnologyRouting(TechnologyRouting technologyRouting) { |
| | | String code = OrderUtils.countTodayByCreateTime(technologyRoutingMapper, "GYLX", "process_route_code"); |
| | | String code = OrderUtils.countTodayByCreateTime(technologyRoutingMapper, "GYLX", "process_route_code", technologyRouting.getCreateTime() != null ? technologyRouting.getCreateTime() : LocalDateTime.now()); |
| | | technologyRouting.setProcessRouteCode(code); |
| | | technologyRoutingMapper.insert(technologyRouting); |
| | | // 带å
¥bom产åç»æ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # 项ç®ç¸å
³é
ç½® |
| | | 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) |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # 项ç®ç¸å
³é
ç½® |
| | | 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:9002/product-inventory-management-hqjc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 |
| | | username: root |
| | | password: hqjc@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_hqjc |
| | | # 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://36.134.77.64:9001 # åååç¼ |
| | | expired: 120 # è¿ææ¶é´(åä½:åé) |
| | | useLimit: 10 # ä½¿ç¨æ¬¡æ° |
| | | compress: true # æ¯å¦å缩 |
| | | needCompressSize: 10MB # å缩éå¼ |
| | | compressQuality: 0.5 # å缩质é(0.0-1.0) |
| | |
| | | å½åæ¥æï¼{{currentDate}}ï¼ä¸å½æ¶åºï¼ã |
| | | |
| | | å·¥ä½è§åï¼ |
| | | 1. ç¨æ·æåºâæ¥ãé®ãç»è®¡ãåæãé¢è¦ãå»ºè®®ãæ¥åâéæ±æ¶ï¼ä¼å
è°ç¨å·¥å
·è¿åç»æå JSONï¼ä¸ç¼é ä¸å¡æ°æ®ã |
| | | 2. å½ä¸ææ¬ã婿¶¦ãåºåèµéãç°éæµãé¢è¦ã驾驶è±ãæ¥æ¥å¨æ¥åºæ¯æ¶ï¼ä¼å
è°ç¨å¯¹åºå·¥å
·ã |
| | | 1. ç¨æ·æåºâæ¥ãé®ãç»è®¡ãåæãé¢è¦ãå»ºè®®ãæ¥åâç±»éæ±æ¶ï¼ä¼å
è°ç¨å·¥å
·è¿åç»æå JSONï¼ä¸ç¼é ä¸å¡æ°æ®ã |
| | | 2. å½ä¸ææ¬ã婿¶¦ãåºåèµéãç°éæµãé¢è¦ã驾驶è±ãæ¥æ¥ã卿¥çåºæ¯æ¶ï¼ä¼å
è°ç¨å¯¹åºå·¥å
·ã |
| | | 3. å·¥å
·è¿å JSON æ¶ï¼ç´æ¥è¾åºåå§ JSON å符串ï¼ä¸è¦é¢å¤å
裹 Markdownï¼ä¹ä¸è¦å¨åå追å è§£éææ¬ã |
| | | 4. å½ç¨æ·é®é¢ç¼ºå°æ¶é´èå´æ¶ï¼é»è®¤ä½¿ç¨å·¥å
·å
ç½®å£å¾ï¼å¦è¿30å¤©ãæ¬æãè¿90天ï¼ï¼å¹¶å¨åç»å¯æéç¨æ·è¡¥å
èå´ã |
| | | 5. ç¨æ·é®â为ä»ä¹å©æ¶¦ä¸éââåªä¸ªè®¢åäºæââåªä¸ªå®¢æ·æèµé±ââåªä¸ªå®¢æ·å©æ¶¦è´¡ç®æé«ââåªä¸ªè½¦é´/å·¥åºææ¬æé«âçé®é¢æ¶ï¼ä¼å
åºäºè®¢å婿¶¦ä¸å·¥åºææ¬åæå·¥å
·ä½çã |
| | | 6. åçå¿
须使ç¨ä¸æï¼è¥æ°æ®ä¸è¶³ä»¥å¾åºç»è®ºï¼æç¡®æåºç¼ºå°åªäºå
³é®å段æç鿡件ã |
| | | 7. ç¨æ·æå°âä»å¹´/æ¬æ/ä»å¤©/æè¿/䏿/å»å¹´âçç¸å¯¹æ¶é´æ¶ï¼å¿
é¡»ä¸¥æ ¼åºäºâå½åæ¥æâæ¢ç®ï¼ç¦æ¢èªè¡å设年份ã |
| | | 4. ç¨æ·æ²¡ææç¡®ç»åºæ¶é´ã客æ·ãä¾åºåã产åã订åãæ°éçç鿡件æ¶ï¼ä¸è¦èªè¡è¡¥å
æ¡ä»¶ï¼å·¥å
·åæ°ä¿æä¸ºç©ºï¼ç±å·¥å
·æå½åä¸å¡å£å¾æ¥è¯¢ã |
| | | 5. ç¨æ·æå°âæ¬å¨ãæ¬æãä»å¹´ãä»å¤©ãæè¿ãè¿30天ã䏿ãå»å¹´âçç¸å¯¹æ¶é´æ¶ï¼å¿
é¡»ä¸¥æ ¼åºäºâå½åæ¥æâæ¢ç®ï¼ç¦æ¢èªè¡å设年份ã |
| | | 6. ç¨æ·é®â为ä»ä¹å©æ¶¦ä¸éââåªä¸ªè®¢åäºæââåªä¸ªå®¢æ·æèµé±ââåªä¸ªå®¢æ·å©æ¶¦è´¡ç®æé«ââåªä¸ªå·¥åºææ¬æé«âçé®é¢æ¶ï¼ä¼å
åºäºè®¢å婿¶¦ä¸å·¥åºææ¬åæå·¥å
·ä½çã |
| | | 7. åçå¿
须使ç¨ä¸æï¼è¥æ°æ®ä¸è¶³ä»¥å¾åºç»è®ºï¼æç¡®æåºç¼ºå°åªäºå
³é®å段æç鿡件ã |
| | |
| | | AND A.apply_date BETWEEN #{req.startDate} AND #{req.endDate} |
| | | </if> |
| | | </where> |
| | | order by A.id desc |
| | | </select> |
| | | |
| | | <select id="getInboundBatchesBySupplier" |
| | |
| | | AND api.issue_date BETWEEN #{req.startDate} AND #{req.endDate} |
| | | </if> |
| | | </where> |
| | | order by api.id desc |
| | | </select> |
| | | <select id="getInboundBatchesBySupplier" |
| | | resultType="com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo"> |
| | |
| | | AND app.payment_date BETWEEN #{req.startDate} AND #{req.endDate} |
| | | </if> |
| | | </where> |
| | | order by app.id desc |
| | | </select> |
| | | <select id="selectPayment" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto"> |
| | | SELECT DATE_FORMAT(payment_date, #{dateFormat}) AS dateStr, |
| | |
| | | AND A.apply_date BETWEEN #{req.startDate} AND #{req.endDate} |
| | | </if> |
| | | </where> |
| | | order by A.id desc |
| | | </select> |
| | | <select id="getOutboundBatchesByCustomer" |
| | | resultType="com.ruoyi.account.bean.vo.sales.SalesOutboundVo"> |
| | |
| | | AND A.collection_date BETWEEN #{req.startDate} AND #{req.endDate} |
| | | </if> |
| | | </where> |
| | | |
| | | order by A.id desc |
| | | </select> |
| | | <select id="existsByStockOutRecordId" resultType="java.lang.Boolean"> |
| | | SELECT COUNT(*) > 0 |
| | |
| | | AND asi.issue_date BETWEEN #{req.startDate} AND #{req.endDate} |
| | | </if> |
| | | </where> |
| | | order by asi.id desc |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | |
| | | <select id="listPage" resultType="com.ruoyi.aftersalesservice.pojo.AfterSalesNearExpiry"> |
| | | select * from after_sales_near_expiry |
| | | where 1 = 1 |
| | | <where> |
| | | <if test="req.expireDate != null"> |
| | | AND expire_date = #{req.expireDate} |
| | | </if> |
| | |
| | | <if test="req.status != null"> |
| | | AND status = #{req.status} |
| | | </if> |
| | | </where> |
| | | order by create_time desc |
| | | </select> |
| | | |
| | |
| | | <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 |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.ApprovalInstanceMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalInstance"> |
| | | <id column="id" property="id" /> |
| | | <result column="instance_no" property="instanceNo" /> |
| | | <result column="template_id" property="templateId" /> |
| | | <result column="template_name" property="templateName" /> |
| | | <result column="business_id" property="businessId" /> |
| | | <result column="business_type" property="businessType" /> |
| | | <result column="title" property="title" /> |
| | | <result column="status" property="status" /> |
| | | <result column="current_level" property="currentLevel" /> |
| | | <result column="applicant_id" property="applicantId" /> |
| | | <result column="applicant_name" property="applicantName" /> |
| | | <result column="apply_time" property="applyTime" /> |
| | | <result column="finish_time" property="finishTime" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="deleted" property="deleted" /> |
| | | </resultMap> |
| | | <select id="listPage" resultType="com.ruoyi.approve.bean.vo.ApprovalInstanceVo"> |
| | | select ai.*,su.nick_name as create_user_name from |
| | | approval_instance ai |
| | | left join sys_user su on ai.create_user = su.user_id |
| | | <where> |
| | | deleted = 0 |
| | | <if test="ew.instanceNo != null"> |
| | | and ai.instance_no like concat('%',#{ew.instanceNo},'%') |
| | | </if> |
| | | <if test="ew.templateName != null"> |
| | | and ai.template_name like concat('%',#{ew.templateName},'%') |
| | | </if> |
| | | <if test="ew.templateId != null "> |
| | | and ai. template_id = #{ew.templateId} |
| | | </if> |
| | | <if test="ew.businessType != null "> |
| | | and ai.business_type = #{ew.businessType} |
| | | </if> |
| | | <if test="ew.createTimeStart != null and ew.createTimeEnd != null"> |
| | | and ai.apply_time >= #{ew.createTimeStart} |
| | | and ai.apply_time <= #{ew.createTimeEnd} |
| | | </if> |
| | | <if test="ew.status != null"> |
| | | and ai.status = #{ew.status} |
| | | </if> |
| | | <if test="ew.applicantName != null and ew.applicantName !=''"> |
| | | and ai.applicant_name = #{ew.applicantName} |
| | | </if> |
| | | </where> |
| | | order by ai.create_time desc |
| | | </select> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.ApprovalInstanceNodeMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalInstanceNode"> |
| | | <id column="id" property="id" /> |
| | | <result column="instance_id" property="instanceId" /> |
| | | <result column="level_no" property="levelNo" /> |
| | | <result column="approve_type" property="approveType" /> |
| | | <result column="status" property="status" /> |
| | | <result column="start_time" property="startTime" /> |
| | | <result column="finish_time" property="finishTime" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="deleted" property="deleted" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.ApprovalRecordMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalRecord"> |
| | | <id column="id" property="id" /> |
| | | <result column="instance_id" property="instanceId" /> |
| | | <result column="node_id" property="nodeId" /> |
| | | <result column="task_id" property="taskId" /> |
| | | <result column="operator_id" property="operatorId" /> |
| | | <result column="operator_name" property="operatorName" /> |
| | | <result column="action" property="action" /> |
| | | <result column="comment" property="comment" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="deleted" property="deleted" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.ApprovalTaskMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTask"> |
| | | <id column="id" property="id" /> |
| | | <result column="instance_id" property="instanceId" /> |
| | | <result column="node_id" property="nodeId" /> |
| | | <result column="level_no" property="levelNo" /> |
| | | <result column="approver_id" property="approverId" /> |
| | | <result column="approver_name" property="approverName" /> |
| | | <result column="task_status" property="taskStatus" /> |
| | | <result column="approve_time" property="approveTime" /> |
| | | <result column="comment" property="comment" /> |
| | | <result column="is_read" property="isRead" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="deleted" property="deleted" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.ApprovalTemplateMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTemplate"> |
| | | <id column="id" property="id" /> |
| | | <result column="template_name" property="templateName" /> |
| | | <result column="enabled" property="enabled" /> |
| | | <result column="description" property="description" /> |
| | | <result column="deleted" property="deleted" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | </resultMap> |
| | | <select id="listPage" resultType="com.ruoyi.approve.bean.vo.ApprovalTemplateVo"> |
| | | select at.*,su.nick_name as create_user_name from |
| | | approval_template at |
| | | left join sys_user su on at.create_user = su.user_id |
| | | <where> |
| | | deleted = 0 |
| | | <if test="ew.templateName != null"> |
| | | and template_name like concat('%',#{ew.templateName},'%') |
| | | </if> |
| | | <if test="ew.templateType != null"> |
| | | and template_type = #{ew.templateType} |
| | | </if> |
| | | <if test="ew.enabled != null"> |
| | | and enabled = #{ew.enabled} |
| | | </if> |
| | | </where> |
| | | order by at.id desc |
| | | </select> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.ApprovalTemplateNodeApproverMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover"> |
| | | <id column="id" property="id" /> |
| | | <result column="node_id" property="nodeId" /> |
| | | <result column="template_id" property="templateId" /> |
| | | <result column="approver_id" property="approverId" /> |
| | | <result column="approver_name" property="approverName" /> |
| | | <result column="sort_no" property="sortNo" /> |
| | | <result column="created_time" property="createdTime" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.ApprovalTemplateNodeMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTemplateNode"> |
| | | <id column="id" property="id" /> |
| | | <result column="template_id" property="templateId" /> |
| | | <result column="level_no" property="levelNo" /> |
| | | <result column="approve_type" property="approveType" /> |
| | | <result column="created_time" property="createdTime" /> |
| | | <result column="updated_time" property="updatedTime" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| | |
| | | <if test="req.approveType != null "> |
| | | and approve_type = #{req.approveType} |
| | | </if> |
| | | order by approve_time desc |
| | | order by id desc |
| | | </select> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.FinReimbursementDetailMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.FinReimbursementDetail"> |
| | | <id column="id" property="id" /> |
| | | <result column="reimbursement_id" property="reimbursementId" /> |
| | | <result column="row_no" property="rowNo" /> |
| | | <result column="invoice_date" property="invoiceDate" /> |
| | | <result column="expense_category" property="expenseCategory" /> |
| | | <result column="amount" property="amount" /> |
| | | <result column="description" property="description" /> |
| | | <result column="invoice_no" property="invoiceNo" /> |
| | | <result column="invoice_type" property="invoiceType" /> |
| | | <result column="invoice_amount" property="invoiceAmount" /> |
| | | <result column="tax_rate" property="taxRate" /> |
| | | <result column="tax_amount" property="taxAmount" /> |
| | | <result column="remark" property="remark" /> |
| | | <result column="tenant_id" property="tenantId" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | <result column="deleted" property="deleted" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.FinReimbursementMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.FinReimbursement"> |
| | | <id column="id" property="id" /> |
| | | <result column="bill_no" property="billNo" /> |
| | | <result column="reimbursement_type" property="reimbursementType" /> |
| | | <result column="expense_type" property="expenseType" /> |
| | | <result column="applicant_id" property="applicantId" /> |
| | | <result column="applicant_code" property="applicantCode" /> |
| | | <result column="applicant_name" property="applicantName" /> |
| | | <result column="applicant_dept_id" property="applicantDeptId" /> |
| | | <result column="applicant_dept_name" property="applicantDeptName" /> |
| | | <result column="reason" property="reason" /> |
| | | <result column="apply_amount" property="applyAmount" /> |
| | | <result column="detail_total_amount" property="detailTotalAmount" /> |
| | | <result column="payee_name" property="payeeName" /> |
| | | <result column="payee_account" property="payeeAccount" /> |
| | | <result column="payee_bank" property="payeeBank" /> |
| | | <result column="approval_instance_id" property="approvalInstanceId" /> |
| | | <result column="approve_process_id" property="approveProcessId" /> |
| | | <result column="bill_status" property="billStatus" /> |
| | | <result column="approved_time" property="approvedTime" /> |
| | | <result column="paid_time" property="paidTime" /> |
| | | <result column="account_expense_id" property="accountExpenseId" /> |
| | | <result column="remark" property="remark" /> |
| | | <result column="tenant_id" property="tenantId" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | <result column="deleted" property="deleted" /> |
| | | </resultMap> |
| | | <select id="listPage" resultType="com.ruoyi.approve.bean.vo.FinReimbursementVo"> |
| | | select fin_reimbursement.*, |
| | | fin_reimbursement_travel.start_time , |
| | | fin_reimbursement_travel.end_time |
| | | from |
| | | fin_reimbursement |
| | | left join fin_reimbursement_travel on fin_reimbursement.id = fin_reimbursement_travel.reimbursement_id |
| | | <where> |
| | | <if test="ew.billNo != null and ew.billNo != ''"> |
| | | bill_no like concat('%',#{ew.billNo},'%') |
| | | </if> |
| | | <if test="ew.applicantName != null and ew.applicantName != ''"> |
| | | and applicant_name like concat('%',#{ew.applicantName},'%') |
| | | </if> |
| | | <if test="ew.applicantCode != null and ew.applicantCode != ''"> |
| | | and applicant_code like concat('%',#{ew.applicantCode},'%') |
| | | </if> |
| | | <if test="ew.createTimeStart != null and ew.createTimeStart !='' and ew.createTimeEnd != null and ew.createTimeEnd != ''"> |
| | | and create_time >= #{ew.createTimeStart} |
| | | and create_time <= #{ew.createTimeEnd} |
| | | </if> |
| | | </where> |
| | | order by create_time desc |
| | | </select> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.approve.mapper.FinReimbursementTravelMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.FinReimbursementTravel"> |
| | | <id column="id" property="id" /> |
| | | <result column="reimbursement_id" property="reimbursementId" /> |
| | | <result column="start_time" property="startTime" /> |
| | | <result column="end_time" property="endTime" /> |
| | | <result column="travel_days" property="travelDays" /> |
| | | <result column="departure_city" property="departureCity" /> |
| | | <result column="destination_city" property="destinationCity" /> |
| | | <result column="hotel_standard" property="hotelStandard" /> |
| | | <result column="lodging_days" property="lodgingDays" /> |
| | | <result column="meal_allowance" property="mealAllowance" /> |
| | | <result column="transport_allowance" property="transportAllowance" /> |
| | | <result column="lodging_limit" property="lodgingLimit" /> |
| | | <result column="standard_tag" property="standardTag" /> |
| | | <result column="within_standard" property="withinStandard" /> |
| | | <result column="tenant_id" property="tenantId" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| | |
| | | and type = #{knowledgeBase.type} |
| | | </if> |
| | | </where> |
| | | |
| | | order by id desc |
| | | </select> |
| | | </mapper> |
| | |
| | | ) |
| | | </if> |
| | | </where> |
| | | order by c.id desc |
| | | </select> |
| | | |
| | | <select id="list" resultType="com.ruoyi.basic.vo.CustomerVo"> |
| | |
| | | ) |
| | | </if> |
| | | </where> |
| | | order by c.id desc |
| | | </select> |
| | | |
| | | <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo"> |
| | |
| | | AND c.customer_name LIKE CONCAT('%', #{customerName}, '%') |
| | | </if> |
| | | </where> |
| | | order by T1.customer_id desc |
| | | </select> |
| | | |
| | | <select id="customewTransactionsDetails" |
| | |
| | | group by sl.id |
| | | )T2 on T2.id = sl.id |
| | | where sl.customer_id = #{customerId} |
| | | order by sl.id desc |
| | | </select> |
| | | </mapper> |
| | |
| | | AND T1.is_white = #{supplierManageDto.isWhite} |
| | | </if> |
| | | </where> |
| | | order by T1.id desc |
| | | </select> |
| | | |
| | | <select id="supplierExportList" resultType="com.ruoyi.basic.excel.SupplierManageExcelDto"> |
| | |
| | | AND sm.supplier_name LIKE CONCAT('%',#{supplierName},'%') |
| | | </if> |
| | | </where> |
| | | order by T1.supplier_id desc |
| | | </select> |
| | | |
| | | <select id="supplierTransactionsDetails" |
| | |
| | | pl.purchase_contract_number, |
| | | pl.execution_date, |
| | | pl.contract_amount, |
| | | IFNULL(T2.InboundAmount, 0) AS shippedAmount, |
| | | pl.contract_amount - IFNULL(T2.InboundAmount, 0) AS unshippedAmount |
| | | FROM purchase_ledger pl |
| | | LEFT JOIN ( |
| | | SELECT t.sales_ledger_id, |
| | | SUM(t.inbound_amount) AS InboundAmount |
| | | FROM ( |
| | | SELECT sir.stock_in_num * slp.tax_inclusive_unit_price AS inbound_amount, slp.sales_ledger_id |
| | | FROM stock_in_record sir |
| | | INNER JOIN sales_ledger_product slp ON slp.id = sir.record_id |
| | | WHERE sir.approval_status = 1 AND sir.record_type = 7 AND slp.type = 2 |
| | | UNION ALL |
| | | IFNULL(T1.paymentAmount, 0) AS paymentAmount, |
| | | IFNULL(T2.InboundAmount, 0) - IFNULL(T3.returnAmount, 0) AS payableAmount |
| | | from purchase_ledger pl |
| | | left join ( |
| | | select |
| | | pl.id, |
| | | sum(app.payment_amount) as paymentAmount |
| | | from account_purchase_payment app |
| | | left join account_payment_application apa on app.account_payment_application_id = apa.id |
| | | left join stock_in_record sir on FIND_IN_SET(sir.id, apa.stock_in_record_ids) > 0 |
| | | -- 10 ç±»åæå
³èè´¨æ£è¡¨ |
| | | LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id |
| | | -- 卿å
³èéè´ï¼èªå¨éé
7 å 10ï¼ |
| | | LEFT JOIN purchase_ledger pl |
| | | ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id) |
| | | WHERE sir.approval_status = 1 |
| | | AND sir.record_type IN ('7','10') |
| | | group by pl.id |
| | | )T1 on T1.id = pl.id |
| | | left join ( |
| | | SELECT |
| | | sir.stock_in_num * slp_agg.tax_inclusive_unit_price AS inbound_amount, |
| | | slp_agg.sales_ledger_id |
| | | pl.id, |
| | | sum(sir.stock_in_num * slp.tax_inclusive_unit_price) AS InboundAmount |
| | | FROM stock_in_record sir |
| | | INNER JOIN quality_inspect qi |
| | | ON qi.id = sir.record_id |
| | | INNER JOIN purchase_ledger pl2 |
| | | ON pl2.id = qi.purchase_ledger_id |
| | | INNER JOIN ( |
| | | SELECT |
| | | sales_ledger_id, |
| | | product_model_id, |
| | | MIN(tax_inclusive_unit_price) AS tax_inclusive_unit_price |
| | | FROM sales_ledger_product |
| | | WHERE type = 2 |
| | | GROUP BY sales_ledger_id, product_model_id |
| | | ) slp_agg |
| | | ON slp_agg.sales_ledger_id = pl2.id |
| | | AND slp_agg.product_model_id = sir.product_model_id |
| | | WHERE sir.approval_status = 1 AND sir.record_type = 10 |
| | | ) t |
| | | GROUP BY t.sales_ledger_id |
| | | ) T2 ON T2.sales_ledger_id = pl.id |
| | | WHERE pl.supplier_id = #{supplierId} |
| | | -- 10 ç±»åæå
³èè´¨æ£è¡¨ |
| | | LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id |
| | | -- 卿å
³èéè´ï¼èªå¨éé
7 å 10ï¼ |
| | | LEFT JOIN purchase_ledger pl |
| | | ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id) |
| | | -- 产åå
³èä¸å¨ |
| | | LEFT JOIN sales_ledger_product slp ON pl.id = slp.sales_ledger_id |
| | | -- æ¡ä»¶ |
| | | WHERE sir.approval_status = 1 AND slp.type = 2 |
| | | AND sir.record_type IN ('7','10') |
| | | group by pl.id |
| | | )T2 on T2.id = pl.id |
| | | left join ( |
| | | select pl.id, |
| | | sum(pro.total_amount) as returnAmount |
| | | from purchase_return_orders pro |
| | | left join purchase_ledger pl on pro.purchase_ledger_id = pl.id |
| | | group by pl.id |
| | | )T3 on T3.id = pl.id |
| | | where pl.supplier_id = #{supplierId} |
| | | order by pl.id desc |
| | | </select> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.collaborativeApproval.mapper.EnterpriseNewsMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.collaborativeApproval.pojo.EnterpriseNews"> |
| | | <id column="id" property="id" /> |
| | | <result column="title" property="title" /> |
| | | <result column="summary" property="summary" /> |
| | | <result column="content" property="content" /> |
| | | <result column="category" property="category" /> |
| | | <result column="read_scope" property="readScope" /> |
| | | <result column="is_required" property="isRequired" /> |
| | | <result column="status" property="status" /> |
| | | <result column="required_read_count" property="requiredReadCount" /> |
| | | <result column="read_count" property="readCount" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | </resultMap> |
| | | <select id="listPage" resultType="com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo"> |
| | | select en.*, u.nick_name as create_user_name from |
| | | enterprise_news en |
| | | left join sys_user u on en.create_user = u.user_id |
| | | <where> |
| | | <if test="enterpriseNewsDto.title != null and enterpriseNewsDto.title != ''"> |
| | | and en.title like concat('%',#{enterpriseNewsDto.title},'%') |
| | | </if> |
| | | <if test="enterpriseNewsDto.category != null and enterpriseNewsDto.category != ''"> |
| | | and en.category = #{enterpriseNewsDto.category} |
| | | </if> |
| | | <if test="enterpriseNewsDto.status != null "> |
| | | and en.status = #{enterpriseNewsDto.status} |
| | | </if> |
| | | <if test="enterpriseNewsDto.createUser != null and enterpriseNewsDto.createUser != ''"> |
| | | and en.create_user = #{enterpriseNewsDto.createUser} |
| | | </if> |
| | | <if test="enterpriseNewsDto.createTimeStart != null and enterpriseNewsDto.createTimeEnd != null"> |
| | | and en.create_time between #{enterpriseNewsDto.createTimeStart} and #{enterpriseNewsDto.createTimeEnd} |
| | | </if> |
| | | </where> |
| | | order by en.create_time desc |
| | | </select> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept"> |
| | | <id column="id" property="id" /> |
| | | <result column="news_id" property="newsId" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | <result column="create_time" property="createTime" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?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.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser"> |
| | | <id column="id" property="id" /> |
| | | <result column="news_id" property="newsId" /> |
| | | <result column="user_id" property="userId" /> |
| | | <result column="create_time" property="createTime" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| | |
| | | and n.status = #{ew.status} |
| | | </if> |
| | | </where> |
| | | order by n.create_time desc |
| | | </select> |
| | | </mapper> |
| | |
| | | and rrm.category = #{ew.category} |
| | | </if> |
| | | </where> |
| | | order by rrm.id desc |
| | | </select> |
| | | </mapper> |
| | |
| | | </if> |
| | | </where> |
| | | GROUP BY sam.id |
| | | order by sam.id desc |
| | | </select> |
| | | </mapper> |
| | |
| | | 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> |
| | | <if test="deviceMaintenanceDto.deviceName != null and deviceMaintenanceDto.deviceName != ''"> |
| | | <if test="deviceMaintenanceDto.deviceName != null"> |
| | | and dl.device_name like concat('%', #{deviceMaintenanceDto.deviceName}, '%') |
| | | </if> |
| | | <if test="deviceMaintenanceDto.deviceModel != null and deviceMaintenanceDto.deviceModel != ''"> |
| | |
| | | date_add(str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d'), interval 1 day) |
| | | </if> |
| | | </where> |
| | | order by |
| | | <!-- å¾
ä¿å
»(0)ä¼å
æå¨ä¸é¢ï¼å·²å®ç»(1)å¨ä¸é¢ --> |
| | | dm.status asc, |
| | | case |
| | | <!-- å½ç¶ææ¯ 0ï¼å¾
ä¿å
»ï¼æ¶ï¼æè®¡åæ¶é´ååºï¼å³æ¶é´æè¿çåå卿ä¸é¢ --> |
| | | when dm.status = 0 then dm.maintenance_plan_time |
| | | end asc, |
| | | case |
| | | <!-- å½ç¶ææ¯ 1ï¼å·²å®ç»ï¼æ¶ï¼æå®é
ä¿å
»æ¶é´éåºï¼æè¿åä¿å
»å®çååå¨å·²å®ç»éææå --> |
| | | when dm.status = 1 then dm.maintenance_actually_time |
| | | end desc |
| | | order by dm.create_time desc |
| | | </select> |
| | | |
| | | <select id="detailById" resultType="com.ruoyi.device.vo.DeviceMaintenanceVo"> |
| | |
| | | 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> |
| | |
| | | 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, |
| | |
| | | AND record_date = DATE_FORMAT(#{req.recordDate},'%Y-%m-%d') |
| | | </if> |
| | | </where> |
| | | ORDER BY update_time DESC |
| | | ORDER BY create_time DESC |
| | | </select> |
| | | </mapper> |
| | |
| | | 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 |
| | |
| | | and sp.name like concat('%',#{spareParts.name},'%') |
| | | </if> |
| | | </where> |
| | | order by sp.create_time desc |
| | | </select> |
| | | </mapper> |
| | |
| | | and sprr.source_type = #{params.sourceType} |
| | | </if> |
| | | </where> |
| | | order by sprr.create_time desc |
| | | </select> |
| | | </mapper> |
| | |
| | | and sl.sales_contract_no like concat('%',#{req.salesContractNo},'%') |
| | | </if> |
| | | </where> |
| | | order by rm.id desc |
| | | </select> |
| | | <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto"> |
| | | select rm.*, |
| | |
| | | and pot.work_order_no like concat('%', #{c.workOrderNo}, '%') |
| | | </if> |
| | | </where> |
| | | order by pot.id desc |
| | | order by pot.production_order_id desc, poro.drag_sort |
| | | </select> |
| | | |
| | | <select id="selectTaskStatisticsByDate" resultType="com.ruoyi.home.dto.ProductionTaskStatisticsDto"> |
| | |
| | | <if test="c.supplierId != null"> |
| | | AND pl.supplier_id = #{c.supplierId} |
| | | </if> |
| | | <if test="c.ids != null and c.ids.size() > 0"> |
| | | AND pl.id IN |
| | | <foreach collection="c.ids" item="id" open="(" separator="," close=")"> |
| | | #{id} |
| | | </foreach> |
| | | </if> |
| | | <if test="c.approvalStatus != null"> |
| | | AND pl.approval_status = #{c.approvalStatus} |
| | | </if> |
| | |
| | | <if test="qualityInspect.entryDateEnd != null and qualityInspect.entryDateEnd != '' "> |
| | | AND qi.check_time <= DATE_FORMAT(#{qualityInspect.entryDateEnd},'%Y-%m-%d') |
| | | </if> |
| | | <if test="qualityInspect.purchaseContractNo != null and qualityInspect.purchaseContractNo != '' "> |
| | | AND pl.purchase_contract_number like concat('%',#{qualityInspect.purchaseContractNo},'%') |
| | | </if> |
| | | <if test="qualityInspect.workOrderNo != null and qualityInspect.workOrderNo != '' "> |
| | | AND pot.work_order_no like concat('%',#{qualityInspect.workOrderNo},'%') |
| | | </if> |
| | | <if test="qualityInspect.salesContractNo != null and qualityInspect.salesContractNo != '' "> |
| | | AND po_sales.sales_contract_no like concat('%',#{qualityInspect.salesContractNo},'%') |
| | | </if> |
| | | ORDER BY qi.check_time DESC |
| | | </select> |
| | | |
| | |
| | | <if test="c.inspectType != null "> |
| | | AND inspect_type =#{c.inspectType} |
| | | </if> |
| | | order by create_time desc |
| | | </select> |
| | | <select id="getQualityTestStandardByProductId" resultType="com.ruoyi.quality.pojo.QualityTestStandard"> |
| | | SELECT qts.* |
| | |
| | | END AS method |
| | | FROM quality_unqualified qu |
| | | LEFT JOIN product_model pm ON qu.model = pm.id |
| | | where |
| | | 1=1 |
| | | <where> |
| | | <if test="qualityUnqualified.inspectType != null "> |
| | | AND inspect_type = #{qualityUnqualified.inspectType} |
| | | </if> |
| | |
| | | <if test="qualityUnqualified.entryDateEnd != null and qualityUnqualified.entryDateEnd != '' "> |
| | | AND check_time <= DATE_FORMAT(#{qualityUnqualified.entryDateEnd},'%Y-%m-%d') |
| | | </if> |
| | | </where> |
| | | order by qu.create_time desc |
| | | </select> |
| | | <select id="qualityUnqualifiedExport" resultType="com.ruoyi.quality.pojo.QualityUnqualified"> |
| | | SELECT |
| | |
| | | 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 |
| | | <where> |
| | | <if test="customerName != null and customerName != '' "> |
| | | and c.customer_name like concat('%',#{customerName},'%') |
| | | </if> |
| | | </where> |
| | | order by sl.entry_date desc |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.sales.mapper.SalesQuotationMapper"> |
| | | <select id="listPage" resultType="com.ruoyi.sales.dto.SalesQuotationDto"> |
| | | SELECT t1.*, |
| | | t2.approve_user_ids |
| | | SELECT t1.* |
| | | FROM sales_quotation t1 |
| | | LEFT JOIN approve_process t2 ON t1.quotation_no = t2.approve_reason and t2.approve_type = 6 |
| | | WHERE 1=1 |
| | | and t2.approve_delete = 0 |
| | | <where> |
| | | <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' "> |
| | | AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%') |
| | | </if> |
| | |
| | | <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' "> |
| | | AND t1.status = #{salesQuotationDto.status} |
| | | </if> |
| | | </where> |
| | | order by t1.id desc |
| | | </select> |
| | | </mapper> |
| | |
| | | and personal_attendance_records.date >= #{params.date} |
| | | and personal_attendance_records.date < DATE_ADD(DATE(#{params.date}), INTERVAL 1 DAY) |
| | | </if> |
| | | order by personal_attendance_records.id desc |
| | | </select> |
| | | |
| | | <!-- æ¥è¯¢æå®æ¥ææ²¡æèå¤è®°å½çå¨èåå·¥ï¼å¨æå®æ¶é´ä¹åå
¥èçï¼ --> |
| | |
| | | </if> |
| | | </where> |
| | | GROUP BY u.id, u.staff_name |
| | | ORDER BY MAX(s.create_time) |
| | | ORDER BY MAX(s.create_time) desc |
| | | </select> |
| | | |
| | | <select id="performanceShiftYear" resultType="java.util.Map"> |
| | |
| | | soj.emergency_contact as emergencyContact, |
| | | soj.emergency_contact_phone as emergencyContactPhone, |
| | | sp.post_name as postName, |
| | | sd.dept_name as deptName |
| | | sd.dept_name as deptName, |
| | | se.education as first_study, |
| | | se.major as profession |
| | | FROM staff_leave |
| | | LEFT JOIN |
| | | staff_on_job soj ON soj.id = staff_leave.staff_on_job_id |
| | | LEFT JOIN |
| | | 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 |
| | | LEFT JOIN staff_on_job soj ON soj.id = staff_leave.staff_on_job_id |
| | | LEFT JOIN sys_post sp ON sp.post_id = soj.sys_post_id |
| | | LEFT JOIN sys_dept sd ON sd.dept_id = soj.sys_dept_id |
| | | LEFT JOIN staff_education se ON se.staff_on_job_id = staff_leave.staff_on_job_id |
| | | AND se.id = ( |
| | | SELECT MAX(se2.id) |
| | | FROM staff_education se2 |
| | | WHERE se2.staff_on_job_id = staff_leave.staff_on_job_id |
| | | ) |
| | | WHERE 1=1 |
| | | <if test="c.staffName != null and c.staffName != '' "> |
| | | AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%') |
| | | </if> |
| | | order by staff_leave.create_time desc |
| | | </select> |
| | | <select id="staffLeaveList" resultType="com.ruoyi.staff.dto.StaffLeaveDto"> |
| | | SELECT |
| | |
| | | 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 |
| | | <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"> |
| | |
| | | <mapper namespace="com.ruoyi.staff.mapper.StaffOnJobMapper"> |
| | | <select id="staffOnJobListPage" resultType="com.ruoyi.staff.dto.StaffOnJobDto"> |
| | | SELECT |
| | | staff_on_job.*, |
| | | sp.post_name as postName, |
| | | sd.dept_name as deptName, |
| | | MIN(t1.contract_start_time) as contract_start_time, -- åææ©ååå¼å§æ¶é´ |
| | | MAX(t1.contract_end_time) as contract_end_time |
| | | staff_on_job.id, |
| | | staff_on_job.staff_state, |
| | | staff_on_job.staff_no, |
| | | staff_on_job.staff_name, |
| | | staff_on_job.sex, |
| | | staff_on_job.native_place, |
| | | staff_on_job.sys_post_id, |
| | | staff_on_job.sys_dept_id, |
| | | staff_on_job.role_id, |
| | | staff_on_job.adress, |
| | | staff_on_job.first_study, |
| | | staff_on_job.profession, |
| | | staff_on_job.identity_card, |
| | | staff_on_job.age, |
| | | staff_on_job.phone, |
| | | staff_on_job.contract_term, |
| | | staff_on_job.contract_expire_time, |
| | | staff_on_job.trial_end_date, |
| | | staff_on_job.trial_start_date, |
| | | staff_on_job.sign_date, |
| | | staff_on_job.salary_select, |
| | | staff_on_job.pro_salary, |
| | | staff_on_job.date_select, |
| | | staff_on_job.remark, |
| | | staff_on_job.create_time, |
| | | staff_on_job.create_user, |
| | | staff_on_job.update_time, |
| | | staff_on_job.update_user, |
| | | staff_on_job.tenant_id, |
| | | staff_on_job.alias, |
| | | staff_on_job.birth_date, |
| | | staff_on_job.nation, |
| | | staff_on_job.marital_status, |
| | | staff_on_job.pro_term, |
| | | staff_on_job.positive_date, |
| | | staff_on_job.basic_salary, |
| | | staff_on_job.dept_id, |
| | | sp.post_name AS post_name, |
| | | sd.dept_name AS dept_name, |
| | | MIN(t1.contract_start_time) AS contract_start_time, |
| | | MAX(t1.contract_end_time) AS contract_end_time, |
| | | ( |
| | | SELECT GROUP_CONCAT(sec.contact_name SEPARATOR ',') |
| | | FROM staff_emergency_contact sec |
| | | WHERE sec.staff_on_job_id = staff_on_job.id |
| | | ) AS emergency_contact, |
| | | ( |
| | | SELECT GROUP_CONCAT(sec.contact_phone SEPARATOR ',') |
| | | FROM staff_emergency_contact sec |
| | | WHERE sec.staff_on_job_id = staff_on_job.id |
| | | ) AS emergency_contact_phone |
| | | FROM staff_on_job |
| | | LEFT JOIN 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 |
| | | LEFT JOIN staff_contract as t1 ON t1.staff_on_job_id = staff_on_job.id |
| | | WHERE 1=1 |
| | | LEFT JOIN staff_contract AS t1 ON t1.staff_on_job_id = staff_on_job.id |
| | | <where> |
| | | <if test="staffOnJob.staffState != null"> |
| | | AND staff_state = #{staffOnJob.staffState} |
| | | AND staff_on_job.staff_state = #{staffOnJob.staffState} |
| | | </if> |
| | | <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' "> |
| | | AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%') |
| | | AND staff_on_job.staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%') |
| | | </if> |
| | | <if test="staffOnJob.entryDateStart != null and staffOnJob.entryDateStart != '' "> |
| | | AND contract_expire_time >= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d') |
| | | AND staff_on_job.contract_expire_time >= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d') |
| | | </if> |
| | | <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' "> |
| | | AND contract_expire_time <= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d') |
| | | AND staff_on_job.contract_expire_time <= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d') |
| | | </if> |
| | | <if test="staffOnJob.sysDeptId != null"> |
| | | AND staff_on_job.sys_dept_id = #{staffOnJob.sysDeptId} |
| | | </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.create_time desc |
| | | </select> |
| | | <select id="staffOnJobList" resultType="com.ruoyi.staff.dto.StaffOnJobDto"> |
| | | SELECT |
| | |
| | | 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 |
| | | <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> |
| | | </where> |
| | | order by staff_on_job.create_time desc |
| | | </select> |
| | | <!-- ç»è®¡æå®æ¥æçå¨èåå·¥æ° --> |
| | | <select id="countOnJobStaffByDate" resultType="java.lang.Integer"> |
| | |
| | | unit, |
| | | product_name, |
| | | product_id |
| | | order by combined.create_time desc |
| | | </select> |
| | | |
| | | <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData"> |
| | |
| | | and sir.create_time <= #{ew.endMonth} |
| | | </if> |
| | | </where> |
| | | order by sir.id desc |
| | | </select> |
| | | |
| | | <select id="stockInAndOutRecord" resultType="com.ruoyi.stock.dto.StockInventoryDto"> |
| | |
| | | <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 |
| | | from sys_dept d |
| | | order by d.create_time desc |
| | | </sql> |
| | | |
| | | <select id="selectDeptList" parameterType="com.ruoyi.project.system.domain.SysDept" resultMap="SysDeptResult"> |
| | |
| | | <!-- æ°æ®èå´è¿æ»¤ --> |
| | | ${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"> |
| | |
| | | <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"> |
| | |
| | | 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"> |
| | |
| | | and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d') |
| | | </if> |
| | | </where> |
| | | order by create_time desc |
| | | </select> |
| | | |
| | | <select id="selectDictTypeAll" resultMap="SysDictTypeResult"> |
| | |
| | | <result property="noticeType" column="notice_type" /> |
| | | <result property="noticeContent" column="notice_content" /> |
| | | <result property="status" column="status" /> |
| | | <result property="senderId" column="sender_id" /> |
| | | <result property="consigneeId" column="consignee_id" /> |
| | | <result property="jumpPath" column="jump_path" /> |
| | | <result property="appJumpPath" column="app_jump_path" /> |
| | | <result property="createBy" column="create_by" /> |
| | | <result property="createTime" column="create_time" /> |
| | | <result property="updateBy" column="update_by" /> |
| | | <result property="updateTime" column="update_time" /> |
| | | <result property="remark" column="remark" /> |
| | | <result property="tenantId" column="tenant_id" /> |
| | | </resultMap> |
| | | |
| | | <sql id="selectNoticeVo"> |
| | |
| | | AND post_name like concat('%', #{postName}, '%') |
| | | </if> |
| | | </where> |
| | | order by create_time desc |
| | | </select> |
| | | |
| | | <select id="selectPostAll" resultMap="SysPostResult"> |
| | |
| | | </sql> |
| | | |
| | | <select id="selectUserList" parameterType="com.ruoyi.project.system.domain.SysUser" resultMap="SysUserResult"> |
| | | select u.user_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, |
| | | u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,T2.dept_names from sys_user u |
| | | select u.user_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,T2.dept_names from sys_user u |
| | | left join |
| | | ( SELECT T1.user_id,GROUP_CONCAT(T2.dept_name SEPARATOR ', ') AS dept_names |
| | | FROM |
| | |
| | | </if> |
| | | <!-- æ°æ®èå´è¿æ»¤ --> |
| | | ${params.dataScope} |
| | | ORDER BY u.create_time DESC |
| | | </select> |
| | | |
| | | <select id="selectAllocatedList" parameterType="com.ruoyi.project.system.domain.SysUser" resultMap="SysUserResult"> |
| | |
| | | ${params.dataScope} |
| | | </select> |
| | | |
| | | <select id="selectUnallocatedList" parameterType="com.ruoyi.project.system.domain.SysUser" |
| | | resultMap="SysUserResult"> |
| | | <select id="selectUnallocatedList" parameterType="com.ruoyi.project.system.domain.SysUser" resultMap="SysUserResult"> |
| | | select distinct u.user_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time |
| | | from sys_user u |
| | | left join sys_user_role ur on u.user_id = ur.user_id |
| | | left join sys_role r on r.role_id = ur.role_id |
| | | where u.del_flag = '0' and (r.role_id != #{roleId} or r.role_id IS NULL) |
| | | and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and |
| | | ur.role_id = #{roleId}) |
| | | and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{roleId}) |
| | | <if test="userName != null and userName != ''"> |
| | | AND u.user_name like concat('%', #{userName}, '%') |
| | | </if> |
| | |
| | | </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"> |
| | |
| | | </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 |
| | |
| | | 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 |
| | |
| | | #{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"> |
| | |
| | | limit 1 |
| | | </select> |
| | | |
| | | <insert id="insertUser" parameterType="com.ruoyi.project.system.domain.SysUser" useGeneratedKeys="true" |
| | | keyProperty="userId"> |
| | | <insert id="insertUser" parameterType="com.ruoyi.project.system.domain.SysUser" useGeneratedKeys="true" keyProperty="userId"> |
| | | insert into sys_user( |
| | | <if test="userId != null and userId != 0">user_id,</if> |
| | | <if test="userName != null and userName != ''">user_name,</if> |
| | |
| | | </update> |
| | | |
| | | <update id="updateUserStatus" parameterType="com.ruoyi.project.system.domain.SysUser"> |
| | | update sys_user |
| | | set status = #{status} |
| | | where user_id = #{userId} |
| | | update sys_user set status = #{status} where user_id = #{userId} |
| | | </update> |
| | | |
| | | <update id="updateUserAvatar" parameterType="com.ruoyi.project.system.domain.SysUser"> |
| | | update sys_user |
| | | set avatar = #{avatar} |
| | | where user_name = #{userName} |
| | | update sys_user set avatar = #{avatar} where user_name = #{userName} |
| | | </update> |
| | | |
| | | <update id="resetUserPwd" parameterType="com.ruoyi.project.system.domain.SysUser"> |
| | | update sys_user |
| | | set password = #{password} |
| | | where user_name = #{userName} |
| | | update sys_user set password = #{password} where user_name = #{userName} |
| | | </update> |
| | | |
| | | <delete id="deleteUserById" parameterType="Long"> |
| | | update sys_user |
| | | set del_flag = '2' |
| | | where user_id = #{userId} |
| | | update sys_user set del_flag = '2' where user_id = #{userId} |
| | | </delete> |
| | | |
| | | <delete id="deleteUserByIds" parameterType="Long"> |
| | |
| | | <foreach collection="array" item="userId" open="(" separator="," close=")"> |
| | | #{userId} |
| | | </foreach> |
| | | </delete> |
| | | and del_flag = '0' |
| | | </select> |
| | | <select id="selectUserByNickName" resultType="com.ruoyi.project.system.domain.SysUser" |
| | | parameterType="java.lang.String"> |
| | | <include refid="selectUserVo"/> |
| | | where u.nick_name = #{nickName} and u.del_flag = '0' |
| | | limit 1 |
| | | </select> |
| | | |
| | | <select id="getUserByRole" resultType="java.lang.Long"> |
| | | select distinct su.user_id |
| | |
| | | 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 |
| | |
| | | and t.type = #{c.type} |
| | | </if> |
| | | </where> |
| | | order by t.id asc |
| | | order by t.id desc |
| | | </select> |
| | | </mapper> |
| | |
| | | and top1.technology_param_id = #{paramId} |
| | | </if> |
| | | </where> |
| | | order by top1.id asc |
| | | order by top1.id desc |
| | | </select> |
| | | </mapper> |
| | |
| | | <if test="documentationBorrowManagement.entryDateEnd != null and documentationBorrowManagement.entryDateEnd != ''"> |
| | | and borrow_date <= DATE_FORMAT(#{documentationBorrowManagement.entryDateEnd},'%Y-%m-%d') |
| | | </if> |
| | | order by dbm.create_time desc |
| | | </select> |
| | | <select id="export" resultType="com.ruoyi.warehouse.dto.DocumentationBorrowManagementDto"> |
| | | select dbm.*,doc.doc_name |
| | |
| | | <if test="documentation.id != null"> |
| | | and doc.id = #{documentation.id} |
| | | </if> |
| | | order by doc.create_time desc |
| | | </select> |
| | | <select id="listByDocumentClassificationId" resultType="com.ruoyi.warehouse.dto.DocumentationDto"> |
| | | SELECT |
| | |
| | | <if test="documentationReturnManagement.entryDateEnd != null and documentationReturnManagement.entryDateEnd != ''"> |
| | | and return_date <= DATE_FORMAT(#{documentationReturnManagement.entryDateEnd},'%Y-%m-%d') |
| | | </if> |
| | | order by dbm.create_time desc |
| | | </select> |
| | | |
| | | <select id="exportrevent" resultType="com.ruoyi.warehouse.dto.ReturnExportDto"> |
| | |
| | | and wgsr.id = #{warehouseGoodsShelvesRowcol.id} |
| | | </if> |
| | | </where> |
| | | order by wgsr.create_time desc |
| | | </select> |
| | | |
| | | </mapper> |