更新项目工作流
This commit is contained in:
parent
84fb4742d3
commit
87f34772b8
@ -1,4 +1,4 @@
|
||||
2026-02-13 09:12:26 [reactor-http-nio-10] INFO d.c.b.a.s.services.StatusUpdater - Couldn't retrieve status for Instance(id=08432fb0b14f, version=2, registration=Registration(name=RuoYi-Vue-Plus, managementUrl=http://192.168.5.200:8033/actuator, healthUrl=http://192.168.5.200:8033/actuator/health, serviceUrl=http://192.168.5.200:8033/, source=http-api), registered=true, statusInfo=StatusInfo(status=UP, details={db={status=UP, details={database=MySQL, validationQuery=isValid()}}, diskSpace={status=UP, details={total=974330859520, free=702342062080, threshold=10485760, exists=true}}, ping={status=UP}, redis={status=UP, details={version=3.0.503}}}), statusTimestamp=2026-02-12T09:50:54.228Z, info=Info(values={}), endpoints=Endpoints(endpoints={caches=Endpoint(id=caches, url=http://192.168.5.200:8033/actuator/caches), loggers=Endpoint(id=loggers, url=http://192.168.5.200:8033/actuator/loggers), logfile=Endpoint(id=logfile, url=http://192.168.5.200:8033/actuator/logfile), health=Endpoint(id=health, url=http://192.168.5.200:8033/actuator/health), env=Endpoint(id=env, url=http://192.168.5.200:8033/actuator/env), heapdump=Endpoint(id=heapdump, url=http://192.168.5.200:8033/actuator/heapdump), scheduledtasks=Endpoint(id=scheduledtasks, url=http://192.168.5.200:8033/actuator/scheduledtasks), mappings=Endpoint(id=mappings, url=http://192.168.5.200:8033/actuator/mappings), startup=Endpoint(id=startup, url=http://192.168.5.200:8033/actuator/startup), beans=Endpoint(id=beans, url=http://192.168.5.200:8033/actuator/beans), configprops=Endpoint(id=configprops, url=http://192.168.5.200:8033/actuator/configprops), threaddump=Endpoint(id=threaddump, url=http://192.168.5.200:8033/actuator/threaddump), metrics=Endpoint(id=metrics, url=http://192.168.5.200:8033/actuator/metrics), conditions=Endpoint(id=conditions, url=http://192.168.5.200:8033/actuator/conditions), info=Endpoint(id=info, url=http://192.168.5.200:8033/actuator/info)}), buildVersion=null, tags=Tags(values={}))
|
||||
2026-04-12 09:48:29 [reactor-http-nio-16] INFO d.c.b.a.s.services.StatusUpdater - Couldn't retrieve status for Instance(id=08432fb0b14f, version=47, registration=Registration(name=RuoYi-Vue-Plus, managementUrl=http://192.168.5.200:8033/actuator, healthUrl=http://192.168.5.200:8033/actuator/health, serviceUrl=http://192.168.5.200:8033/, source=http-api), registered=true, statusInfo=StatusInfo(status=UP, details={db={status=UP, details={database=MySQL, validationQuery=isValid()}}, diskSpace={status=UP, details={total=974330859520, free=697055969280, threshold=10485760, exists=true}}, ping={status=UP}, redis={status=UP, details={version=3.0.503}}}), statusTimestamp=2026-04-11T09:53:14.781Z, info=Info(values={}), endpoints=Endpoints(endpoints={caches=Endpoint(id=caches, url=http://192.168.5.200:8033/actuator/caches), loggers=Endpoint(id=loggers, url=http://192.168.5.200:8033/actuator/loggers), logfile=Endpoint(id=logfile, url=http://192.168.5.200:8033/actuator/logfile), health=Endpoint(id=health, url=http://192.168.5.200:8033/actuator/health), env=Endpoint(id=env, url=http://192.168.5.200:8033/actuator/env), heapdump=Endpoint(id=heapdump, url=http://192.168.5.200:8033/actuator/heapdump), mappings=Endpoint(id=mappings, url=http://192.168.5.200:8033/actuator/mappings), scheduledtasks=Endpoint(id=scheduledtasks, url=http://192.168.5.200:8033/actuator/scheduledtasks), startup=Endpoint(id=startup, url=http://192.168.5.200:8033/actuator/startup), configprops=Endpoint(id=configprops, url=http://192.168.5.200:8033/actuator/configprops), beans=Endpoint(id=beans, url=http://192.168.5.200:8033/actuator/beans), threaddump=Endpoint(id=threaddump, url=http://192.168.5.200:8033/actuator/threaddump), metrics=Endpoint(id=metrics, url=http://192.168.5.200:8033/actuator/metrics), conditions=Endpoint(id=conditions, url=http://192.168.5.200:8033/actuator/conditions), info=Endpoint(id=info, url=http://192.168.5.200:8033/actuator/info)}), buildVersion=null, tags=Tags(values={}))
|
||||
org.springframework.web.reactive.function.client.WebClientRequestException: Connection refused: no further information: /192.168.5.200:8033; nested exception is io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: /192.168.5.200:8033
|
||||
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141)
|
||||
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
|
||||
@ -76,5 +76,5 @@ Caused by: java.net.ConnectException: Connection refused: no further information
|
||||
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
|
||||
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
2026-02-13 09:12:26 [reactor-http-nio-10] INFO c.r.m.admin.notifier.CustomNotifier - Instance Status Change: [RuoYi-Vue-Plus],[08432fb0b14f],[OFFLINE]
|
||||
2026-02-13 09:13:02 [reactor-http-nio-12] INFO c.r.m.admin.notifier.CustomNotifier - Instance Status Change: [RuoYi-Vue-Plus],[08432fb0b14f],[UP]
|
||||
2026-04-12 09:48:29 [reactor-http-nio-16] INFO c.r.m.admin.notifier.CustomNotifier - Instance Status Change: [RuoYi-Vue-Plus],[08432fb0b14f],[OFFLINE]
|
||||
2026-04-12 09:48:57 [reactor-http-nio-2] INFO c.r.m.admin.notifier.CustomNotifier - Instance Status Change: [RuoYi-Vue-Plus],[08432fb0b14f],[UP]
|
||||
|
||||
@ -1,27 +1,51 @@
|
||||
2026-02-12 00:29:30 [HikariCP housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - HikariCP - Thread starvation or clock leap detected (housekeeper delta=46s179ms400µs600ns).
|
||||
2026-02-12 02:57:51 [HikariCP housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - HikariCP - Thread starvation or clock leap detected (housekeeper delta=45s809ms35µs400ns).
|
||||
2026-02-12 03:21:20 [HikariCP housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - HikariCP - Thread starvation or clock leap detected (housekeeper delta=58s730ms424µs).
|
||||
2026-02-12 17:50:07 [main] INFO c.x.job.admin.XxlJobAdminApplication - Starting XxlJobAdminApplication using Java 1.8.0_421 on PC-20250327GIRN with PID 32340 (F:\evo_k3cloud\ruoyi-extend\ruoyi-xxl-job-admin\target\classes started by Administrator in F:\evo_k3cloud)
|
||||
2026-02-12 17:50:07 [main] INFO c.x.job.admin.XxlJobAdminApplication - The following 1 profile is active: "dev"
|
||||
2026-02-12 17:50:11 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port(s): 9100 (http)
|
||||
2026-02-12 17:50:11 [main] INFO o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-9100"]
|
||||
2026-02-12 17:50:11 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
|
||||
2026-02-12 17:50:11 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.74]
|
||||
2026-02-12 17:50:11 [main] INFO o.a.c.c.C.[.[.[/xxl-job-admin] - Initializing Spring embedded WebApplicationContext
|
||||
2026-02-12 17:50:11 [main] INFO o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 4328 ms
|
||||
2026-02-12 17:50:12 [main] INFO c.x.j.a.c.scheduler.XxlJobScheduler - >>>>>>>>> init xxl-job admin success.
|
||||
2026-02-12 17:50:12 [xxl-job, admin JobFailMonitorHelper] INFO com.zaxxer.hikari.HikariDataSource - HikariCP - Starting...
|
||||
2026-02-12 17:50:12 [main] INFO o.s.b.a.w.s.WelcomePageHandlerMapping - Adding welcome page template: index
|
||||
2026-02-12 17:50:12 [xxl-job, admin JobFailMonitorHelper] INFO com.zaxxer.hikari.HikariDataSource - HikariCP - Start completed.
|
||||
2026-02-12 17:50:13 [main] INFO o.s.b.a.e.web.EndpointLinksResolver - Exposing 14 endpoint(s) beneath base path '/actuator'
|
||||
2026-02-12 17:50:13 [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-9100"]
|
||||
2026-02-12 17:50:13 [main] INFO o.a.c.c.C.[.[.[/xxl-job-admin] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
2026-02-12 17:50:13 [main] INFO o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
|
||||
2026-02-12 17:50:13 [main] INFO o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
|
||||
2026-02-12 17:50:13 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 9100 (http) with context path '/xxl-job-admin'
|
||||
2026-02-12 17:50:13 [main] INFO c.x.job.admin.XxlJobAdminApplication - Started XxlJobAdminApplication in 7.259 seconds (JVM running for 8.794)
|
||||
2026-02-12 17:50:15 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect. Further attempts are logged on DEBUG level
|
||||
2026-02-12 17:50:17 [xxl-job, admin JobScheduleHelper#scheduleThread] INFO c.x.j.a.c.thread.JobScheduleHelper - >>>>>>>>> init xxl-job admin scheduler success.
|
||||
2026-02-12 17:50:17 [xxl-job, admin JobScheduleHelper#scheduleThread] WARN c.x.j.a.c.thread.JobScheduleHelper - >>>>>>>>>>> xxl-job, schedule misfire, jobId = 2
|
||||
2026-02-12 17:50:17 [xxl-job, admin JobScheduleHelper#scheduleThread] WARN c.x.j.a.c.thread.JobScheduleHelper - >>>>>>>>>>> xxl-job, schedule misfire, jobId = 23
|
||||
2026-02-12 17:50:24 [registrationTask1] INFO d.c.b.a.c.r.ApplicationRegistrator - Application registered itself as cc38f3b5f0d4
|
||||
2026-04-11 01:34:00 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 02:44:00 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 02:44:40 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 05:14:20 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 06:07:30 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 06:07:46 [HikariCP housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - HikariCP - Thread starvation or clock leap detected (housekeeper delta=46s635ms171µs800ns).
|
||||
2026-04-11 06:44:51 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 07:09:50 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 08:50:10 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 08:50:16 [HikariCP housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - HikariCP - Thread starvation or clock leap detected (housekeeper delta=49s98ms91µs500ns).
|
||||
2026-04-11 08:50:40 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 09:57:00 [registrationTask1] WARN d.c.b.a.c.r.ApplicationRegistrator - Failed to register application as Application(name=ruoyi-xxl-job-admin, managementUrl=http://192.168.5.200:9100/xxl-job-admin/actuator, healthUrl=http://192.168.5.200:9100/xxl-job-admin/actuator/health, serviceUrl=http://192.168.5.200:9100/xxl-job-admin) at spring-boot-admin ([http://localhost:9090/admin/instances]): I/O error on POST request for "http://localhost:9090/admin/instances": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out. Further attempts are logged on DEBUG level
|
||||
2026-04-11 09:57:19 [HikariCP housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - HikariCP - Thread starvation or clock leap detected (housekeeper delta=48s548ms12µs600ns).
|
||||
2026-04-11 17:00:04 [xxl-job, admin JobFailMonitorHelper] ERROR c.x.j.a.c.alarm.impl.EmailJobAlarm - >>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:5974
|
||||
org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 535 Login fail. Account is abnormal, service is not open, password is incorrect, login frequency limited, or system is busy. More information at https://help.mail.qq.com/detail/108/1023
|
||||
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:440)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:361)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:356)
|
||||
at com.xxl.job.admin.core.alarm.impl.EmailJobAlarm.doAlarm(EmailJobAlarm.java:74)
|
||||
at com.xxl.job.admin.core.alarm.JobAlarmer.alarm(JobAlarmer.java:52)
|
||||
at com.xxl.job.admin.core.thread.JobFailMonitorHelper$1.run(JobFailMonitorHelper.java:66)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
Caused by: javax.mail.AuthenticationFailedException: 535 Login fail. Account is abnormal, service is not open, password is incorrect, login frequency limited, or system is busy. More information at https://help.mail.qq.com/detail/108/1023
|
||||
|
||||
at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:947)
|
||||
at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:858)
|
||||
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:762)
|
||||
at javax.mail.Service.connect(Service.java:342)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:518)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:437)
|
||||
... 6 common frames omitted
|
||||
2026-04-11 17:30:06 [xxl-job, admin JobFailMonitorHelper] ERROR c.x.j.a.c.alarm.impl.EmailJobAlarm - >>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:5975
|
||||
org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 535 Login fail. Account is abnormal, service is not open, password is incorrect, login frequency limited, or system is busy. More information at https://help.mail.qq.com/detail/108/1023
|
||||
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:440)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:361)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:356)
|
||||
at com.xxl.job.admin.core.alarm.impl.EmailJobAlarm.doAlarm(EmailJobAlarm.java:74)
|
||||
at com.xxl.job.admin.core.alarm.JobAlarmer.alarm(JobAlarmer.java:52)
|
||||
at com.xxl.job.admin.core.thread.JobFailMonitorHelper$1.run(JobFailMonitorHelper.java:66)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
Caused by: javax.mail.AuthenticationFailedException: 535 Login fail. Account is abnormal, service is not open, password is incorrect, login frequency limited, or system is busy. More information at https://help.mail.qq.com/detail/108/1023
|
||||
|
||||
at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:947)
|
||||
at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:858)
|
||||
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:762)
|
||||
at javax.mail.Service.connect(Service.java:342)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:518)
|
||||
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:437)
|
||||
... 6 common frames omitted
|
||||
|
||||
1408
logs/sys-console.log
1408
logs/sys-console.log
File diff suppressed because one or more lines are too long
@ -1,160 +1 @@
|
||||
2026-02-13 07:15:53 [Thread-80] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:01 [xxl-job, JobThread-17-1770941700025] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:02 [xxl-job, JobThread-18-1770941700022] ERROR o.a.poi.ooxml.POIXMLDocumentPart - Skipped invalid entry /xl/sharedlinks.xml
|
||||
2026-02-13 08:15:04 [xxl-job, JobThread-23-1770941700019] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:05 [xxl-job, JobThread-22-1770941700018] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:05 [xxl-job, JobThread-21-1770941700016] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:05 [xxl-job, JobThread-19-1770941700023] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:07 [xxl-job, JobThread-18-1770941700022] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:10 [xxl-job, JobThread-14-1770941700025] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:11 [xxl-job, JobThread-13-1770941700014] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:11 [xxl-job, JobThread-20-1770941700024] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:15:43 [xxl-job, JobThread-16-1770941700020] ERROR c.ruoyi.framework.aspectj.LogAspect - 异常信息:null
|
||||
2026-02-13 08:58:31 [Thread-92] ERROR o.a.poi.ooxml.POIXMLDocumentPart - Skipped invalid entry /xl/sharedlinks.xml
|
||||
2026-02-13 09:12:45 [main] ERROR c.b.m.core.MybatisConfiguration - mapper[com.ruoyi.system.mapper.ProcessRouteMapper.existsByProcessNoAndMaterialCode] is ignored, because it exists, maybe from xml file
|
||||
2026-02-13 09:13:35 [Thread-78] ERROR c.r.s.c.KingdeeWorkCenterDataController - 发送钉钉文件失败: 机一工段生产延期数据_20260213091331.xlsx
|
||||
java.lang.RuntimeException: 发送企业机器人文件消息失败
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:108)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController.sendMassageDelayDate(KingdeeWorkCenterDataController.java:672)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$FastClassBySpringCGLIB$$812397e7.invoke(<generated>)
|
||||
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:123)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$EnhancerBySpringCGLIB$$8eb42999.sendMassageDelayDate(<generated>)
|
||||
at com.ruoyi.web.controller.dingtalk.CallbackController.lambda$processEvent$1(CallbackController.java:269)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
Caused by: java.lang.RuntimeException: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 00096AFE-A0E4-7243-ACC2-7CE5CBA58014
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:622)
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:105)
|
||||
... 16 common frames omitted
|
||||
Caused by: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 00096AFE-A0E4-7243-ACC2-7CE5CBA58014
|
||||
at com.aliyun.gateway.dingtalk.Client.modifyResponse(Client.java:42)
|
||||
at com.aliyun.teaopenapi.Client.execute(Client.java:984)
|
||||
at com.aliyun.dingtalkrobot_1_0.Client.orgGroupSendWithOptions(Client.java:541)
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:615)
|
||||
... 17 common frames omitted
|
||||
2026-02-13 09:13:37 [Thread-78] ERROR c.r.s.c.KingdeeWorkCenterDataController - 发送钉钉文件失败: 装二工段生产延期数据_20260213091337.xlsx
|
||||
java.lang.RuntimeException: 发送企业机器人文件消息失败
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:108)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController.sendMassageDelayDate(KingdeeWorkCenterDataController.java:672)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$FastClassBySpringCGLIB$$812397e7.invoke(<generated>)
|
||||
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:123)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$EnhancerBySpringCGLIB$$8eb42999.sendMassageDelayDate(<generated>)
|
||||
at com.ruoyi.web.controller.dingtalk.CallbackController.lambda$processEvent$1(CallbackController.java:269)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
Caused by: java.lang.RuntimeException: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 089C935B-61ED-7EF3-B636-475DD1117BEF
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:622)
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:105)
|
||||
... 16 common frames omitted
|
||||
Caused by: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 089C935B-61ED-7EF3-B636-475DD1117BEF
|
||||
at com.aliyun.gateway.dingtalk.Client.modifyResponse(Client.java:42)
|
||||
at com.aliyun.teaopenapi.Client.execute(Client.java:984)
|
||||
at com.aliyun.dingtalkrobot_1_0.Client.orgGroupSendWithOptions(Client.java:541)
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:615)
|
||||
... 17 common frames omitted
|
||||
2026-02-13 09:13:39 [Thread-78] ERROR c.r.s.c.KingdeeWorkCenterDataController - 发送钉钉文件失败: 委外中心生产延期数据_20260213091338.xlsx
|
||||
java.lang.RuntimeException: 发送企业机器人文件消息失败
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:108)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController.sendMassageDelayDate(KingdeeWorkCenterDataController.java:672)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$FastClassBySpringCGLIB$$812397e7.invoke(<generated>)
|
||||
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:123)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$EnhancerBySpringCGLIB$$8eb42999.sendMassageDelayDate(<generated>)
|
||||
at com.ruoyi.web.controller.dingtalk.CallbackController.lambda$processEvent$1(CallbackController.java:269)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
Caused by: java.lang.RuntimeException: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: F0B57460-B5BE-7787-AF92-C0A6B29E15C9
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:622)
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:105)
|
||||
... 16 common frames omitted
|
||||
Caused by: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: F0B57460-B5BE-7787-AF92-C0A6B29E15C9
|
||||
at com.aliyun.gateway.dingtalk.Client.modifyResponse(Client.java:42)
|
||||
at com.aliyun.teaopenapi.Client.execute(Client.java:984)
|
||||
at com.aliyun.dingtalkrobot_1_0.Client.orgGroupSendWithOptions(Client.java:541)
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:615)
|
||||
... 17 common frames omitted
|
||||
2026-02-13 09:13:40 [Thread-78] ERROR c.r.s.c.KingdeeWorkCenterDataController - 发送钉钉文件失败: 铆焊工段生产延期数据_20260213091339.xlsx
|
||||
java.lang.RuntimeException: 发送企业机器人文件消息失败
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:108)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController.sendMassageDelayDate(KingdeeWorkCenterDataController.java:672)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$FastClassBySpringCGLIB$$812397e7.invoke(<generated>)
|
||||
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:123)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$EnhancerBySpringCGLIB$$8eb42999.sendMassageDelayDate(<generated>)
|
||||
at com.ruoyi.web.controller.dingtalk.CallbackController.lambda$processEvent$1(CallbackController.java:269)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
Caused by: java.lang.RuntimeException: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 86E9FA56-DB58-74D0-A80C-33D3AE558357
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:622)
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupFile(DingUtil.java:105)
|
||||
... 16 common frames omitted
|
||||
Caused by: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 86E9FA56-DB58-74D0-A80C-33D3AE558357
|
||||
at com.aliyun.gateway.dingtalk.Client.modifyResponse(Client.java:42)
|
||||
at com.aliyun.teaopenapi.Client.execute(Client.java:984)
|
||||
at com.aliyun.dingtalkrobot_1_0.Client.orgGroupSendWithOptions(Client.java:541)
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:615)
|
||||
... 17 common frames omitted
|
||||
2026-02-13 09:13:40 [Thread-78] ERROR c.r.s.c.KingdeeWorkCenterDataController - 发送工段数据失败
|
||||
java.lang.RuntimeException: 发送企业机器人Markdown消息失败
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupMarkdown(DingUtil.java:84)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController.sendDingTalkMessage(KingdeeWorkCenterDataController.java:151)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController.sendMassageDelayDate(KingdeeWorkCenterDataController.java:693)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$FastClassBySpringCGLIB$$812397e7.invoke(<generated>)
|
||||
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:123)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
|
||||
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
|
||||
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
|
||||
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
|
||||
at com.ruoyi.system.controller.KingdeeWorkCenterDataController$$EnhancerBySpringCGLIB$$8eb42999.sendMassageDelayDate(<generated>)
|
||||
at com.ruoyi.web.controller.dingtalk.CallbackController.lambda$processEvent$1(CallbackController.java:269)
|
||||
at java.lang.Thread.run(Thread.java:750)
|
||||
Caused by: java.lang.RuntimeException: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 8F77C7D6-4E88-76CE-9E67-D9CF1007231B
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:622)
|
||||
at com.ruoyi.common.dingding.DingUtil.sendOrgGroupMarkdown(DingUtil.java:81)
|
||||
... 17 common frames omitted
|
||||
Caused by: com.aliyun.tea.TeaException: code: 400, 错误描述: robot 不存在;解决方案:请确认 robotCode 是否正确; request id: 8F77C7D6-4E88-76CE-9E67-D9CF1007231B
|
||||
at com.aliyun.gateway.dingtalk.Client.modifyResponse(Client.java:42)
|
||||
at com.aliyun.teaopenapi.Client.execute(Client.java:984)
|
||||
at com.aliyun.dingtalkrobot_1_0.Client.orgGroupSendWithOptions(Client.java:541)
|
||||
at com.ruoyi.common.dingding.DingUtil.robotGroupSend(DingUtil.java:615)
|
||||
... 18 common frames omitted
|
||||
2026-04-12 09:48:37 [main] ERROR c.b.m.core.MybatisConfiguration - mapper[com.ruoyi.system.mapper.ProcessRouteMapper.existsByProcessNoAndMaterialCode] is ignored, because it exists, maybe from xml file
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -6,7 +6,9 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.config.DingTalkProperties;
|
||||
import com.ruoyi.common.dingding.DingCallbackCrypto;
|
||||
import com.ruoyi.common.dingding.robot.RobotGroupService;
|
||||
import com.ruoyi.common.dingding.util.DingUtil;
|
||||
import com.ruoyi.common.dingding.webhook.WebhookClient;
|
||||
import com.ruoyi.system.controller.KingdeeWorkCenterDataController;
|
||||
import com.ruoyi.system.domain.ProcessOrderPro;
|
||||
import com.ruoyi.system.mapper.ProcessOrderProMapper;
|
||||
@ -43,9 +45,9 @@ public class CallbackController {
|
||||
private com.ruoyi.common.dingding.card.CardService cardService;
|
||||
|
||||
@Autowired
|
||||
private com.ruoyi.common.dingding.robot.RobotGroupService robotGroupService; // 注入 RobotGroupService
|
||||
private RobotGroupService robotGroupService; // 注入 RobotGroupService
|
||||
@Autowired
|
||||
private com.ruoyi.common.dingding.webhook.WebhookClient webhookClient; // 注入 WebhookClient
|
||||
private WebhookClient webhookClient; // 注入 WebhookClient
|
||||
|
||||
@PostMapping("/receive")
|
||||
public Map<String, String> callBack(@RequestBody(required = false) Map<String, Object> json) {
|
||||
@ -110,6 +112,11 @@ public class CallbackController {
|
||||
update.setUpdateBy("DingDing");
|
||||
processOrderProMapper.updateById(update);
|
||||
bizLogger.info("更新 ProcessOrderPro 状态成功: id={}, status={}", order.getId(), status);
|
||||
|
||||
// 如果审核通过(假设 status 为 1L 表示通过),给配置的人发消息
|
||||
if (status != null && status == 1L) {
|
||||
sendAuditCompleteNotification(order);
|
||||
}
|
||||
} else {
|
||||
bizLogger.warn("未找到对应的 审核卡片Id: notificationCardId={}", outTrackId);
|
||||
}
|
||||
@ -118,6 +125,28 @@ public class CallbackController {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendAuditCompleteNotification(ProcessOrderPro order) {
|
||||
try {
|
||||
String notifyUserIds = dingTalkProperties.getSyncK3NotifyUserIds();
|
||||
if (notifyUserIds == null || notifyUserIds.trim().isEmpty()) {
|
||||
bizLogger.warn("未配置审核完成通知人(syncK3NotifyUserIds)");
|
||||
return;
|
||||
}
|
||||
|
||||
String message = String.format("项目【%s】工艺审核已完成,可以上传金蝶了!", order.getProductionOrderNo());
|
||||
String[] userIds = notifyUserIds.split(",");
|
||||
|
||||
for (String userId : userIds) {
|
||||
if (userId != null && !userId.trim().isEmpty()) {
|
||||
robotGroupService.sendMarkdownToUser(userId.trim(), "项目审核完成通知", message);
|
||||
bizLogger.info("已发送审核完成通知给用户: {}", userId.trim());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
bizLogger.error("发送审核完成通知异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理具体事件逻辑
|
||||
*/
|
||||
|
||||
@ -49,8 +49,8 @@ spring:
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
#url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
#url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: root
|
||||
# 从库数据源
|
||||
@ -58,16 +58,16 @@ spring:
|
||||
lazy: true
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
#url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
#url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: root
|
||||
sqlserver:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
|
||||
url: jdbc:sqlserver://192.168.5.8:14330;databaseName=AIS20241010133631;encrypt=false;trustServerCertificate=true
|
||||
url: jdbc:sqlserver://192.168.5.9:1433;databaseName=AIS20241010133631;encrypt=false;trustServerCertificate=true
|
||||
username: sa
|
||||
password: 1a!
|
||||
password: Hbyt@2026
|
||||
hikari:
|
||||
# 最大连接池数量
|
||||
maxPoolSize: 20
|
||||
|
||||
@ -52,8 +52,8 @@ spring:
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
# url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
# url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: root
|
||||
# 从库数据源
|
||||
@ -61,16 +61,16 @@ spring:
|
||||
lazy: true
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
#url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
#url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: root
|
||||
sqlserver:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
|
||||
url: jdbc:sqlserver://192.168.5.8:14330;databaseName=AIS20241010133631;encrypt=false;trustServerCertificate=true
|
||||
url: jdbc:sqlserver://192.168.5.9:1433;databaseName=AIS20241010133631;encrypt=false;trustServerCertificate=true
|
||||
username: sa
|
||||
password: 1a!
|
||||
password: Hbyt@2026
|
||||
# oracle:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: oracle.jdbc.OracleDriver
|
||||
|
||||
@ -29,7 +29,7 @@ server:
|
||||
port: 8033
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /dev-api
|
||||
context-path: /
|
||||
undertow:
|
||||
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
|
||||
max-http-post-size: -1
|
||||
@ -151,6 +151,10 @@ security:
|
||||
- /system/orderPro/**
|
||||
- /system/cost/**
|
||||
- /ding/callback/**
|
||||
- /system/material/receive
|
||||
- /system/material/getObtainPartData2
|
||||
- /system/operationPlanningMain/sync
|
||||
- /system/operationPlanningMain/cascadeList
|
||||
|
||||
# MyBatisPlus配置
|
||||
# https://baomidou.com/config/
|
||||
@ -303,3 +307,5 @@ dingtalk:
|
||||
callback-route-key: update_route_status2
|
||||
notify-user-id: 0754423610937470
|
||||
route-approval-id: 01201038164023402380
|
||||
plan-user-id: 313514580637707245,192530665123549546
|
||||
sync-k3-notify-user-ids: 313514580637707245,351225426738742993,192530665123549546
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#??ID-PROD
|
||||
X-KDApi-AcctID = 698ad0d6c0c3fe
|
||||
X-KDApi-AcctID = 69d0e0f3a5801e
|
||||
X-KDApi-UserName = Administrator
|
||||
#??IDID
|
||||
X-KDApi-AppID = 288012_Rc0C0zCG2lga0/Vs2Y4pzYSL6hQcWOko
|
||||
|
||||
@ -284,6 +284,11 @@
|
||||
<artifactId>app-stream-client</artifactId>
|
||||
<version>1.3.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@ -57,6 +57,10 @@ public class DingTalkProperties {
|
||||
* 工艺卡片模版id
|
||||
*/
|
||||
private String routeCardTemplateId;
|
||||
/**
|
||||
* 工艺计划接收人
|
||||
*/
|
||||
private String planUserId;
|
||||
|
||||
|
||||
|
||||
@ -64,11 +68,16 @@ public class DingTalkProperties {
|
||||
* 接收工艺表格个人用户ID
|
||||
*/
|
||||
private String notifyUserId;
|
||||
/**
|
||||
/**
|
||||
* 接收工艺审批用户ID
|
||||
*/
|
||||
private String routeApprovalId;
|
||||
|
||||
/**
|
||||
* 项目审核完成后通知上传金蝶的用户ID(多个用户ID用逗号分隔)
|
||||
*/
|
||||
private String syncK3NotifyUserIds;
|
||||
|
||||
/**
|
||||
* 回调路由 Key
|
||||
*/
|
||||
|
||||
@ -190,22 +190,12 @@ public class ExcelTemplateProc {
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加调试信息:显示静态数据处理
|
||||
if (dataPrefix == null || dataPrefix.isEmpty()) {
|
||||
System.out.println("开始处理静态数据行: " + row.getRowNum() + ", dataMap=" + dataMap.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (int i = firstCellNum; i < lastCellNum; i++) {
|
||||
XSSFCell cell = row.getCell(i);
|
||||
if (cell != null) {
|
||||
// 添加调试信息:检查静态数据处理
|
||||
if (dataPrefix == null || dataPrefix.isEmpty()) {
|
||||
String cellValue = cell.getStringCellValue();
|
||||
if (cellValue != null && cellValue.contains("{{")) {
|
||||
System.out.println("处理静态数据行,单元格" + i + ": " + cellValue);
|
||||
}
|
||||
}
|
||||
fillInTemplateCellDataValue(cell, dataPrefix, dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FtpDownloadTest {
|
||||
|
||||
@Test
|
||||
public void testDownload() {
|
||||
String host = "192.168.5.18";
|
||||
int port = 21; // 使用21端口测试下载,根据你之前代F码的默认行为,一般下载用21
|
||||
String username = "admin";
|
||||
String password = "hbyt2025";
|
||||
|
||||
// 你的实际测试路径
|
||||
String remoteDir = "/2026/QB-26-072-XCL"; // 你想测试下载的 FTP 目录
|
||||
String localDir = "D:\\file\\test_download"; // 本地存放目录
|
||||
|
||||
System.out.println("开始测试下载 FTP 目录: " + remoteDir);
|
||||
|
||||
// 调用 FtpUtil 下载文件
|
||||
R<String> result = FtpUtil.downloadFtpDirectoryFiles(
|
||||
host, username, password, port, remoteDir, localDir, null
|
||||
);
|
||||
|
||||
System.out.println("下载结果 Code: " + result.getCode());
|
||||
System.out.println("下载结果 Msg: " + result.getMsg());
|
||||
System.out.println("下载结果 Data: " + result.getData());
|
||||
}
|
||||
}
|
||||
@ -43,6 +43,7 @@ public class SecurityConfig {
|
||||
.antMatchers("/dev-api/system/material/list/").anonymous()
|
||||
.antMatchers("/dev-api/system/dict/data/type/**").anonymous()
|
||||
.antMatchers("/dev-api/index/list/").anonymous()
|
||||
.antMatchers("/dev-api/system/material/").anonymous()
|
||||
.antMatchers("/jmreport/**").anonymous()
|
||||
.antMatchers(adminContextPath + "/assets/**"
|
||||
, adminContextPath + "/login"
|
||||
|
||||
@ -2260,6 +2260,9 @@ public class BomDetailsController extends BaseController {
|
||||
List<BomDetails> list = new ArrayList<>();
|
||||
if (rows != null) {
|
||||
for (ElectricalMaterialBomVO bomVO : rows) {
|
||||
if(bomVO.getRemark().equals("研发已领用")|| bomVO.getRemark().contains("领用")){
|
||||
continue;
|
||||
}
|
||||
BomDetails bomDetails = new BomDetails();
|
||||
bomDetails.setTotalWeight(bomVO.getProductionOrderNo());
|
||||
bomDetails.setFNumber(bomVO.getParentDrawingNo());
|
||||
|
||||
@ -27,6 +27,7 @@ import com.ruoyi.system.domain.bo.ImMaterialBo;
|
||||
import com.ruoyi.system.domain.vo.ImMaterialVo;
|
||||
import com.ruoyi.system.mapper.ImMaterialMapper;
|
||||
import com.ruoyi.system.service.IImMaterialService;
|
||||
import com.ruoyi.system.service.IPartCostService;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
@ -47,6 +48,12 @@ import java.util.*;
|
||||
|
||||
import static com.ruoyi.common.core.mapper.BaseMapperPlus.log;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.system.domain.ImMaterial;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 物料检索
|
||||
*
|
||||
@ -57,12 +64,56 @@ import static com.ruoyi.common.core.mapper.BaseMapperPlus.log;
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/material")
|
||||
@Slf4j
|
||||
public class ImMaterialController extends BaseController {
|
||||
|
||||
private final IImMaterialService iImMaterialService;
|
||||
@Resource
|
||||
private final ImMaterialMapper imMaterialMapper;
|
||||
|
||||
private final IPartCostService iPartCostService;
|
||||
/**
|
||||
* 金蝶物料保存 → 自动推送到此接口
|
||||
* SaIgnore 注解用于开放此接口,无需 token 验证
|
||||
*/
|
||||
@SaIgnore
|
||||
@PostMapping(value = "/receive", consumes = "application/x-www-form-urlencoded;charset=UTF-8")
|
||||
public String receiveMaterial(
|
||||
@RequestParam String materialCode, @RequestParam String materialName, @RequestParam(required = false) String specification,
|
||||
@RequestParam(required = false) String materialType, @RequestParam(required = false) String baseUnit,
|
||||
@RequestParam(required = false) String createTime, @RequestParam(required = false) String enabled) {
|
||||
try {
|
||||
log.info("收到金蝶物料:code={}, name={}, spec={}, type={}, unit={}, time={}, enabled={}",
|
||||
materialCode, materialName, specification, materialType, baseUnit, createTime, enabled);
|
||||
|
||||
if (materialCode != null && !materialCode.isEmpty()) {
|
||||
// TODO: 根据实际推送的数据进行处理
|
||||
// 示例:
|
||||
// // 检查是否存在
|
||||
// LambdaQueryWrapper<ImMaterial> wrapper = new LambdaQueryWrapper<>();
|
||||
// wrapper.eq(ImMaterial::getCode, materialCode);
|
||||
// ImMaterial material = imMaterialMapper.selectOne(wrapper);
|
||||
// if (material == null) {
|
||||
// // 新增
|
||||
// material = new ImMaterial();
|
||||
// material.setCode(materialCode);
|
||||
// material.setName(materialName);
|
||||
// // 设置其他必要字段...
|
||||
// imMaterialMapper.insert(material);
|
||||
// } else {
|
||||
// // 更新
|
||||
// material.setName(materialName);
|
||||
// imMaterialMapper.updateById(material);
|
||||
// }
|
||||
}
|
||||
|
||||
return "success";
|
||||
} catch (Exception e) {
|
||||
log.error("处理金蝶推送物料失败", e);
|
||||
return "fail";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询物料检索列表
|
||||
*/
|
||||
@ -434,4 +485,11 @@ public class ImMaterialController extends BaseController {
|
||||
public TableDataInfo search(@RequestParam(value = "query", required = false) String query) {
|
||||
return iImMaterialService.search(query);
|
||||
}
|
||||
@SaIgnore
|
||||
@PostMapping("/getObtainPartData2")
|
||||
public R<Void> getObtainPartData() {
|
||||
log.info("接口进来了");
|
||||
return toAjax(iPartCostService.getObtainPartData());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -114,7 +114,6 @@ public class PartCostController extends BaseController {
|
||||
@Log(title = "在金蝶获取成本价", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/getObtainPartData")
|
||||
@XxlJob("getObtainPartData")
|
||||
public R<Void> getObtainPartData() {
|
||||
return toAjax(iPartCostService.getObtainPartData());
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -92,6 +92,12 @@ public class ProcessRouteController extends BaseController {
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private com.ruoyi.common.dingding.robot.RobotGroupService robotGroupService;
|
||||
|
||||
@Autowired
|
||||
private com.ruoyi.common.dingding.media.MediaService mediaService;
|
||||
|
||||
private Long generateUniqueParentId(Long originalId) {
|
||||
return originalId + 1000;
|
||||
}
|
||||
@ -407,6 +413,21 @@ public class ProcessRouteController extends BaseController {
|
||||
return R.fail("项目 " + productionOrderNo + "已导入过工艺 ,请先清空再上传");
|
||||
}
|
||||
if (iProcessRouteService.saveData(list1, list)) {
|
||||
// 保存临时文件用于发送给指定用户
|
||||
File tempFile = null;
|
||||
try {
|
||||
String tempDirPath = "D:/file/";
|
||||
File tempDir = new File(tempDirPath);
|
||||
if (!tempDir.exists()) {
|
||||
tempDir.mkdirs();
|
||||
}
|
||||
tempFile = new File(tempDirPath + System.currentTimeMillis() + "_" + originalFilename);
|
||||
file.transferTo(tempFile);
|
||||
} catch (Exception e) {
|
||||
log.error("保存上传临时文件失败", e);
|
||||
}
|
||||
final File uploadFile = tempFile;
|
||||
|
||||
// 获取当前登录用户昵称(需要在异步线程前获取)
|
||||
String currentUserNickName = "未知用户";
|
||||
try {
|
||||
@ -421,9 +442,39 @@ public class ProcessRouteController extends BaseController {
|
||||
log.warn("获取当前登录用户昵称失败", e);
|
||||
}
|
||||
|
||||
// 异步发送钉钉卡片
|
||||
// 异步发送钉钉卡片和文件
|
||||
String finalNickName = currentUserNickName;
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
if (uploadFile != null && uploadFile.exists()) {
|
||||
String planUserIds = dingTalkProperties.getPlanUserId();
|
||||
if (StringUtils.isNotBlank(planUserIds)) {
|
||||
String mediaId = mediaService.uploadMedia("file", uploadFile.getAbsolutePath());
|
||||
// 支持多人推送,假设多个 userId 以逗号分隔
|
||||
String[] userIds = planUserIds.split(",");
|
||||
for (String userId : userIds) {
|
||||
String cleanUserId = userId.trim();
|
||||
if (StringUtils.isNotBlank(cleanUserId)) {
|
||||
try {
|
||||
robotGroupService.sendFileToUser(cleanUserId, mediaId, originalFilename);
|
||||
log.info("上传的工艺计划表已成功发送给用户: {}", cleanUserId);
|
||||
} catch (Exception e) {
|
||||
log.error("发送文件给用户 {} 异常", cleanUserId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("未配置钉钉工艺计划接收人(planUserId),跳过发送文件");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送文件给工艺计划接收人异常", e);
|
||||
} finally {
|
||||
if (uploadFile != null && uploadFile.exists()) {
|
||||
uploadFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ProcessOrderPro order = processOrderProService.selectByProjectNumber(productionOrderNo);
|
||||
if (order != null) {
|
||||
@ -443,13 +494,13 @@ public class ProcessRouteController extends BaseController {
|
||||
DingUtil.createAndDeliverCardToUser(
|
||||
accessToken,
|
||||
dingTalkProperties.getRouteCardTemplateId(),
|
||||
Collections.singletonList(dingTalkProperties.getNotifyUserId()),
|
||||
Collections.singletonList(dingTalkProperties.getRouteApprovalId()),
|
||||
outTrackId,
|
||||
cardDataMap,
|
||||
dingTalkProperties.getCallbackRouteKey()
|
||||
);
|
||||
// 统一存入带 userId 后缀的完整 outTrackId,以便和钉钉回调保持一致
|
||||
String fullOutTrackId = outTrackId + "_" + dingTalkProperties.getNotifyUserId();
|
||||
String fullOutTrackId = outTrackId + "_" + dingTalkProperties.getRouteApprovalId();
|
||||
// 更新数据库中的AuditCardId
|
||||
ProcessOrderPro update = new ProcessOrderPro();
|
||||
update.setId(order.getId());
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import com.ruoyi.common.annotation.RepeatSubmit;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningDetailVo;
|
||||
import com.ruoyi.system.domain.bo.SfcOperationPlanningDetailBo;
|
||||
import com.ruoyi.system.service.ISfcOperationPlanningDetailService;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 工序计划明细
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/operationPlanningDetail")
|
||||
public class SfcOperationPlanningDetailController extends BaseController {
|
||||
|
||||
private final ISfcOperationPlanningDetailService iSfcOperationPlanningDetailService;
|
||||
|
||||
/**
|
||||
* 查询工序计划明细列表
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningDetail:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SfcOperationPlanningDetailVo> list(SfcOperationPlanningDetailBo bo, PageQuery pageQuery) {
|
||||
return iSfcOperationPlanningDetailService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工序计划明细列表
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningDetail:export")
|
||||
@Log(title = "工序计划明细", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(SfcOperationPlanningDetailBo bo, HttpServletResponse response) {
|
||||
List<SfcOperationPlanningDetailVo> list = iSfcOperationPlanningDetailService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "工序计划明细", SfcOperationPlanningDetailVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工序计划明细详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningDetail:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<SfcOperationPlanningDetailVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(iSfcOperationPlanningDetailService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工序计划明细
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningDetail:add")
|
||||
@Log(title = "工序计划明细", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody SfcOperationPlanningDetailBo bo) {
|
||||
return toAjax(iSfcOperationPlanningDetailService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工序计划明细
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningDetail:edit")
|
||||
@Log(title = "工序计划明细", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SfcOperationPlanningDetailBo bo) {
|
||||
return toAjax(iSfcOperationPlanningDetailService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工序计划明细
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningDetail:remove")
|
||||
@Log(title = "工序计划明细", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(iSfcOperationPlanningDetailService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import com.ruoyi.common.annotation.RepeatSubmit;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningMainVo;
|
||||
import com.ruoyi.system.domain.bo.SfcOperationPlanningMainBo;
|
||||
import com.ruoyi.system.service.ISfcOperationPlanningMainService;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
|
||||
import com.ruoyi.system.domain.dto.OperationPlanResultDTO;
|
||||
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
|
||||
/**
|
||||
* 工序计划主
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/operationPlanningMain")
|
||||
public class SfcOperationPlanningMainController extends BaseController {
|
||||
|
||||
private final ISfcOperationPlanningMainService iSfcOperationPlanningMainService;
|
||||
|
||||
/**
|
||||
* 同步工序计划数据
|
||||
*/
|
||||
@SaIgnore
|
||||
//@SaCheckPermission("system:operationPlanningMain:sync")
|
||||
@Log(title = "工序计划数据同步", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/sync")
|
||||
public R<Void> syncData() {
|
||||
return toAjax(iSfcOperationPlanningMainService.syncOperationPlanning());
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询工序计划主及明细级联数据
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/cascadeList")
|
||||
public TableDataInfo<OperationPlanResultDTO> cascadeList(SfcOperationPlanningMainBo bo, PageQuery pageQuery) {
|
||||
return iSfcOperationPlanningMainService.queryCascadePageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工序计划主列表
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningMain:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SfcOperationPlanningMainVo> list(SfcOperationPlanningMainBo bo, PageQuery pageQuery) {
|
||||
return iSfcOperationPlanningMainService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工序计划主列表
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningMain:export")
|
||||
@Log(title = "工序计划主", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(SfcOperationPlanningMainBo bo, HttpServletResponse response) {
|
||||
List<SfcOperationPlanningMainVo> list = iSfcOperationPlanningMainService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "工序计划主", SfcOperationPlanningMainVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工序计划主详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningMain:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<SfcOperationPlanningMainVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(iSfcOperationPlanningMainService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工序计划主
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningMain:add")
|
||||
@Log(title = "工序计划主", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody SfcOperationPlanningMainBo bo) {
|
||||
return toAjax(iSfcOperationPlanningMainService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工序计划主
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningMain:edit")
|
||||
@Log(title = "工序计划主", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SfcOperationPlanningMainBo bo) {
|
||||
return toAjax(iSfcOperationPlanningMainService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工序计划主
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:operationPlanningMain:remove")
|
||||
@Log(title = "工序计划主", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(iSfcOperationPlanningMainService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||||
}
|
||||
}
|
||||
@ -89,4 +89,9 @@ public class FigureSave extends BaseEntity {
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date drawTime;
|
||||
/**
|
||||
* 上传时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date uploadTime;
|
||||
}
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class ProcessRouteExcelDTO1 {
|
||||
|
||||
@ExcelProperty(index = 0)
|
||||
private String materialCode;
|
||||
|
||||
@ExcelProperty(index = 1)
|
||||
private String materialName;
|
||||
|
||||
@ExcelProperty(index = 2)
|
||||
private String material;
|
||||
|
||||
@ExcelProperty(index = 3)
|
||||
private Double discWeight;
|
||||
|
||||
@ExcelProperty(index = 4)
|
||||
private String rawMaterialCode;
|
||||
|
||||
@ExcelProperty(index = 5)
|
||||
private String rawMaterialName;
|
||||
|
||||
@ExcelProperty(index = 6)
|
||||
private String bomMaterial;
|
||||
|
||||
@ExcelProperty(index = 7)
|
||||
private Double bomDanZhong;
|
||||
|
||||
@ExcelProperty(index = 8)
|
||||
private String discUsage;
|
||||
|
||||
@ExcelProperty(index = 9)
|
||||
private String bomUnit;
|
||||
|
||||
@ExcelProperty(index = 10)
|
||||
private Long processNo;
|
||||
|
||||
@ExcelProperty(index = 11)
|
||||
private String workCenter;
|
||||
|
||||
@ExcelProperty(index = 12)
|
||||
private String processName;
|
||||
|
||||
@ExcelProperty(index = 13)
|
||||
private String processDescription;
|
||||
|
||||
@ExcelProperty(index = 14)
|
||||
private String processControl;
|
||||
|
||||
@ExcelProperty(index = 15)
|
||||
private Double activityDuration;
|
||||
|
||||
@ExcelProperty(index = 16)
|
||||
private String activityUnit;
|
||||
|
||||
@ExcelProperty(index = 17)
|
||||
private Double unitQuantity;
|
||||
|
||||
@ExcelProperty(index = 18)
|
||||
private Long batchQuantity;
|
||||
|
||||
@ExcelProperty(index = 19)
|
||||
private Date xuStartTime;
|
||||
|
||||
@ExcelProperty(index = 21)
|
||||
private Date xuEndTime;
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
/**
|
||||
* 工序计划明细对象 sfc_operation_planning_detail
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sfc_operation_planning_detail")
|
||||
public class SfcOperationPlanningDetail extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID=1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
/**
|
||||
* 关联主表ID(sfc_operation_planning_main.id)
|
||||
*/
|
||||
private Long mainId;
|
||||
/**
|
||||
* 主表FID冗余
|
||||
*/
|
||||
private Long fid;
|
||||
/**
|
||||
* 单据体内码(K3明细内码)
|
||||
*/
|
||||
private Long entityEntryId;
|
||||
/**
|
||||
* 工序号
|
||||
*/
|
||||
private Long operNumber;
|
||||
/**
|
||||
* 工序数量
|
||||
*/
|
||||
private BigDecimal operQty;
|
||||
/**
|
||||
* 合格数量
|
||||
*/
|
||||
private BigDecimal qualifiedQty;
|
||||
/**
|
||||
* 转出数量
|
||||
*/
|
||||
private BigDecimal transOutQty;
|
||||
/**
|
||||
* 废品数量
|
||||
*/
|
||||
private BigDecimal unqualifiedQty;
|
||||
/**
|
||||
* 损耗数量
|
||||
*/
|
||||
private BigDecimal wastageQty;
|
||||
/**
|
||||
* 转入数量
|
||||
*/
|
||||
private BigDecimal transInQty;
|
||||
/**
|
||||
* 加工车间
|
||||
*/
|
||||
private String departmentName;
|
||||
/**
|
||||
* 工作中心
|
||||
*/
|
||||
private String workCenterName;
|
||||
/**
|
||||
* 作业
|
||||
*/
|
||||
private String processName;
|
||||
/**
|
||||
* 作业人员
|
||||
*/
|
||||
private String operator;
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String operStatus;
|
||||
/**
|
||||
* 工序序列-计划开始时间
|
||||
*/
|
||||
private Date seqPlanStartTime;
|
||||
/**
|
||||
* 工序序列-计划完成时间
|
||||
*/
|
||||
private Date seqPlanFinishTime;
|
||||
/**
|
||||
* 工序控制码
|
||||
*/
|
||||
private String optCtrlCode;
|
||||
/**
|
||||
* 关键工序
|
||||
*/
|
||||
private String keyOper;
|
||||
/**
|
||||
* 活动一名称
|
||||
*/
|
||||
private String activityName;
|
||||
/**
|
||||
* 工序说明
|
||||
*/
|
||||
private String operDescription;
|
||||
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
/**
|
||||
* 工序计划主对象 sfc_operation_planning_main
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sfc_operation_planning_main")
|
||||
public class SfcOperationPlanningMain extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID=1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
/**
|
||||
* FID(K3单据内码)
|
||||
*/
|
||||
private Long fid;
|
||||
/**
|
||||
* 单据编号
|
||||
*/
|
||||
private String billNo;
|
||||
/**
|
||||
* 生产订单编号
|
||||
*/
|
||||
private String moNumber;
|
||||
/**
|
||||
* 生产令号
|
||||
*/
|
||||
private String hbytSclh;
|
||||
/**
|
||||
* 产品编码
|
||||
*/
|
||||
private String productCode;
|
||||
/**
|
||||
* 产品名称
|
||||
*/
|
||||
private String productName;
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
private String proSpecification;
|
||||
/**
|
||||
* 计划开始时间
|
||||
*/
|
||||
private Date planStartTime;
|
||||
/**
|
||||
* 计划结束时间
|
||||
*/
|
||||
private Date planFinishTime;
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
@ -26,7 +27,6 @@ public class FigureSaveBo extends BaseEntity {
|
||||
/**
|
||||
* 关联项目表
|
||||
*/
|
||||
@NotNull(message = "关联项目表不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long pid;
|
||||
|
||||
/**
|
||||
@ -50,7 +50,6 @@ public class FigureSaveBo extends BaseEntity {
|
||||
/**
|
||||
* 图纸路径
|
||||
*/
|
||||
@NotBlank(message = "图纸路径不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String drawPath;
|
||||
|
||||
/**
|
||||
@ -108,4 +107,7 @@ public class FigureSaveBo extends BaseEntity {
|
||||
*/
|
||||
@NotNull(message = "出图时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date drawTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date uploadTime;
|
||||
}
|
||||
|
||||
@ -0,0 +1,158 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
/**
|
||||
* 工序计划明细业务对象 sfc_operation_planning_detail
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SfcOperationPlanningDetailBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 关联主表ID(sfc_operation_planning_main.id)
|
||||
*/
|
||||
@NotNull(message = "关联主表ID(sfc_operation_planning_main.id)不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long mainId;
|
||||
|
||||
/**
|
||||
* 主表FID冗余
|
||||
*/
|
||||
@NotNull(message = "主表FID冗余不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long fid;
|
||||
|
||||
/**
|
||||
* 单据体内码(K3明细内码)
|
||||
*/
|
||||
@NotNull(message = "单据体内码(K3明细内码)不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long entityEntryId;
|
||||
|
||||
/**
|
||||
* 工序号
|
||||
*/
|
||||
@NotNull(message = "工序号不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long operNumber;
|
||||
|
||||
/**
|
||||
* 工序数量
|
||||
*/
|
||||
@NotNull(message = "工序数量不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private BigDecimal operQty;
|
||||
|
||||
/**
|
||||
* 合格数量
|
||||
*/
|
||||
@NotNull(message = "合格数量不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private BigDecimal qualifiedQty;
|
||||
|
||||
/**
|
||||
* 转出数量
|
||||
*/
|
||||
@NotNull(message = "转出数量不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private BigDecimal transOutQty;
|
||||
|
||||
/**
|
||||
* 废品数量
|
||||
*/
|
||||
@NotNull(message = "废品数量不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private BigDecimal unqualifiedQty;
|
||||
|
||||
/**
|
||||
* 损耗数量
|
||||
*/
|
||||
@NotNull(message = "损耗数量不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private BigDecimal wastageQty;
|
||||
|
||||
/**
|
||||
* 转入数量
|
||||
*/
|
||||
@NotNull(message = "转入数量不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private BigDecimal transInQty;
|
||||
|
||||
/**
|
||||
* 加工车间
|
||||
*/
|
||||
@NotBlank(message = "加工车间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String departmentName;
|
||||
|
||||
/**
|
||||
* 工作中心
|
||||
*/
|
||||
@NotBlank(message = "工作中心不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String workCenterName;
|
||||
|
||||
/**
|
||||
* 作业
|
||||
*/
|
||||
@NotBlank(message = "作业不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 作业人员
|
||||
*/
|
||||
@NotBlank(message = "作业人员不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@NotBlank(message = "状态不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String operStatus;
|
||||
|
||||
/**
|
||||
* 工序序列-计划开始时间
|
||||
*/
|
||||
@NotNull(message = "工序序列-计划开始时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date seqPlanStartTime;
|
||||
|
||||
/**
|
||||
* 工序序列-计划完成时间
|
||||
*/
|
||||
@NotNull(message = "工序序列-计划完成时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date seqPlanFinishTime;
|
||||
|
||||
/**
|
||||
* 工序控制码
|
||||
*/
|
||||
@NotBlank(message = "工序控制码不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String optCtrlCode;
|
||||
|
||||
/**
|
||||
* 关键工序
|
||||
*/
|
||||
@NotBlank(message = "关键工序不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String keyOper;
|
||||
|
||||
/**
|
||||
* 活动一名称
|
||||
*/
|
||||
@NotBlank(message = "活动一名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String activityName;
|
||||
|
||||
/**
|
||||
* 工序说明
|
||||
*/
|
||||
@NotBlank(message = "工序说明不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String operDescription;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
/**
|
||||
* 工序计划主业务对象 sfc_operation_planning_main
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SfcOperationPlanningMainBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* FID(K3单据内码)
|
||||
*/
|
||||
@NotNull(message = "FID(K3单据内码)不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long fid;
|
||||
|
||||
/**
|
||||
* 单据编号
|
||||
*/
|
||||
@NotBlank(message = "单据编号不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String billNo;
|
||||
|
||||
/**
|
||||
* 生产订单编号
|
||||
*/
|
||||
@NotBlank(message = "生产订单编号不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String moNumber;
|
||||
|
||||
/**
|
||||
* 生产令号
|
||||
*/
|
||||
@NotBlank(message = "生产令号不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String hbytSclh;
|
||||
|
||||
/**
|
||||
* 产品编码
|
||||
*/
|
||||
@NotBlank(message = "产品编码不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String productCode;
|
||||
|
||||
/**
|
||||
* 产品名称
|
||||
*/
|
||||
@NotBlank(message = "产品名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
@NotBlank(message = "规格型号不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String proSpecification;
|
||||
|
||||
/**
|
||||
* 计划开始时间
|
||||
*/
|
||||
@NotNull(message = "计划开始时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date planStartTime;
|
||||
|
||||
/**
|
||||
* 计划结束时间
|
||||
*/
|
||||
@NotNull(message = "计划结束时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Date planFinishTime;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package com.ruoyi.system.domain.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class OperationPlanResultDTO {
|
||||
|
||||
/**
|
||||
* 生产订单编号
|
||||
*/
|
||||
private String moNumber;
|
||||
|
||||
/**
|
||||
* 生产令号
|
||||
*/
|
||||
private String batchNo;
|
||||
|
||||
/**
|
||||
* 产品编码
|
||||
*/
|
||||
private String productCode;
|
||||
|
||||
/**
|
||||
* 产品名称
|
||||
*/
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* 单据编号
|
||||
*/
|
||||
private String billNo;
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
private String proSpecification;
|
||||
|
||||
/**
|
||||
* 计划开始时间
|
||||
*/
|
||||
private String planStartTime;
|
||||
|
||||
/**
|
||||
* 计划结束时间
|
||||
*/
|
||||
private String planFinishTime;
|
||||
|
||||
/**
|
||||
* FID
|
||||
*/
|
||||
private Long fid;
|
||||
|
||||
/**
|
||||
* 原生计划开始时间(内部排序用,不输出到JSON)
|
||||
*/
|
||||
@JsonIgnore
|
||||
private Date rawPlanStartTime;
|
||||
|
||||
/**
|
||||
* 工序列表
|
||||
*/
|
||||
private List<ProcessDetailDTO> processList;
|
||||
}
|
||||
@ -0,0 +1,176 @@
|
||||
package com.ruoyi.system.domain.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OperationPlanningDTO {
|
||||
|
||||
/**
|
||||
* FID
|
||||
*/
|
||||
@JsonProperty("FID")
|
||||
private Long fid;
|
||||
|
||||
/**
|
||||
* 单据体内码
|
||||
*/
|
||||
@JsonProperty("FENTITY_FEntryID")
|
||||
private Long fEntityFEntryId;
|
||||
|
||||
/**
|
||||
* 生产令号
|
||||
*/
|
||||
@JsonProperty("F_HBYT_SCLH")
|
||||
private String fHbytSclh;
|
||||
|
||||
/**
|
||||
* 生产订单编号
|
||||
*/
|
||||
@JsonProperty("FMONumber")
|
||||
private String fMoNumber;
|
||||
|
||||
/**
|
||||
* 产品编码
|
||||
*/
|
||||
@JsonProperty("FProductId.FNumber")
|
||||
private String fProductIdFNumber;
|
||||
|
||||
/**
|
||||
* 产品名称
|
||||
*/
|
||||
@JsonProperty("FProductName")
|
||||
private String fProductName;
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
@JsonProperty("FProSpecification")
|
||||
private String fProSpecification;
|
||||
|
||||
/**
|
||||
* 单据编号
|
||||
*/
|
||||
@JsonProperty("FBillNo")
|
||||
private String fBillNo;
|
||||
|
||||
/**
|
||||
* 工序号
|
||||
*/
|
||||
@JsonProperty("FOperNumber")
|
||||
private Integer fOperNumber;
|
||||
|
||||
/**
|
||||
* 工序数量
|
||||
*/
|
||||
@JsonProperty("FOperQty")
|
||||
private Double fOperQty;
|
||||
|
||||
/**
|
||||
* 合格数量
|
||||
*/
|
||||
@JsonProperty("FQualifiedQty")
|
||||
private Double fQualifiedQty;
|
||||
|
||||
/**
|
||||
* 转出数量
|
||||
*/
|
||||
@JsonProperty("FTransOutQty")
|
||||
private Double fTransOutQty;
|
||||
|
||||
/**
|
||||
* 废品数量
|
||||
*/
|
||||
@JsonProperty("FUnqualifiedQty")
|
||||
private Double fUnqualifiedQty;
|
||||
|
||||
/**
|
||||
* 损耗数量
|
||||
*/
|
||||
@JsonProperty("FWastageQty")
|
||||
private Double fWastageQty;
|
||||
|
||||
/**
|
||||
* 转入数量
|
||||
*/
|
||||
@JsonProperty("FTransInQty")
|
||||
private Double fTransInQty;
|
||||
|
||||
/**
|
||||
* 加工车间
|
||||
*/
|
||||
@JsonProperty("FDepartmentId.FName")
|
||||
private String fDepartmentIdFName;
|
||||
|
||||
/**
|
||||
* 工作中心
|
||||
*/
|
||||
@JsonProperty("FWorkCenterId.FName")
|
||||
private String fWorkCenterIdFName;
|
||||
|
||||
/**
|
||||
* 作业
|
||||
*/
|
||||
@JsonProperty("FProcessId.FName")
|
||||
private String fProcessIdFName;
|
||||
|
||||
/**
|
||||
* 作业人员
|
||||
*/
|
||||
@JsonProperty("F_HBYT_ZYRY")
|
||||
private String fHbytZyry;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@JsonProperty("FOperStatus")
|
||||
private String fOperStatus;
|
||||
|
||||
/**
|
||||
* 工序序列-计划开始时间
|
||||
*/
|
||||
@JsonProperty("FSeqPlanStartTime")
|
||||
private String fSeqPlanStartTime;
|
||||
|
||||
/**
|
||||
* 工序序列-计划完成时间
|
||||
*/
|
||||
@JsonProperty("FSeqPlanFinishTime")
|
||||
private String fSeqPlanFinishTime;
|
||||
|
||||
/**
|
||||
* 计划开始时间
|
||||
*/
|
||||
@JsonProperty("FOperPlanStartTime")
|
||||
private String fOperPlanStartTime;
|
||||
|
||||
/**
|
||||
* 计划结束时间
|
||||
*/
|
||||
@JsonProperty("FOperPlanFinishTime")
|
||||
private String fOperPlanFinishTime;
|
||||
|
||||
/**
|
||||
* 工序控制码
|
||||
*/
|
||||
@JsonProperty("FOptCtrlCodeId.FName")
|
||||
private String fOptCtrlCodeIdFName;
|
||||
|
||||
/**
|
||||
* 关键工序
|
||||
*/
|
||||
@JsonProperty("FKeyOper")
|
||||
private String fKeyOper;
|
||||
|
||||
/**
|
||||
* 活动一名称
|
||||
*/
|
||||
@JsonProperty("FActivity1Name")
|
||||
private String fActivity1Name;
|
||||
|
||||
/**
|
||||
* 工序说明
|
||||
*/
|
||||
@JsonProperty("FOperDescription")
|
||||
private String fOperDescription;
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package com.ruoyi.system.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ProcessDetailDTO {
|
||||
|
||||
/**
|
||||
* 工序号
|
||||
*/
|
||||
private Integer operNo;
|
||||
|
||||
/**
|
||||
* 作业/工序名称
|
||||
*/
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 工作中心
|
||||
*/
|
||||
private String workCenter;
|
||||
|
||||
/**
|
||||
* 计划/工序数量
|
||||
*/
|
||||
private Double planQty;
|
||||
|
||||
/**
|
||||
* 转入数量
|
||||
*/
|
||||
private Double inQty;
|
||||
|
||||
/**
|
||||
* 合格数量
|
||||
*/
|
||||
private Double okQty;
|
||||
|
||||
/**
|
||||
* 废品数量
|
||||
*/
|
||||
private Double ngQty;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
/**
|
||||
* 转出数量
|
||||
*/
|
||||
private Double transOutQty;
|
||||
|
||||
/**
|
||||
* 损耗数量
|
||||
*/
|
||||
private Double wastageQty;
|
||||
|
||||
/**
|
||||
* 加工车间
|
||||
*/
|
||||
private String department;
|
||||
|
||||
/**
|
||||
* 作业人员
|
||||
*/
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 工序序列-计划开始时间
|
||||
*/
|
||||
private String seqPlanStartTime;
|
||||
|
||||
/**
|
||||
* 工序序列-计划完成时间
|
||||
*/
|
||||
private String seqPlanFinishTime;
|
||||
|
||||
/**
|
||||
* 工序控制码
|
||||
*/
|
||||
private String optCtrlCode;
|
||||
|
||||
/**
|
||||
* 关键工序
|
||||
*/
|
||||
private String keyOper;
|
||||
|
||||
/**
|
||||
* 活动一名称
|
||||
*/
|
||||
private String activity1Name;
|
||||
|
||||
/**
|
||||
* 工序说明
|
||||
*/
|
||||
private String operDescription;
|
||||
|
||||
/**
|
||||
* 单据体内码
|
||||
*/
|
||||
private Long entityEntryId;
|
||||
}
|
||||
@ -2,6 +2,7 @@ package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
@ -104,4 +105,7 @@ public class FigureSaveVo {
|
||||
*/
|
||||
@ExcelProperty(value = "出图时间")
|
||||
private Date drawTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date uploadTime;
|
||||
}
|
||||
|
||||
@ -0,0 +1,158 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.ruoyi.common.annotation.ExcelDictFormat;
|
||||
import com.ruoyi.common.convert.ExcelDictConvert;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 工序计划明细视图对象 sfc_operation_planning_detail
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class SfcOperationPlanningDetailVo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 关联主表ID(sfc_operation_planning_main.id)
|
||||
*/
|
||||
@ExcelProperty(value = "关联主表ID(sfc_operation_planning_main.id)")
|
||||
private Long mainId;
|
||||
|
||||
/**
|
||||
* 主表FID冗余
|
||||
*/
|
||||
@ExcelProperty(value = "主表FID冗余")
|
||||
private Long fid;
|
||||
|
||||
/**
|
||||
* 单据体内码(K3明细内码)
|
||||
*/
|
||||
@ExcelProperty(value = "单据体内码(K3明细内码)")
|
||||
private Long entityEntryId;
|
||||
|
||||
/**
|
||||
* 工序号
|
||||
*/
|
||||
@ExcelProperty(value = "工序号")
|
||||
private Long operNumber;
|
||||
|
||||
/**
|
||||
* 工序数量
|
||||
*/
|
||||
@ExcelProperty(value = "工序数量")
|
||||
private BigDecimal operQty;
|
||||
|
||||
/**
|
||||
* 合格数量
|
||||
*/
|
||||
@ExcelProperty(value = "合格数量")
|
||||
private BigDecimal qualifiedQty;
|
||||
|
||||
/**
|
||||
* 转出数量
|
||||
*/
|
||||
@ExcelProperty(value = "转出数量")
|
||||
private BigDecimal transOutQty;
|
||||
|
||||
/**
|
||||
* 废品数量
|
||||
*/
|
||||
@ExcelProperty(value = "废品数量")
|
||||
private BigDecimal unqualifiedQty;
|
||||
|
||||
/**
|
||||
* 损耗数量
|
||||
*/
|
||||
@ExcelProperty(value = "损耗数量")
|
||||
private BigDecimal wastageQty;
|
||||
|
||||
/**
|
||||
* 转入数量
|
||||
*/
|
||||
@ExcelProperty(value = "转入数量")
|
||||
private BigDecimal transInQty;
|
||||
|
||||
/**
|
||||
* 加工车间
|
||||
*/
|
||||
@ExcelProperty(value = "加工车间")
|
||||
private String departmentName;
|
||||
|
||||
/**
|
||||
* 工作中心
|
||||
*/
|
||||
@ExcelProperty(value = "工作中心")
|
||||
private String workCenterName;
|
||||
|
||||
/**
|
||||
* 作业
|
||||
*/
|
||||
@ExcelProperty(value = "作业")
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 作业人员
|
||||
*/
|
||||
@ExcelProperty(value = "作业人员")
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@ExcelProperty(value = "状态")
|
||||
private String operStatus;
|
||||
|
||||
/**
|
||||
* 工序序列-计划开始时间
|
||||
*/
|
||||
@ExcelProperty(value = "工序序列-计划开始时间")
|
||||
private Date seqPlanStartTime;
|
||||
|
||||
/**
|
||||
* 工序序列-计划完成时间
|
||||
*/
|
||||
@ExcelProperty(value = "工序序列-计划完成时间")
|
||||
private Date seqPlanFinishTime;
|
||||
|
||||
/**
|
||||
* 工序控制码
|
||||
*/
|
||||
@ExcelProperty(value = "工序控制码")
|
||||
private String optCtrlCode;
|
||||
|
||||
/**
|
||||
* 关键工序
|
||||
*/
|
||||
@ExcelProperty(value = "关键工序")
|
||||
private String keyOper;
|
||||
|
||||
/**
|
||||
* 活动一名称
|
||||
*/
|
||||
@ExcelProperty(value = "活动一名称")
|
||||
private String activityName;
|
||||
|
||||
/**
|
||||
* 工序说明
|
||||
*/
|
||||
@ExcelProperty(value = "工序说明")
|
||||
private String operDescription;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.ruoyi.common.annotation.ExcelDictFormat;
|
||||
import com.ruoyi.common.convert.ExcelDictConvert;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 工序计划主视图对象 sfc_operation_planning_main
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class SfcOperationPlanningMainVo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* FID(K3单据内码)
|
||||
*/
|
||||
@ExcelProperty(value = "FID(K3单据内码)")
|
||||
private Long fid;
|
||||
|
||||
/**
|
||||
* 单据编号
|
||||
*/
|
||||
@ExcelProperty(value = "单据编号")
|
||||
private String billNo;
|
||||
|
||||
/**
|
||||
* 生产订单编号
|
||||
*/
|
||||
@ExcelProperty(value = "生产订单编号")
|
||||
private String moNumber;
|
||||
|
||||
/**
|
||||
* 生产令号
|
||||
*/
|
||||
@ExcelProperty(value = "生产令号")
|
||||
private String hbytSclh;
|
||||
|
||||
/**
|
||||
* 产品编码
|
||||
*/
|
||||
@ExcelProperty(value = "产品编码")
|
||||
private String productCode;
|
||||
|
||||
/**
|
||||
* 产品名称
|
||||
*/
|
||||
@ExcelProperty(value = "产品名称")
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
@ExcelProperty(value = "规格型号")
|
||||
private String proSpecification;
|
||||
|
||||
/**
|
||||
* 计划开始时间
|
||||
*/
|
||||
@ExcelProperty(value = "计划开始时间")
|
||||
private Date planStartTime;
|
||||
|
||||
/**
|
||||
* 计划结束时间
|
||||
*/
|
||||
@ExcelProperty(value = "计划结束时间")
|
||||
private Date planFinishTime;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.ruoyi.system.job;
|
||||
|
||||
import com.ruoyi.system.service.ISfcOperationPlanningMainService;
|
||||
import com.xxl.job.core.context.XxlJobHelper;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 工序计划定时任务
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SfcOperationPlanningJob {
|
||||
|
||||
private final ISfcOperationPlanningMainService iSfcOperationPlanningMainService;
|
||||
|
||||
/**
|
||||
* 定时同步K3工序计划数据
|
||||
* XXL-JOB 定时任务入口
|
||||
*/
|
||||
@XxlJob("syncOperationPlanningJobHandler")
|
||||
public void syncOperationPlanningJobHandler() throws Exception {
|
||||
XxlJobHelper.log("XXL-JOB, 开始同步K3工序计划数据...");
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
Boolean result = iSfcOperationPlanningMainService.syncOperationPlanning();
|
||||
if (result) {
|
||||
XxlJobHelper.log("XXL-JOB, 同步K3工序计划数据成功!耗时: {}ms", (System.currentTimeMillis() - startTime));
|
||||
XxlJobHelper.handleSuccess("同步成功");
|
||||
} else {
|
||||
XxlJobHelper.log("XXL-JOB, 同步K3工序计划数据失败!");
|
||||
XxlJobHelper.handleFail("同步失败,请查看系统日志");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("同步K3工序计划数据异常", e);
|
||||
XxlJobHelper.log("XXL-JOB, 同步K3工序计划数据异常: {}", e.getMessage());
|
||||
XxlJobHelper.handleFail("同步异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.system.domain.SfcOperationPlanningDetail;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningDetailVo;
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 工序计划明细Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
public interface SfcOperationPlanningDetailMapper extends BaseMapperPlus<SfcOperationPlanningDetailMapper, SfcOperationPlanningDetail, SfcOperationPlanningDetailVo> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.system.domain.SfcOperationPlanningMain;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningMainVo;
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 工序计划主Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
public interface SfcOperationPlanningMainMapper extends BaseMapperPlus<SfcOperationPlanningMainMapper, SfcOperationPlanningMain, SfcOperationPlanningMainVo> {
|
||||
|
||||
}
|
||||
@ -3659,4 +3659,147 @@ public class JdUtil {
|
||||
return plannedProcessList; // 返回结果
|
||||
}
|
||||
|
||||
/**
|
||||
* SFC_OperationPlanning查询
|
||||
* @return
|
||||
*/
|
||||
public static List<OperationPlanningDTO> getOperationPlanningList() {
|
||||
|
||||
K3CloudApi client = new K3CloudApi();
|
||||
|
||||
// 请求参数,要求为json字符串
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("FormId", "SFC_OperationPlanning");
|
||||
json.addProperty("FieldKeys",
|
||||
"FID,FENTITY_FEntryID,F_HBYT_SCLH,FMONumber,FProductId.FNumber,FProductName,FProSpecification,FBillNo,FOperNumber,FOperQty,FQualifiedQty,FTransOutQty,FUnqualifiedQty,FWastageQty,FTransInQty,FDepartmentId.FName,FWorkCenterId.FName,FProcessId.FName,F_HBYT_ZYRY,FOperStatus,FSeqPlanStartTime,FSeqPlanFinishTime,FOperPlanStartTime,FOperPlanFinishTime,FOptCtrlCodeId.FName,FKeyOper,FActivity1Name,FOperDescription");
|
||||
// 创建过滤条件
|
||||
JsonArray filterString = new JsonArray();
|
||||
JsonObject filterObject = new JsonObject();
|
||||
/* filterObject.addProperty("FieldName", "FOperStatus");
|
||||
filterObject.addProperty("Compare", "105");
|
||||
filterObject.addProperty("Value", "4");
|
||||
filterObject.addProperty("Left", "");
|
||||
filterObject.addProperty("Right", "");
|
||||
filterObject.addProperty("Logic", 0);
|
||||
filterString.add(filterObject);*/
|
||||
json.add("FilterString", filterString);
|
||||
json.addProperty("OrderString", "");
|
||||
json.addProperty("TopRowCount", 0);
|
||||
json.addProperty("StartRow", 0);
|
||||
json.addProperty("Limit", 10000);
|
||||
json.addProperty("SubSystemId", "");
|
||||
|
||||
String jsonData = json.toString();
|
||||
List<OperationPlanningDTO> operationPlanningList = null;
|
||||
try {
|
||||
// 调用接口
|
||||
String resultJson = String.valueOf(client.billQuery(jsonData));
|
||||
JsonArray jsonArray = new Gson().fromJson(resultJson, JsonArray.class);
|
||||
|
||||
// 使用 ObjectMapper 将 JsonArray 转换为 List<OperationPlanningDTO>
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
operationPlanningList = objectMapper.readValue(jsonArray.toString(),
|
||||
new TypeReference<List<OperationPlanningDTO>>() {
|
||||
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(); // 输出异常日志
|
||||
}
|
||||
|
||||
return operationPlanningList; // 返回结果
|
||||
}
|
||||
|
||||
/**
|
||||
* 将查询出的工序计划数据整理成层级结构的JSON格式
|
||||
* @return
|
||||
*/
|
||||
public static List<OperationPlanResultDTO> getStructuredOperationPlanningList() {
|
||||
List<OperationPlanningDTO> rawList = getOperationPlanningList();
|
||||
if (rawList == null || rawList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 按生产订单编号(FMONumber)分组
|
||||
Map<String, List<OperationPlanningDTO>> groupedMap = rawList.stream()
|
||||
.filter(item -> item.getFMoNumber() != null)
|
||||
.collect(java.util.stream.Collectors.groupingBy(OperationPlanningDTO::getFMoNumber));
|
||||
|
||||
List<OperationPlanResultDTO> resultList = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, List<OperationPlanningDTO>> entry : groupedMap.entrySet()) {
|
||||
String moNumber = entry.getKey();
|
||||
List<OperationPlanningDTO> items = entry.getValue();
|
||||
|
||||
OperationPlanningDTO firstItem = items.get(0);
|
||||
|
||||
OperationPlanResultDTO resultDTO = new OperationPlanResultDTO();
|
||||
resultDTO.setMoNumber(moNumber);
|
||||
resultDTO.setBatchNo(firstItem.getFHbytSclh()); // 生产令号
|
||||
resultDTO.setProductCode(firstItem.getFProductIdFNumber());
|
||||
resultDTO.setProductName(firstItem.getFProductName());
|
||||
resultDTO.setBillNo(firstItem.getFBillNo());
|
||||
resultDTO.setProSpecification(firstItem.getFProSpecification());
|
||||
resultDTO.setPlanStartTime(firstItem.getFOperPlanStartTime());
|
||||
resultDTO.setPlanFinishTime(firstItem.getFOperPlanFinishTime());
|
||||
resultDTO.setFid(firstItem.getFid());
|
||||
|
||||
List<ProcessDetailDTO> processList = new ArrayList<>();
|
||||
for (OperationPlanningDTO item : items) {
|
||||
ProcessDetailDTO detail = new ProcessDetailDTO();
|
||||
detail.setOperNo(item.getFOperNumber());
|
||||
detail.setProcessName(item.getFProcessIdFName());
|
||||
detail.setWorkCenter(item.getFWorkCenterIdFName());
|
||||
detail.setPlanQty(item.getFOperQty());
|
||||
detail.setInQty(item.getFTransInQty());
|
||||
detail.setOkQty(item.getFQualifiedQty());
|
||||
detail.setNgQty(item.getFUnqualifiedQty());
|
||||
|
||||
// 补充其余遗漏数据
|
||||
detail.setTransOutQty(item.getFTransOutQty());
|
||||
detail.setWastageQty(item.getFWastageQty());
|
||||
detail.setDepartment(item.getFDepartmentIdFName());
|
||||
detail.setOperator(item.getFHbytZyry());
|
||||
detail.setSeqPlanStartTime(item.getFSeqPlanStartTime());
|
||||
detail.setSeqPlanFinishTime(item.getFSeqPlanFinishTime());
|
||||
detail.setOptCtrlCode(item.getFOptCtrlCodeIdFName());
|
||||
detail.setKeyOper(item.getFKeyOper());
|
||||
detail.setActivity1Name(item.getFActivity1Name());
|
||||
detail.setOperDescription(item.getFOperDescription());
|
||||
detail.setEntityEntryId(item.getFEntityFEntryId());
|
||||
|
||||
// 状态映射:K3里4通常表示已完工
|
||||
String statusStr = item.getFOperStatus();
|
||||
if ("0".equals(statusStr)) {
|
||||
detail.setStatus("创建");
|
||||
}
|
||||
else if ("1".equals(statusStr)) {
|
||||
detail.setStatus("计划");
|
||||
} else if ("2".equals(statusStr)) {
|
||||
detail.setStatus("计划确认");
|
||||
} else if ("3".equals(statusStr)) {
|
||||
detail.setStatus("下达");
|
||||
} else if ("4".equals(statusStr)) {
|
||||
detail.setStatus("开工");
|
||||
} else if ("5".equals(statusStr)) {
|
||||
detail.setStatus("完工");
|
||||
} else if ("6".equals(statusStr)) {
|
||||
detail.setStatus("关闭");
|
||||
} else {
|
||||
detail.setStatus(statusStr);
|
||||
}
|
||||
|
||||
processList.add(detail);
|
||||
}
|
||||
|
||||
// 按工序号排序
|
||||
processList.sort(Comparator.comparing(ProcessDetailDTO::getOperNo, Comparator.nullsLast(Integer::compareTo)));
|
||||
|
||||
resultDTO.setProcessList(processList);
|
||||
resultList.add(resultDTO);
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
@ -72,7 +73,7 @@ public class PDFGenerator {
|
||||
|
||||
nextX += colWidths[i];
|
||||
}
|
||||
|
||||
|
||||
return contentStream; // 返回可能更新后的contentStream
|
||||
}
|
||||
|
||||
@ -93,9 +94,8 @@ public class PDFGenerator {
|
||||
return LosslessFactory.createFromImage(document, bufferedImage);
|
||||
}
|
||||
|
||||
public static String writeToPdf(List<CombinedDTO> combinedVoList, String rooteProdet) {
|
||||
public static String writeToPdf(List<CombinedDTO> combinedVoList, String rooteProdet) throws IOException {
|
||||
String fontPath = "C:\\Users\\Administrator\\Desktop\\arial unicode ms.ttf";
|
||||
List<String> pdfPaths = new ArrayList<>();
|
||||
String directoryPath = "D:\\上传BOM\\" + rooteProdet;
|
||||
// 检查目录是否存在,如果不存在则创建
|
||||
File directory = new File(directoryPath);
|
||||
@ -125,216 +125,47 @@ public class PDFGenerator {
|
||||
throw new RuntimeException("清理目录失败: " + directoryPath + ", " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
for (CombinedDTO combinedVo : combinedVoList) {
|
||||
if (!combinedVoList.isEmpty()) {
|
||||
// 获取物料编码
|
||||
String materialCode1 = combinedVo.getMaterialCode();
|
||||
String fProcessName;
|
||||
String productionOrderNumber;
|
||||
Date starttime;
|
||||
Date endtime;
|
||||
String materialCode;
|
||||
Long fmoQty;
|
||||
String fDepartmentName;
|
||||
String OrderNumber = "";
|
||||
// 获取第一序的工序名称
|
||||
List<PlannedProcessVo> processes = combinedVo.getProcesses();
|
||||
if (!processes.isEmpty()) {
|
||||
fProcessName = processes.get(0).getFProcessName();
|
||||
OrderNumber = combinedVo.getOrderNumber();
|
||||
productionOrderNumber = String.valueOf(combinedVo.getProcesses().get(0).getFBillNo());
|
||||
starttime = combinedVo.getProcesses().get(0).getFPlanStartTime();
|
||||
endtime = combinedVo.getProcesses().get(0).getFPlanFinishTime();
|
||||
materialCode = String.valueOf(combinedVo.getMaterialUsageDTOList().get(0).getBillNumber());
|
||||
fmoQty = combinedVo.getProcesses().get(0).getFMOQty();
|
||||
fDepartmentName = combinedVo.getProcesses().get(0).getFWorkCenterName();
|
||||
// 其他代码...
|
||||
} else {
|
||||
// 处理列表为空的情况
|
||||
fProcessName = "A";
|
||||
productionOrderNumber = "A";
|
||||
starttime = new Date();
|
||||
endtime = new Date();
|
||||
materialCode = "0";
|
||||
fmoQty = 0L;
|
||||
fDepartmentName = "";
|
||||
Map<String, List<CombinedDTO>> groupMap = new LinkedHashMap<>();
|
||||
if (combinedVoList != null) {
|
||||
for (CombinedDTO dto : combinedVoList) {
|
||||
if (dto == null) {
|
||||
continue;
|
||||
}
|
||||
String prefix = getMaterialPrefix(dto.getMaterialCode());
|
||||
groupMap.computeIfAbsent(prefix, k -> new ArrayList<>()).add(dto);
|
||||
}
|
||||
}
|
||||
|
||||
String pdfFileName = fDepartmentName + "_" + materialCode1.replace("/", "_") + "_独立" + ".pdf";
|
||||
for (Map.Entry<String, List<CombinedDTO>> entry : groupMap.entrySet()) {
|
||||
List<CombinedDTO> groupList = entry.getValue();
|
||||
if (groupList == null || groupList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (groupList.size() == 1) {
|
||||
createSinglePdf(groupList.get(0), rooteProdet, directoryPath, fontPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1. 获取文件夹名
|
||||
String departmentFolderName = fDepartmentName;
|
||||
if (departmentFolderName == null || departmentFolderName.trim().isEmpty()) {
|
||||
// 兜底:从文件名取前四个字或以'_'分割第一个元素
|
||||
String[] parts = pdfFileName.split("_");
|
||||
departmentFolderName = parts.length > 0 ? parts[0]
|
||||
: pdfFileName.substring(0, Math.min(4, pdfFileName.length()));
|
||||
if (groupList.size() == 2) {
|
||||
generateTwoInOnePage(groupList.get(0), groupList.get(1), rooteProdet, directoryPath, fontPath, entry.getKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
List<String> groupPdfPaths = new ArrayList<>();
|
||||
for (CombinedDTO combinedVo : groupList) {
|
||||
String pdfPath = createSinglePdf(combinedVo, rooteProdet, directoryPath, fontPath);
|
||||
if (pdfPath != null && !pdfPath.trim().isEmpty()) {
|
||||
groupPdfPaths.add(pdfPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 创建文件夹
|
||||
File departmentFolder = new File(directoryPath, departmentFolderName);
|
||||
if (!departmentFolder.exists()) {
|
||||
departmentFolder.mkdirs();
|
||||
}
|
||||
|
||||
// 获取当前时间的毫秒时间戳
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String pdfPath = departmentFolder.getAbsolutePath() + "\\" + fDepartmentName + "_" + materialCode1.replace("/", "_") + "_" + productionOrderNumber + ".pdf";
|
||||
PDDocument document = null;
|
||||
try {
|
||||
// 创建 PDF 文件并写入内容
|
||||
document = new PDDocument();
|
||||
PDPage page = new PDPage();
|
||||
document.addPage(page);
|
||||
PDType0Font font = PDType0Font.load(document, new File(fontPath));
|
||||
|
||||
try {
|
||||
font = PDType0Font.load(document, new File(fontPath));
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Font file not found: " + fontPath);
|
||||
throw new RuntimeException("Font file not found", e);
|
||||
}
|
||||
|
||||
PDPageContentStream contentStream = new PDPageContentStream(document, page);
|
||||
|
||||
float margin = 15;
|
||||
float yStart = 740;
|
||||
float rowHeight = 20f;
|
||||
float pageHeight = page.getMediaBox().getHeight();
|
||||
float tableWidth = page.getMediaBox().getWidth() - 2 * margin;
|
||||
|
||||
// 添加标题和右上角的二维码
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, 14);
|
||||
contentStream.newLineAtOffset(220, 780);
|
||||
contentStream.showText("工序计划单");
|
||||
contentStream.endText();
|
||||
|
||||
PDImageXObject qrCode1 = generateQRCode(document, productionOrderNumber, 90, 90);
|
||||
contentStream.drawImage(qrCode1, page.getMediaBox().getWidth() - 110, 710);
|
||||
SimpleDateFormat formatte11r = new SimpleDateFormat("yyyy-MM-dd");
|
||||
// 绘制生产订单信息表格
|
||||
float[] colWidths = { 60, 95, 110, 110, 60, 60, 55 };
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, colWidths,
|
||||
new String[] { "生产令号", rooteProdet,
|
||||
"开始时间", formatte11r.format(starttime), "结束时间", formatte11r.format(endtime) },
|
||||
document, page, rowHeight, pageHeight);
|
||||
|
||||
yStart -= rowHeight;
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, colWidths,
|
||||
new String[] { "单据编号", "生产订单编号", "产品名称", "产品编码", "数量", "生产车间", "次数" }, document, page,
|
||||
rowHeight, pageHeight);
|
||||
|
||||
yStart -= rowHeight;
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, colWidths, new String[] {
|
||||
productionOrderNumber, combinedVo.getOrderNumber(), combinedVo.getMaterialName(),
|
||||
combinedVo.getMaterialCode(),
|
||||
String.valueOf(fmoQty), fDepartmentName, "1", "" }, document, page, rowHeight, pageHeight);
|
||||
|
||||
// 绘制工序表格
|
||||
float processTableStartY = yStart - 2 * rowHeight - 10;
|
||||
float[] processColWidths = { 35, 78, 35, 55, 45, 170, 55, 60, 60 };
|
||||
yStart = processTableStartY;
|
||||
|
||||
// 使用支持换行的方法绘制表头
|
||||
Object[] result = drawTableRowWithWrap(contentStream, font, margin, yStart, tableWidth, processColWidths, new String[] {
|
||||
"工序号", "工序名称", "数量", "工作中心", "工时(分)", "工序说明", "控制码", "开始时间", "结束时间" }, document, page,
|
||||
rowHeight, pageHeight);
|
||||
contentStream = (PDPageContentStream) result[0];
|
||||
yStart = (Float) result[1];
|
||||
|
||||
for (PlannedProcessVo process : combinedVo.getProcesses()) {
|
||||
SimpleDateFormat formatte1r = new SimpleDateFormat("yyyy-MM-dd");
|
||||
// 使用支持换行的方法绘制数据行,特别是工序说明字段
|
||||
Object[] processResult = drawTableRowWithWrap(contentStream, font, margin, yStart, tableWidth, processColWidths, new String[] {
|
||||
String.valueOf(process.getFOperNumber()), process.getFProcessName(),
|
||||
String.valueOf(process.getFOperQty()),
|
||||
process.getFWorkCenterName(), String.format("%.2f", process.getFActivity1BaseQty()),
|
||||
process.getFOperDescription(),
|
||||
process.getFOptCtrlCodeIFName(),
|
||||
process.getFSeqPlanStartTime() != null
|
||||
? formatte1r.format(process.getFSeqPlanStartTime())
|
||||
: "",
|
||||
process.getFSeqPlanFinishTime() != null
|
||||
? (formatte1r.format(process.getFSeqPlanFinishTime()))
|
||||
: ""
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
contentStream = (PDPageContentStream) processResult[0];
|
||||
yStart = (Float) processResult[1];
|
||||
}
|
||||
|
||||
// 添加生产用料清单标题
|
||||
yStart = yStart - 2 * rowHeight - 40;
|
||||
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, 12);
|
||||
contentStream.newLineAtOffset(margin, yStart);
|
||||
contentStream.showText("生产用料清单:");
|
||||
contentStream.showText(materialCode);
|
||||
contentStream.endText();
|
||||
|
||||
// 在用料清单标题右侧添加二维码,与右上角二维码对齐
|
||||
PDImageXObject qrCode2 = generateQRCode(document, materialCode, 90, 90);
|
||||
contentStream.drawImage(qrCode2, page.getMediaBox().getWidth() - 110, yStart - 31);
|
||||
|
||||
yStart -= rowHeight;
|
||||
|
||||
// 用料清单表头
|
||||
float[] materialColWidths = { 25, 130, 110, 50, 35, 40, 40, 80, 50 };
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, materialColWidths, new String[] {
|
||||
"序号", "物料编码", "物料名称", "规格型号", "单位", "应发数", "已领数", "仓库", "备注"
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
|
||||
List<MaterialUsageDTO> materialUsageDTOList = combinedVo.getMaterialUsageDTOList();
|
||||
if (materialUsageDTOList != null && !materialUsageDTOList.isEmpty()) {
|
||||
for (int i = 0; i < materialUsageDTOList.size(); i++) {
|
||||
yStart -= rowHeight;
|
||||
|
||||
// 如果空间不足,则新建页面
|
||||
if (yStart - rowHeight < margin) {
|
||||
contentStream.close();
|
||||
page = new PDPage();
|
||||
document.addPage(page);
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
yStart = pageHeight - margin;
|
||||
|
||||
// 绘制用料清单表头
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, materialColWidths,
|
||||
new String[] {
|
||||
"序号", "物料编码", "物料名称", "规格型号", "单位", "应发数", "已领数", "仓库", "备注"
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
yStart -= rowHeight;
|
||||
}
|
||||
|
||||
MaterialUsageDTO material = materialUsageDTOList.get(i);
|
||||
SimpleDateFormat formatte1r = new SimpleDateFormat("yyyy-MM-dd");
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, materialColWidths,
|
||||
new String[] {
|
||||
String.valueOf(i + 1), material.getMaterialCode(),
|
||||
material.getMaterialName(),
|
||||
material.getSpecification(), material.getUnit(),
|
||||
String.valueOf(material.getRequiredQty()),
|
||||
String.valueOf(material.getPickedQty()),
|
||||
|
||||
material.getStockName(),
|
||||
material.getRemarks() != null ? material.getRemarks() : ""
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
}
|
||||
}
|
||||
|
||||
contentStream.close();
|
||||
document.save(pdfPath);
|
||||
pdfPaths.add(pdfPath);
|
||||
} catch (IOException | WriterException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (document != null) {
|
||||
try {
|
||||
document.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (groupPdfPaths.size() > 1) {
|
||||
String mergedPath = buildMergedPdfPath(groupList.get(0), directoryPath, entry.getKey());
|
||||
Files.move(Paths.get(groupPdfPaths.get(0)), Paths.get(mergedPath), StandardCopyOption.REPLACE_EXISTING);
|
||||
for (int i = 1; i < groupPdfPaths.size(); i++) {
|
||||
String p = groupPdfPaths.get(i);
|
||||
mergePdfToExisting(p, mergedPath);
|
||||
Files.deleteIfExists(Paths.get(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,6 +181,502 @@ public class PDFGenerator {
|
||||
|
||||
}
|
||||
|
||||
private static String getMaterialPrefix(String materialCode) {
|
||||
if (materialCode == null) {
|
||||
return "";
|
||||
}
|
||||
int index = materialCode.indexOf("-");
|
||||
if (index > 0) {
|
||||
return materialCode.substring(0, index);
|
||||
}
|
||||
return materialCode;
|
||||
}
|
||||
|
||||
private static String getDepartmentName(CombinedDTO combinedVo) {
|
||||
List<PlannedProcessVo> processes = combinedVo.getProcesses();
|
||||
if (processes != null && !processes.isEmpty()) {
|
||||
String d = processes.get(0).getFWorkCenterName();
|
||||
return d != null ? d : "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String buildMergedPdfPath(CombinedDTO anyVo, String directoryPath, String prefix) {
|
||||
String dept = getDepartmentName(anyVo);
|
||||
String safePrefix = prefix != null ? prefix.replace("/", "_") : "";
|
||||
String fileName = (safePrefix.isEmpty() ? ("UNKNOWN_" + System.currentTimeMillis()) : safePrefix) + ".pdf";
|
||||
|
||||
String folderName = dept;
|
||||
if (folderName == null || folderName.trim().isEmpty()) {
|
||||
String[] parts = fileName.split("_");
|
||||
folderName = parts.length > 0 ? parts[0] : "merge";
|
||||
}
|
||||
|
||||
File departmentFolder = new File(directoryPath, folderName);
|
||||
if (!departmentFolder.exists()) {
|
||||
departmentFolder.mkdirs();
|
||||
}
|
||||
return departmentFolder.getAbsolutePath() + "\\" + fileName;
|
||||
}
|
||||
|
||||
private static String generateTwoInOnePage(CombinedDTO dto1, CombinedDTO dto2, String rooteProdet, String directoryPath, String fontPath, String prefix) {
|
||||
if (dto1 == null || dto2 == null) {
|
||||
return null;
|
||||
}
|
||||
String outputPath = buildMergedPdfPath(dto1, directoryPath, prefix);
|
||||
PDDocument document = null;
|
||||
PDPageContentStream contentStream = null;
|
||||
try {
|
||||
document = new PDDocument();
|
||||
PDPage page = new PDPage();
|
||||
document.addPage(page);
|
||||
|
||||
PDType0Font font = PDType0Font.load(document, new File(fontPath));
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
|
||||
float topStart = 780f;
|
||||
float bottomStart = 380f;
|
||||
drawPlanIntoArea(dto1, document, page, contentStream, font, rooteProdet, topStart, 400f);
|
||||
drawPlanIntoArea(dto2, document, page, contentStream, font, rooteProdet, bottomStart, 15f);
|
||||
|
||||
contentStream.close();
|
||||
contentStream = null;
|
||||
document.save(outputPath);
|
||||
return outputPath;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (contentStream != null) {
|
||||
try {
|
||||
contentStream.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
if (document != null) {
|
||||
try {
|
||||
document.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawPlanIntoArea(CombinedDTO combinedVo, PDDocument document, PDPage page, PDPageContentStream contentStream,
|
||||
PDType0Font font, String rooteProdet, float titleY, float minY) throws IOException, WriterException {
|
||||
float margin = 15f;
|
||||
float rowHeight = 14f;
|
||||
float fontSize = 8f;
|
||||
float pageWidth = page.getMediaBox().getWidth();
|
||||
float qrSize = 90f;
|
||||
float qrGap = 5f;
|
||||
float tableWidth = pageWidth - 2 * margin - (qrSize + qrGap);
|
||||
float qrX = pageWidth - margin - qrSize;
|
||||
|
||||
List<PlannedProcessVo> processes = combinedVo.getProcesses();
|
||||
String productionOrderNumber;
|
||||
Date starttime;
|
||||
Date endtime;
|
||||
Long fmoQty;
|
||||
String fDepartmentName;
|
||||
if (processes != null && !processes.isEmpty()) {
|
||||
productionOrderNumber = String.valueOf(processes.get(0).getFBillNo());
|
||||
starttime = processes.get(0).getFPlanStartTime();
|
||||
endtime = processes.get(0).getFPlanFinishTime();
|
||||
fmoQty = processes.get(0).getFMOQty();
|
||||
fDepartmentName = processes.get(0).getFWorkCenterName();
|
||||
} else {
|
||||
productionOrderNumber = "A";
|
||||
starttime = new Date();
|
||||
endtime = new Date();
|
||||
fmoQty = 0L;
|
||||
fDepartmentName = "";
|
||||
}
|
||||
|
||||
String materialCodeQr;
|
||||
if (combinedVo.getMaterialUsageDTOList() != null && !combinedVo.getMaterialUsageDTOList().isEmpty()) {
|
||||
materialCodeQr = String.valueOf(combinedVo.getMaterialUsageDTOList().get(0).getBillNumber());
|
||||
} else {
|
||||
materialCodeQr = "0";
|
||||
}
|
||||
|
||||
if (titleY - qrSize >= minY) {
|
||||
PDImageXObject qrCode1 = generateQRCode(document, productionOrderNumber, (int) qrSize, (int) qrSize);
|
||||
contentStream.drawImage(qrCode1, qrX, titleY - qrSize + 5);
|
||||
}
|
||||
|
||||
SimpleDateFormat formatte11r = new SimpleDateFormat("yyyy-MM-dd");
|
||||
float[] colWidths = scaleWidths(new float[] { 60, 95, 110, 110, 60, 60, 55 }, tableWidth);
|
||||
float yStart = titleY - 5;
|
||||
|
||||
Object[] r1 = drawTableRowWithWrapNoPageBreak(contentStream, font, fontSize, margin, yStart, tableWidth, colWidths,
|
||||
new String[] { "生产令号", rooteProdet, "开始时间", formatte11r.format(starttime), "结束时间", formatte11r.format(endtime) },
|
||||
rowHeight, minY);
|
||||
yStart = (Float) r1[1];
|
||||
|
||||
Object[] r2 = drawTableRowWithWrapNoPageBreak(contentStream, font, fontSize, margin, yStart - 2, tableWidth, colWidths,
|
||||
new String[] { "单据编号", "生产订单编号", "产品名称", "产品编码", "数量", "生产车间", "次数" }, rowHeight, minY);
|
||||
yStart = (Float) r2[1];
|
||||
|
||||
Object[] r3 = drawTableRowWithWrapNoPageBreak(contentStream, font, fontSize, margin, yStart - 2, tableWidth, colWidths, new String[] {
|
||||
productionOrderNumber, combinedVo.getOrderNumber(), combinedVo.getMaterialName(), combinedVo.getMaterialCode(),
|
||||
String.valueOf(fmoQty), fDepartmentName, "1", "" }, rowHeight, minY);
|
||||
yStart = (Float) r3[1];
|
||||
|
||||
float[] processColWidths = scaleWidths(new float[] { 35, 78, 35, 55, 45, 170, 55, 60, 60 }, tableWidth);
|
||||
yStart = yStart - 6;
|
||||
Object[] headerResult = drawTableRowWithWrapNoPageBreak(contentStream, font, fontSize, margin, yStart, tableWidth, processColWidths,
|
||||
new String[] { "工序号", "工序名称", "数量", "工作中心", "工时(分)", "工序说明", "控制码", "开始时间", "结束时间" },
|
||||
rowHeight, minY);
|
||||
yStart = (Float) headerResult[1];
|
||||
|
||||
if (processes != null) {
|
||||
SimpleDateFormat formatte1r = new SimpleDateFormat("yyyy-MM-dd");
|
||||
for (PlannedProcessVo process : processes) {
|
||||
yStart = yStart - rowHeight;
|
||||
Object[] rowResult = drawTableRowWithWrapNoPageBreak(contentStream, font, fontSize, margin, yStart, tableWidth, processColWidths, new String[] {
|
||||
String.valueOf(process.getFOperNumber()), process.getFProcessName(),
|
||||
String.valueOf(process.getFOperQty()),
|
||||
process.getFWorkCenterName(), String.format("%.2f", process.getFActivity1BaseQty()),
|
||||
process.getFOperDescription(),
|
||||
process.getFOptCtrlCodeIFName(),
|
||||
process.getFSeqPlanStartTime() != null ? formatte1r.format(process.getFSeqPlanStartTime()) : "",
|
||||
process.getFSeqPlanFinishTime() != null ? formatte1r.format(process.getFSeqPlanFinishTime()) : ""
|
||||
}, rowHeight, minY);
|
||||
float nextY = (Float) rowResult[1];
|
||||
if (nextY == yStart || nextY <= minY) {
|
||||
break;
|
||||
}
|
||||
yStart = nextY;
|
||||
}
|
||||
}
|
||||
|
||||
yStart = yStart - 15;
|
||||
if (yStart >= minY) {
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, 10);
|
||||
contentStream.newLineAtOffset(margin, yStart);
|
||||
contentStream.showText("生产用料清单:" + materialCodeQr);
|
||||
contentStream.endText();
|
||||
|
||||
PDImageXObject qrCode2 = generateQRCode(document, materialCodeQr, (int) qrSize, (int) qrSize);
|
||||
float qrTopY = yStart + 10;
|
||||
if (qrTopY - qrSize >= minY - 20) {
|
||||
contentStream.drawImage(qrCode2, qrX, qrTopY - qrSize);
|
||||
}
|
||||
yStart = yStart - 12;
|
||||
}
|
||||
|
||||
yStart = yStart - 2;
|
||||
float[] materialColWidths = scaleWidths(new float[] { 25, 130, 110, 50, 35, 40, 40, 80, 50 }, tableWidth);
|
||||
Object[] mHeader = drawTableRowWithWrapNoPageBreak(contentStream, font, fontSize, margin, yStart, tableWidth, materialColWidths, new String[] {
|
||||
"序号", "物料编码", "物料名称", "规格型号", "单位", "应发数", "已领数", "仓库", "备注"
|
||||
}, rowHeight, minY);
|
||||
yStart = (Float) mHeader[1];
|
||||
|
||||
List<MaterialUsageDTO> materialUsageDTOList = combinedVo.getMaterialUsageDTOList();
|
||||
if (materialUsageDTOList != null && !materialUsageDTOList.isEmpty()) {
|
||||
for (int i = 0; i < materialUsageDTOList.size(); i++) {
|
||||
MaterialUsageDTO material = materialUsageDTOList.get(i);
|
||||
Object[] mr = drawTableRowWithWrapNoPageBreak(contentStream, font, fontSize, margin, yStart - 2, tableWidth, materialColWidths, new String[] {
|
||||
String.valueOf(i + 1), material.getMaterialCode(),
|
||||
material.getMaterialName(),
|
||||
material.getSpecification(), material.getUnit(),
|
||||
String.valueOf(material.getRequiredQty()),
|
||||
String.valueOf(material.getPickedQty()),
|
||||
material.getStockName(),
|
||||
material.getRemarks() != null ? material.getRemarks() : ""
|
||||
}, rowHeight, minY);
|
||||
float nextY = (Float) mr[1];
|
||||
if (nextY == yStart - 2 || nextY <= minY) {
|
||||
break;
|
||||
}
|
||||
yStart = nextY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float[] scaleWidths(float[] widths, float targetTotal) {
|
||||
float sum = 0f;
|
||||
for (float w : widths) {
|
||||
sum += w;
|
||||
}
|
||||
if (sum <= 0f || targetTotal <= 0f) {
|
||||
return widths;
|
||||
}
|
||||
float scale = targetTotal / sum;
|
||||
float[] scaled = new float[widths.length];
|
||||
for (int i = 0; i < widths.length; i++) {
|
||||
scaled[i] = widths[i] * scale;
|
||||
}
|
||||
return scaled;
|
||||
}
|
||||
|
||||
private static float drawTableRowNoPageBreak(PDPageContentStream contentStream, PDType0Font font, float fontSize, float margin,
|
||||
float yStart, float tableWidth, float[] colWidths, String[] cells, float rowHeight, float minY) throws IOException {
|
||||
if (yStart - rowHeight < minY) {
|
||||
return yStart;
|
||||
}
|
||||
|
||||
float nextX = margin;
|
||||
float nextY = yStart;
|
||||
int maxColumns = Math.min(cells.length, colWidths.length);
|
||||
|
||||
for (int i = 0; i < maxColumns; i++) {
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, fontSize);
|
||||
contentStream.newLineAtOffset(nextX + 4, nextY - (fontSize + 4));
|
||||
String cellText = cells[i] != null ? cells[i].replace("\n", "") : "";
|
||||
contentStream.showText(cellText);
|
||||
contentStream.endText();
|
||||
|
||||
contentStream.moveTo(nextX, nextY);
|
||||
contentStream.lineTo(nextX, nextY - rowHeight);
|
||||
contentStream.lineTo(nextX + colWidths[i], nextY - rowHeight);
|
||||
contentStream.lineTo(nextX + colWidths[i], nextY);
|
||||
contentStream.closeAndStroke();
|
||||
|
||||
nextX += colWidths[i];
|
||||
}
|
||||
|
||||
return yStart - rowHeight;
|
||||
}
|
||||
|
||||
private static Object[] drawTableRowWithWrapNoPageBreak(PDPageContentStream contentStream, PDType0Font font, float fontSize,
|
||||
float margin, float yStart, float tableWidth, float[] colWidths, String[] cells, float baseRowHeight, float minY)
|
||||
throws IOException {
|
||||
float nextX = margin;
|
||||
float nextY = yStart;
|
||||
int maxColumns = Math.min(cells.length, colWidths.length);
|
||||
|
||||
List<List<String>> allCellLines = new ArrayList<List<String>>();
|
||||
int maxLines = 1;
|
||||
|
||||
for (int i = 0; i < maxColumns; i++) {
|
||||
String cellText = cells[i] != null ? cells[i] : "";
|
||||
List<String> lines = wrapText(cellText, font, fontSize, colWidths[i]);
|
||||
allCellLines.add(lines);
|
||||
maxLines = Math.max(maxLines, lines.size());
|
||||
}
|
||||
|
||||
float actualRowHeight = baseRowHeight * maxLines;
|
||||
if (nextY - actualRowHeight < minY) {
|
||||
return new Object[]{contentStream, yStart};
|
||||
}
|
||||
|
||||
nextX = margin;
|
||||
for (int i = 0; i < maxColumns; i++) {
|
||||
List<String> lines = allCellLines.get(i);
|
||||
|
||||
contentStream.moveTo(nextX, nextY);
|
||||
contentStream.lineTo(nextX, nextY - actualRowHeight);
|
||||
contentStream.lineTo(nextX + colWidths[i], nextY - actualRowHeight);
|
||||
contentStream.lineTo(nextX + colWidths[i], nextY);
|
||||
contentStream.closeAndStroke();
|
||||
|
||||
for (int lineIndex = 0; lineIndex < lines.size(); lineIndex++) {
|
||||
String line = lines.get(lineIndex);
|
||||
if (!line.isEmpty()) {
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, fontSize);
|
||||
float textY = nextY - (fontSize + 4) - (lineIndex * (fontSize + 1));
|
||||
contentStream.newLineAtOffset(nextX + 4, textY);
|
||||
contentStream.showText(line);
|
||||
contentStream.endText();
|
||||
}
|
||||
}
|
||||
|
||||
nextX += colWidths[i];
|
||||
}
|
||||
|
||||
return new Object[]{contentStream, nextY - actualRowHeight};
|
||||
}
|
||||
|
||||
private static String createSinglePdf(CombinedDTO combinedVo, String rooteProdet, String directoryPath, String fontPath) {
|
||||
if (combinedVo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String materialCode1 = combinedVo.getMaterialCode();
|
||||
String productionOrderNumber;
|
||||
Date starttime;
|
||||
Date endtime;
|
||||
String materialCode;
|
||||
Long fmoQty;
|
||||
String fDepartmentName;
|
||||
|
||||
List<PlannedProcessVo> processes = combinedVo.getProcesses();
|
||||
if (processes != null && !processes.isEmpty()) {
|
||||
productionOrderNumber = String.valueOf(processes.get(0).getFBillNo());
|
||||
starttime = processes.get(0).getFPlanStartTime();
|
||||
endtime = processes.get(0).getFPlanFinishTime();
|
||||
if (combinedVo.getMaterialUsageDTOList() != null && !combinedVo.getMaterialUsageDTOList().isEmpty()) {
|
||||
materialCode = String.valueOf(combinedVo.getMaterialUsageDTOList().get(0).getBillNumber());
|
||||
} else {
|
||||
materialCode = "0";
|
||||
}
|
||||
fmoQty = processes.get(0).getFMOQty();
|
||||
fDepartmentName = processes.get(0).getFWorkCenterName();
|
||||
} else {
|
||||
productionOrderNumber = "A";
|
||||
starttime = new Date();
|
||||
endtime = new Date();
|
||||
materialCode = "0";
|
||||
fmoQty = 0L;
|
||||
fDepartmentName = "";
|
||||
}
|
||||
|
||||
String pdfFileName = fDepartmentName + "_" + (materialCode1 != null ? materialCode1.replace("/", "_") : "") + "_独立" + ".pdf";
|
||||
|
||||
String departmentFolderName = fDepartmentName;
|
||||
if (departmentFolderName == null || departmentFolderName.trim().isEmpty()) {
|
||||
String[] parts = pdfFileName.split("_");
|
||||
departmentFolderName = parts.length > 0 ? parts[0] : pdfFileName.substring(0, Math.min(4, pdfFileName.length()));
|
||||
}
|
||||
|
||||
File departmentFolder = new File(directoryPath, departmentFolderName);
|
||||
if (!departmentFolder.exists()) {
|
||||
departmentFolder.mkdirs();
|
||||
}
|
||||
|
||||
String pdfPath = departmentFolder.getAbsolutePath() + "\\" + fDepartmentName + "_" + (materialCode1 != null ? materialCode1.replace("/", "_") : "") + "_" + productionOrderNumber + ".pdf";
|
||||
PDDocument document = null;
|
||||
try {
|
||||
document = new PDDocument();
|
||||
PDPage page = new PDPage();
|
||||
document.addPage(page);
|
||||
PDType0Font font;
|
||||
|
||||
try {
|
||||
font = PDType0Font.load(document, new File(fontPath));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Font file not found", e);
|
||||
}
|
||||
|
||||
PDPageContentStream contentStream = new PDPageContentStream(document, page);
|
||||
|
||||
float margin = 15;
|
||||
float yStart = 740;
|
||||
float rowHeight = 20f;
|
||||
float pageHeight = page.getMediaBox().getHeight();
|
||||
float pageWidth = page.getMediaBox().getWidth();
|
||||
float qrSize = 90f;
|
||||
float qrGap = 5f;
|
||||
float tableWidth = pageWidth - 2 * margin - (qrSize + qrGap);
|
||||
float qrX = pageWidth - margin - qrSize;
|
||||
|
||||
PDImageXObject qrCode1 = generateQRCode(document, productionOrderNumber, (int)qrSize, (int)qrSize);
|
||||
contentStream.drawImage(qrCode1, qrX, yStart - qrSize + 25);
|
||||
|
||||
SimpleDateFormat formatte11r = new SimpleDateFormat("yyyy-MM-dd");
|
||||
float[] colWidths = scaleWidths(new float[] { 60, 95, 110, 110, 60, 60, 55 }, tableWidth);
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, colWidths,
|
||||
new String[] { "生产令号", rooteProdet, "开始时间", formatte11r.format(starttime), "结束时间", formatte11r.format(endtime) },
|
||||
document, page, rowHeight, pageHeight);
|
||||
|
||||
yStart -= rowHeight;
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, colWidths,
|
||||
new String[] { "单据编号", "生产订单编号", "产品名称", "产品编码", "数量", "生产车间", "次数" }, document, page,
|
||||
rowHeight, pageHeight);
|
||||
|
||||
yStart -= rowHeight;
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, colWidths, new String[] {
|
||||
productionOrderNumber, combinedVo.getOrderNumber(), combinedVo.getMaterialName(), combinedVo.getMaterialCode(),
|
||||
String.valueOf(fmoQty), fDepartmentName, "1", "" }, document, page, rowHeight, pageHeight);
|
||||
|
||||
float processTableStartY = yStart - 2 * rowHeight - 10;
|
||||
float[] processColWidths = scaleWidths(new float[] { 35, 78, 35, 55, 45, 170, 55, 60, 60 }, tableWidth);
|
||||
yStart = processTableStartY;
|
||||
|
||||
Object[] result = drawTableRowWithWrap(contentStream, font, margin, yStart, tableWidth, processColWidths, new String[] {
|
||||
"工序号", "工序名称", "数量", "工作中心", "工时(分)", "工序说明", "控制码", "开始时间", "结束时间" }, document, page,
|
||||
rowHeight, pageHeight);
|
||||
contentStream = (PDPageContentStream) result[0];
|
||||
yStart = (Float) result[1];
|
||||
|
||||
if (processes != null) {
|
||||
for (PlannedProcessVo process : processes) {
|
||||
SimpleDateFormat formatte1r = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Object[] processResult = drawTableRowWithWrap(contentStream, font, margin, yStart, tableWidth, processColWidths, new String[] {
|
||||
String.valueOf(process.getFOperNumber()), process.getFProcessName(),
|
||||
String.valueOf(process.getFOperQty()),
|
||||
process.getFWorkCenterName(), String.format("%.2f", process.getFActivity1BaseQty()),
|
||||
process.getFOperDescription(),
|
||||
process.getFOptCtrlCodeIFName(),
|
||||
process.getFSeqPlanStartTime() != null ? formatte1r.format(process.getFSeqPlanStartTime()) : "",
|
||||
process.getFSeqPlanFinishTime() != null ? formatte1r.format(process.getFSeqPlanFinishTime()) : ""
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
contentStream = (PDPageContentStream) processResult[0];
|
||||
yStart = (Float) processResult[1];
|
||||
}
|
||||
}
|
||||
|
||||
yStart = yStart - 25;
|
||||
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(font, 12);
|
||||
contentStream.newLineAtOffset(margin, yStart);
|
||||
contentStream.showText("生产用料清单:");
|
||||
contentStream.showText(materialCode);
|
||||
contentStream.endText();
|
||||
|
||||
PDImageXObject qrCode2 = generateQRCode(document, materialCode, (int)qrSize, (int)qrSize);
|
||||
float qrTopY = yStart + 10;
|
||||
contentStream.drawImage(qrCode2, qrX, qrTopY - qrSize);
|
||||
|
||||
yStart = yStart - 15;
|
||||
|
||||
float[] materialColWidths = scaleWidths(new float[] { 25, 130, 110, 50, 35, 40, 40, 80, 50 }, tableWidth);
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, materialColWidths, new String[] {
|
||||
"序号", "物料编码", "物料名称", "规格型号", "单位", "应发数", "已领数", "仓库", "备注"
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
|
||||
List<MaterialUsageDTO> materialUsageDTOList = combinedVo.getMaterialUsageDTOList();
|
||||
if (materialUsageDTOList != null && !materialUsageDTOList.isEmpty()) {
|
||||
for (int i = 0; i < materialUsageDTOList.size(); i++) {
|
||||
yStart -= rowHeight;
|
||||
if (yStart - rowHeight < margin) {
|
||||
contentStream.close();
|
||||
page = new PDPage();
|
||||
document.addPage(page);
|
||||
contentStream = new PDPageContentStream(document, page);
|
||||
yStart = pageHeight - margin;
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, materialColWidths, new String[] {
|
||||
"序号", "物料编码", "物料名称", "规格型号", "单位", "应发数", "已领数", "仓库", "备注"
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
yStart -= rowHeight;
|
||||
}
|
||||
MaterialUsageDTO material = materialUsageDTOList.get(i);
|
||||
contentStream = drawTableRow(contentStream, font, margin, yStart, tableWidth, materialColWidths,
|
||||
new String[] {
|
||||
String.valueOf(i + 1), material.getMaterialCode(),
|
||||
material.getMaterialName(),
|
||||
material.getSpecification(), material.getUnit(),
|
||||
String.valueOf(material.getRequiredQty()),
|
||||
String.valueOf(material.getPickedQty()),
|
||||
material.getStockName(),
|
||||
material.getRemarks() != null ? material.getRemarks() : ""
|
||||
}, document, page, rowHeight, pageHeight);
|
||||
}
|
||||
}
|
||||
|
||||
contentStream.close();
|
||||
document.save(pdfPath);
|
||||
return pdfPath;
|
||||
} catch (IOException | WriterException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (document != null) {
|
||||
try {
|
||||
document.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建ZIP文件
|
||||
*
|
||||
|
||||
@ -2,13 +2,9 @@ package com.ruoyi.system.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.system.domain.MrpResultCheck;
|
||||
import com.ruoyi.system.domain.ProcessOrderPro;
|
||||
import com.ruoyi.system.domain.ProcessRoute;
|
||||
import com.ruoyi.system.domain.bo.FigureSaveBo;
|
||||
import com.ruoyi.system.domain.dto.ProcessRouteExcelDTO;
|
||||
import com.ruoyi.system.domain.dto.ProcessRoutePushResultDTO;
|
||||
import com.ruoyi.system.domain.dto.ProcessRouteXuDTO;
|
||||
import com.ruoyi.system.domain.dto.excuteDrawing.RigidChainModelDTO;
|
||||
import com.ruoyi.system.domain.vo.OverdueProjectVo;
|
||||
import com.ruoyi.system.domain.vo.ProcessOrderProVo;
|
||||
@ -89,22 +85,22 @@ public interface IProcessOrderProService {
|
||||
|
||||
List<OverdueProjectVo> getOverdueProjects();
|
||||
|
||||
List<ProcessRouteExcelDTO> getRouteAndBomDetail(List<ProcessRoute> routlist,List<ProductionOrderVo> processDataList,ProcessOrderPro orderPro);
|
||||
List<ProcessRoute> getRouteAndBomDetail(List<ProcessRoute> routlist, List<ProductionOrderVo> processDataList, ProcessOrderPro orderPro);
|
||||
|
||||
R<String> getRouteLog(Long id) throws JsonProcessingException;
|
||||
|
||||
SseEmitter startDrawingSse(Long id);
|
||||
|
||||
R<String> downloadDwg(List<Long> ids);
|
||||
R<String> downloadDwg(Long id);
|
||||
|
||||
/**
|
||||
* 推送钉钉卡片
|
||||
* @param id 项目ID
|
||||
*/
|
||||
void pushDingTalkCard(Long id);
|
||||
|
||||
/**
|
||||
* 刷新并计算项目延期状态
|
||||
*/
|
||||
void pushDingTalkCard(Long id);
|
||||
|
||||
/**
|
||||
* 刷新并计算项目延期状态
|
||||
*/
|
||||
void refreshDelayStatus();
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import com.ruoyi.system.jdmain.rouplan.Model;
|
||||
import com.ruoyi.system.domain.vo.ProcessRouteVo;
|
||||
import com.ruoyi.system.domain.vo.ProductionOrderVo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@ -95,7 +96,7 @@ public interface IProcessRouteService {
|
||||
/**
|
||||
* 生成这个项目的pdf
|
||||
*/
|
||||
String generatePDFs(String rooteProdet);
|
||||
String generatePDFs(String rooteProdet) throws IOException;
|
||||
/**
|
||||
* 更新计划时间
|
||||
*/
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.system.domain.SfcOperationPlanningDetail;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningDetailVo;
|
||||
import com.ruoyi.system.domain.bo.SfcOperationPlanningDetailBo;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工序计划明细Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
public interface ISfcOperationPlanningDetailService {
|
||||
|
||||
/**
|
||||
* 查询工序计划明细
|
||||
*/
|
||||
SfcOperationPlanningDetailVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询工序计划明细列表
|
||||
*/
|
||||
TableDataInfo<SfcOperationPlanningDetailVo> queryPageList(SfcOperationPlanningDetailBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询工序计划明细列表
|
||||
*/
|
||||
List<SfcOperationPlanningDetailVo> queryList(SfcOperationPlanningDetailBo bo);
|
||||
|
||||
/**
|
||||
* 新增工序计划明细
|
||||
*/
|
||||
Boolean insertByBo(SfcOperationPlanningDetailBo bo);
|
||||
|
||||
/**
|
||||
* 修改工序计划明细
|
||||
*/
|
||||
Boolean updateByBo(SfcOperationPlanningDetailBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除工序计划明细信息
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.system.domain.SfcOperationPlanningMain;
|
||||
import com.ruoyi.system.domain.dto.OperationPlanResultDTO;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningMainVo;
|
||||
import com.ruoyi.system.domain.bo.SfcOperationPlanningMainBo;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工序计划主Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
public interface ISfcOperationPlanningMainService {
|
||||
|
||||
/**
|
||||
* 查询工序计划主
|
||||
*/
|
||||
SfcOperationPlanningMainVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询工序计划主列表
|
||||
*/
|
||||
TableDataInfo<SfcOperationPlanningMainVo> queryPageList(SfcOperationPlanningMainBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询工序计划主列表
|
||||
*/
|
||||
List<SfcOperationPlanningMainVo> queryList(SfcOperationPlanningMainBo bo);
|
||||
|
||||
/**
|
||||
* 新增工序计划主
|
||||
*/
|
||||
Boolean insertByBo(SfcOperationPlanningMainBo bo);
|
||||
|
||||
/**
|
||||
* 修改工序计划主
|
||||
*/
|
||||
Boolean updateByBo(SfcOperationPlanningMainBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除工序计划主信息
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 同步K3工序计划数据
|
||||
*/
|
||||
Boolean syncOperationPlanning();
|
||||
|
||||
/**
|
||||
* 分页查询工序计划主及明细级联数据
|
||||
*/
|
||||
TableDataInfo<OperationPlanResultDTO> queryCascadePageList(SfcOperationPlanningMainBo bo, PageQuery pageQuery);
|
||||
}
|
||||
@ -116,11 +116,12 @@ public class PartCostServiceImpl implements IPartCostService {
|
||||
|
||||
@Override
|
||||
public boolean getObtainPartData() {
|
||||
System.out.println("1 开始");
|
||||
List<PartCost> list = JdUtil.getObtainPartData();
|
||||
if (list == null || list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out.println("2 获取数据:" + (list==null?0:list.size()));
|
||||
Map<String, PartCost> uniq = new LinkedHashMap<>();
|
||||
for (PartCost pc : list) {
|
||||
if (pc == null) continue;
|
||||
|
||||
@ -27,7 +27,9 @@ import com.ruoyi.common.utils.HttpUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.file.DeleteFile;
|
||||
import com.ruoyi.common.utils.file.PDFDocHelper;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import com.ruoyi.oss.factory.OssFactory;
|
||||
import com.ruoyi.system.controller.ProcessOrderProController;
|
||||
import com.ruoyi.system.domain.*;
|
||||
import com.ruoyi.system.domain.bo.ProcessOrderProBo;
|
||||
import com.ruoyi.system.domain.dto.ProcessRouteExcelDTO;
|
||||
@ -742,6 +744,21 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
// 2. 查询生产令号是否重复
|
||||
ProcessOrderPro processOrderPro = checkProductionOrderExists(orderPro.getProductionOrderNo());
|
||||
|
||||
// 2.1 检查出图服务是否繁忙
|
||||
try {
|
||||
String preStatusResp = HttpUtils.sendGet(statusUrl, "UTF-8");
|
||||
JSONObject preJson = JSONObject.parseObject(preStatusResp);
|
||||
String preStatus = preJson.getString("status");
|
||||
if ("busy".equals(preStatus)) {
|
||||
throw new RuntimeException("出图服务忙,有其他人在出图中,请稍后再试");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof RuntimeException && e.getMessage().contains("出图服务忙")) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
log.warn("检查出图服务状态失败,继续执行: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 3. 构建路径
|
||||
String dirPath = buildDirectoryPath(orderPro.getProductionOrderNo());
|
||||
|
||||
@ -753,13 +770,11 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
pwProductionBill.setProduct(productInfos);
|
||||
|
||||
// 6. 调用API
|
||||
String apiResult = null;
|
||||
try {
|
||||
return callDrawingApi(pwProductionBill, orderPro);
|
||||
} catch (Exception e) {
|
||||
log.error("调用出图API失败", e);
|
||||
throw e;
|
||||
} finally {
|
||||
// 无论成功失败,都发送通知和文件
|
||||
apiResult = callDrawingApi(pwProductionBill, orderPro);
|
||||
|
||||
// 只有成功才发送通知和文件
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
@ -789,7 +804,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
}
|
||||
|
||||
if (po.getIsEnterpriseStandard() != null) {
|
||||
markdownText.append("> **是否企标**: ").append(po.getIsEnterpriseStandard() == "1" ? "是" : "否").append("\n\n");
|
||||
markdownText.append("> **是否企标**: ").append(Objects.equals(po.getIsEnterpriseStandard(), "1") ? "是" : "否").append("\n\n");
|
||||
}
|
||||
|
||||
String bomStatus = null;
|
||||
@ -827,63 +842,44 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
}
|
||||
|
||||
// 下载并发送Excel文件
|
||||
log.info("开始下载并发送Excel文件: {}", orderPro.getProductionOrderNo());
|
||||
log.info("开始生成并发送Excel模板文件: {}", orderPro.getProductionOrderNo());
|
||||
|
||||
// 1. 解析年份
|
||||
String productionCode = orderPro.getProductionOrderNo();
|
||||
String year = "2025"; // 默认
|
||||
if (productionCode != null) {
|
||||
if (productionCode.startsWith("EY")) {
|
||||
if (productionCode.length() >= 4) {
|
||||
try {
|
||||
Integer.parseInt(productionCode.substring(2, 4));
|
||||
year = "20" + productionCode.substring(2, 4);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int firstDash = productionCode.indexOf("-");
|
||||
if (firstDash >= 0 && firstDash + 3 <= productionCode.length()) {
|
||||
try {
|
||||
Integer.parseInt(productionCode.substring(firstDash + 1, firstDash + 3));
|
||||
year = "20" + productionCode.substring(firstDash + 1, firstDash + 3);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
String filePath = null;
|
||||
try {
|
||||
ProcessOrderProController controller = SpringUtils.getBean(ProcessOrderProController.class);
|
||||
filePath = controller.generateRoute3Excel(orderPro.getId(), true);
|
||||
} catch (Exception e) {
|
||||
log.error("生成工艺计划表模板Excel失败", e);
|
||||
}
|
||||
|
||||
// 2. 构建FTP路径和本地路径
|
||||
String ftpPath = "/" + year + "/" + productionCode;
|
||||
String localPath = "D:\\file\\";
|
||||
|
||||
// 3. 下载文件(只下载汇总表.xlsx)
|
||||
R<String> downloadResult = FtpUtil.downloadFtpDirectoryFiles("192.168.5.18", "admin", "hbyt2025", 21, ftpPath, localPath, "汇总表.xlsx");
|
||||
if (downloadResult == null || downloadResult.getCode() != 200) {
|
||||
log.warn("Excel文件下载可能失败: productionCode={}, ftpPath={}, result={}", productionCode, ftpPath, downloadResult);
|
||||
}
|
||||
|
||||
// 4. 构建本地文件路径并发送钉钉文件
|
||||
String filePath = localPath + productionCode + "汇总表.xlsx";
|
||||
File excelFile = new File(filePath);
|
||||
if (excelFile.exists() && excelFile.length() > 0) {
|
||||
// Upload media
|
||||
String mediaId = mediaService.uploadMedia("file", filePath);
|
||||
// Send file给个人
|
||||
if (StringUtils.isNotBlank(dingTalkProperties.getNotifyUserId())) {
|
||||
robotGroupService.sendFileToUser(dingTalkProperties.getNotifyUserId(), mediaId, excelFile.getName());
|
||||
log.info("Excel文件发送成功");
|
||||
if (filePath != null) {
|
||||
File excelFile = new File(filePath);
|
||||
if (excelFile.exists() && excelFile.length() > 0) {
|
||||
// Upload media
|
||||
String mediaId = mediaService.uploadMedia("file", filePath);
|
||||
// Send file给个人
|
||||
if (StringUtils.isNotBlank(dingTalkProperties.getNotifyUserId())) {
|
||||
robotGroupService.sendFileToUser(dingTalkProperties.getNotifyUserId(), mediaId, excelFile.getName());
|
||||
log.info("Excel模板文件发送成功");
|
||||
} else {
|
||||
log.warn("未配置钉钉通知个人ID(notifyUserId),跳过发送Excel文件");
|
||||
}
|
||||
// 发送完毕后删除生成的临时文件
|
||||
excelFile.delete();
|
||||
} else {
|
||||
log.warn("未配置钉钉通知个人ID(notifyUserId),跳过发送Excel文件");
|
||||
log.warn("Excel文件未找到或为空: {}", filePath);
|
||||
}
|
||||
} else {
|
||||
log.warn("Excel文件未找到或为空: {}", filePath);
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
log.error("发送出图通知/文件失败", ex);
|
||||
}
|
||||
});
|
||||
|
||||
return apiResult;
|
||||
} catch (Exception e) {
|
||||
log.error("调用出图API失败", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1264,13 +1260,6 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
String drawingDate = JSONObject.toJSONString(pwProductionBill);
|
||||
log.info("请求出图报文=====>{}", drawingDate);
|
||||
|
||||
String preStatusResp = HttpUtils.sendGet(statusUrl, "UTF-8");
|
||||
JSONObject preJson = JSONObject.parseObject(preStatusResp);
|
||||
String preStatus = preJson.getString("status");
|
||||
if ("busy".equals(preStatus)) {
|
||||
throw new RuntimeException("出图服务忙,请稍后再试");
|
||||
}
|
||||
|
||||
// 1. 先发起生成请求
|
||||
String response = HttpUtils.sendPost(apiUrl, drawingDate);
|
||||
log.info("出图接口响应=====>{}", response);
|
||||
@ -1357,78 +1346,134 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
/**
|
||||
* 下载DWG文件
|
||||
*
|
||||
* @param ids FigureSaveVo ID列表
|
||||
* @param id FigureSaveVo ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public R<String> downloadDwg(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return R.fail("ID列表为空");
|
||||
}
|
||||
|
||||
// 只下载第一个文件
|
||||
FigureSave figureSave = figureSaveMapper.selectById(ids.get(0));
|
||||
if (figureSave == null) {
|
||||
return R.fail("未找到对应的产品信息");
|
||||
}
|
||||
|
||||
// 创建临时目录
|
||||
String tempBaseDir = "D:\\dwg_temp\\" + System.currentTimeMillis();
|
||||
File tempDir = new File(tempBaseDir);
|
||||
if (!tempDir.exists() && !tempDir.mkdirs()) {
|
||||
return R.fail("创建临时目录失败");
|
||||
public R<String> downloadDwg(Long id) {
|
||||
if (id == null) {
|
||||
return R.fail("ID为空");
|
||||
}
|
||||
|
||||
try {
|
||||
String productionCode = figureSave.getProductionCode();
|
||||
String figureName = figureSave.getFigureName();
|
||||
|
||||
if (StringUtils.isEmpty(productionCode) || StringUtils.isEmpty(figureName)) {
|
||||
return R.fail("产品信息不完整");
|
||||
// 根据id查询对应的图号
|
||||
FigureSave figureSave = figureSaveMapper.selectById(id);
|
||||
if (figureSave == null) {
|
||||
return R.fail("未找到对应的产品信息");
|
||||
}
|
||||
// 1. 解析年份
|
||||
|
||||
// 假设这些文件属于同一个项目(生产令号)
|
||||
String productionCode = figureSave.getProductionCode();
|
||||
if (StringUtils.isBlank(productionCode)) {
|
||||
return R.fail("产品信息中生产令号为空");
|
||||
}
|
||||
|
||||
// 解析年份
|
||||
String year = "2025"; // 默认
|
||||
if (productionCode.startsWith("EY")) {
|
||||
if (productionCode.length() >= 4) {
|
||||
String yearShort = productionCode.substring(2, 4);
|
||||
try {
|
||||
Integer.parseInt(productionCode.substring(2, 4));
|
||||
year = "20" + productionCode.substring(2, 4);
|
||||
} catch (Exception ignore) {
|
||||
int y = Integer.parseInt(yearShort);
|
||||
if (y > 10 && y < 50) {
|
||||
year = "20" + yearShort;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 解析失败,保持默认
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int firstDash = productionCode.indexOf("-");
|
||||
if (firstDash >= 0 && firstDash + 3 <= productionCode.length()) {
|
||||
String yearShort = productionCode.substring(firstDash + 1, firstDash + 3);
|
||||
try {
|
||||
Integer.parseInt(productionCode.substring(firstDash + 1, firstDash + 3));
|
||||
year = "20" + productionCode.substring(firstDash + 1, firstDash + 3);
|
||||
int yearInt = Integer.parseInt(yearShort);
|
||||
if (yearInt > 10 && yearInt < 50) {
|
||||
year = "20" + yearShort;
|
||||
} else {
|
||||
if (firstDash >= 4 && productionCode.startsWith("EY")) {
|
||||
String altYearShort = productionCode.substring(2, 4);
|
||||
try {
|
||||
Integer.parseInt(altYearShort);
|
||||
year = "20" + altYearShort;
|
||||
} catch (Exception ex) {}
|
||||
} else if (firstDash >= 2) {
|
||||
String altYearShort = productionCode.substring(firstDash - 2, firstDash);
|
||||
try {
|
||||
Integer.parseInt(altYearShort);
|
||||
year = "20" + altYearShort;
|
||||
} catch (Exception ex) {}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. 构建FTP路径和本地路径
|
||||
// FTP路径: /{year}/{productionCode}/{figureName}.dwg
|
||||
String ftpPath = "/" + year + "/" + productionCode + "/" + figureName + ".dwg";
|
||||
String fileName = figureName + ".dwg";
|
||||
// 本地存放路径
|
||||
String localFilePath = tempBaseDir + File.separator + fileName;
|
||||
log.info("开始下载DWG: ftpPath={}, localFilePath={}", ftpPath, localFilePath);
|
||||
// 3. 下载文件
|
||||
boolean downloadSuccess = FtpUtil.downloadSingleFile("192.168.5.18", "admin", "hbyt2025", 21, ftpPath, localFilePath);
|
||||
if (downloadSuccess) {
|
||||
File downloadedFile = new File(localFilePath);
|
||||
if (downloadedFile.exists() && downloadedFile.length() > 0) {
|
||||
// 直接返回文件路径,无需打包
|
||||
return R.ok("操作成功", localFilePath);
|
||||
} else {
|
||||
return R.fail("文件下载失败:文件为空");
|
||||
}
|
||||
|
||||
// 生成到本地的路径: 根据项目号生成文件夹
|
||||
String localBaseDir = buildWorkDirectory(productionCode);
|
||||
String targetDir = localBaseDir + productionCode + "/dwg";
|
||||
File targetDirectory = new File(targetDir);
|
||||
if (!targetDirectory.exists()) {
|
||||
targetDirectory.mkdirs();
|
||||
} else {
|
||||
return R.fail("文件下载失败");
|
||||
// 清空之前的旧文件
|
||||
File[] files = targetDirectory.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String figureNumber = figureSave.getFigureNumber();
|
||||
if (StringUtils.isBlank(figureNumber)) {
|
||||
return R.fail("图号为空,无法下载DWG文件");
|
||||
}
|
||||
|
||||
// 替换图号中的 / 为 -
|
||||
String safeFigureNumber = figureNumber.replace("/", "-");
|
||||
String dwgFileName = safeFigureNumber + ".dwg";
|
||||
|
||||
// 修正:有些 DWG 文件就放在根目录下,所以直接用生产令号作为远程目录
|
||||
// 或者有可能 DWG 放在 ` /{year}/{productionCode}/dwg ` 下
|
||||
String rootRemoteDir = "/" + year + "/" + productionCode;
|
||||
|
||||
log.info("尝试下载DWG文件, 远程目录: {}, 本地目录: {}, 文件名: {}", rootRemoteDir, targetDir, dwgFileName);
|
||||
|
||||
// 参考单元测试,调用 FtpUtil 下载文件
|
||||
R<String> downloadResult = FtpUtil.downloadFtpDirectoryFiles(
|
||||
"192.168.5.18", "admin", "hbyt2025", 21, rootRemoteDir, targetDir, dwgFileName
|
||||
);
|
||||
|
||||
if (downloadResult.getCode() != 200) {
|
||||
// 尝试加一层子目录去下载(防止它藏在图号同名文件夹下)
|
||||
String subRemoteDir = "/" + year + "/" + productionCode + "/" + safeFigureNumber;
|
||||
log.warn("根目录 {} 下载失败,尝试从子目录 {} 下载", rootRemoteDir, subRemoteDir);
|
||||
downloadResult = FtpUtil.downloadFtpDirectoryFiles(
|
||||
"192.168.5.18", "admin", "hbyt2025", 21, subRemoteDir, targetDir, dwgFileName
|
||||
);
|
||||
}
|
||||
|
||||
if (downloadResult.getCode() != 200) {
|
||||
// 有的旧数据可能在 /2026/KC-26-026-FS/dwg 下
|
||||
String dwgRemoteDir = "/" + year + "/" + productionCode + "/dwg";
|
||||
log.warn("子目录下载也失败,尝试从dwg子目录 {} 下载", dwgRemoteDir);
|
||||
downloadResult = FtpUtil.downloadFtpDirectoryFiles(
|
||||
"192.168.5.18", "admin", "hbyt2025", 21, dwgRemoteDir, targetDir, dwgFileName
|
||||
);
|
||||
}
|
||||
|
||||
if (downloadResult.getCode() != 200) {
|
||||
return R.fail("DWG文件下载失败: " + dwgFileName + " - " + downloadResult.getMsg());
|
||||
}
|
||||
|
||||
// 直接返回目标文件夹路径
|
||||
return R.ok(targetDir);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("下载DWG过程中发生异常", e);
|
||||
return R.fail("下载异常: " + e.getMessage());
|
||||
log.error("下载DWG文件失败", e);
|
||||
return R.fail("下载DWG文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1491,8 +1536,10 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
if (productionCode.length() >= 4) {
|
||||
String yearShort = productionCode.substring(2, 4);
|
||||
try {
|
||||
Integer.parseInt(yearShort);
|
||||
year = "20" + yearShort;
|
||||
int y = Integer.parseInt(yearShort);
|
||||
if (y > 10 && y < 50) {
|
||||
year = "20" + yearShort;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 解析失败,保持默认
|
||||
}
|
||||
@ -1503,8 +1550,31 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
if (firstDash >= 0 && firstDash + 3 <= productionCode.length()) {
|
||||
String yearShort = productionCode.substring(firstDash + 1, firstDash + 3);
|
||||
try {
|
||||
Integer.parseInt(yearShort); // 验证是否为数字
|
||||
year = "20" + yearShort;
|
||||
// 简单的年份合理性校验,比如 > 10 且 < 50
|
||||
int yearInt = Integer.parseInt(yearShort);
|
||||
if (yearInt > 10 && yearInt < 50) {
|
||||
year = "20" + yearShort;
|
||||
} else {
|
||||
// 如果提取出来的像 00,可能格式是 EY2609-002,横杠前其实是年份 26
|
||||
if (firstDash >= 4 && productionCode.startsWith("EY")) {
|
||||
String altYearShort = productionCode.substring(2, 4);
|
||||
try {
|
||||
Integer.parseInt(altYearShort);
|
||||
year = "20" + altYearShort;
|
||||
} catch (Exception ex) {
|
||||
// ignore
|
||||
}
|
||||
} else if (firstDash >= 2) {
|
||||
// 比如 XX26-001,可能是字母加数字
|
||||
String altYearShort = productionCode.substring(firstDash - 2, firstDash);
|
||||
try {
|
||||
Integer.parseInt(altYearShort);
|
||||
year = "20" + altYearShort;
|
||||
} catch (Exception ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 解析失败,保持默认
|
||||
}
|
||||
@ -1958,15 +2028,13 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
e.printStackTrace();
|
||||
return "上传到FTP失败";
|
||||
}
|
||||
|
||||
if (!ftpSuccess) {
|
||||
return "上传到FTP失败";
|
||||
}
|
||||
|
||||
// 5. 反写FTP路径到数据库
|
||||
figureSave.setDrawPath("E:" + basePath + ftpPath + fullLocalPath);
|
||||
figureSave.setUploadTime(new Date());
|
||||
int updateCount = figureSaveMapper.updateById(figureSave);
|
||||
|
||||
return updateCount == 1 ? "上传成功" : "上传失败";
|
||||
}
|
||||
|
||||
@ -2047,7 +2115,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<ProcessRouteExcelDTO> getRouteAndBomDetail(List<ProcessRoute> routlist, List<ProductionOrderVo> processDataList, ProcessOrderPro orderPro) {
|
||||
public List<ProcessRoute> getRouteAndBomDetail(List<ProcessRoute> routlist, List<ProductionOrderVo> processDataList, ProcessOrderPro orderPro) {
|
||||
String proRoot = orderPro.getProductionOrderNo();
|
||||
|
||||
// 查出所有工艺路线和BOM
|
||||
@ -2064,7 +2132,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
copyPropertiesIgnoreNull(source, route); // 用 routlist 的非空字段补充 allRoutes
|
||||
}
|
||||
}
|
||||
List<ProcessRouteExcelDTO> resultList = new ArrayList<>();
|
||||
List<ProcessRoute> resultList = new ArrayList<>();
|
||||
|
||||
// 规范化 key
|
||||
Function<String, String> normalize = s -> s == null ? "" : s.trim().toUpperCase();
|
||||
@ -2105,21 +2173,21 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
|
||||
if (maxSize == 0 && orderVo != null) maxSize = 1;
|
||||
|
||||
for (int i = 0; i < maxSize; i++) {
|
||||
ProcessRouteExcelDTO dto = new ProcessRouteExcelDTO();
|
||||
ProcessRoute dto = new ProcessRoute();
|
||||
|
||||
// 补充名称/材质/单重字段
|
||||
dto.setMaterialCode(originalMaterialCode);
|
||||
dto.setMaterialName(originalMaterialName);
|
||||
dto.setMaterial(originalMaterial);
|
||||
dto.setDiscWeight(String.valueOf(originalUnitWeight));
|
||||
dto.setUnitQuantity(String.valueOf(unitQuantity));
|
||||
dto.setBatchQuantity(String.valueOf(batchQuantity));
|
||||
dto.setDiscWeight(Double.valueOf(String.valueOf(originalUnitWeight)));
|
||||
dto.setUnitQuantity(Double.valueOf(String.valueOf(unitQuantity)));
|
||||
dto.setBatchQuantity(Long.parseLong(String.valueOf(String.valueOf(batchQuantity))) );
|
||||
|
||||
// 订单数量信息
|
||||
if (orderVo != null) {
|
||||
dto.setRouteDescription(orderVo.getProductionOrderNo());
|
||||
dto.setUnitQuantity(String.valueOf(orderVo.getQuantity()));
|
||||
dto.setBatchQuantity(orderVo.getBatchQuantity());
|
||||
dto.setUnitQuantity(Double.valueOf(String.valueOf(orderVo.getQuantity())));
|
||||
dto.setBatchQuantity(Long.valueOf(orderVo.getBatchQuantity()));
|
||||
}
|
||||
|
||||
// 工艺部分
|
||||
|
||||
@ -44,6 +44,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
@ -1441,7 +1442,7 @@ public class ProcessRouteServiceImpl implements IProcessRouteService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generatePDFs(String rooteProdet) {
|
||||
public String generatePDFs(String rooteProdet) throws IOException {
|
||||
List<CombinedDTO> selecPlanRouteList = getSelecPlanRouteList(rooteProdet);
|
||||
String pathZIP = PDFGenerator.writeToPdf(selecPlanRouteList, rooteProdet);
|
||||
return pathZIP;
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.system.domain.bo.SfcOperationPlanningDetailBo;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningDetailVo;
|
||||
import com.ruoyi.system.domain.SfcOperationPlanningDetail;
|
||||
import com.ruoyi.system.mapper.SfcOperationPlanningDetailMapper;
|
||||
import com.ruoyi.system.service.ISfcOperationPlanningDetailService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 工序计划明细Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SfcOperationPlanningDetailServiceImpl implements ISfcOperationPlanningDetailService {
|
||||
|
||||
private final SfcOperationPlanningDetailMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询工序计划明细
|
||||
*/
|
||||
@Override
|
||||
public SfcOperationPlanningDetailVo queryById(Long id){
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工序计划明细列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<SfcOperationPlanningDetailVo> queryPageList(SfcOperationPlanningDetailBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<SfcOperationPlanningDetail> lqw = buildQueryWrapper(bo);
|
||||
Page<SfcOperationPlanningDetailVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工序计划明细列表
|
||||
*/
|
||||
@Override
|
||||
public List<SfcOperationPlanningDetailVo> queryList(SfcOperationPlanningDetailBo bo) {
|
||||
LambdaQueryWrapper<SfcOperationPlanningDetail> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SfcOperationPlanningDetail> buildQueryWrapper(SfcOperationPlanningDetailBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<SfcOperationPlanningDetail> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getMainId() != null, SfcOperationPlanningDetail::getMainId, bo.getMainId());
|
||||
lqw.eq(bo.getFid() != null, SfcOperationPlanningDetail::getFid, bo.getFid());
|
||||
lqw.eq(bo.getEntityEntryId() != null, SfcOperationPlanningDetail::getEntityEntryId, bo.getEntityEntryId());
|
||||
lqw.eq(bo.getOperNumber() != null, SfcOperationPlanningDetail::getOperNumber, bo.getOperNumber());
|
||||
lqw.eq(bo.getOperQty() != null, SfcOperationPlanningDetail::getOperQty, bo.getOperQty());
|
||||
lqw.eq(bo.getQualifiedQty() != null, SfcOperationPlanningDetail::getQualifiedQty, bo.getQualifiedQty());
|
||||
lqw.eq(bo.getTransOutQty() != null, SfcOperationPlanningDetail::getTransOutQty, bo.getTransOutQty());
|
||||
lqw.eq(bo.getUnqualifiedQty() != null, SfcOperationPlanningDetail::getUnqualifiedQty, bo.getUnqualifiedQty());
|
||||
lqw.eq(bo.getWastageQty() != null, SfcOperationPlanningDetail::getWastageQty, bo.getWastageQty());
|
||||
lqw.eq(bo.getTransInQty() != null, SfcOperationPlanningDetail::getTransInQty, bo.getTransInQty());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getDepartmentName()), SfcOperationPlanningDetail::getDepartmentName, bo.getDepartmentName());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getWorkCenterName()), SfcOperationPlanningDetail::getWorkCenterName, bo.getWorkCenterName());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getProcessName()), SfcOperationPlanningDetail::getProcessName, bo.getProcessName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOperator()), SfcOperationPlanningDetail::getOperator, bo.getOperator());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOperStatus()), SfcOperationPlanningDetail::getOperStatus, bo.getOperStatus());
|
||||
lqw.eq(bo.getSeqPlanStartTime() != null, SfcOperationPlanningDetail::getSeqPlanStartTime, bo.getSeqPlanStartTime());
|
||||
lqw.eq(bo.getSeqPlanFinishTime() != null, SfcOperationPlanningDetail::getSeqPlanFinishTime, bo.getSeqPlanFinishTime());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOptCtrlCode()), SfcOperationPlanningDetail::getOptCtrlCode, bo.getOptCtrlCode());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getKeyOper()), SfcOperationPlanningDetail::getKeyOper, bo.getKeyOper());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getActivityName()), SfcOperationPlanningDetail::getActivityName, bo.getActivityName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOperDescription()), SfcOperationPlanningDetail::getOperDescription, bo.getOperDescription());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工序计划明细
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(SfcOperationPlanningDetailBo bo) {
|
||||
SfcOperationPlanningDetail add = BeanUtil.toBean(bo, SfcOperationPlanningDetail.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工序计划明细
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(SfcOperationPlanningDetailBo bo) {
|
||||
SfcOperationPlanningDetail update = BeanUtil.toBean(bo, SfcOperationPlanningDetail.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(SfcOperationPlanningDetail entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除工序计划明细
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,334 @@
|
||||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ruoyi.system.domain.bo.SfcOperationPlanningMainBo;
|
||||
import com.ruoyi.system.domain.vo.SfcOperationPlanningMainVo;
|
||||
import com.ruoyi.system.domain.SfcOperationPlanningMain;
|
||||
import com.ruoyi.system.mapper.SfcOperationPlanningMainMapper;
|
||||
import com.ruoyi.system.mapper.SfcOperationPlanningDetailMapper;
|
||||
import com.ruoyi.system.domain.SfcOperationPlanningDetail;
|
||||
import com.ruoyi.system.runner.JdUtil;
|
||||
import com.ruoyi.system.domain.dto.OperationPlanResultDTO;
|
||||
import com.ruoyi.system.domain.dto.ProcessDetailDTO;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import java.util.Date;
|
||||
import com.ruoyi.system.service.ISfcOperationPlanningMainService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* 工序计划主Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-04-10
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SfcOperationPlanningMainServiceImpl implements ISfcOperationPlanningMainService {
|
||||
|
||||
private final SfcOperationPlanningMainMapper baseMapper;
|
||||
private final SfcOperationPlanningDetailMapper detailMapper;
|
||||
|
||||
/**
|
||||
* 同步K3工序计划数据
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean syncOperationPlanning() {
|
||||
List<OperationPlanResultDTO> structuredList = JdUtil.getStructuredOperationPlanningList();
|
||||
if (structuredList == null || structuredList.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (OperationPlanResultDTO resultDTO : structuredList) {
|
||||
// 查询是否已经存在该主表记录 (根据 fid)
|
||||
SfcOperationPlanningMain mainEntity = baseMapper.selectOne(Wrappers.<SfcOperationPlanningMain>lambdaQuery()
|
||||
.eq(SfcOperationPlanningMain::getFid, resultDTO.getFid()));
|
||||
|
||||
if (mainEntity == null) {
|
||||
mainEntity = new SfcOperationPlanningMain();
|
||||
}
|
||||
|
||||
mainEntity.setFid(resultDTO.getFid());
|
||||
mainEntity.setBillNo(resultDTO.getBillNo());
|
||||
mainEntity.setMoNumber(resultDTO.getMoNumber());
|
||||
mainEntity.setHbytSclh(resultDTO.getBatchNo());
|
||||
mainEntity.setProductCode(resultDTO.getProductCode());
|
||||
mainEntity.setProductName(resultDTO.getProductName());
|
||||
mainEntity.setProSpecification(resultDTO.getProSpecification());
|
||||
if (StringUtils.isNotBlank(resultDTO.getPlanStartTime())) {
|
||||
mainEntity.setPlanStartTime(DateUtil.parse(resultDTO.getPlanStartTime()));
|
||||
}
|
||||
if (StringUtils.isNotBlank(resultDTO.getPlanFinishTime())) {
|
||||
mainEntity.setPlanFinishTime(DateUtil.parse(resultDTO.getPlanFinishTime()));
|
||||
}
|
||||
|
||||
if (mainEntity.getId() == null) {
|
||||
baseMapper.insert(mainEntity);
|
||||
} else {
|
||||
baseMapper.updateById(mainEntity);
|
||||
}
|
||||
|
||||
Long mainId = mainEntity.getId();
|
||||
|
||||
// 处理子表数据
|
||||
List<ProcessDetailDTO> processList = resultDTO.getProcessList();
|
||||
if (processList != null && !processList.isEmpty()) {
|
||||
for (ProcessDetailDTO detailDTO : processList) {
|
||||
// 查询是否已经存在该明细记录 (根据 fid 和 entity_entry_id 和 oper_number)
|
||||
SfcOperationPlanningDetail detailEntity = detailMapper.selectOne(Wrappers.<SfcOperationPlanningDetail>lambdaQuery()
|
||||
.eq(SfcOperationPlanningDetail::getFid, resultDTO.getFid())
|
||||
.eq(SfcOperationPlanningDetail::getEntityEntryId, detailDTO.getEntityEntryId())
|
||||
.eq(detailDTO.getOperNo() != null, SfcOperationPlanningDetail::getOperNumber, detailDTO.getOperNo()));
|
||||
|
||||
if (detailEntity == null) {
|
||||
detailEntity = new SfcOperationPlanningDetail();
|
||||
}
|
||||
|
||||
detailEntity.setMainId(mainId);
|
||||
detailEntity.setFid(resultDTO.getFid());
|
||||
detailEntity.setEntityEntryId(detailDTO.getEntityEntryId());
|
||||
if (detailDTO.getOperNo() != null) {
|
||||
detailEntity.setOperNumber(Long.valueOf(detailDTO.getOperNo()));
|
||||
}
|
||||
detailEntity.setOperQty(detailDTO.getPlanQty() != null ? BigDecimal.valueOf(detailDTO.getPlanQty()) : null);
|
||||
detailEntity.setQualifiedQty(detailDTO.getOkQty() != null ? BigDecimal.valueOf(detailDTO.getOkQty()) : null);
|
||||
detailEntity.setTransOutQty(detailDTO.getTransOutQty() != null ? BigDecimal.valueOf(detailDTO.getTransOutQty()) : null);
|
||||
detailEntity.setUnqualifiedQty(detailDTO.getNgQty() != null ? BigDecimal.valueOf(detailDTO.getNgQty()) : null);
|
||||
detailEntity.setWastageQty(detailDTO.getWastageQty() != null ? BigDecimal.valueOf(detailDTO.getWastageQty()) : null);
|
||||
detailEntity.setTransInQty(detailDTO.getInQty() != null ? BigDecimal.valueOf(detailDTO.getInQty()) : null);
|
||||
|
||||
detailEntity.setDepartmentName(detailDTO.getDepartment());
|
||||
detailEntity.setWorkCenterName(detailDTO.getWorkCenter());
|
||||
detailEntity.setProcessName(detailDTO.getProcessName());
|
||||
detailEntity.setOperator(detailDTO.getOperator());
|
||||
detailEntity.setOperStatus(detailDTO.getStatus());
|
||||
|
||||
if (StringUtils.isNotBlank(detailDTO.getSeqPlanStartTime())) {
|
||||
detailEntity.setSeqPlanStartTime(DateUtil.parse(detailDTO.getSeqPlanStartTime()));
|
||||
}
|
||||
if (StringUtils.isNotBlank(detailDTO.getSeqPlanFinishTime())) {
|
||||
detailEntity.setSeqPlanFinishTime(DateUtil.parse(detailDTO.getSeqPlanFinishTime()));
|
||||
}
|
||||
|
||||
detailEntity.setOptCtrlCode(detailDTO.getOptCtrlCode());
|
||||
detailEntity.setKeyOper(detailDTO.getKeyOper());
|
||||
detailEntity.setActivityName(detailDTO.getActivity1Name());
|
||||
detailEntity.setOperDescription(detailDTO.getOperDescription());
|
||||
|
||||
if (detailEntity.getId() == null) {
|
||||
detailMapper.insert(detailEntity);
|
||||
} else {
|
||||
detailMapper.updateById(detailEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工序计划主
|
||||
*/
|
||||
@Override
|
||||
public SfcOperationPlanningMainVo queryById(Long id){
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工序计划主列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<SfcOperationPlanningMainVo> queryPageList(SfcOperationPlanningMainBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<SfcOperationPlanningMain> lqw = buildQueryWrapper(bo);
|
||||
Page<SfcOperationPlanningMainVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工序计划主列表
|
||||
*/
|
||||
@Override
|
||||
public List<SfcOperationPlanningMainVo> queryList(SfcOperationPlanningMainBo bo) {
|
||||
LambdaQueryWrapper<SfcOperationPlanningMain> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询工序计划主及明细级联数据
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<OperationPlanResultDTO> queryCascadePageList(SfcOperationPlanningMainBo bo, PageQuery pageQuery) {
|
||||
// 1. 构建主表查询条件
|
||||
LambdaQueryWrapper<SfcOperationPlanningMain> lqw = buildQueryWrapper(bo);
|
||||
// 过滤生产令号包含“暂停”的记录
|
||||
lqw.notLike(SfcOperationPlanningMain::getHbytSclh, "暂停");
|
||||
|
||||
// 2. 分页查询主表数据
|
||||
Page<SfcOperationPlanningMain> page = baseMapper.selectPage(pageQuery.build(), lqw);
|
||||
List<SfcOperationPlanningMain> mainList = page.getRecords();
|
||||
|
||||
if (mainList == null || mainList.isEmpty()) {
|
||||
return TableDataInfo.build(new ArrayList<>());
|
||||
}
|
||||
|
||||
// 3. 提取当前页所有主表的 ID
|
||||
List<Long> mainIds = mainList.stream().map(SfcOperationPlanningMain::getId).collect(Collectors.toList());
|
||||
|
||||
// 4. 根据主表 ID 批量查询子表数据,并按工序号升序排序
|
||||
List<SfcOperationPlanningDetail> detailList = detailMapper.selectList(Wrappers.<SfcOperationPlanningDetail>lambdaQuery()
|
||||
.in(SfcOperationPlanningDetail::getMainId, mainIds)
|
||||
.orderByAsc(SfcOperationPlanningDetail::getOperNumber));
|
||||
|
||||
// 5. 按主表 ID 对子表数据进行分组
|
||||
Map<Long, List<SfcOperationPlanningDetail>> detailMap = detailList.stream()
|
||||
.collect(Collectors.groupingBy(SfcOperationPlanningDetail::getMainId));
|
||||
|
||||
// 6. 组装返回给前端的层级结构
|
||||
List<OperationPlanResultDTO> resultList = new ArrayList<>();
|
||||
for (SfcOperationPlanningMain main : mainList) {
|
||||
OperationPlanResultDTO resultDTO = new OperationPlanResultDTO();
|
||||
resultDTO.setMoNumber(main.getMoNumber());
|
||||
resultDTO.setBatchNo(main.getHbytSclh());
|
||||
resultDTO.setProductCode(main.getProductCode());
|
||||
resultDTO.setProductName(main.getProductName());
|
||||
resultDTO.setBillNo(main.getBillNo());
|
||||
resultDTO.setProSpecification(main.getProSpecification());
|
||||
resultDTO.setPlanStartTime(main.getPlanStartTime() != null ? DateUtil.formatDateTime(main.getPlanStartTime()) : null);
|
||||
resultDTO.setPlanFinishTime(main.getPlanFinishTime() != null ? DateUtil.formatDateTime(main.getPlanFinishTime()) : null);
|
||||
resultDTO.setFid(main.getFid());
|
||||
|
||||
// 获取原生Date以便于后续排序使用
|
||||
resultDTO.setRawPlanStartTime(main.getPlanStartTime());
|
||||
|
||||
// 组装关联的子表数据
|
||||
List<ProcessDetailDTO> processList = new ArrayList<>();
|
||||
List<SfcOperationPlanningDetail> relatedDetails = detailMap.get(main.getId());
|
||||
if (relatedDetails != null) {
|
||||
for (SfcOperationPlanningDetail detail : relatedDetails) {
|
||||
ProcessDetailDTO detailDTO = new ProcessDetailDTO();
|
||||
detailDTO.setOperNo(detail.getOperNumber() != null ? detail.getOperNumber().intValue() : null);
|
||||
detailDTO.setProcessName(detail.getProcessName());
|
||||
detailDTO.setWorkCenter(detail.getWorkCenterName());
|
||||
detailDTO.setPlanQty(detail.getOperQty() != null ? detail.getOperQty().doubleValue() : null);
|
||||
detailDTO.setInQty(detail.getTransInQty() != null ? detail.getTransInQty().doubleValue() : null);
|
||||
detailDTO.setOkQty(detail.getQualifiedQty() != null ? detail.getQualifiedQty().doubleValue() : null);
|
||||
detailDTO.setNgQty(detail.getUnqualifiedQty() != null ? detail.getUnqualifiedQty().doubleValue() : null);
|
||||
detailDTO.setTransOutQty(detail.getTransOutQty() != null ? detail.getTransOutQty().doubleValue() : null);
|
||||
detailDTO.setWastageQty(detail.getWastageQty() != null ? detail.getWastageQty().doubleValue() : null);
|
||||
detailDTO.setDepartment(detail.getDepartmentName());
|
||||
detailDTO.setOperator(detail.getOperator());
|
||||
|
||||
// 状态映射
|
||||
String statusStr = detail.getOperStatus();
|
||||
if ("1".equals(statusStr)) detailDTO.setStatus("计划");
|
||||
else if ("2".equals(statusStr)) detailDTO.setStatus("计划下达");
|
||||
else if ("3".equals(statusStr)) detailDTO.setStatus("开工");
|
||||
else if ("4".equals(statusStr)) detailDTO.setStatus("已完工");
|
||||
else if ("5".equals(statusStr)) detailDTO.setStatus("结案");
|
||||
else if ("6".equals(statusStr)) detailDTO.setStatus("结算");
|
||||
else detailDTO.setStatus(statusStr);
|
||||
|
||||
detailDTO.setSeqPlanStartTime(detail.getSeqPlanStartTime() != null ? DateUtil.formatDateTime(detail.getSeqPlanStartTime()) : null);
|
||||
detailDTO.setSeqPlanFinishTime(detail.getSeqPlanFinishTime() != null ? DateUtil.formatDateTime(detail.getSeqPlanFinishTime()) : null);
|
||||
detailDTO.setOptCtrlCode(detail.getOptCtrlCode());
|
||||
detailDTO.setKeyOper(detail.getKeyOper());
|
||||
detailDTO.setActivity1Name(detail.getActivityName());
|
||||
detailDTO.setOperDescription(detail.getOperDescription());
|
||||
detailDTO.setEntityEntryId(detail.getEntityEntryId());
|
||||
|
||||
processList.add(detailDTO);
|
||||
}
|
||||
}
|
||||
resultDTO.setProcessList(processList);
|
||||
resultList.add(resultDTO);
|
||||
}
|
||||
|
||||
// 对当前页结果进行排序:先按生产令号升序,再按计划开始时间升序
|
||||
resultList.sort(Comparator
|
||||
.comparing(OperationPlanResultDTO::getBatchNo, Comparator.nullsLast(String::compareTo))
|
||||
.thenComparing(OperationPlanResultDTO::getRawPlanStartTime, Comparator.nullsLast(Date::compareTo)));
|
||||
|
||||
// 构造返回带 total 的 TableDataInfo
|
||||
TableDataInfo<OperationPlanResultDTO> rspData = new TableDataInfo<>();
|
||||
rspData.setCode(200);
|
||||
rspData.setRows(resultList);
|
||||
rspData.setMsg("查询成功");
|
||||
rspData.setTotal(page.getTotal());
|
||||
return rspData;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SfcOperationPlanningMain> buildQueryWrapper(SfcOperationPlanningMainBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<SfcOperationPlanningMain> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getFid() != null, SfcOperationPlanningMain::getFid, bo.getFid());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getBillNo()), SfcOperationPlanningMain::getBillNo, bo.getBillNo());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getMoNumber()), SfcOperationPlanningMain::getMoNumber, bo.getMoNumber());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getHbytSclh()), SfcOperationPlanningMain::getHbytSclh, bo.getHbytSclh());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getProductCode()), SfcOperationPlanningMain::getProductCode, bo.getProductCode());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getProductName()), SfcOperationPlanningMain::getProductName, bo.getProductName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getProSpecification()), SfcOperationPlanningMain::getProSpecification, bo.getProSpecification());
|
||||
lqw.eq(bo.getPlanStartTime() != null, SfcOperationPlanningMain::getPlanStartTime, bo.getPlanStartTime());
|
||||
lqw.eq(bo.getPlanFinishTime() != null, SfcOperationPlanningMain::getPlanFinishTime, bo.getPlanFinishTime());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工序计划主
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(SfcOperationPlanningMainBo bo) {
|
||||
SfcOperationPlanningMain add = BeanUtil.toBean(bo, SfcOperationPlanningMain.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工序计划主
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(SfcOperationPlanningMainBo bo) {
|
||||
SfcOperationPlanningMain update = BeanUtil.toBean(bo, SfcOperationPlanningMain.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(SfcOperationPlanningMain entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除工序计划主
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
3
ruoyi-system/src/main/resources/a
Normal file
3
ruoyi-system/src/main/resources/a
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -25,6 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="whetherDelay" column="whether_delay"/>
|
||||
<result property="delayDay" column="delay_day"/>
|
||||
<result property="drawTime" column="draw_time"/>
|
||||
<result property="uploadTime" column="upload_time"/>
|
||||
</resultMap>
|
||||
|
||||
</mapper>
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.SfcOperationPlanningDetailMapper">
|
||||
|
||||
<resultMap type="com.ruoyi.system.domain.SfcOperationPlanningDetail" id="SfcOperationPlanningDetailResult">
|
||||
<result property="id" column="id"/>
|
||||
<result property="mainId" column="main_id"/>
|
||||
<result property="fid" column="fid"/>
|
||||
<result property="entityEntryId" column="entity_entry_id"/>
|
||||
<result property="operNumber" column="oper_number"/>
|
||||
<result property="operQty" column="oper_qty"/>
|
||||
<result property="qualifiedQty" column="qualified_qty"/>
|
||||
<result property="transOutQty" column="trans_out_qty"/>
|
||||
<result property="unqualifiedQty" column="unqualified_qty"/>
|
||||
<result property="wastageQty" column="wastage_qty"/>
|
||||
<result property="transInQty" column="trans_in_qty"/>
|
||||
<result property="departmentName" column="department_name"/>
|
||||
<result property="workCenterName" column="work_center_name"/>
|
||||
<result property="processName" column="process_name"/>
|
||||
<result property="operator" column="operator"/>
|
||||
<result property="operStatus" column="oper_status"/>
|
||||
<result property="seqPlanStartTime" column="seq_plan_start_time"/>
|
||||
<result property="seqPlanFinishTime" column="seq_plan_finish_time"/>
|
||||
<result property="optCtrlCode" column="opt_ctrl_code"/>
|
||||
<result property="keyOper" column="key_oper"/>
|
||||
<result property="activityName" column="activity_name"/>
|
||||
<result property="operDescription" column="oper_description"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
</resultMap>
|
||||
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,25 @@
|
||||
<?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.system.mapper.SfcOperationPlanningMainMapper">
|
||||
|
||||
<resultMap type="com.ruoyi.system.domain.SfcOperationPlanningMain" id="SfcOperationPlanningMainResult">
|
||||
<result property="id" column="id"/>
|
||||
<result property="fid" column="fid"/>
|
||||
<result property="billNo" column="bill_no"/>
|
||||
<result property="moNumber" column="mo_number"/>
|
||||
<result property="hbytSclh" column="hbyt_sclh"/>
|
||||
<result property="productCode" column="product_code"/>
|
||||
<result property="productName" column="product_name"/>
|
||||
<result property="proSpecification" column="pro_specification"/>
|
||||
<result property="planStartTime" column="plan_start_time"/>
|
||||
<result property="planFinishTime" column="plan_finish_time"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
</resultMap>
|
||||
|
||||
|
||||
</mapper>
|
||||
28
ruoyi-system/src/main/resources/sql/bom_details.sql
Normal file
28
ruoyi-system/src/main/resources/sql/bom_details.sql
Normal file
@ -0,0 +1,28 @@
|
||||
-- ----------------------------
|
||||
-- Table structure for bom_details
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `bom_details`;
|
||||
CREATE TABLE `bom_details` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`sequence_number` int(11) DEFAULT NULL COMMENT '序号',
|
||||
`drawing_number` varchar(100) DEFAULT NULL COMMENT '图号',
|
||||
`name` varchar(200) DEFAULT NULL COMMENT '名称',
|
||||
`quantity` decimal(10,2) DEFAULT NULL COMMENT '数量',
|
||||
`material` varchar(100) DEFAULT NULL COMMENT '材料',
|
||||
`unit_weight` decimal(10,3) DEFAULT NULL COMMENT '单重(kg)',
|
||||
`total_weight` decimal(10,3) DEFAULT NULL COMMENT '总重(kg)',
|
||||
`remarks` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`batch_quantity` int(11) DEFAULT NULL COMMENT '批次数量',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建者',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新者',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_drawing_number` (`drawing_number`) COMMENT '图号索引',
|
||||
KEY `idx_create_time` (`create_time`) COMMENT '创建时间索引'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='BOM物料清单明细表';
|
||||
|
||||
-- ----------------------------
|
||||
-- 表注释
|
||||
-- ----------------------------
|
||||
ALTER TABLE bom_details COMMENT 'BOM物料清单明细表';
|
||||
Loading…
Reference in New Issue
Block a user