更新,工序计划
This commit is contained in:
parent
9fb3090015
commit
ee9fb0149f
@ -332,7 +332,7 @@ dingtalk:
|
||||
#工艺审核互动卡片回调路由Key
|
||||
callback-route-key: update_route_status2
|
||||
# 出图完成 / PLM图纸到达通知(钉钉 userId/staffId,多人逗号分隔;以 0 开头的须加引号)
|
||||
notify-user-id: "293047444820124967,0108392663561185496,03610242692523410847"
|
||||
notify-user-id: "293047444820124967,0108392663561185496,03610242692523410847,283633411029629036"
|
||||
# 工艺审核互动卡片
|
||||
route-approval-id: "0108392663561185496"
|
||||
# 工艺计划推送用户;无需做工艺且出图完成后、PLM 总装BOM与工艺都到齐后通知生管
|
||||
|
||||
BIN
ruoyi-admin/src/main/resources/jpg/生产及工艺计划模版.xlsx
Normal file
BIN
ruoyi-admin/src/main/resources/jpg/生产及工艺计划模版.xlsx
Normal file
Binary file not shown.
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.common.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 图纸/模型文件查询服务。工艺路线「下载图纸模型」由后端转发,不让浏览器直连。
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "app.drawing.file-query")
|
||||
public class DrawingFileQueryProperties {
|
||||
|
||||
/** 文件服务根地址,如 http://192.168.5.20:28000 */
|
||||
private String baseUrl = "http://192.168.5.20:28000";
|
||||
|
||||
/** 下载路径 */
|
||||
private String downloadPath = "/api/FileQuery/download";
|
||||
|
||||
/** 读取超时(毫秒),模型文件可能较大 */
|
||||
private int readTimeoutMs = 300_000;
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.ruoyi.common.convert;
|
||||
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||
import com.alibaba.excel.metadata.data.ReadCellData;
|
||||
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 单台数量按原文读取。数字单元格转成普通数字文本,文本单元格保留 {@code 1/4}、{@code 1/3} 这类分数。
|
||||
*/
|
||||
public class NullableQuantityTextConverter implements Converter<String> {
|
||||
|
||||
@Override
|
||||
public Class<String> supportJavaTypeKey() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
if (cellData == null || cellData.getType() == null || cellData.getType() == CellDataTypeEnum.EMPTY) {
|
||||
return null;
|
||||
}
|
||||
switch (cellData.getType()) {
|
||||
case NUMBER:
|
||||
BigDecimal number = cellData.getNumberValue();
|
||||
if (number == null) {
|
||||
return null;
|
||||
}
|
||||
return number.stripTrailingZeros().toPlainString();
|
||||
case STRING:
|
||||
return ExcelNullableText.normalize(cellData.getStringValue());
|
||||
case BOOLEAN:
|
||||
if (cellData.getBooleanValue() == null) {
|
||||
return null;
|
||||
}
|
||||
return cellData.getBooleanValue() ? "1" : "0";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
return new WriteCellData<>(value == null ? "" : value);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,343 @@
|
||||
package com.ruoyi.common.dingding.relay;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ruoyi.common.config.DingTalkProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 待办中转:先登录取 token,再以 multipart 调用 /ding/send 的 createToBeDoneUser。
|
||||
*/
|
||||
@Service
|
||||
public class DingTodoRelayClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DingTodoRelayClient.class);
|
||||
|
||||
/**
|
||||
* 中转按该名字反射钉钉创建待办
|
||||
*/
|
||||
public static final String METHOD_CREATE_TO_BE_DONE_USER = "createToBeDoneUser";
|
||||
|
||||
private static final long DAY_MILLIS = 24L * 60L * 60L * 1000L;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private final DingTalkProperties properties;
|
||||
|
||||
private volatile String cachedToken;
|
||||
|
||||
public DingTodoRelayClient(DingTalkProperties properties) {
|
||||
this.properties = properties;
|
||||
RestTemplate template = new RestTemplate();
|
||||
template.setErrorHandler(new DefaultResponseErrorHandler() {
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
// 业务码在响应体里,HTTP 4xx 也把正文带回来
|
||||
}
|
||||
});
|
||||
this.restTemplate = template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 账号未配齐时不调用中转。
|
||||
*/
|
||||
public boolean isConfigured() {
|
||||
return StringUtils.hasText(properties.getTodoBaseUrl())
|
||||
&& StringUtils.hasText(properties.getTodoUsername())
|
||||
&& StringUtils.hasText(properties.getTodoPassword())
|
||||
&& StringUtils.hasText(properties.getTodoUuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按场景文案创建工艺计划表待办。
|
||||
*
|
||||
* @param userIdsCsv 逗号分隔的钉钉 userId
|
||||
* @param createUser 创建人,空则用接收人中的第一个
|
||||
* @param dueDays 没有未来的工艺预计完成时间时,截止为当前时间加该天数
|
||||
* @param contentLead 正文第一句,空则用默认说明
|
||||
* @param recipientType code=考勤工号,name=姓名
|
||||
*/
|
||||
public String createProcessPlanTodo(String productionOrderNo, String projectName, Date routeUptime,
|
||||
File excelFile, String userIdsCsv, String createUser, int dueDays,
|
||||
String contentLead, String recipientType) {
|
||||
List<String> users = splitUsers(userIdsCsv);
|
||||
if (users.isEmpty()) {
|
||||
throw new IllegalStateException("待办接收人为空");
|
||||
}
|
||||
String creator = StringUtils.hasText(createUser) ? createUser.trim() : users.get(0);
|
||||
String orderNo = StringUtils.hasText(productionOrderNo) ? productionOrderNo.trim() : "";
|
||||
String name = StringUtils.hasText(projectName) ? projectName.trim() : "";
|
||||
String fileName = excelFile != null ? excelFile.getName() : "";
|
||||
String title = "工艺任务待处理" + (StringUtils.hasText(orderNo) ? " " + orderNo : "");
|
||||
StringBuilder content = new StringBuilder();
|
||||
content.append(StringUtils.hasText(contentLead) ? contentLead.trim() : "工艺计划表已下发至ERP数据推送机器人或登录EVO物料系统导出工艺计划表,请及时处理。");
|
||||
if (StringUtils.hasText(orderNo)) {
|
||||
content.append("\n项目令号:").append(orderNo);
|
||||
}
|
||||
if (StringUtils.hasText(name)) {
|
||||
content.append("\n项目名称:").append(name);
|
||||
}
|
||||
if (StringUtils.hasText(fileName)) {
|
||||
content.append("\n文件:").append(fileName);
|
||||
}
|
||||
return createToBeDoneUser(users, creator, title, content.toString(), resolveDueTime(routeUptime, dueDays), excelFile, recipientType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用中转创建待办。
|
||||
*
|
||||
* @param users 接收人,类型由 recipientInfo / recipientType 决定
|
||||
* @param createUser 创建人,类型同接收人
|
||||
* @param dueTimeMillis 截止时间,毫秒时间戳
|
||||
* @param attachment 可选附件,表单字段名 files
|
||||
* @return 中转原始响应
|
||||
*/
|
||||
public String createToBeDoneUser(List<String> users, String createUser, String title, String content,
|
||||
long dueTimeMillis, File attachment, String recipientType) {
|
||||
if (!isConfigured()) {
|
||||
throw new IllegalStateException("未配置钉钉待办中转账号(dingtalk.todo-username/password/uuid/todo-base-url)");
|
||||
}
|
||||
if (users == null || users.isEmpty()) {
|
||||
throw new IllegalArgumentException("待办接收人不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(createUser)) {
|
||||
throw new IllegalArgumentException("待办创建人不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(title) || !StringUtils.hasText(content)) {
|
||||
throw new IllegalArgumentException("待办标题和内容不能为空");
|
||||
}
|
||||
if (dueTimeMillis <= 0) {
|
||||
throw new IllegalArgumentException("待办截止时间无效");
|
||||
}
|
||||
|
||||
String robotCode = resolveRobotCode();
|
||||
String clientId = StringUtils.hasText(properties.getAppKey()) ? properties.getAppKey().trim() : robotCode;
|
||||
String clientSecret = properties.getAppSecret();
|
||||
if (!StringUtils.hasText(clientSecret)) {
|
||||
throw new IllegalStateException("未配置 dingtalk.app-secret,无法创建待办");
|
||||
}
|
||||
|
||||
JSONObject data = new JSONObject(true);
|
||||
data.put("month", METHOD_CREATE_TO_BE_DONE_USER);
|
||||
data.put("robotCode", robotCode);
|
||||
data.put("clientId", clientId);
|
||||
data.put("clientSecret", clientSecret.trim());
|
||||
data.put("recipientType", normalizeRecipientType(recipientType));
|
||||
data.put("recipientInfo", "unionId");
|
||||
data.put("createUser", createUser.trim());
|
||||
data.put("users", users);
|
||||
data.put("title", title);
|
||||
data.put("content", content);
|
||||
data.put("dueTime", dueTimeMillis);
|
||||
|
||||
String body = postSend(data.toJSONString(), attachment, false);
|
||||
log.info("钉钉待办已创建 title={} users={} dueTime={}", title, users.size(), dueTimeMillis);
|
||||
return body;
|
||||
}
|
||||
|
||||
private String postSend(String dataJson, File attachment, boolean retried) {
|
||||
String token = currentToken(retried);
|
||||
String url = join(properties.getTodoBaseUrl(), "/ding/send");
|
||||
|
||||
MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
|
||||
form.add("data", dataJson);
|
||||
if (attachment != null && attachment.exists() && attachment.length() > 0) {
|
||||
form.add("files", new FileSystemResource(attachment));
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
String headerName = defaultText(properties.getTodoTokenHeader(), "Authorization");
|
||||
String prefix = properties.getTodoTokenPrefix() == null ? "" : properties.getTodoTokenPrefix();
|
||||
headers.set(headerName, prefix + token);
|
||||
|
||||
log.info("创建钉钉待办请求 POST {} Content-Type=multipart/form-data file={} data={}",
|
||||
url, attachment == null ? "" : attachment.getName(), dataJson);
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url, HttpMethod.POST, new HttpEntity<>(form, headers), String.class);
|
||||
String body = response.getBody();
|
||||
log.info("创建钉钉待办响应 status={} body={}", response.getStatusCodeValue(), body);
|
||||
if (isAuthFailure(response.getStatusCodeValue(), body) && !retried) {
|
||||
log.info("待办中转 token 失效,重新登录后重试");
|
||||
cachedToken = null;
|
||||
return postSend(dataJson, attachment, true);
|
||||
}
|
||||
assertBizOk(body, "创建待办");
|
||||
return body;
|
||||
}
|
||||
|
||||
private String currentToken(boolean forceLogin) {
|
||||
if (!forceLogin && StringUtils.hasText(cachedToken)) {
|
||||
return cachedToken;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!forceLogin && StringUtils.hasText(cachedToken)) {
|
||||
return cachedToken;
|
||||
}
|
||||
cachedToken = login();
|
||||
return cachedToken;
|
||||
}
|
||||
}
|
||||
|
||||
private String login() {
|
||||
String url = join(properties.getTodoBaseUrl(), "/login");
|
||||
JSONObject body = new JSONObject(true);
|
||||
body.put("username", properties.getTodoUsername().trim());
|
||||
body.put("password", properties.getTodoPassword());
|
||||
body.put("uuid", properties.getTodoUuid().trim());
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
String payload = body.toJSONString();
|
||||
log.info("待办中转登录请求 POST {} Content-Type=application/json body={}", url, payload);
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url, HttpMethod.POST, new HttpEntity<>(payload, headers), String.class);
|
||||
String raw = response.getBody();
|
||||
log.info("待办中转登录响应 status={} body={}", response.getStatusCodeValue(), raw);
|
||||
assertBizOk(raw, "待办中转登录");
|
||||
JSONObject root = JSON.parseObject(raw);
|
||||
String token = root == null ? null : root.getString("token");
|
||||
if (!StringUtils.hasText(token)) {
|
||||
throw new IllegalStateException("待办中转登录成功但未返回 token");
|
||||
}
|
||||
return token.trim();
|
||||
}
|
||||
|
||||
/** 截止时间用工艺预计完成时间;未填或已过期时,用当前时间加截止天数。 */
|
||||
private long resolveDueTime(Date routeUptime, int dueDays) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (routeUptime != null && routeUptime.getTime() > now) {
|
||||
return routeUptime.getTime();
|
||||
}
|
||||
int days = dueDays > 0 ? dueDays : 3;
|
||||
return now + days * DAY_MILLIS;
|
||||
}
|
||||
|
||||
private String resolveRobotCode() {
|
||||
if (StringUtils.hasText(properties.getRobotCode())) {
|
||||
return properties.getRobotCode().trim();
|
||||
}
|
||||
if (StringUtils.hasText(properties.getAppKey())) {
|
||||
return properties.getAppKey().trim();
|
||||
}
|
||||
throw new IllegalStateException("未配置 dingtalk.robot-code,无法创建待办");
|
||||
}
|
||||
|
||||
private static List<String> splitUsers(String userIdsCsv) {
|
||||
if (!StringUtils.hasText(userIdsCsv)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> users = new ArrayList<>();
|
||||
for (String part : userIdsCsv.split("[,,]")) {
|
||||
if (StringUtils.hasText(part)) {
|
||||
users.add(part.trim());
|
||||
}
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
private static void assertBizOk(String body, String action) {
|
||||
if (!StringUtils.hasText(body)) {
|
||||
throw new IllegalStateException(action + "响应为空");
|
||||
}
|
||||
JSONObject root;
|
||||
try {
|
||||
root = JSON.parseObject(body);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException(action + "响应无法解析: " + abbreviate(body));
|
||||
}
|
||||
if (root == null) {
|
||||
throw new IllegalStateException(action + "响应为空");
|
||||
}
|
||||
Integer code = root.getInteger("code");
|
||||
if (code == null || code != 200) {
|
||||
String msg = firstText(root.getString("mes"), root.getString("msg"), abbreviate(body));
|
||||
throw new IllegalStateException(action + "失败: " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAuthFailure(int httpStatus, String body) {
|
||||
if (httpStatus == 401 || httpStatus == 403) {
|
||||
return true;
|
||||
}
|
||||
if (!StringUtils.hasText(body)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
JSONObject root = JSON.parseObject(body);
|
||||
if (root == null) {
|
||||
return false;
|
||||
}
|
||||
Integer code = root.getInteger("code");
|
||||
if (code != null && code == 200) {
|
||||
return false;
|
||||
}
|
||||
if (code != null && (code == 401 || code == 403)) {
|
||||
return true;
|
||||
}
|
||||
if (code != null) {
|
||||
return false;
|
||||
}
|
||||
String msg = firstText(root.getString("mes"), root.getString("msg"), "");
|
||||
return msg.contains("token") || msg.contains("登录") || msg.contains("未授权") || msg.contains("认证");
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String join(String base, String path) {
|
||||
String b = base == null ? "" : base.trim();
|
||||
while (b.endsWith("/")) {
|
||||
b = b.substring(0, b.length() - 1);
|
||||
}
|
||||
String p = path.startsWith("/") ? path : "/" + path;
|
||||
return b + p;
|
||||
}
|
||||
|
||||
private static String normalizeRecipientType(String recipientType) {
|
||||
return "code".equalsIgnoreCase(defaultText(recipientType, "")) ? "code" : "name";
|
||||
}
|
||||
|
||||
private static String defaultText(String value, String fallback) {
|
||||
return StringUtils.hasText(value) ? value.trim() : fallback;
|
||||
}
|
||||
|
||||
private static String firstText(String... values) {
|
||||
if (values == null) {
|
||||
return "";
|
||||
}
|
||||
for (String value : values) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String abbreviate(String text) {
|
||||
if (text == null) {
|
||||
return "";
|
||||
}
|
||||
return text.length() <= 300 ? text : text.substring(0, 300);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package com.ruoyi.common.helper;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.context.second.SaTokenSecondContext;
|
||||
import cn.dev33.satoken.exception.SaTokenContextException;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 给定时任务线程补登录用户。
|
||||
* 不注册成 Spring Bean,避免和 Sa-Token 的 Web 上下文抢同一个注入点。
|
||||
* Web 请求仍走原来的上下文;只有当前线程调用过 {@link #login(LoginUser)} 时,这里才会生效。
|
||||
*/
|
||||
public class JobLoginContext implements SaTokenSecondContext {
|
||||
|
||||
private static final JobLoginContext INSTANCE = new JobLoginContext();
|
||||
|
||||
private static final ThreadLocal<Map<String, Object>> STORE = new ThreadLocal<>();
|
||||
|
||||
private final SaStorage storage = new ThreadStorage();
|
||||
|
||||
private JobLoginContext() {
|
||||
}
|
||||
|
||||
public static void login(LoginUser loginUser) {
|
||||
if (SaManager.getSaTokenSecondContext() != INSTANCE) {
|
||||
SaManager.setSaTokenSecondContext(INSTANCE);
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(LoginHelper.LOGIN_USER_KEY, loginUser);
|
||||
if (loginUser.getUserId() != null) {
|
||||
map.put(LoginHelper.USER_KEY, loginUser.getUserId());
|
||||
}
|
||||
STORE.set(map);
|
||||
}
|
||||
|
||||
public static void logout() {
|
||||
STORE.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return STORE.get() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaRequest getRequest() {
|
||||
throw new SaTokenContextException("定时任务登录上下文不提供 Request");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaResponse getResponse() {
|
||||
throw new SaTokenContextException("定时任务登录上下文不提供 Response");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaStorage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final class ThreadStorage implements SaStorage {
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return STORE.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
Map<String, Object> map = STORE.get();
|
||||
return map == null ? null : map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaStorage set(String key, Object value) {
|
||||
Map<String, Object> map = STORE.get();
|
||||
if (map == null) {
|
||||
map = new HashMap<>();
|
||||
STORE.set(map);
|
||||
}
|
||||
map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaStorage delete(String key) {
|
||||
Map<String, Object> map = STORE.get();
|
||||
if (map != null) {
|
||||
map.remove(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 项目整体出图时间驱动的延期/完成判定。
|
||||
* 工作日按一周六天(周一至周六),周日不计入延期天数。
|
||||
*/
|
||||
public final class DrawingDelayCalculator {
|
||||
|
||||
public static final ZoneId ZONE = ZoneId.of("Asia/Shanghai");
|
||||
|
||||
/** 方案设计完成 */
|
||||
public static final long BOM_STATUS_SCHEME_DONE = 0L;
|
||||
/** 设计出图完成 */
|
||||
public static final long BOM_STATUS_DRAWING_DONE = 1L;
|
||||
|
||||
private DrawingDelayCalculator() {
|
||||
}
|
||||
|
||||
public static boolean allProductsHaveDrawPath(Collection<String> drawPaths) {
|
||||
if (drawPaths == null || drawPaths.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (String path : drawPaths) {
|
||||
if (path == null || path.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 出图时间之后到 now 之间的工作日天数(不含出图当天,周日不计)。
|
||||
*/
|
||||
public static long delayWorkdays(Date drawingTime, Date now) {
|
||||
if (drawingTime == null || now == null || !now.after(drawingTime)) {
|
||||
return 0L;
|
||||
}
|
||||
LocalDate start = toLocalDate(drawingTime);
|
||||
LocalDate end = toLocalDate(now);
|
||||
long days = 0L;
|
||||
for (LocalDate d = start.plusDays(1); !d.isAfter(end); d = d.plusDays(1)) {
|
||||
if (d.getDayOfWeek() != DayOfWeek.SUNDAY) {
|
||||
days++;
|
||||
}
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
public static DrawingDelayResult evaluate(Date drawingTime, Date now, boolean drawingComplete, Long bomStatus) {
|
||||
boolean overdue = drawingTime != null && now != null && now.after(drawingTime);
|
||||
long days = delayWorkdays(drawingTime, now);
|
||||
|
||||
DrawingDelayResult result = new DrawingDelayResult();
|
||||
result.setDrawingComplete(drawingComplete);
|
||||
result.setWhetherDelay(overdue ? 1 : 0);
|
||||
result.setDelayDay(overdue ? days : 0L);
|
||||
if (drawingComplete) {
|
||||
result.setCurrentlyDelayed(false);
|
||||
result.setProgressLabel("设计出图完成");
|
||||
result.setBomStatus(promoteBomStatus(bomStatus));
|
||||
} else {
|
||||
result.setCurrentlyDelayed(overdue);
|
||||
result.setProgressLabel(overdue ? "延期" : "进行中");
|
||||
result.setBomStatus(bomStatus);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Long promoteBomStatus(Long bomStatus) {
|
||||
if (bomStatus == null || bomStatus == BOM_STATUS_SCHEME_DONE) {
|
||||
return BOM_STATUS_DRAWING_DONE;
|
||||
}
|
||||
return bomStatus;
|
||||
}
|
||||
|
||||
private static LocalDate toLocalDate(Date date) {
|
||||
return date.toInstant().atZone(ZONE).toLocalDate();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 项目出图延期计算结果。
|
||||
*/
|
||||
@Data
|
||||
public class DrawingDelayResult {
|
||||
|
||||
/** 已填产品是否全部有图纸路径 */
|
||||
private boolean drawingComplete;
|
||||
|
||||
/** 超期标识:0否 1是(完成后仍保留) */
|
||||
private Integer whetherDelay;
|
||||
|
||||
/** 延期工作日天数(完成后冻结) */
|
||||
private Long delayDay;
|
||||
|
||||
/** 回写的项目状态;完成时 0→1,已进入后续流程则保持不变 */
|
||||
private Long bomStatus;
|
||||
|
||||
/** 当前仍处于延期中(未完成且已过出图时间) */
|
||||
private boolean currentlyDelayed;
|
||||
|
||||
/** 进行中 / 延期 / 设计出图完成 */
|
||||
private String progressLabel;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
/**
|
||||
* 技术预研令号:令号中含 JSYY 时不属于生产令号,禁止更新物料清单、推送工艺。
|
||||
*/
|
||||
public final class JsyyOrderNo {
|
||||
|
||||
public static final String MARK = "JSYY";
|
||||
public static final String MSG = "JSYY技术预研令号不属于生产令号,严禁直接用于生产执行,禁止更新物料清单、禁止推送工艺";
|
||||
|
||||
private JsyyOrderNo() {
|
||||
}
|
||||
|
||||
public static boolean matches(String orderNo) {
|
||||
return orderNo != null && orderNo.toUpperCase().contains(MARK);
|
||||
}
|
||||
|
||||
public static String denyIfMatches(String orderNo) {
|
||||
return matches(orderNo) ? MSG : null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
/**
|
||||
* 项目状态(process_order_pro.bom_status)操作锁。
|
||||
* <ul>
|
||||
* <li>8 工艺审核完成:禁止出图、禁止上传工艺路线</li>
|
||||
* <li>6 开始生产 / 7 生产完成:禁止出图、上传工艺路线、推送工艺、更新物料清单</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class ProjectStatusLock {
|
||||
|
||||
/** 方案设计完成 / 设计出图中 */
|
||||
public static final long SCHEME_DONE = 0L;
|
||||
/** 设计出图完成 */
|
||||
public static final long DRAWING_DONE = 1L;
|
||||
/** 工艺待审核 */
|
||||
public static final long ROUTE_PENDING = 2L;
|
||||
/** 已推送BOM */
|
||||
public static final long BOM_PUSHED = 3L;
|
||||
/** 已推送工艺 */
|
||||
public static final long ROUTE_PUSHED = 4L;
|
||||
/** 开始生产 */
|
||||
public static final long IN_PRODUCTION = 6L;
|
||||
/** 生产完成 */
|
||||
public static final long PRODUCTION_DONE = 7L;
|
||||
/** 工艺审核完成 */
|
||||
public static final long ROUTE_APPROVED = 8L;
|
||||
/** 工艺审核拒绝 */
|
||||
public static final long ROUTE_REJECTED = 9L;
|
||||
|
||||
public enum Action {
|
||||
/** 执行出图 */
|
||||
DRAWING("出图"),
|
||||
/** 上传/导入工艺路线 */
|
||||
UPLOAD_ROUTE("上传工艺路线"),
|
||||
/** 推送工艺到金蝶 */
|
||||
PUSH_ROUTE("推送工艺"),
|
||||
/** 更新物料清单到金蝶 */
|
||||
UPDATE_MATERIAL("更新物料清单");
|
||||
|
||||
private final String label;
|
||||
|
||||
Action(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
private ProjectStatusLock() {
|
||||
}
|
||||
|
||||
public static String statusLabel(Long bomStatus) {
|
||||
if (bomStatus == null) {
|
||||
return "未知";
|
||||
}
|
||||
long s = bomStatus;
|
||||
if (s == SCHEME_DONE) return "方案设计完成";
|
||||
if (s == DRAWING_DONE) return "设计出图完成";
|
||||
if (s == ROUTE_PENDING) return "工艺待审核";
|
||||
if (s == BOM_PUSHED) return "已推送BOM";
|
||||
if (s == ROUTE_PUSHED) return "已推送工艺";
|
||||
if (s == 5L || s == IN_PRODUCTION) return "开始生产";
|
||||
if (s == PRODUCTION_DONE) return "生产完成";
|
||||
if (s == ROUTE_APPROVED) return "工艺审核完成";
|
||||
if (s == ROUTE_REJECTED) return "工艺审核拒绝";
|
||||
return String.valueOf(bomStatus);
|
||||
}
|
||||
|
||||
public static boolean isLocked(Long bomStatus, Action action) {
|
||||
return denyIfLocked(bomStatus, action) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 禁止时返回提示文案;允许时返回 null
|
||||
*/
|
||||
public static String denyIfLocked(Long bomStatus, Action action) {
|
||||
if (bomStatus == null || action == null) {
|
||||
return null;
|
||||
}
|
||||
long s = bomStatus;
|
||||
boolean productionLocked = s == 5L || s == IN_PRODUCTION || s == PRODUCTION_DONE;
|
||||
boolean routeApproved = s == ROUTE_APPROVED;
|
||||
|
||||
switch (action) {
|
||||
case DRAWING:
|
||||
case UPLOAD_ROUTE:
|
||||
if (productionLocked || routeApproved) {
|
||||
return buildMsg(bomStatus, action);
|
||||
}
|
||||
return null;
|
||||
case PUSH_ROUTE:
|
||||
case UPDATE_MATERIAL:
|
||||
if (productionLocked) {
|
||||
return buildMsg(bomStatus, action);
|
||||
}
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildMsg(Long bomStatus, Action action) {
|
||||
return "当前项目状态为「" + statusLabel(bomStatus) + "」,不允许" + action.getLabel();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package com.ruoyi.common.utils.file;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* PLM 落盘图纸文件名与物料编码(图号)匹配。
|
||||
* <p>
|
||||
* 标准名 {@code 图号_名称.pdf};历史名可能带 {@code -14位以上数字} 时间戳。
|
||||
* 不能用「图号-」做前缀匹配,否则 {@code QXCL3015040.01.0} 会误删 {@code QXCL3015040.01.0-MP*} 毛坯图。
|
||||
*/
|
||||
public final class PlmDrawingFileNames {
|
||||
|
||||
private static final Pattern TIMESTAMP_SUFFIX = Pattern.compile("-\\d{14,}$");
|
||||
|
||||
private PlmDrawingFileNames() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName 文件名或带路径的文件名
|
||||
* @param materialCode 物料编码/图号
|
||||
* @param separator 图号与名称分隔符,默认 {@code _}
|
||||
* @param extension 不含点或含点,如 {@code pdf} / {@code .dwg}
|
||||
*/
|
||||
public static boolean matchesMaterialCode(String fileName, String materialCode,
|
||||
String separator, String extension) {
|
||||
if (fileName == null || materialCode == null) {
|
||||
return false;
|
||||
}
|
||||
String code = materialCode.trim();
|
||||
if (code.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String name = fileNameOnly(fileName);
|
||||
String ext = normalizeExtension(extension);
|
||||
if (ext.isEmpty() || !name.toLowerCase(Locale.ROOT).endsWith(ext)) {
|
||||
return false;
|
||||
}
|
||||
String base = name.substring(0, name.length() - ext.length());
|
||||
base = TIMESTAMP_SUFFIX.matcher(base).replaceFirst("");
|
||||
if (base.equalsIgnoreCase(code)) {
|
||||
return true;
|
||||
}
|
||||
String sep = (separator == null || separator.isEmpty()) ? "_" : separator;
|
||||
String prefix = code + sep;
|
||||
return base.regionMatches(true, 0, prefix, 0, prefix.length());
|
||||
}
|
||||
|
||||
private static String fileNameOnly(String fileName) {
|
||||
String s = fileName.trim();
|
||||
int slash = Math.max(s.lastIndexOf('/'), s.lastIndexOf('\\'));
|
||||
return slash >= 0 ? s.substring(slash + 1) : s;
|
||||
}
|
||||
|
||||
private static String normalizeExtension(String extension) {
|
||||
if (extension == null) {
|
||||
return "";
|
||||
}
|
||||
String e = extension.trim().toLowerCase(Locale.ROOT);
|
||||
if (e.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return e.startsWith(".") ? e : ("." + e);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.ruoyi.system.constant;
|
||||
|
||||
/**
|
||||
* 可以勾选是否创建钉钉待办的业务方法。
|
||||
*/
|
||||
public final class DingTodoSceneCodes {
|
||||
|
||||
private DingTodoSceneCodes() {
|
||||
}
|
||||
|
||||
/** 出图完成后,工艺计划表发给工艺接收人 */
|
||||
public static final String DRAWING_PLAN_EXCEL = "DRAWING_PLAN_EXCEL";
|
||||
|
||||
/** 手动上传工艺计划表后,发给生管 */
|
||||
public static final String ROUTE_UPLOAD_PLAN = "ROUTE_UPLOAD_PLAN";
|
||||
|
||||
/** PLM 总装与工艺都到齐后,计划表发给生管 */
|
||||
public static final String PLM_PLAN_COMPLETE = "PLM_PLAN_COMPLETE";
|
||||
|
||||
/** 接收人留空时,用 dingtalk.notify-user-id */
|
||||
public static final String USER_SOURCE_NOTIFY = "NOTIFY";
|
||||
|
||||
/** 接收人留空时,用 dingtalk.plan-user-id */
|
||||
public static final String USER_SOURCE_PLAN = "PLAN";
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.ruoyi.system.constant;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.domain.ProcessOrderPro;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 项目令号出图模式 {@code process_order_pro.drawing_mode}。
|
||||
* 执行出图 / 添加产品前必须显式选择,不允许为空。
|
||||
*/
|
||||
public final class DrawingMode {
|
||||
|
||||
/** 正常:每个产品一行一个 ProductInfo,不按图纸路径合并 */
|
||||
public static final String NORMAL = "normal";
|
||||
|
||||
/** 零部件:同一图纸路径下多个子件合成一个 ProductInfo */
|
||||
public static final String PART = "part";
|
||||
|
||||
private DrawingMode() {
|
||||
}
|
||||
|
||||
public static boolean isValid(String mode) {
|
||||
if (StringUtils.isBlank(mode)) {
|
||||
return false;
|
||||
}
|
||||
String m = mode.trim().toLowerCase(Locale.ROOT);
|
||||
return NORMAL.equals(m) || PART.equals(m);
|
||||
}
|
||||
|
||||
public static String normalize(String mode) {
|
||||
if (StringUtils.isBlank(mode)) {
|
||||
return null;
|
||||
}
|
||||
return mode.trim().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 出图模式不能为空,必须为 normal / part。
|
||||
*/
|
||||
public static void requireConfigured(ProcessOrderPro order) {
|
||||
if (order == null) {
|
||||
throw new IllegalArgumentException("项目不存在");
|
||||
}
|
||||
requireConfigured(order.getDrawingMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 出图模式不能为空,必须为 normal / part。
|
||||
*/
|
||||
public static void requireConfigured(String drawingMode) {
|
||||
String mode = normalize(drawingMode);
|
||||
if (!isValid(mode)) {
|
||||
throw new IllegalArgumentException("出图模式不能为空,请先选择出图模式(正常/零部件)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否零部件出图模式。仅认显式 {@link #PART};未设置视为未配置(由 {@link #requireConfigured} 拦截)。
|
||||
*/
|
||||
public static boolean isPartMode(ProcessOrderPro order) {
|
||||
if (order == null) {
|
||||
return false;
|
||||
}
|
||||
return PART.equals(normalize(order.getDrawingMode()));
|
||||
}
|
||||
|
||||
/** 图纸路径比较用:去空白、统一斜杠 */
|
||||
public static String normalizeDrawPath(String drawPath) {
|
||||
if (StringUtils.isBlank(drawPath)) {
|
||||
return "";
|
||||
}
|
||||
return drawPath.trim().replace('\\', '/');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.annotation.RepeatSubmit;
|
||||
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.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.bo.DesignTaskDesignDimBo;
|
||||
import com.ruoyi.system.domain.bo.DesignTaskQueryBo;
|
||||
import com.ruoyi.system.domain.bo.DesignTaskReportBo;
|
||||
import com.ruoyi.system.domain.vo.DesignAccountSyncVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskBoardVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskReportVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskSummaryVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskVo;
|
||||
import com.ruoyi.system.service.IDesignTaskService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设计个人工作台
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/designTask")
|
||||
public class DesignTaskController extends BaseController {
|
||||
|
||||
private final IDesignTaskService designTaskService;
|
||||
|
||||
@SaCheckPermission("system:designTask:list")
|
||||
@GetMapping("/summary")
|
||||
public R<DesignTaskSummaryVo> summary() {
|
||||
return R.ok(designTaskService.summaryMine());
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:list")
|
||||
@GetMapping("/board")
|
||||
public R<DesignTaskBoardVo> board(String scope) {
|
||||
return R.ok(designTaskService.boardMine(scope));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:list")
|
||||
@GetMapping("/mine")
|
||||
public TableDataInfo<DesignTaskVo> mine(DesignTaskQueryBo bo, PageQuery pageQuery) {
|
||||
return designTaskService.queryMine(bo, pageQuery);
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:report")
|
||||
@Log(title = "设计报工", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit
|
||||
@PostMapping("/report")
|
||||
public R<Long> report(@Validated @RequestBody DesignTaskReportBo bo) {
|
||||
return R.ok(designTaskService.report(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:report")
|
||||
@Log(title = "接收设计任务", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit
|
||||
@PostMapping("/accept/{taskId}")
|
||||
public R<Void> accept(@NotNull @PathVariable Long taskId) {
|
||||
designTaskService.accept(taskId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:report")
|
||||
@Log(title = "标注设计类型", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/designDim")
|
||||
public R<Void> designDim(@Validated @RequestBody DesignTaskDesignDimBo bo) {
|
||||
designTaskService.updateDesignDim(bo);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:list")
|
||||
@GetMapping("/reports/{taskId}")
|
||||
public R<List<DesignTaskReportVo>> reports(@NotNull @PathVariable Long taskId) {
|
||||
return R.ok(designTaskService.listMyReports(taskId));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:report")
|
||||
@Log(title = "删除设计报工", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/reports/{reportId}")
|
||||
public R<Void> deleteReport(@NotNull @PathVariable Long reportId) {
|
||||
designTaskService.deleteReport(reportId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:orderPro:list")
|
||||
@GetMapping("/byOrder/{orderId}")
|
||||
public R<List<DesignTaskVo>> byOrder(@NotNull @PathVariable Long orderId) {
|
||||
return R.ok(designTaskService.listByOrder(orderId));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:designTask:sync")
|
||||
@Log(title = "同步设计账号", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/syncAccounts")
|
||||
public R<DesignAccountSyncVo> syncAccounts() {
|
||||
return R.ok(designTaskService.syncAccounts());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 北通道实验台设备状态
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/open/devstat")
|
||||
public class DevStatController {
|
||||
|
||||
private static final String STAT_URL = "http://192.168.5.25:9001/devstat/stat";
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@SaIgnore
|
||||
@GetMapping("/stat")
|
||||
public R<Object> stat() {
|
||||
try {
|
||||
String body = HttpRequest.get(STAT_URL).timeout(4000).execute().body();
|
||||
return R.ok(objectMapper.readValue(body, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.warn("北通道实验台状态获取失败: {}", e.getMessage());
|
||||
return R.fail("实验台状态接口不可达");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
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.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.DingTodoLog;
|
||||
import com.ruoyi.system.domain.DingTodoScene;
|
||||
import com.ruoyi.system.service.IDingTodoDispatchService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 选择哪些方法创建钉钉待办,并查看发送记录。
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/dingTodo")
|
||||
public class DingTodoController extends BaseController {
|
||||
|
||||
private final IDingTodoDispatchService dingTodoDispatchService;
|
||||
|
||||
@SaCheckPermission("system:dingTodo:list")
|
||||
@GetMapping("/scenes")
|
||||
public R<List<DingTodoScene>> scenes() {
|
||||
return R.ok(dingTodoDispatchService.listScenes());
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:dingTodo:edit")
|
||||
@Log(title = "钉钉待办方法", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/scenes")
|
||||
public R<Void> updateScene(@RequestBody DingTodoScene scene) {
|
||||
dingTodoDispatchService.updateScene(scene);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:dingTodo:list")
|
||||
@GetMapping("/logs")
|
||||
public TableDataInfo<DingTodoLog> logs(DingTodoLog query, PageQuery pageQuery) {
|
||||
return dingTodoDispatchService.queryLogs(query, pageQuery);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
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.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.bo.DrawingRouteOperLogBo;
|
||||
import com.ruoyi.system.domain.vo.DrawingRouteOperLogVo;
|
||||
import com.ruoyi.system.service.DrawingRouteOperLogRenotifyService;
|
||||
import com.ruoyi.system.service.IDrawingRouteOperLogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 出图 / 工艺上传 / 钉钉推送留痕
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/drawingRouteOperLog")
|
||||
public class DrawingRouteOperLogController extends BaseController {
|
||||
|
||||
private final IDrawingRouteOperLogService drawingRouteOperLogService;
|
||||
private final DrawingRouteOperLogRenotifyService drawingRouteOperLogRenotifyService;
|
||||
|
||||
@SaCheckPermission("system:drawingRouteOperLog:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<DrawingRouteOperLogVo> list(DrawingRouteOperLogBo bo, PageQuery pageQuery) {
|
||||
return drawingRouteOperLogService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:drawingRouteOperLog:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<DrawingRouteOperLogVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(drawingRouteOperLogService.queryDetail(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按这条日志重发对应钉钉通知。出图执行、工艺上传不能重发。
|
||||
*/
|
||||
@SaCheckPermission("system:drawingRouteOperLog:query")
|
||||
@Log(title = "重新通知", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/{id}/renotify")
|
||||
public R<Void> renotify(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(drawingRouteOperLogRenotifyService.renotify(id));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.domain.vo.mes.MesDrawingListVo;
|
||||
import com.ruoyi.system.service.IMesDrawingService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* MES 图纸开放接口:按生产令号查询路径与下载地址,并按图号下载 PDF / DWG。
|
||||
* <p>
|
||||
* 鉴权:请求头 {@code X-App-Key} / {@code X-App-Secret},或 query 参数 {@code appKey} / {@code appSecret}。
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/open/mes/drawing")
|
||||
public class MesDrawingController extends BaseController {
|
||||
|
||||
private final IMesDrawingService mesDrawingService;
|
||||
|
||||
/**
|
||||
* 按生产令号列出图纸。可选 materialCode 只返回该图号。
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/list")
|
||||
public R<MesDrawingListVo> list(
|
||||
@RequestHeader(value = "X-App-Key", required = false) String headerAppKey,
|
||||
@RequestHeader(value = "X-App-Secret", required = false) String headerAppSecret,
|
||||
@RequestParam(value = "appKey", required = false) String appKey,
|
||||
@RequestParam(value = "appSecret", required = false) String appSecret,
|
||||
@RequestParam("productionOrderNo") String productionOrderNo,
|
||||
@RequestParam(value = "materialCode", required = false) String materialCode) {
|
||||
mesDrawingService.assertAuth(firstNonBlank(headerAppKey, appKey), firstNonBlank(headerAppSecret, appSecret));
|
||||
return R.ok(mesDrawingService.listDrawings(productionOrderNo, materialCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按生产令号 + 物料编码下载图纸。fileType:pdf(默认)或 dwg。
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/download")
|
||||
public void download(
|
||||
@RequestHeader(value = "X-App-Key", required = false) String headerAppKey,
|
||||
@RequestHeader(value = "X-App-Secret", required = false) String headerAppSecret,
|
||||
@RequestParam(value = "appKey", required = false) String appKey,
|
||||
@RequestParam(value = "appSecret", required = false) String appSecret,
|
||||
@RequestParam("productionOrderNo") String productionOrderNo,
|
||||
@RequestParam("materialCode") String materialCode,
|
||||
@RequestParam(value = "fileType", required = false) String fileType,
|
||||
HttpServletResponse response) {
|
||||
try {
|
||||
mesDrawingService.assertAuth(firstNonBlank(headerAppKey, appKey), firstNonBlank(headerAppSecret, appSecret));
|
||||
File file = mesDrawingService.resolveDownloadFile(productionOrderNo, materialCode, fileType);
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
String fileName = URLEncoder.encode(file.getName(), StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
|
||||
response.setContentLengthLong(file.length());
|
||||
try (FileInputStream fis = new FileInputStream(file);
|
||||
OutputStream os = response.getOutputStream()) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
while ((length = fis.read(buffer)) > 0) {
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
} catch (ServiceException e) {
|
||||
writeJsonFail(response, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("MES下载图纸失败 productionOrderNo={} materialCode={}", productionOrderNo, materialCode, e);
|
||||
writeJsonFail(response, "下载失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static String firstNonBlank(String a, String b) {
|
||||
return StringUtils.isNotBlank(a) ? a.trim() : StringUtils.trimToEmpty(b);
|
||||
}
|
||||
|
||||
private void writeJsonFail(HttpServletResponse response, String msg) {
|
||||
try {
|
||||
if (!response.isCommitted()) {
|
||||
response.reset();
|
||||
}
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
response.getWriter().write(JSONObject.toJSONString(R.fail(msg)));
|
||||
} catch (IOException ex) {
|
||||
log.error("写入MES图纸错误响应失败", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
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.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.bo.OrderProProgressReportBo;
|
||||
import com.ruoyi.system.domain.vo.OrderProProgressDesignerSummaryVo;
|
||||
import com.ruoyi.system.domain.vo.OrderProProgressReportExportVo;
|
||||
import com.ruoyi.system.domain.vo.OrderProProgressReportOverviewVo;
|
||||
import com.ruoyi.system.domain.vo.OrderProProgressReportVo;
|
||||
import com.ruoyi.system.service.IDrawingWorkloadService;
|
||||
import com.ruoyi.system.service.IOrderProProgressReportService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/orderProProgress")
|
||||
public class OrderProProgressReportController extends BaseController {
|
||||
|
||||
private final IOrderProProgressReportService orderProProgressReportService;
|
||||
private final IDrawingWorkloadService drawingWorkloadService;
|
||||
|
||||
@SaCheckPermission("system:orderProProgress:list")
|
||||
@GetMapping("/overview")
|
||||
public R<OrderProProgressReportOverviewVo> overview(OrderProProgressReportBo bo) {
|
||||
return R.ok(orderProProgressReportService.queryOverview(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:orderProProgress:list")
|
||||
@GetMapping("/designerSummary")
|
||||
public R<List<OrderProProgressDesignerSummaryVo>> designerSummary(OrderProProgressReportBo bo) {
|
||||
return R.ok(orderProProgressReportService.queryDesignerSummary(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:orderProProgress:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<OrderProProgressReportVo> list(OrderProProgressReportBo bo, PageQuery pageQuery) {
|
||||
return orderProProgressReportService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:orderProProgress:list")
|
||||
@Log(title = "读取图纸工作量", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/syncWorkload")
|
||||
public R<Map<String, Object>> syncWorkload(@RequestBody List<Long> ids) {
|
||||
return R.ok(drawingWorkloadService.syncFromFtpBatch(ids));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:orderProProgress:export")
|
||||
@Log(title = "生产项目进度报表", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(OrderProProgressReportBo bo, HttpServletResponse response) {
|
||||
List<OrderProProgressReportExportVo> exportList = orderProProgressReportService.queryList(bo).stream()
|
||||
.map(OrderProProgressReportExportVo::from)
|
||||
.collect(Collectors.toList());
|
||||
ExcelUtil.exportExcel(exportList, "生产项目进度报表", OrderProProgressReportExportVo.class, response);
|
||||
}
|
||||
}
|
||||
@ -314,6 +314,12 @@ public class ProcessOrderProController extends BaseController {
|
||||
String s = iProcessOrderProService.executDrawing(orderPro);
|
||||
return R.ok(s);
|
||||
}
|
||||
@SaCheckPermission("system:orderPro:query")
|
||||
@GetMapping("/drawingState/{id}")
|
||||
public R<ProcessOrderProVo> captureDrawingState(@PathVariable Long id) {
|
||||
return R.ok(iProcessOrderProService.captureCurrentDrawingState(id));
|
||||
}
|
||||
|
||||
@GetMapping("/getstate")
|
||||
public R<JSONObject> getPlotState() {
|
||||
String result = HttpUtils.sendGet("http://192.168.14.218:9000/getstate", "UTF-8");
|
||||
@ -2002,6 +2008,7 @@ public class ProcessOrderProController extends BaseController {
|
||||
*/
|
||||
@SaCheckPermission("system:orderPro:sendProcessRoutePlanExcel")
|
||||
@Log(title = "发送工艺计划表", businessType = BusinessType.OTHER)
|
||||
@RepeatSubmit
|
||||
@PostMapping("/sendProcessRoutePlanExcel/{id}")
|
||||
public R<Void> sendProcessRoutePlanExcel(@NotNull(message = "项目ID不能为空") @PathVariable Long id) {
|
||||
return iProcessOrderProService.sendProcessRoutePlanExcel(id);
|
||||
|
||||
@ -499,7 +499,7 @@ public class ProcessRouteController extends BaseController {
|
||||
}
|
||||
|
||||
String todoProjectName = orderForLock == null ? null : orderForLock.getProductionName();
|
||||
Date todoProjectEndTime = orderForLock == null ? null : orderForLock.getProjectEndTime();
|
||||
Date todoRouteUptime = orderForLock == null ? null : orderForLock.getRouteUptime();
|
||||
String planUserIds = dingTalkProperties.getPlanUserId();
|
||||
// 异步:按 plan-user-id 发文件 + 发工艺审核卡片(落盘文件保留)
|
||||
String finalNickName = currentUserNickName;
|
||||
@ -516,7 +516,7 @@ public class ProcessRouteController extends BaseController {
|
||||
DingTodoSceneCodes.ROUTE_UPLOAD_PLAN,
|
||||
productionOrderNo,
|
||||
todoProjectName,
|
||||
todoProjectEndTime,
|
||||
todoRouteUptime,
|
||||
uploadFile,
|
||||
planUserIds);
|
||||
if (StringUtils.isNotBlank(todoError) && filePush != null) {
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.annotation.RepeatSubmit;
|
||||
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.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.bo.VmiMaterialBo;
|
||||
import com.ruoyi.system.domain.bo.VmiPurchaseReqLogBo;
|
||||
import com.ruoyi.system.domain.bo.VmiReplenishConfirmBo;
|
||||
import com.ruoyi.system.domain.bo.VmiReplenishPreviewBo;
|
||||
import com.ruoyi.system.domain.bo.VmiStockAlertLogBo;
|
||||
import com.ruoyi.system.domain.vo.VmiMaterialVo;
|
||||
import com.ruoyi.system.domain.vo.VmiPurchaseReqLogVo;
|
||||
import com.ruoyi.system.domain.vo.VmiReplenishPreviewItemVo;
|
||||
import com.ruoyi.system.domain.vo.VmiStockAlertLogVo;
|
||||
import com.ruoyi.system.service.IVmiMaterialService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* VMI物料安全库存
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/vmiMaterial")
|
||||
public class VmiMaterialController extends BaseController {
|
||||
|
||||
private final IVmiMaterialService vmiMaterialService;
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<VmiMaterialVo> list(VmiMaterialBo bo, PageQuery pageQuery) {
|
||||
return vmiMaterialService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<VmiMaterialVo> getInfo(@NotNull @PathVariable Long id) {
|
||||
return R.ok(vmiMaterialService.queryById(id));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:add")
|
||||
@Log(title = "VMI物料", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit
|
||||
@PostMapping
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody VmiMaterialBo bo) {
|
||||
return toAjax(vmiMaterialService.insertByBo(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:edit")
|
||||
@Log(title = "VMI物料", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit
|
||||
@PutMapping
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody VmiMaterialBo bo) {
|
||||
return toAjax(vmiMaterialService.updateByBo(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:remove")
|
||||
@Log(title = "VMI物料", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty @PathVariable Long[] ids) {
|
||||
return toAjax(vmiMaterialService.deleteWithValidByIds(Arrays.asList(ids)));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:edit")
|
||||
@Log(title = "VMI低库存扫描", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/scan")
|
||||
public R<Integer> scan() {
|
||||
return R.ok(vmiMaterialService.scanLowStock());
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:edit")
|
||||
@PostMapping("/confirm/preview")
|
||||
public R<List<VmiReplenishPreviewItemVo>> confirmPreview(@RequestBody VmiReplenishPreviewBo bo) {
|
||||
return R.ok(vmiMaterialService.previewReplenish(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:edit")
|
||||
@Log(title = "VMI补货确认", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/confirm")
|
||||
public R<Integer> confirm(@RequestBody VmiReplenishConfirmBo bo) {
|
||||
return R.ok(vmiMaterialService.confirmReplenish(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:edit")
|
||||
@Log(title = "VMI忽略补货", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/ignore/{alertId}")
|
||||
public R<Void> ignore(@NotNull @PathVariable Long alertId) {
|
||||
vmiMaterialService.ignoreAlert(alertId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:edit")
|
||||
@Log(title = "VMI需求预测通知", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/forecast/notify/{id}")
|
||||
public R<Void> notifyForecast(@NotNull @PathVariable Long id) {
|
||||
vmiMaterialService.notifyForecast(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:list")
|
||||
@GetMapping("/alert/list")
|
||||
public TableDataInfo<VmiStockAlertLogVo> alertList(VmiStockAlertLogBo bo, PageQuery pageQuery) {
|
||||
return vmiMaterialService.queryAlertPage(bo, pageQuery);
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:vmiMaterial:list")
|
||||
@GetMapping("/req/list")
|
||||
public TableDataInfo<VmiPurchaseReqLogVo> reqList(VmiPurchaseReqLogBo bo, PageQuery pageQuery) {
|
||||
return vmiMaterialService.queryReqPage(bo, pageQuery);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
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.system.domain.bo.WorkAlertGroupPushBo;
|
||||
import com.ruoyi.system.domain.bo.WorkAlertGroupPushConfigBo;
|
||||
import com.ruoyi.system.domain.vo.WorkAlertGroupPushConfigVo;
|
||||
import com.ruoyi.system.service.IWorkAlertGroupPushConfigService;
|
||||
import com.ruoyi.system.service.WorkAlertPushScheduleService;
|
||||
import com.ruoyi.system.service.support.WorkAlertPushTypeRegistry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 指定钉钉群会话推送预警数据(与预警推送配置同一套任务数据)
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/workAlertGroupPush")
|
||||
public class WorkAlertGroupPushController extends BaseController {
|
||||
|
||||
private final WorkAlertPushScheduleService workAlertPushScheduleService;
|
||||
private final IWorkAlertGroupPushConfigService workAlertGroupPushConfigService;
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:list")
|
||||
@GetMapping("/pushTypes")
|
||||
public R<List<Map<String, String>>> pushTypes() {
|
||||
List<Map<String, String>> list = new ArrayList<>();
|
||||
for (WorkAlertPushTypeRegistry.PushType type : WorkAlertPushTypeRegistry.groupConversationTypes()) {
|
||||
list.add(type.toOption());
|
||||
}
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:send")
|
||||
@Log(title = "指定群预警推送", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/send")
|
||||
public R<Void> send(@Validated @RequestBody WorkAlertGroupPushBo bo) {
|
||||
workAlertPushScheduleService.executeGroupPush(
|
||||
bo.getPushCode(), bo.getOpenConversationId(), bo.getRobotCode());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:list")
|
||||
@GetMapping("/schedule/list")
|
||||
public R<List<WorkAlertGroupPushConfigVo>> scheduleList() {
|
||||
return R.ok(workAlertGroupPushConfigService.queryList());
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:list")
|
||||
@GetMapping("/schedule/{id}")
|
||||
public R<WorkAlertGroupPushConfigVo> scheduleInfo(@NotNull @PathVariable Long id) {
|
||||
return R.ok(workAlertGroupPushConfigService.queryById(id));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:send")
|
||||
@Log(title = "群预警定时配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/schedule")
|
||||
public R<Void> addSchedule(@Validated(AddGroup.class) @RequestBody WorkAlertGroupPushConfigBo bo) {
|
||||
return toAjax(workAlertGroupPushConfigService.insertByBo(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:send")
|
||||
@Log(title = "群预警定时配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/schedule")
|
||||
public R<Void> editSchedule(@Validated(EditGroup.class) @RequestBody WorkAlertGroupPushConfigBo bo) {
|
||||
return toAjax(workAlertGroupPushConfigService.updateByBo(bo));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:send")
|
||||
@Log(title = "群预警定时配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/schedule/{ids}")
|
||||
public R<Void> removeSchedule(@NotEmpty @PathVariable Long[] ids) {
|
||||
return toAjax(workAlertGroupPushConfigService.deleteWithValidByIds(Arrays.asList(ids)));
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:workAlertGroupPush:send")
|
||||
@Log(title = "群预警定时配置", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/schedule/trigger/{id}")
|
||||
public R<Void> triggerSchedule(@NotNull @PathVariable Long id) {
|
||||
workAlertGroupPushConfigService.trigger(id);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 已经发起的钉钉待办 ding_todo_log
|
||||
*/
|
||||
@Data
|
||||
@TableName("ding_todo_log")
|
||||
public class DingTodoLog implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String sceneCode;
|
||||
|
||||
private String sceneName;
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
private String projectName;
|
||||
|
||||
private String title;
|
||||
|
||||
private String content;
|
||||
|
||||
private String userIds;
|
||||
|
||||
private String createUser;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date dueTime;
|
||||
|
||||
private String fileName;
|
||||
|
||||
/** SUCCESS 或 FAIL */
|
||||
private String status;
|
||||
|
||||
private String errorMsg;
|
||||
|
||||
private String responseBody;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 哪些业务方法要创建钉钉待办 ding_todo_scene
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("ding_todo_scene")
|
||||
public class DingTodoScene extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 方法编码,与代码里的调用点对应 */
|
||||
private String sceneCode;
|
||||
|
||||
/** 页面上看到的方法名称 */
|
||||
private String sceneName;
|
||||
|
||||
/** 0 不发待办,1 发送待办 */
|
||||
private String enabled;
|
||||
|
||||
/**
|
||||
* 接收人。类型为 name 时填姓名,多个用逗号,例如田志阳;类型为 code 时填考勤工号。
|
||||
* 类型为 code 且这里为空时,用 notify-user-id 或 plan-user-id。
|
||||
*/
|
||||
private String userIds;
|
||||
|
||||
/** NOTIFY 或 PLAN,仅 recipientType=code 且接收人为空时使用 */
|
||||
private String userSource;
|
||||
|
||||
/** code=考勤工号,name=姓名 */
|
||||
private String recipientType;
|
||||
|
||||
/** 创建人,类型与接收人一致。空则用接收人里的第一个 */
|
||||
private String createUser;
|
||||
|
||||
/** 没有未来的项目结束时间时,截止为当前时间加该天数 */
|
||||
private Integer dueDays;
|
||||
|
||||
/** 待办正文第一句 */
|
||||
private String contentLead;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 出图 / 工艺上传 / 钉钉推送业务留痕。
|
||||
*/
|
||||
@Data
|
||||
@TableName("drawing_route_oper_log")
|
||||
public class DrawingRouteOperLog {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** DRAWING_EXEC / DRAWING_NOTIFY / ROUTE_UPLOAD / ROUTE_NOTIFY / SKIP_PROCESS_NOTIFY / ELECTRICAL_BOM_NOTIFY */
|
||||
private String bizType;
|
||||
|
||||
/** MANUAL / PLM */
|
||||
private String channel;
|
||||
|
||||
private String apiPath;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
private String drawingMode;
|
||||
|
||||
/** MARKDOWN / FILE / CARD / MARKDOWN_AND_FILE / FILE_AND_CARD */
|
||||
private String notifyType;
|
||||
|
||||
private String notifyUserIds;
|
||||
|
||||
private String messageTitle;
|
||||
|
||||
private String messageContent;
|
||||
|
||||
private String fileName;
|
||||
|
||||
/** SUCCESS / FAIL / PARTIAL */
|
||||
private String status;
|
||||
|
||||
private String errorMsg;
|
||||
|
||||
private String summaryJson;
|
||||
|
||||
private String operator;
|
||||
|
||||
private String clientIp;
|
||||
|
||||
private Long durationMs;
|
||||
|
||||
private Date operTime;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 设计报工明细
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("process_design_report")
|
||||
public class ProcessDesignReport extends BaseEntity {
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Long taskId;
|
||||
|
||||
private Date reportDate;
|
||||
|
||||
private BigDecimal workHours;
|
||||
|
||||
private Integer progress;
|
||||
|
||||
private String content;
|
||||
|
||||
private String reportBy;
|
||||
|
||||
private Date reportTime;
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 设计个人任务
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("process_design_task")
|
||||
public class ProcessDesignTask extends BaseEntity {
|
||||
|
||||
public static final int STATUS_PENDING = 0;
|
||||
public static final int STATUS_WORKING = 1;
|
||||
public static final int STATUS_DONE = 2;
|
||||
/** 进度达到 80 且未到 100 */
|
||||
public static final int STATUS_DRAWING = 3;
|
||||
/** 进度达到 100,仍可继续补报工时 */
|
||||
public static final int STATUS_REVIEW = 4;
|
||||
public static final int STATUS_REVOKED = 9;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Long orderProId;
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
/** 设计人登录名,等于字典 plan_proer 的键值 */
|
||||
private String designerUserName;
|
||||
|
||||
/** 设计类型:2D 二维,3D 三维 */
|
||||
private String designDim;
|
||||
|
||||
/** 0待接收 1设计中 2已完成 3设计出图中 4工艺待审核 9已撤回 */
|
||||
private Integer status;
|
||||
|
||||
private Integer progress;
|
||||
|
||||
private BigDecimal totalHours;
|
||||
|
||||
private Date assignTime;
|
||||
|
||||
private Date startTime;
|
||||
|
||||
private Date finishTime;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
@ -45,6 +45,22 @@ public class ProcessOrderPro extends BaseEntity {
|
||||
* 出图模式:{@link com.ruoyi.system.constant.DrawingMode#NORMAL} / {@link com.ruoyi.system.constant.DrawingMode#PART};执行出图前必填
|
||||
*/
|
||||
private String drawingMode;
|
||||
/**
|
||||
* 最近一次出图 processstate:4 完成,5 失败
|
||||
*/
|
||||
private Integer drawingState;
|
||||
/**
|
||||
* 最近一次出图提示,例如「图号唯一性检查失败!」
|
||||
*/
|
||||
private String drawingStateMsg;
|
||||
/**
|
||||
* 最近一次出图输出路径
|
||||
*/
|
||||
private String drawingStatePath;
|
||||
/**
|
||||
* 最近一次出图状态写入时间
|
||||
*/
|
||||
private Date drawingStateTime;
|
||||
/**
|
||||
* 是否做工艺:0否 1是
|
||||
*/
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("ratio_model")
|
||||
public class RatioModel extends BaseEntity {
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 所属速比 */
|
||||
private Long ratioConfigId;
|
||||
|
||||
/** 减速机型号 */
|
||||
private String modelCode;
|
||||
|
||||
private Integer status;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* VMI物料安全库存对象 vmi_material
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("vmi_material")
|
||||
public class VmiMaterial extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键 */
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 名称 */
|
||||
private String materialName;
|
||||
|
||||
/** 材料 */
|
||||
private String materialQuality;
|
||||
|
||||
/** 供应商编码 */
|
||||
private String supplierCode;
|
||||
|
||||
/** 供应商 */
|
||||
private String supplierName;
|
||||
|
||||
/** 最小包装量 */
|
||||
private BigDecimal minPackQty;
|
||||
|
||||
/** 建议最高安全库存 */
|
||||
private BigDecimal maxSafeQty;
|
||||
|
||||
/** 建议最低安全库存 */
|
||||
private BigDecimal minSafeQty;
|
||||
|
||||
/** 仓储部VMI负责人钉钉userId,多个逗号分隔 */
|
||||
private String dingUserId;
|
||||
|
||||
/** 需求预测数量 */
|
||||
private BigDecimal forecastQty;
|
||||
|
||||
/** 需求预测说明(旺季、订单波动等) */
|
||||
private String forecastRemark;
|
||||
|
||||
/** 即时库存(最近一次扫描) */
|
||||
private BigDecimal lastStockQty;
|
||||
|
||||
/** 最近一次扫描的预计可用库存 */
|
||||
private BigDecimal lastAvailableQty;
|
||||
|
||||
/** 最近一次扫描时间 */
|
||||
private Date lastCheckTime;
|
||||
|
||||
/** 库存状态:NORMAL正常 / LOW低于安全库存 */
|
||||
private String stockStatus;
|
||||
|
||||
/** 是否启用:0停用 1启用 */
|
||||
private String enabled;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* VMI采购申请推送记录对象 vmi_purchase_req_log
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("vmi_purchase_req_log")
|
||||
public class VmiPurchaseReqLog extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键 */
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 对应的低库存触发记录ID */
|
||||
private Long alertLogId;
|
||||
|
||||
/** VMI物料主键 */
|
||||
private Long materialId;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 物料名称 */
|
||||
private String materialName;
|
||||
|
||||
/** 供应商编码 */
|
||||
private String supplierCode;
|
||||
|
||||
/** 采购申请数量 */
|
||||
private BigDecimal reqQty;
|
||||
|
||||
/** 金蝶采购申请单号 */
|
||||
private String kingdeeBillNo;
|
||||
|
||||
/** 金蝶采购申请FID */
|
||||
private String kingdeeFid;
|
||||
|
||||
/** 推送结果:SUCCESS成功 / FAIL失败 */
|
||||
private String pushStatus;
|
||||
|
||||
/** 金蝶返回或失败原因 */
|
||||
private String pushMsg;
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* VMI低库存触发记录对象 vmi_stock_alert_log
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("vmi_stock_alert_log")
|
||||
public class VmiStockAlertLog extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键 */
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** VMI物料主键 */
|
||||
private Long materialId;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 物料名称 */
|
||||
private String materialName;
|
||||
|
||||
/** 触发时的最低安全库存 */
|
||||
private BigDecimal minSafeQty;
|
||||
|
||||
/** 触发时的即时库存 */
|
||||
private BigDecimal currentQty;
|
||||
|
||||
/** 触发时的预计可用库存 */
|
||||
private BigDecimal availableQty;
|
||||
|
||||
/** 处理状态:PENDING待确认 / CONFIRMED已确认 / IGNORED已忽略 */
|
||||
private String alertStatus;
|
||||
|
||||
/** 钉钉推送结果:SUCCESS成功 / FAIL失败 */
|
||||
private String dingPushStatus;
|
||||
|
||||
/** 钉钉推送返回信息 */
|
||||
private String dingPushMsg;
|
||||
|
||||
/** 确认人或忽略人 */
|
||||
private String confirmBy;
|
||||
|
||||
/** 确认或忽略时间 */
|
||||
private Date confirmTime;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 指定群预警定时推送配置 work_alert_group_push_config
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("work_alert_group_push_config")
|
||||
public class WorkAlertGroupPushConfig extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 配置名称 */
|
||||
private String configName;
|
||||
|
||||
/** 群会话 ID */
|
||||
private String openConversationId;
|
||||
|
||||
/** 机器人 Code */
|
||||
private String robotCode;
|
||||
|
||||
/** 推送任务编码,逗号分隔 */
|
||||
private String pushCodes;
|
||||
|
||||
/** 每日推送时间 HH:mm */
|
||||
private String pushTime;
|
||||
|
||||
/** 是否启用 0否 1是 */
|
||||
private String enabled;
|
||||
|
||||
/** 上次推送日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date lastPushDate;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 标注任务的二维 / 三维
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskDesignDimBo {
|
||||
|
||||
@NotNull(message = "任务不能为空")
|
||||
private Long taskId;
|
||||
|
||||
@NotBlank(message = "请选择二维或三维")
|
||||
private String designDim;
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 我的设计任务查询
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskQueryBo {
|
||||
|
||||
/** 0待接收 1设计中 2已完成 3设计出图中 4工艺待审核 9已撤回。空则不含已撤回 */
|
||||
private Integer status;
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
/** true 只看逾期,false 只看未逾期 */
|
||||
private Boolean overdue;
|
||||
|
||||
/** mine 我的,all 全部,pending 待我接收 */
|
||||
private String scope;
|
||||
|
||||
/** 生产令号、项目名称、设计师 */
|
||||
private String keyword;
|
||||
|
||||
/** 2D、3D,空为全部;none 为未标注 */
|
||||
private String designDim;
|
||||
|
||||
/** overdue 已逾期,today 今天到期,upcoming 未逾期 */
|
||||
private String due;
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 设计报工
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskReportBo {
|
||||
|
||||
@NotNull(message = "任务不能为空")
|
||||
private Long taskId;
|
||||
|
||||
@NotNull(message = "请填写本次工时")
|
||||
private BigDecimal workHours;
|
||||
|
||||
@NotNull(message = "请填写当前进度")
|
||||
private Integer progress;
|
||||
|
||||
@NotBlank(message = "请填写工作说明")
|
||||
@Size(max = 1000, message = "工作说明不能超过1000个字符")
|
||||
private String content;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date reportDate;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DrawingRouteOperLogBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
/** DRAWING_EXEC / DRAWING_NOTIFY / ROUTE_UPLOAD / ROUTE_NOTIFY */
|
||||
private String bizType;
|
||||
|
||||
/** MANUAL / PLM */
|
||||
private String channel;
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
/** SUCCESS / FAIL / PARTIAL */
|
||||
private String status;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量设置项目状态入参。
|
||||
* 不走整单编辑校验(设计人等),只改 bomStatus。
|
||||
*/
|
||||
@Data
|
||||
public class OrderProBomStatusBo {
|
||||
|
||||
/** 选中的项目主键 */
|
||||
@NotEmpty(message = "项目不能为空")
|
||||
private List<Long> ids;
|
||||
|
||||
/** 项目状态(字典 process_status) */
|
||||
@NotNull(message = "项目状态不能为空")
|
||||
private Long bomStatus;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表查询。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OrderProProgressReportBo extends BaseEntity {
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
private String productionName;
|
||||
|
||||
/** 设计人(字典值,模糊匹配) */
|
||||
private String drawingBy;
|
||||
|
||||
/** 仅查询未填写设计人的项目 */
|
||||
private Boolean unassignedDesigner;
|
||||
|
||||
/** 手动 / PLM */
|
||||
private String source;
|
||||
|
||||
private String isEnterpriseStandard;
|
||||
|
||||
private List<String> isEnterpriseStandardList;
|
||||
|
||||
private String createBy;
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
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.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* VMI物料安全库存业务对象
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class VmiMaterialBo extends BaseEntity {
|
||||
|
||||
/** 主键 */
|
||||
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/** 物料编码 */
|
||||
@NotBlank(message = "物料编码不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String materialCode;
|
||||
|
||||
/** 名称 */
|
||||
private String materialName;
|
||||
|
||||
/** 材料 */
|
||||
private String materialQuality;
|
||||
|
||||
/** 供应商编码 */
|
||||
private String supplierCode;
|
||||
|
||||
/** 供应商 */
|
||||
private String supplierName;
|
||||
|
||||
/** 最小包装量 */
|
||||
private BigDecimal minPackQty;
|
||||
|
||||
/** 建议最高安全库存 */
|
||||
private BigDecimal maxSafeQty;
|
||||
|
||||
/** 建议最低安全库存 */
|
||||
@NotNull(message = "建议最低安全库存不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private BigDecimal minSafeQty;
|
||||
|
||||
/** 仓储部VMI负责人钉钉userId,多个逗号分隔 */
|
||||
private String dingUserId;
|
||||
|
||||
/** 需求预测数量 */
|
||||
private BigDecimal forecastQty;
|
||||
|
||||
/** 需求预测说明(旺季、订单波动等) */
|
||||
private String forecastRemark;
|
||||
|
||||
/** 是否启用:0停用 1启用 */
|
||||
private String enabled;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
/** 库存状态:NORMAL正常 / LOW低于安全库存 */
|
||||
private String stockStatus;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* VMI采购申请推送记录查询对象
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class VmiPurchaseReqLogBo extends BaseEntity {
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 推送结果:SUCCESS成功 / FAIL失败 */
|
||||
private String pushStatus;
|
||||
|
||||
/** 金蝶采购申请单号 */
|
||||
private String kingdeeBillNo;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* VMI补货确认请求对象(支持单条或批量勾选)
|
||||
*/
|
||||
@Data
|
||||
public class VmiReplenishConfirmBo {
|
||||
|
||||
/** 单条触发记录ID(兼容旧用法) */
|
||||
private Long alertId;
|
||||
|
||||
/** 批量勾选的触发记录ID(兼容;优先使用 lines) */
|
||||
private List<Long> alertIds;
|
||||
|
||||
/** 单条确认时的采购申请数量(兼容) */
|
||||
private BigDecimal reqQty;
|
||||
|
||||
/** 带数量的明细行(预览窗口确认后提交) */
|
||||
private List<VmiReplenishLineBo> lines;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* VMI补货确认明细行
|
||||
*/
|
||||
@Data
|
||||
public class VmiReplenishLineBo {
|
||||
|
||||
/** 触发记录ID */
|
||||
private Long alertId;
|
||||
|
||||
/** 采购申请数量(管理员可改) */
|
||||
private BigDecimal reqQty;
|
||||
|
||||
/** 到货时间,格式 yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss */
|
||||
private String arrivalDate;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* VMI补货预览请求
|
||||
*/
|
||||
@Data
|
||||
public class VmiReplenishPreviewBo {
|
||||
|
||||
private Long alertId;
|
||||
|
||||
private List<Long> alertIds;
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* VMI低库存触发记录查询对象
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class VmiStockAlertLogBo extends BaseEntity {
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 处理状态:PENDING待确认 / CONFIRMED已确认 / IGNORED已忽略 */
|
||||
private String alertStatus;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 指定钉钉群会话推送预警数据
|
||||
*/
|
||||
@Data
|
||||
public class WorkAlertGroupPushBo {
|
||||
|
||||
@NotBlank(message = "推送编码不能为空")
|
||||
private String pushCode;
|
||||
|
||||
@NotBlank(message = "群会话ID不能为空")
|
||||
private String openConversationId;
|
||||
|
||||
/** 机器人 Code,空则使用默认预警推送机器人 */
|
||||
private String robotCode;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
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.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* 指定群预警定时推送配置业务对象
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkAlertGroupPushConfigBo extends BaseEntity {
|
||||
|
||||
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
private String configName;
|
||||
|
||||
@NotBlank(message = "群会话ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String openConversationId;
|
||||
|
||||
private String robotCode;
|
||||
|
||||
@NotBlank(message = "请选择推送任务", groups = { AddGroup.class, EditGroup.class })
|
||||
private String pushCodes;
|
||||
|
||||
@NotBlank(message = "推送时间不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Pattern(regexp = "^([01]\\d|2[0-3]):[0-5]\\d$", message = "推送时间格式应为 HH:mm", groups = { AddGroup.class, EditGroup.class })
|
||||
private String pushTime;
|
||||
|
||||
@NotBlank(message = "启用状态不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String enabled;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package com.ruoyi.system.domain.bo.plm;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* PLM 总图号(图号/物料编码以 {@code .00} 结尾)识别与归属。
|
||||
*/
|
||||
public final class PlmGeneralDrawingUtils {
|
||||
|
||||
public static final String GENERAL_DRAWING_SUFFIX = ".00";
|
||||
|
||||
private PlmGeneralDrawingUtils() {
|
||||
}
|
||||
|
||||
/** 工艺状态:1=已审核 */
|
||||
public static boolean isRouteApproved(Long routeStatus) {
|
||||
return routeStatus != null && routeStatus == 1L;
|
||||
}
|
||||
|
||||
/** 是否总图号:trim 后以 {@code .00} 结尾。 */
|
||||
public static boolean isGeneralDrawingNo(String code) {
|
||||
String s = StringUtils.trimToEmpty(code);
|
||||
return s.endsWith(GENERAL_DRAWING_SUFFIX);
|
||||
}
|
||||
|
||||
/** {@code FS26062.00} → {@code FS26062};非总图号原样返回 trim 值。 */
|
||||
public static String generalDrawingPrefix(String generalDrawingNo) {
|
||||
String s = StringUtils.trimToEmpty(generalDrawingNo);
|
||||
if (s.endsWith(GENERAL_DRAWING_SUFFIX)) {
|
||||
return s.substring(0, s.length() - GENERAL_DRAWING_SUFFIX.length());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static Set<String> collectGeneralDrawingNos(Collection<String> codes) {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
if (codes == null) {
|
||||
return result;
|
||||
}
|
||||
for (String code : codes) {
|
||||
String s = StringUtils.trimToEmpty(code);
|
||||
if (isGeneralDrawingNo(s)) {
|
||||
result.add(s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 物料是否归属某总图号:等于总图号,或以「去掉 .00 后的前缀 + .」开头。
|
||||
*/
|
||||
public static boolean belongsToGeneralDrawing(String materialCode, String generalDrawingNo) {
|
||||
String code = StringUtils.trimToEmpty(materialCode);
|
||||
String general = StringUtils.trimToEmpty(generalDrawingNo);
|
||||
if (code.isEmpty() || general.isEmpty() || !isGeneralDrawingNo(general)) {
|
||||
return false;
|
||||
}
|
||||
if (code.equals(general)) {
|
||||
return true;
|
||||
}
|
||||
String prefix = generalDrawingPrefix(general);
|
||||
return StringUtils.isNotBlank(prefix) && code.startsWith(prefix + ".");
|
||||
}
|
||||
|
||||
/** 在候选总图号中取最长匹配(更具体的总图优先)。 */
|
||||
public static String resolveOwnerGeneralDrawing(String materialCode, Collection<String> generalDrawings) {
|
||||
String best = null;
|
||||
if (generalDrawings == null) {
|
||||
return null;
|
||||
}
|
||||
for (String general : generalDrawings) {
|
||||
if (!belongsToGeneralDrawing(materialCode, general)) {
|
||||
continue;
|
||||
}
|
||||
if (best == null || general.length() > best.length()) {
|
||||
best = general;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
public static Set<String> intersection(Set<String> left, Set<String> right) {
|
||||
Set<String> overlap = new LinkedHashSet<>();
|
||||
if (left == null || right == null || left.isEmpty() || right.isEmpty()) {
|
||||
return overlap;
|
||||
}
|
||||
for (String item : left) {
|
||||
if (right.contains(item)) {
|
||||
overlap.add(item);
|
||||
}
|
||||
}
|
||||
return overlap;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.ruoyi.system.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 进度报表时间聚合(按项目或令号)。
|
||||
*/
|
||||
@Data
|
||||
public class OrderProProgressTimeAgg {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
private Date eventTime;
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.ruoyi.system.domain.dto.ratio;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RatioBomParseResult {
|
||||
|
||||
private List<SheetBom> sheets = new ArrayList<>();
|
||||
|
||||
private List<String> skippedSheets = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
public static class SheetBom {
|
||||
private String sheetName;
|
||||
private String ratioValue;
|
||||
private List<LineBom> commonParts = new ArrayList<>();
|
||||
private List<ModelBom> models = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ModelBom {
|
||||
private String modelCode;
|
||||
private List<LineBom> parts = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LineBom {
|
||||
private String partCategory;
|
||||
private String materialCode;
|
||||
private String materialName;
|
||||
private String material;
|
||||
private BigDecimal bomQty;
|
||||
/** Excel 行号,从 1 开始 */
|
||||
private int excelRow;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.ruoyi.system.domain.dto.ratio;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RatioImportResultDTO {
|
||||
|
||||
private String message;
|
||||
|
||||
private int sheetCount;
|
||||
|
||||
private int modelCount;
|
||||
|
||||
private int commonCount;
|
||||
|
||||
private int specialCount;
|
||||
|
||||
private List<String> details = new ArrayList<>();
|
||||
|
||||
private List<String> skippedSheets = new ArrayList<>();
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.ruoyi.system.domain.dto.ratio;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RatioModelOptionDTO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String modelCode;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设计账号同步结果
|
||||
*/
|
||||
@Data
|
||||
public class DesignAccountSyncVo {
|
||||
|
||||
private int createdUsers;
|
||||
|
||||
private int attachedRoles;
|
||||
|
||||
private int skipped;
|
||||
|
||||
private int backfillCreated;
|
||||
|
||||
private List<String> messages = new ArrayList<>();
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设计工作台顶部卡片
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskBoardVo {
|
||||
|
||||
private BigDecimal weekHours = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal weekTarget = new BigDecimal("40");
|
||||
|
||||
private BigDecimal todayHours = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal todayTarget = new BigDecimal("8");
|
||||
|
||||
private long todayReportCount;
|
||||
|
||||
private long workingCount;
|
||||
|
||||
private long pendingCount;
|
||||
|
||||
private long overdueCount;
|
||||
|
||||
private long mineCount;
|
||||
|
||||
private long allCount;
|
||||
|
||||
private long weekNewCount;
|
||||
|
||||
private List<DesignTaskDayHoursVo> last7 = new ArrayList<>();
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 近 7 天中的一天工时
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskDayHoursVo {
|
||||
|
||||
/** yyyy-MM-dd */
|
||||
private String date;
|
||||
|
||||
/** 一 到 日 */
|
||||
private String label;
|
||||
|
||||
private BigDecimal hours = BigDecimal.ZERO;
|
||||
|
||||
private Boolean today;
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 设计报工明细
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskReportVo {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long taskId;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date reportDate;
|
||||
|
||||
private BigDecimal workHours;
|
||||
|
||||
private Integer progress;
|
||||
|
||||
private String content;
|
||||
|
||||
private String reportBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date reportTime;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 首页设计任务摘要
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskSummaryVo {
|
||||
|
||||
private long pendingCount;
|
||||
|
||||
private long overdueCount;
|
||||
|
||||
private long finishedThisMonth;
|
||||
|
||||
private List<DesignTaskVo> recent = new ArrayList<>();
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 设计任务展示
|
||||
*/
|
||||
@Data
|
||||
public class DesignTaskVo {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long orderProId;
|
||||
|
||||
private String productionOrderNo;
|
||||
|
||||
private String productionName;
|
||||
|
||||
private String projectType;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date drawingTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date projectEndTime;
|
||||
|
||||
private Long bomStatus;
|
||||
|
||||
private Integer whetherDelay;
|
||||
|
||||
private String designerUserName;
|
||||
|
||||
private String designerName;
|
||||
|
||||
/** 2D / 3D,未标注为空 */
|
||||
private String designDim;
|
||||
|
||||
private Integer status;
|
||||
|
||||
private Integer progress;
|
||||
|
||||
private BigDecimal totalHours;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date assignTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date startTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date finishTime;
|
||||
|
||||
private String remark;
|
||||
|
||||
/** 预计出图日早于今天,且任务未完成、未撤回 */
|
||||
private Boolean overdue;
|
||||
|
||||
/** 该项目除已撤回外的设计任务都已完成 */
|
||||
private Boolean allDesignersDone;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 出图 / 工艺 / 钉钉推送留痕列表与详情。
|
||||
*/
|
||||
@Data
|
||||
public class DrawingRouteOperLogVo {
|
||||
|
||||
private Long id;
|
||||
private String bizType;
|
||||
private String channel;
|
||||
private String apiPath;
|
||||
private Long projectId;
|
||||
private String productionOrderNo;
|
||||
private String drawingMode;
|
||||
private String notifyType;
|
||||
private String notifyUserIds;
|
||||
private String messageTitle;
|
||||
private String messageContent;
|
||||
private String fileName;
|
||||
private String status;
|
||||
private String errorMsg;
|
||||
private String summaryJson;
|
||||
private String operator;
|
||||
private String clientIp;
|
||||
private Long durationMs;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date operTime;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 临时导入:按生产令号更新出图时间、项目完成时间、工艺完成时间、计划排产时间、BOM/物料更新、设计人。
|
||||
* 空单元格不更新。
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class OrderProDrawingImportVo {
|
||||
|
||||
@ExcelProperty(value = "生产令号")
|
||||
private String productionOrderNo;
|
||||
|
||||
@ExcelProperty(value = "出图时间")
|
||||
private String drawingTime;
|
||||
|
||||
@ExcelProperty(value = "项目完成时间")
|
||||
private String projectEndTime;
|
||||
|
||||
@ExcelProperty(value = "工艺完成时间")
|
||||
private String routeUptime;
|
||||
|
||||
@ExcelProperty(value = "计划排产时间")
|
||||
private String planUptime;
|
||||
|
||||
@ExcelProperty(value = "BOM/物料更新")
|
||||
private String bomUptime;
|
||||
|
||||
@ExcelProperty(value = "设计人")
|
||||
private String drawingBy;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表:按设计人汇总。
|
||||
*/
|
||||
@Data
|
||||
public class OrderProProgressDesignerSummaryVo {
|
||||
|
||||
/** 设计人(字典 plan_proer 的值) */
|
||||
private String drawingBy;
|
||||
|
||||
/** 参与项目数。同一项目多名设计人时,分别计入每位设计人。 */
|
||||
private long projectCount;
|
||||
|
||||
/** 已有实际出图时间的项目数 */
|
||||
private long drawn;
|
||||
|
||||
/** 出图逾期项目数(预计出图当天完成不算逾期) */
|
||||
private long delayed;
|
||||
|
||||
/** 未逾期项目数 */
|
||||
private long onTime;
|
||||
|
||||
/** A4 图纸数量合计 */
|
||||
private long a4Total;
|
||||
|
||||
/** 等效 A4 合计 */
|
||||
private long equivalentA4Total;
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表导出。
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class OrderProProgressReportExportVo {
|
||||
|
||||
private static final Map<String, String> PROJECT_TYPE_LABELS = new HashMap<>();
|
||||
|
||||
static {
|
||||
PROJECT_TYPE_LABELS.put("CP", "产品");
|
||||
PROJECT_TYPE_LABELS.put("FB", "非标");
|
||||
PROJECT_TYPE_LABELS.put("BG", "变更");
|
||||
PROJECT_TYPE_LABELS.put("QB", "企标");
|
||||
PROJECT_TYPE_LABELS.put("EY", "研发");
|
||||
PROJECT_TYPE_LABELS.put("GZ", "固资");
|
||||
PROJECT_TYPE_LABELS.put("ZP", "展品");
|
||||
PROJECT_TYPE_LABELS.put("KC", "库存");
|
||||
PROJECT_TYPE_LABELS.put("产品", "产品");
|
||||
PROJECT_TYPE_LABELS.put("非标", "非标");
|
||||
PROJECT_TYPE_LABELS.put("变更", "变更");
|
||||
PROJECT_TYPE_LABELS.put("研发", "研发");
|
||||
PROJECT_TYPE_LABELS.put("企标", "企标");
|
||||
PROJECT_TYPE_LABELS.put("库存", "库存");
|
||||
PROJECT_TYPE_LABELS.put("固资", "固资");
|
||||
PROJECT_TYPE_LABELS.put("展品", "展品");
|
||||
}
|
||||
|
||||
@ExcelProperty(value = "生产令号")
|
||||
private String productionOrderNo;
|
||||
|
||||
@ExcelProperty(value = "预计出图时间")
|
||||
private String drawingTime;
|
||||
|
||||
@ExcelProperty(value = "实际出图时间")
|
||||
private String actualDrawingTime;
|
||||
|
||||
@ExcelProperty(value = "出图是否逾期")
|
||||
private String drawingOverdueLabel;
|
||||
|
||||
@ExcelProperty(value = "项目名称")
|
||||
private String productionName;
|
||||
|
||||
@ExcelProperty(value = "设计人")
|
||||
private String drawingBy;
|
||||
|
||||
@ExcelProperty(value = "来源")
|
||||
private String source;
|
||||
|
||||
@ExcelProperty(value = "项目类型")
|
||||
private String projectTypeLabel;
|
||||
|
||||
@ExcelProperty(value = "项目完成时间")
|
||||
private String projectEndTime;
|
||||
|
||||
@ExcelProperty(value = "工艺预计完成时间")
|
||||
private String routeUptime;
|
||||
|
||||
@ExcelProperty(value = "实际完成时间")
|
||||
private String actualCompleteTime;
|
||||
|
||||
@ExcelProperty(value = "计划排产时间")
|
||||
private String planUptime;
|
||||
|
||||
@ExcelProperty(value = "BOM物料更新时间")
|
||||
private String bomUptime;
|
||||
|
||||
@ExcelProperty(value = "A4图纸数量")
|
||||
private Integer a4Count;
|
||||
|
||||
@ExcelProperty(value = "等效A4")
|
||||
private Integer equivalentA4Total;
|
||||
|
||||
@ExcelProperty(value = "创建人")
|
||||
private String createBy;
|
||||
|
||||
public static OrderProProgressReportExportVo from(OrderProProgressReportVo vo) {
|
||||
OrderProProgressReportExportVo row = new OrderProProgressReportExportVo();
|
||||
if (vo == null) {
|
||||
return row;
|
||||
}
|
||||
row.setProductionOrderNo(vo.getProductionOrderNo());
|
||||
row.setDrawingTime(formatDate(vo.getDrawingTime()));
|
||||
row.setActualDrawingTime(formatDateTime(vo.getActualDrawingTime()));
|
||||
row.setDrawingOverdueLabel(formatOverdue(vo.getDrawingOverdue()));
|
||||
row.setProductionName(vo.getProductionName());
|
||||
row.setDrawingBy(vo.getDrawingBy());
|
||||
row.setSource(formatSource(vo.getSource()));
|
||||
row.setProjectTypeLabel(StringUtils.isNotBlank(vo.getProjectTypeLabel())
|
||||
? vo.getProjectTypeLabel()
|
||||
: formatProjectType(vo.getIsEnterpriseStandard()));
|
||||
row.setProjectEndTime(formatDateTime(vo.getProjectEndTime()));
|
||||
row.setRouteUptime(formatDateTime(vo.getRouteUptime()));
|
||||
row.setActualCompleteTime(formatDateTime(vo.getActualCompleteTime()));
|
||||
row.setPlanUptime(formatDateTime(vo.getPlanUptime()));
|
||||
row.setBomUptime(formatDateTime(vo.getBomUptime()));
|
||||
row.setA4Count(vo.getA4Count());
|
||||
row.setEquivalentA4Total(vo.getEquivalentA4Total());
|
||||
row.setCreateBy(vo.getCreateBy());
|
||||
return row;
|
||||
}
|
||||
|
||||
private static String formatSource(String value) {
|
||||
return StringUtils.isNotBlank(value) ? value.trim() : "手动";
|
||||
}
|
||||
|
||||
private static String formatProjectType(String code) {
|
||||
if (StringUtils.isBlank(code)) {
|
||||
return "-";
|
||||
}
|
||||
return PROJECT_TYPE_LABELS.getOrDefault(code.trim(), code.trim());
|
||||
}
|
||||
|
||||
private static String formatOverdue(Boolean overdue) {
|
||||
if (overdue == null) {
|
||||
return "";
|
||||
}
|
||||
return overdue ? "逾期" : "未逾期";
|
||||
}
|
||||
|
||||
private static String formatDate(Date date) {
|
||||
return date == null ? "" : DateUtil.format(date, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
private static String formatDateTime(Date date) {
|
||||
return date == null ? "" : DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表顶部统计。
|
||||
*/
|
||||
@Data
|
||||
public class OrderProProgressReportOverviewVo {
|
||||
|
||||
/** 项目总数 */
|
||||
private long total;
|
||||
|
||||
/** 已有实际出图时间 */
|
||||
private long drawn;
|
||||
|
||||
/** 出图逾期:实际出图日晚于预计出图日;预计出图当天完成不算逾期 */
|
||||
private long delayed;
|
||||
|
||||
/** 已有工艺实际完成时间 */
|
||||
private long routeDone;
|
||||
|
||||
/** 已填 BOM 物料更新时间 */
|
||||
private long bomUpdated;
|
||||
|
||||
/** 已读取图纸工作量的项目数 */
|
||||
private long workloadReady;
|
||||
|
||||
/** A4 图纸数量合计 */
|
||||
private long a4Total;
|
||||
|
||||
/** 等效 A4 合计 */
|
||||
private long equivalentA4Total;
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表。
|
||||
*/
|
||||
@Data
|
||||
public class OrderProProgressReportVo {
|
||||
|
||||
private Long id;
|
||||
|
||||
/** 生产令号 */
|
||||
private String productionOrderNo;
|
||||
|
||||
/** 预计出图时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date drawingTime;
|
||||
|
||||
/** 实际出图时间:产品图纸最后一次上传时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualDrawingTime;
|
||||
|
||||
/**
|
||||
* 出图是否逾期。预计出图当天及之前完成不算逾期;
|
||||
* 尚无实际出图时,过了预计出图当天算逾期。
|
||||
*/
|
||||
private Boolean drawingOverdue;
|
||||
|
||||
/** 项目名称 */
|
||||
private String productionName;
|
||||
|
||||
/** 设计人(字典 plan_proer,多人逗号分隔) */
|
||||
private String drawingBy;
|
||||
|
||||
/** 来源:手动 / PLM */
|
||||
private String source;
|
||||
|
||||
/** 项目类型原始值 */
|
||||
private String isEnterpriseStandard;
|
||||
|
||||
/** 项目类型展示名 */
|
||||
private String projectTypeLabel;
|
||||
|
||||
/** 项目完成时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date projectEndTime;
|
||||
|
||||
/** 工艺预计完成时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date routeUptime;
|
||||
|
||||
/** 实际完成时间:工艺实际上传完成 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualCompleteTime;
|
||||
|
||||
/** 计划排产时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date planUptime;
|
||||
|
||||
/** BOM 物料更新时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date bomUptime;
|
||||
|
||||
/** A4 图纸数量(图纸工作量汇总) */
|
||||
private Integer a4Count;
|
||||
|
||||
/** 等效 A4 合计(图纸工作量汇总) */
|
||||
private Integer equivalentA4Total;
|
||||
|
||||
/** 创建人 */
|
||||
private String createBy;
|
||||
}
|
||||
@ -64,6 +64,22 @@ public class ProcessOrderProVo {
|
||||
@ExcelProperty(value = "出图模式")
|
||||
private String drawingMode;
|
||||
|
||||
/** 最近一次出图 processstate:4 完成,5 失败 */
|
||||
@ExcelIgnore
|
||||
private Integer drawingState;
|
||||
|
||||
/** 最近一次出图提示 */
|
||||
@ExcelIgnore
|
||||
private String drawingStateMsg;
|
||||
|
||||
/** 最近一次出图输出路径 */
|
||||
@ExcelIgnore
|
||||
private String drawingStatePath;
|
||||
|
||||
/** 最近一次出图状态写入时间 */
|
||||
@ExcelIgnore
|
||||
private Date drawingStateTime;
|
||||
|
||||
/**
|
||||
* 是否做工艺:0否 1是
|
||||
*/
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* VMI低库存钉钉提醒 Excel 行
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@ColumnWidth(16)
|
||||
public class VmiLowStockAlertExportVo {
|
||||
|
||||
@ExcelProperty("物料编码")
|
||||
@ColumnWidth(18)
|
||||
private String materialCode;
|
||||
|
||||
@ExcelProperty("名称")
|
||||
@ColumnWidth(22)
|
||||
private String materialName;
|
||||
|
||||
@ExcelProperty("材料")
|
||||
private String materialQuality;
|
||||
|
||||
@ExcelProperty("供应商")
|
||||
@ColumnWidth(14)
|
||||
private String supplierName;
|
||||
|
||||
@ExcelProperty("即时库存")
|
||||
private BigDecimal currentQty;
|
||||
|
||||
@ExcelProperty("预计可用")
|
||||
private BigDecimal availableQty;
|
||||
|
||||
@ExcelProperty("最低安全库存")
|
||||
private BigDecimal minSafeQty;
|
||||
|
||||
@ExcelProperty("最高安全库存")
|
||||
private BigDecimal maxSafeQty;
|
||||
|
||||
@ExcelProperty("最小包装量")
|
||||
private BigDecimal minPackQty;
|
||||
|
||||
@ExcelProperty("建议补货数量")
|
||||
private BigDecimal suggestQty;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
@ColumnWidth(20)
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* VMI物料安全库存视图对象
|
||||
*/
|
||||
@Data
|
||||
public class VmiMaterialVo {
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 名称 */
|
||||
private String materialName;
|
||||
|
||||
/** 材料 */
|
||||
private String materialQuality;
|
||||
|
||||
/** 供应商编码 */
|
||||
private String supplierCode;
|
||||
|
||||
/** 供应商 */
|
||||
private String supplierName;
|
||||
|
||||
/** 最小包装量 */
|
||||
private BigDecimal minPackQty;
|
||||
|
||||
/** 建议最高安全库存 */
|
||||
private BigDecimal maxSafeQty;
|
||||
|
||||
/** 建议最低安全库存 */
|
||||
private BigDecimal minSafeQty;
|
||||
|
||||
/** 仓储部VMI负责人钉钉userId,多个逗号分隔 */
|
||||
private String dingUserId;
|
||||
|
||||
/** 需求预测数量 */
|
||||
private BigDecimal forecastQty;
|
||||
|
||||
/** 需求预测说明(旺季、订单波动等) */
|
||||
private String forecastRemark;
|
||||
|
||||
/** 即时库存 */
|
||||
private BigDecimal lastStockQty;
|
||||
|
||||
/** 最近一次扫描的预计可用库存 */
|
||||
private BigDecimal lastAvailableQty;
|
||||
|
||||
/** 最近一次扫描时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastCheckTime;
|
||||
|
||||
/** 库存状态:NORMAL正常 / LOW低于安全库存 */
|
||||
private String stockStatus;
|
||||
|
||||
/** 是否启用:0停用 1启用 */
|
||||
private String enabled;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
/** 创建时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* VMI采购申请推送记录视图对象
|
||||
*/
|
||||
@Data
|
||||
public class VmiPurchaseReqLogVo {
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 对应的低库存触发记录ID */
|
||||
private Long alertLogId;
|
||||
|
||||
/** VMI物料主键 */
|
||||
private Long materialId;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 物料名称 */
|
||||
private String materialName;
|
||||
|
||||
/** 供应商编码 */
|
||||
private String supplierCode;
|
||||
|
||||
/** 采购申请数量 */
|
||||
private BigDecimal reqQty;
|
||||
|
||||
/** 金蝶采购申请单号 */
|
||||
private String kingdeeBillNo;
|
||||
|
||||
/** 金蝶采购申请FID */
|
||||
private String kingdeeFid;
|
||||
|
||||
/** 推送结果:SUCCESS成功 / FAIL失败 */
|
||||
private String pushStatus;
|
||||
|
||||
/** 金蝶返回或失败原因 */
|
||||
private String pushMsg;
|
||||
|
||||
/** 操作人 */
|
||||
private String createBy;
|
||||
|
||||
/** 推送时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* VMI补货确认预览行(推送金蝶前供管理员核对/修改数量)
|
||||
*/
|
||||
@Data
|
||||
public class VmiReplenishPreviewItemVo {
|
||||
|
||||
private Long alertId;
|
||||
|
||||
private Long materialId;
|
||||
|
||||
private String materialCode;
|
||||
|
||||
private String materialName;
|
||||
|
||||
private String supplierCode;
|
||||
|
||||
private String supplierName;
|
||||
|
||||
/** 触发时即时库存 */
|
||||
private BigDecimal currentQty;
|
||||
|
||||
/** 触发时预计可用 */
|
||||
private BigDecimal availableQty;
|
||||
|
||||
private BigDecimal minSafeQty;
|
||||
|
||||
private BigDecimal maxSafeQty;
|
||||
|
||||
private BigDecimal minPackQty;
|
||||
|
||||
/** 系统按规则计算的建议补货数量 */
|
||||
private BigDecimal suggestQty;
|
||||
|
||||
/** 实际申请数量(默认同 suggestQty,前端可改) */
|
||||
private BigDecimal reqQty;
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* VMI低库存触发记录视图对象
|
||||
*/
|
||||
@Data
|
||||
public class VmiStockAlertLogVo {
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** VMI物料主键 */
|
||||
private Long materialId;
|
||||
|
||||
/** 物料编码 */
|
||||
private String materialCode;
|
||||
|
||||
/** 物料名称 */
|
||||
private String materialName;
|
||||
|
||||
/** 供应商编码(来自VMI物料) */
|
||||
private String supplierCode;
|
||||
|
||||
/** 供应商名称(来自VMI物料) */
|
||||
private String supplierName;
|
||||
|
||||
/** 触发时的最低安全库存 */
|
||||
private BigDecimal minSafeQty;
|
||||
|
||||
/** 触发时的即时库存 */
|
||||
private BigDecimal currentQty;
|
||||
|
||||
/** 触发时的预计可用库存 */
|
||||
private BigDecimal availableQty;
|
||||
|
||||
/** 处理状态:PENDING待确认 / CONFIRMED已确认 / IGNORED已忽略 */
|
||||
private String alertStatus;
|
||||
|
||||
/** 钉钉推送结果:SUCCESS成功 / FAIL失败 */
|
||||
private String dingPushStatus;
|
||||
|
||||
/** 钉钉推送返回信息 */
|
||||
private String dingPushMsg;
|
||||
|
||||
/** 确认人或忽略人 */
|
||||
private String confirmBy;
|
||||
|
||||
/** 确认或忽略时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date confirmTime;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
/** 触发时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 指定群预警定时推送配置视图对象
|
||||
*/
|
||||
@Data
|
||||
public class WorkAlertGroupPushConfigVo {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String configName;
|
||||
|
||||
private String openConversationId;
|
||||
|
||||
private String robotCode;
|
||||
|
||||
private String pushCodes;
|
||||
|
||||
/** 任务中文名,逗号分隔 */
|
||||
private String pushCodeNames;
|
||||
|
||||
private String pushTime;
|
||||
|
||||
private String enabled;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date lastPushDate;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package com.ruoyi.system.domain.vo.mes;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* MES 图纸清单中的一张图(同一物料编码下 PDF / DWG 合并为一条)。
|
||||
*/
|
||||
@Data
|
||||
public class MesDrawingItemVo {
|
||||
|
||||
/** 推送明细 ID,磁盘扫描补全的图纸可能为空 */
|
||||
private Long itemId;
|
||||
|
||||
/** 生产令号 */
|
||||
private String productionOrderNo;
|
||||
|
||||
/** 物料编码 / 图号 */
|
||||
private String materialCode;
|
||||
|
||||
/** 物料名称 */
|
||||
private String materialName;
|
||||
|
||||
/** PDF 文件名 */
|
||||
private String pdfFileName;
|
||||
|
||||
/** DWG 文件名 */
|
||||
private String dwgFileName;
|
||||
|
||||
/** PDF 相对路径,如 令号/图号_名称.pdf */
|
||||
private String pdfRelativePath;
|
||||
|
||||
/** DWG 相对路径,如 令号/dwg/图号_名称.dwg */
|
||||
private String dwgRelativePath;
|
||||
|
||||
/** PDF 下载地址(相对本系统根路径,MES 需拼接主机) */
|
||||
private String pdfDownloadUrl;
|
||||
|
||||
/** DWG 下载地址 */
|
||||
private String dwgDownloadUrl;
|
||||
|
||||
/** PDF 是否存在 */
|
||||
private Boolean pdfExists;
|
||||
|
||||
/** DWG 是否存在 */
|
||||
private Boolean dwgExists;
|
||||
|
||||
/** PDF 字节数 */
|
||||
private Long pdfSize;
|
||||
|
||||
/** DWG 字节数 */
|
||||
private Long dwgSize;
|
||||
|
||||
/** 单台数量 */
|
||||
private java.math.BigDecimal unitQty;
|
||||
|
||||
/** 本批数量 */
|
||||
private java.math.BigDecimal batchQty;
|
||||
|
||||
/** 是否变更图纸 */
|
||||
private Boolean isDrawingChanged;
|
||||
|
||||
/** 最近一次接收时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date receiveTime;
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.ruoyi.system.domain.vo.mes;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* MES 按生产令号查询图纸清单。
|
||||
*/
|
||||
@Data
|
||||
public class MesDrawingListVo {
|
||||
|
||||
/** 生产令号 */
|
||||
private String productionOrderNo;
|
||||
|
||||
/** 项目名称 */
|
||||
private String productionName;
|
||||
|
||||
/** 图纸是否已齐:0否 1是 */
|
||||
private Integer drawingQtyIssued;
|
||||
|
||||
/** 是否允许下载:0否 1是 */
|
||||
private Integer isDownload;
|
||||
|
||||
/** 图纸条数 */
|
||||
private Integer itemCount;
|
||||
|
||||
/** 图纸列表 */
|
||||
private List<MesDrawingItemVo> items;
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.ruoyi.system.job;
|
||||
|
||||
import com.ruoyi.system.service.IOrderProDrawingDelayService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 每日刷新项目出图延期天数(工作日一周六天)。
|
||||
* XXL-JOB Cron 建议:0 10 0 * * ? (每天 00:10)
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DrawingDelayRefreshJob {
|
||||
|
||||
private final IOrderProDrawingDelayService orderProDrawingDelayService;
|
||||
|
||||
@XxlJob("drawingDelayRefreshHandler")
|
||||
public void drawingDelayRefreshHandler() {
|
||||
XxlJobHelper.log("开始刷新项目出图延期状态...");
|
||||
try {
|
||||
orderProDrawingDelayService.refreshAll();
|
||||
XxlJobHelper.handleSuccess("刷新完成");
|
||||
} catch (Exception e) {
|
||||
log.error("刷新项目出图延期状态异常", e);
|
||||
XxlJobHelper.handleFail("刷新异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.ruoyi.system.job;
|
||||
|
||||
import com.ruoyi.system.service.IVmiMaterialService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 扫描VMI物料即时库存,低于最低安全库存则钉钉提醒仓储部VMI负责人。
|
||||
* XXL-JOB JobHandler:vmiSafeStockCheckHandler
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class VmiSafeStockCheckJob {
|
||||
|
||||
private final IVmiMaterialService vmiMaterialService;
|
||||
|
||||
@XxlJob("vmiSafeStockCheckHandler")
|
||||
public void vmiSafeStockCheckHandler() {
|
||||
XxlJobHelper.log("开始扫描VMI低库存...");
|
||||
try {
|
||||
int hit = vmiMaterialService.scanLowStock();
|
||||
XxlJobHelper.handleSuccess("扫描完成,新触发 " + hit + " 条");
|
||||
} catch (Exception e) {
|
||||
log.error("VMI低库存扫描异常", e);
|
||||
XxlJobHelper.handleFail("扫描异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.DingTodoLog;
|
||||
|
||||
/**
|
||||
* 钉钉待办记录
|
||||
*/
|
||||
public interface DingTodoLogMapper extends BaseMapperPlus<DingTodoLogMapper, DingTodoLog, DingTodoLog> {
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.DingTodoScene;
|
||||
|
||||
/**
|
||||
* 钉钉待办场景
|
||||
*/
|
||||
public interface DingTodoSceneMapper extends BaseMapperPlus<DingTodoSceneMapper, DingTodoScene, DingTodoScene> {
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.DrawingRouteOperLog;
|
||||
import com.ruoyi.system.domain.vo.DrawingRouteOperLogVo;
|
||||
|
||||
public interface DrawingRouteOperLogMapper extends BaseMapperPlus<DrawingRouteOperLogMapper, DrawingRouteOperLog, DrawingRouteOperLogVo> {
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.system.domain.dto.OrderProProgressTimeAgg;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表时间聚合。
|
||||
*/
|
||||
public interface OrderProProgressReportMapper {
|
||||
|
||||
List<OrderProProgressTimeAgg> selectActualDrawingTimes(@Param("ids") List<Long> ids);
|
||||
|
||||
List<OrderProProgressTimeAgg> selectActualRouteLogTimes(@Param("orderNos") List<String> orderNos);
|
||||
|
||||
List<OrderProProgressTimeAgg> selectActualRouteCreateTimes(@Param("orderNos") List<String> orderNos);
|
||||
|
||||
/** PLM 图纸推送接收记录的接收时间。 */
|
||||
List<OrderProProgressTimeAgg> selectPlmDrawingReceiveTimes(@Param("orderNos") List<String> orderNos);
|
||||
|
||||
/** PLM 工艺路线接收记录的接收时间。 */
|
||||
List<OrderProProgressTimeAgg> selectPlmRouteReceiveTimes(@Param("orderNos") List<String> orderNos);
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.ProcessDesignReport;
|
||||
|
||||
/**
|
||||
* 设计报工明细
|
||||
*/
|
||||
public interface ProcessDesignReportMapper extends BaseMapperPlus<ProcessDesignReportMapper, ProcessDesignReport, ProcessDesignReport> {
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.ProcessDesignTask;
|
||||
|
||||
/**
|
||||
* 设计个人任务
|
||||
*/
|
||||
public interface ProcessDesignTaskMapper extends BaseMapperPlus<ProcessDesignTaskMapper, ProcessDesignTask, ProcessDesignTask> {
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.RatioModel;
|
||||
|
||||
public interface RatioModelMapper extends BaseMapperPlus<RatioModelMapper, RatioModel, RatioModel> {
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.VmiMaterial;
|
||||
import com.ruoyi.system.domain.vo.VmiMaterialVo;
|
||||
|
||||
/**
|
||||
* VMI物料安全库存 Mapper
|
||||
*/
|
||||
public interface VmiMaterialMapper extends BaseMapperPlus<VmiMaterialMapper, VmiMaterial, VmiMaterialVo> {
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.VmiPurchaseReqLog;
|
||||
import com.ruoyi.system.domain.vo.VmiPurchaseReqLogVo;
|
||||
|
||||
/**
|
||||
* VMI采购申请推送记录 Mapper
|
||||
*/
|
||||
public interface VmiPurchaseReqLogMapper extends BaseMapperPlus<VmiPurchaseReqLogMapper, VmiPurchaseReqLog, VmiPurchaseReqLogVo> {
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.VmiStockAlertLog;
|
||||
import com.ruoyi.system.domain.vo.VmiStockAlertLogVo;
|
||||
|
||||
/**
|
||||
* VMI低库存触发记录 Mapper
|
||||
*/
|
||||
public interface VmiStockAlertLogMapper extends BaseMapperPlus<VmiStockAlertLogMapper, VmiStockAlertLog, VmiStockAlertLogVo> {
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.system.domain.WorkAlertGroupPushConfig;
|
||||
import com.ruoyi.system.domain.vo.WorkAlertGroupPushConfigVo;
|
||||
|
||||
/**
|
||||
* 指定群预警定时推送配置 Mapper
|
||||
*/
|
||||
public interface WorkAlertGroupPushConfigMapper extends BaseMapperPlus<WorkAlertGroupPushConfigMapper, WorkAlertGroupPushConfig, WorkAlertGroupPushConfigVo> {
|
||||
}
|
||||
@ -0,0 +1,248 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.ruoyi.common.config.DrawingFileQueryProperties;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.constant.ProcessOrderProSource;
|
||||
import com.ruoyi.system.domain.ProcessOrderPro;
|
||||
import com.ruoyi.system.mapper.ProcessOrderProMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 按图号转发图纸/模型下载。仅 PLM 来源项目允许调用。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DrawingFileQueryService {
|
||||
|
||||
private static final Set<String> FILE_TYPES = new HashSet<>(Arrays.asList(
|
||||
"sldprt", "sldasm", "dwg", "pdf", "x_t"
|
||||
));
|
||||
|
||||
private static final Pattern FILENAME_STAR = Pattern.compile("filename\\*=(?:UTF-8'')?([^;]+)", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern FILENAME = Pattern.compile("filename=\"?([^\";]+)\"?", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final DrawingFileQueryProperties properties;
|
||||
private final ProcessOrderProMapper processOrderProMapper;
|
||||
|
||||
public void download(String productionOrderNo, String materialCode, String fileType, HttpServletResponse response) {
|
||||
String orderNo = StringUtils.trimToEmpty(productionOrderNo);
|
||||
String code = normalizeMaterialCode(materialCode);
|
||||
String type = normalizeFileType(fileType);
|
||||
assertPlmOrder(orderNo);
|
||||
|
||||
String url = buildUrl(code, type);
|
||||
HttpResponse remote = null;
|
||||
try {
|
||||
remote = HttpRequest.get(url).timeout(properties.getReadTimeoutMs()).executeAsync();
|
||||
int status = remote.getStatus();
|
||||
String contentType = StringUtils.trimToEmpty(remote.header("Content-Type"));
|
||||
if (status < 200 || status >= 300 || isErrorContent(contentType)) {
|
||||
String body = "";
|
||||
try {
|
||||
body = remote.body();
|
||||
} catch (Exception ignored) {
|
||||
// 错误正文读不到时只返回状态码
|
||||
}
|
||||
log.warn("图纸文件服务返回异常 status={} url={} body={}", status, url, abbreviate(body));
|
||||
throw new ServiceException(briefError(body, status));
|
||||
}
|
||||
String filename = resolveFilename(remote.header("Content-Disposition"), code, type);
|
||||
String responseType = StringUtils.isBlank(contentType) ? "application/octet-stream" : contentType;
|
||||
response.setContentType(responseType);
|
||||
String encoded = URLEncoder.encode(filename, StandardCharsets.UTF_8.name()).replace("+", "%20");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encoded);
|
||||
long length = remote.contentLength();
|
||||
if (length > 0) {
|
||||
response.setContentLengthLong(length);
|
||||
}
|
||||
try (InputStream in = remote.bodyStream(); OutputStream out = response.getOutputStream()) {
|
||||
IoUtil.copy(in, out);
|
||||
out.flush();
|
||||
}
|
||||
} catch (ServiceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
if (isConnectFailure(e)) {
|
||||
String target = StringUtils.trimToEmpty(properties.getBaseUrl());
|
||||
log.warn("图纸文件服务连不上 url={} msg={}", url, rootMessage(e));
|
||||
if (!response.isCommitted()) {
|
||||
throw new ServiceException("无法连接图纸文件服务 " + target + ",连接被拒绝。请确认该地址和端口对本机开放");
|
||||
}
|
||||
return;
|
||||
}
|
||||
log.error("下载图纸模型失败 order={} code={} type={}", orderNo, code, type, e);
|
||||
if (!response.isCommitted()) {
|
||||
throw new ServiceException("图纸模型下载失败,请稍后重试");
|
||||
}
|
||||
} finally {
|
||||
if (remote != null) {
|
||||
remote.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isConnectFailure(Throwable error) {
|
||||
Throwable current = error;
|
||||
while (current != null) {
|
||||
if (current instanceof java.net.SocketException || current instanceof java.net.ConnectException) {
|
||||
return true;
|
||||
}
|
||||
String message = current.getMessage();
|
||||
if (message != null && (message.contains("Permission denied: connect")
|
||||
|| message.contains("connect timed out")
|
||||
|| message.contains("Connection refused")
|
||||
|| message.contains("访问权限不允许"))) {
|
||||
return true;
|
||||
}
|
||||
current = current.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String rootMessage(Throwable error) {
|
||||
Throwable current = error;
|
||||
while (current.getCause() != null) {
|
||||
current = current.getCause();
|
||||
}
|
||||
return StringUtils.trimToEmpty(current.getMessage());
|
||||
}
|
||||
|
||||
private void assertPlmOrder(String orderNo) {
|
||||
if (StringUtils.isBlank(orderNo)) {
|
||||
throw new ServiceException("生产令号不能为空");
|
||||
}
|
||||
ProcessOrderPro order = processOrderProMapper.selectOne(
|
||||
Wrappers.<ProcessOrderPro>lambdaQuery()
|
||||
.eq(ProcessOrderPro::getProductionOrderNo, orderNo)
|
||||
.last("LIMIT 1"));
|
||||
if (order == null) {
|
||||
throw new ServiceException("未找到项目令号: " + orderNo);
|
||||
}
|
||||
String source = StringUtils.trimToEmpty(order.getSource());
|
||||
if (!ProcessOrderProSource.PLM.equalsIgnoreCase(source)) {
|
||||
throw new ServiceException("仅 PLM 来源项目可下载图纸模型");
|
||||
}
|
||||
}
|
||||
|
||||
private String buildUrl(String materialCode, String fileType) {
|
||||
String base = StringUtils.trimToEmpty(properties.getBaseUrl());
|
||||
if (base.endsWith("/")) {
|
||||
base = base.substring(0, base.length() - 1);
|
||||
}
|
||||
String path = StringUtils.trimToEmpty(properties.getDownloadPath());
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
if (StringUtils.isBlank(base)) {
|
||||
throw new ServiceException("图纸文件服务地址未配置");
|
||||
}
|
||||
return base + path
|
||||
+ "?propertyValue=" + urlEncode(materialCode)
|
||||
+ "&fileType=" + urlEncode(fileType);
|
||||
}
|
||||
|
||||
private static String normalizeMaterialCode(String materialCode) {
|
||||
String code = StringUtils.trimToEmpty(materialCode);
|
||||
if (StringUtils.isBlank(code) || code.length() > 128
|
||||
|| code.contains("..") || code.indexOf('/') >= 0 || code.indexOf('\\') >= 0
|
||||
|| code.indexOf('\r') >= 0 || code.indexOf('\n') >= 0) {
|
||||
throw new ServiceException("物料编码不合法");
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
private static String normalizeFileType(String fileType) {
|
||||
String type = StringUtils.trimToEmpty(fileType).toLowerCase(Locale.ROOT);
|
||||
if (!FILE_TYPES.contains(type)) {
|
||||
throw new ServiceException("不支持的图纸格式");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private static boolean isErrorContent(String contentType) {
|
||||
String type = contentType.toLowerCase(Locale.ROOT);
|
||||
return type.contains("json") || type.contains("text/html");
|
||||
}
|
||||
|
||||
private static String abbreviate(String body) {
|
||||
String text = StringUtils.trimToEmpty(body).replaceAll("\\s+", " ");
|
||||
return text.length() > 200 ? text.substring(0, 200) : text;
|
||||
}
|
||||
|
||||
private static String briefError(String body, int status) {
|
||||
String text = StringUtils.trimToEmpty(body).replaceAll("\\s+", " ");
|
||||
if (text.length() > 200) {
|
||||
text = text.substring(0, 200);
|
||||
}
|
||||
if (StringUtils.isBlank(text) || text.startsWith("<") || text.startsWith("{")) {
|
||||
return "图纸模型下载失败(HTTP " + status + ")";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static String resolveFilename(String disposition, String materialCode, String fileType) {
|
||||
String name = parseDispositionFilename(disposition);
|
||||
if (StringUtils.isBlank(name)) {
|
||||
name = materialCode + "." + fileType;
|
||||
}
|
||||
name = name.replace("\\", "/");
|
||||
int slash = name.lastIndexOf('/');
|
||||
if (slash >= 0) {
|
||||
name = name.substring(slash + 1);
|
||||
}
|
||||
name = name.replace("\r", "").replace("\n", "").replace("\"", "").trim();
|
||||
return StringUtils.isBlank(name) ? materialCode + "." + fileType : name;
|
||||
}
|
||||
|
||||
private static String parseDispositionFilename(String disposition) {
|
||||
if (StringUtils.isBlank(disposition)) {
|
||||
return "";
|
||||
}
|
||||
Matcher star = FILENAME_STAR.matcher(disposition);
|
||||
if (star.find()) {
|
||||
return urlDecode(star.group(1).trim());
|
||||
}
|
||||
Matcher plain = FILENAME.matcher(disposition);
|
||||
if (plain.find()) {
|
||||
return plain.group(1).trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String urlEncode(String value) {
|
||||
try {
|
||||
return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("参数编码失败");
|
||||
}
|
||||
}
|
||||
|
||||
private static String urlDecode(String value) {
|
||||
try {
|
||||
return URLDecoder.decode(value, StandardCharsets.UTF_8.name());
|
||||
} catch (Exception e) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,178 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.domain.DrawingRouteOperLog;
|
||||
import com.ruoyi.system.domain.ProcessOrderPro;
|
||||
import com.ruoyi.system.mapper.DrawingRouteOperLogMapper;
|
||||
import com.ruoyi.system.mapper.ProcessOrderProMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 按推送日志重发对应钉钉通知。执行出图、工艺上传本身不是通知,不在这里重放。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DrawingRouteOperLogRenotifyService {
|
||||
|
||||
private final DrawingRouteOperLogMapper drawingRouteOperLogMapper;
|
||||
private final ProcessOrderProMapper processOrderProMapper;
|
||||
private final PlmPlanCompleteNotifyService plmPlanCompleteNotifyService;
|
||||
private final PlmDrawingArriveNotifyService plmDrawingArriveNotifyService;
|
||||
private final SkipProcessNotifyService skipProcessNotifyService;
|
||||
private final IProcessOrderProService processOrderProService;
|
||||
private final ProcessPlanExcelPushService processPlanExcelPushService;
|
||||
private final IDrawingRouteOperLogService drawingRouteOperLogService;
|
||||
|
||||
public String renotify(Long id) {
|
||||
if (id == null) {
|
||||
throw new ServiceException("日志ID不能为空");
|
||||
}
|
||||
DrawingRouteOperLog row = drawingRouteOperLogMapper.selectById(id);
|
||||
if (row == null) {
|
||||
throw new ServiceException("日志不存在");
|
||||
}
|
||||
if (IDrawingRouteOperLogService.STATUS_SKIP.equals(row.getStatus())) {
|
||||
throw new ServiceException("这条是跳过记录,没有发出去的通知");
|
||||
}
|
||||
String bizType = StringUtils.trimToEmpty(row.getBizType());
|
||||
String result;
|
||||
if (IDrawingRouteOperLogService.BIZ_ROUTE_NOTIFY.equals(bizType)) {
|
||||
result = renotifyRoute(row);
|
||||
} else if (IDrawingRouteOperLogService.BIZ_DRAWING_NOTIFY.equals(bizType)) {
|
||||
result = renotifyDrawing(row);
|
||||
} else if (IDrawingRouteOperLogService.BIZ_SKIP_PROCESS_NOTIFY.equals(bizType)) {
|
||||
result = skipProcessNotifyService.resendDrawingComplete(loadOrder(row));
|
||||
} else if (IDrawingRouteOperLogService.BIZ_ELECTRICAL_BOM_NOTIFY.equals(bizType)) {
|
||||
result = skipProcessNotifyService.resendElectricalBom(loadOrder(row), savedCount(row));
|
||||
} else {
|
||||
throw new ServiceException("该类型不能重新通知");
|
||||
}
|
||||
return requireSent(result);
|
||||
}
|
||||
|
||||
/** PLM 到齐通知重导计划表发给生管;手动上传只重发已落盘的计划表,不重发审核卡片。 */
|
||||
private String renotifyRoute(DrawingRouteOperLog row) {
|
||||
if (isPlmRouteNotify(row)) {
|
||||
return plmPlanCompleteNotifyService.notifyIfAssemblyAndRouteComplete(row.getProductionOrderNo(), null);
|
||||
}
|
||||
return resendSavedPlanFile(row);
|
||||
}
|
||||
|
||||
private String renotifyDrawing(DrawingRouteOperLog row) {
|
||||
if (IDrawingRouteOperLogService.CHANNEL_PLM.equals(row.getChannel())) {
|
||||
return plmDrawingArriveNotifyService.resend(row.getProductionOrderNo(), row.getSummaryJson());
|
||||
}
|
||||
ProcessOrderPro order = loadOrder(row);
|
||||
if (order == null || order.getId() == null) {
|
||||
return "项目不存在,未发送";
|
||||
}
|
||||
R<Void> sent = processOrderProService.sendProcessRoutePlanExcel(order.getId());
|
||||
if (sent == null) {
|
||||
return "发送失败:没有返回结果";
|
||||
}
|
||||
if (sent.getCode() != R.SUCCESS) {
|
||||
throw new ServiceException(StringUtils.defaultIfBlank(sent.getMsg(), "发送失败"));
|
||||
}
|
||||
String msg = StringUtils.defaultIfBlank(sent.getMsg(), "已重新发送出图后推送");
|
||||
if (msg.contains("跳过")) {
|
||||
return "项目无需做工艺,未推送给工艺";
|
||||
}
|
||||
return "已重新发送出图后推送 令号=" + order.getProductionOrderNo();
|
||||
}
|
||||
|
||||
private String resendSavedPlanFile(DrawingRouteOperLog row) {
|
||||
String orderNo = StringUtils.trimToEmpty(row.getProductionOrderNo());
|
||||
String fileName = StringUtils.trimToEmpty(row.getFileName());
|
||||
if (StringUtils.isBlank(orderNo) || StringUtils.isBlank(fileName)) {
|
||||
return "缺少令号或文件名,未发送";
|
||||
}
|
||||
File file = new File(new File(processPlanExcelPushService.getPlanExcelBaseDir(), orderNo), fileName);
|
||||
if (!file.exists() || file.length() <= 0) {
|
||||
return "服务器上找不到已落盘的计划表,未发送";
|
||||
}
|
||||
long started = System.currentTimeMillis();
|
||||
ProcessPlanExcelPushService.FilePushResult filePush =
|
||||
processPlanExcelPushService.sendToPlanUsers(file, fileName);
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("resend", true);
|
||||
summary.put("sourceLogId", row.getId());
|
||||
summary.put("fileStatus", filePush.resolveStatus());
|
||||
drawingRouteOperLogService.record(IDrawingRouteOperLogService.BIZ_ROUTE_NOTIFY, logRow -> {
|
||||
logRow.setChannel(IDrawingRouteOperLogService.CHANNEL_MANUAL);
|
||||
logRow.setApiPath("/system/drawingRouteOperLog/renotify");
|
||||
logRow.setProjectId(row.getProjectId());
|
||||
logRow.setProductionOrderNo(orderNo);
|
||||
logRow.setNotifyType(IDrawingRouteOperLogService.NOTIFY_FILE);
|
||||
logRow.setNotifyUserIds(filePush.getUserIds());
|
||||
logRow.setMessageTitle("重新发送工艺计划表");
|
||||
logRow.setMessageContent("按日志重发已落盘计划表,不重发审核卡片");
|
||||
logRow.setFileName(fileName);
|
||||
logRow.setStatus(filePush.resolveStatus());
|
||||
logRow.setErrorMsg(filePush.getErrorMsg());
|
||||
logRow.setSummaryJson(JSON.toJSONString(summary));
|
||||
logRow.setDurationMs(System.currentTimeMillis() - started);
|
||||
});
|
||||
if (StringUtils.isNotBlank(filePush.getErrorMsg())) {
|
||||
return "计划表重发未完成:" + filePush.getErrorMsg();
|
||||
}
|
||||
return "已重发工艺计划表给生管 令号=" + orderNo + " 文件=" + fileName + "(未重发审核卡片)";
|
||||
}
|
||||
|
||||
private static boolean isPlmRouteNotify(DrawingRouteOperLog row) {
|
||||
if (IDrawingRouteOperLogService.CHANNEL_PLM.equals(row.getChannel())) {
|
||||
return true;
|
||||
}
|
||||
String apiPath = StringUtils.defaultString(row.getApiPath());
|
||||
return apiPath.contains("/system/plm/");
|
||||
}
|
||||
|
||||
private ProcessOrderPro loadOrder(DrawingRouteOperLog row) {
|
||||
if (row.getProjectId() != null) {
|
||||
ProcessOrderPro byId = processOrderProMapper.selectById(row.getProjectId());
|
||||
if (byId != null) {
|
||||
return byId;
|
||||
}
|
||||
}
|
||||
String orderNo = StringUtils.trimToEmpty(row.getProductionOrderNo());
|
||||
if (StringUtils.isBlank(orderNo)) {
|
||||
return null;
|
||||
}
|
||||
return processOrderProMapper.selectByProjectNumber(orderNo);
|
||||
}
|
||||
|
||||
private static int savedCount(DrawingRouteOperLog row) {
|
||||
if (StringUtils.isBlank(row.getSummaryJson())) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
JSONObject obj = JSON.parseObject(row.getSummaryJson());
|
||||
return obj == null ? 0 : obj.getIntValue("savedCount");
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static String requireSent(String result) {
|
||||
String msg = StringUtils.defaultIfBlank(result, "重新通知没有返回结果");
|
||||
if (msg.contains("未发送")
|
||||
|| msg.contains("暂不发送")
|
||||
|| msg.contains("发送失败")
|
||||
|| msg.contains("未完成")
|
||||
|| msg.contains("未推送")
|
||||
|| msg.contains("失败")) {
|
||||
throw new ServiceException(msg);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.utils.ProjectStatusLock;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.domain.ProcessOrderPro;
|
||||
|
||||
/**
|
||||
* 发送计划表、创建待办前,确认项目已经出图成功。
|
||||
*/
|
||||
public final class DrawingSuccess {
|
||||
|
||||
private DrawingSuccess() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 不能发送或创建待办时的提示;可以继续时返回 null
|
||||
*/
|
||||
public static String denyIfNotSuccess(ProcessOrderPro order) {
|
||||
if (order == null) {
|
||||
return "项目不存在,不能发送计划表或创建待办";
|
||||
}
|
||||
Integer state = order.getDrawingState();
|
||||
String msg = StringUtils.trimToEmpty(order.getDrawingStateMsg());
|
||||
if (state != null) {
|
||||
if (state == 4 && !messageFailed(msg)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(msg)) {
|
||||
return "项目尚未出图成功:" + msg;
|
||||
}
|
||||
if (state == 5) {
|
||||
return "项目出图失败,不能发送计划表或创建待办";
|
||||
}
|
||||
return "项目出图尚未完成,不能发送计划表或创建待办";
|
||||
}
|
||||
Long bomStatus = order.getBomStatus();
|
||||
if (bomStatus != null && bomStatus != ProjectStatusLock.SCHEME_DONE) {
|
||||
return null;
|
||||
}
|
||||
return "项目尚未出图成功,不能发送计划表或创建待办";
|
||||
}
|
||||
|
||||
private static boolean messageFailed(String msg) {
|
||||
return msg.contains("失败") || msg.contains("未找到") || msg.contains("错误") || msg.contains("异常");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.system.domain.ProcessOrderPro;
|
||||
import com.ruoyi.system.domain.bo.DesignTaskDesignDimBo;
|
||||
import com.ruoyi.system.domain.bo.DesignTaskQueryBo;
|
||||
import com.ruoyi.system.domain.bo.DesignTaskReportBo;
|
||||
import com.ruoyi.system.domain.vo.DesignAccountSyncVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskBoardVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskReportVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskSummaryVo;
|
||||
import com.ruoyi.system.domain.vo.DesignTaskVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设计个人任务
|
||||
*/
|
||||
public interface IDesignTaskService {
|
||||
|
||||
DesignTaskSummaryVo summaryMine();
|
||||
|
||||
/**
|
||||
* @param scope all 且当前用户是管理员时,汇总全部设计人;否则只统计本人
|
||||
*/
|
||||
DesignTaskBoardVo boardMine(String scope);
|
||||
|
||||
TableDataInfo<DesignTaskVo> queryMine(DesignTaskQueryBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* @return 新报工记录 id
|
||||
*/
|
||||
Long report(DesignTaskReportBo bo);
|
||||
|
||||
void accept(Long taskId);
|
||||
|
||||
void updateDesignDim(DesignTaskDesignDimBo bo);
|
||||
|
||||
void deleteReport(Long reportId);
|
||||
|
||||
List<DesignTaskReportVo> listMyReports(Long taskId);
|
||||
|
||||
List<DesignTaskVo> listByOrder(Long orderId);
|
||||
|
||||
DesignAccountSyncVo syncAccounts();
|
||||
|
||||
/**
|
||||
* 按项目当前设计人同步任务。backfill 为 true 时,新任务按项目状态补历史;false 时新任务一律待接收。
|
||||
*
|
||||
* @return 新插入的任务数
|
||||
*/
|
||||
int syncOrder(ProcessOrderPro order, boolean backfill);
|
||||
|
||||
void withdrawOpenByOrderIds(Collection<Long> orderIds);
|
||||
|
||||
void updateOrderNo(Long orderId, String productionOrderNo);
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.system.domain.DingTodoLog;
|
||||
import com.ruoyi.system.domain.DingTodoScene;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 按数据库里勾选的方法创建钉钉待办,并把结果写入 ding_todo_log。
|
||||
*/
|
||||
public interface IDingTodoDispatchService {
|
||||
|
||||
List<DingTodoScene> listScenes();
|
||||
|
||||
void updateScene(DingTodoScene scene);
|
||||
|
||||
TableDataInfo<DingTodoLog> queryLogs(DingTodoLog query, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 该场景未勾选时什么都不做,返回 null。
|
||||
* 勾选后发送失败则写入记录并返回失败原因。
|
||||
*/
|
||||
String sendIfEnabled(String sceneCode, String productionOrderNo, String projectName, Date routeUptime,
|
||||
File excelFile, String fallbackUserIds);
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.system.domain.DrawingRouteOperLog;
|
||||
import com.ruoyi.system.domain.bo.DrawingRouteOperLogBo;
|
||||
import com.ruoyi.system.domain.vo.DrawingRouteOperLogVo;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 出图 / 工艺上传 / 钉钉推送留痕。写入失败不影响主流程。
|
||||
* <p>
|
||||
* 业务类型:执行出图、出图后推送、工艺上传、上传后钉钉、无需做工艺通知、电气 BOM 通知。
|
||||
*/
|
||||
public interface IDrawingRouteOperLogService {
|
||||
|
||||
String BIZ_DRAWING_EXEC = "DRAWING_EXEC";
|
||||
String BIZ_DRAWING_NOTIFY = "DRAWING_NOTIFY";
|
||||
String BIZ_ROUTE_UPLOAD = "ROUTE_UPLOAD";
|
||||
String BIZ_ROUTE_NOTIFY = "ROUTE_NOTIFY";
|
||||
/** 无需做工艺且出图完成后通知生管 */
|
||||
String BIZ_SKIP_PROCESS_NOTIFY = "SKIP_PROCESS_NOTIFY";
|
||||
/** 电气 BOM 导入后的钉钉通知 */
|
||||
String BIZ_ELECTRICAL_BOM_NOTIFY = "ELECTRICAL_BOM_NOTIFY";
|
||||
|
||||
String CHANNEL_MANUAL = "MANUAL";
|
||||
String CHANNEL_PLM = "PLM";
|
||||
|
||||
String STATUS_SUCCESS = "SUCCESS";
|
||||
String STATUS_FAIL = "FAIL";
|
||||
String STATUS_PARTIAL = "PARTIAL";
|
||||
/** 无需做工艺,出图后推送未发给工艺 */
|
||||
String STATUS_SKIP = "SKIP";
|
||||
|
||||
String NOTIFY_MARKDOWN = "MARKDOWN";
|
||||
String NOTIFY_FILE = "FILE";
|
||||
String NOTIFY_CARD = "CARD";
|
||||
String NOTIFY_MARKDOWN_AND_FILE = "MARKDOWN_AND_FILE";
|
||||
String NOTIFY_FILE_AND_CARD = "FILE_AND_CARD";
|
||||
|
||||
/**
|
||||
* 追加一条记录;自动补操作人、IP、时间。内部吞掉异常。
|
||||
*/
|
||||
void record(String bizType, Consumer<DrawingRouteOperLog> filler);
|
||||
|
||||
String currentOperator();
|
||||
|
||||
String currentClientIp();
|
||||
|
||||
TableDataInfo<DrawingRouteOperLogVo> queryPageList(DrawingRouteOperLogBo bo, PageQuery pageQuery);
|
||||
|
||||
DrawingRouteOperLogVo queryDetail(Long id);
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.system.domain.vo.mes.MesDrawingListVo;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* MES 开放图纸查询 / 下载。
|
||||
*/
|
||||
public interface IMesDrawingService {
|
||||
|
||||
/**
|
||||
* 校验 MES appKey / appSecret。
|
||||
*/
|
||||
void assertAuth(String appKey, String appSecret);
|
||||
|
||||
/**
|
||||
* 按生产令号列出图纸;materialCode 非空时只返回该图号。
|
||||
*/
|
||||
MesDrawingListVo listDrawings(String productionOrderNo, String materialCode);
|
||||
|
||||
/**
|
||||
* 解析可下载文件。fileType:pdf / dwg。
|
||||
*/
|
||||
File resolveDownloadFile(String productionOrderNo, String materialCode, String fileType);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
/**
|
||||
* 按项目整体出图时间刷新设计出图延期/完成状态。
|
||||
*/
|
||||
public interface IOrderProDrawingDelayService {
|
||||
|
||||
/** 刷新所有仍处于「方案设计完成」且已填出图时间的项目 */
|
||||
void refreshAll();
|
||||
|
||||
/** 刷新单个项目;已进入出图完成及后续状态的不再改延期天数 */
|
||||
void refreshByProjectId(Long projectId);
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.system.domain.bo.OrderProProgressReportBo;
|
||||
import com.ruoyi.system.domain.vo.OrderProProgressDesignerSummaryVo;
|
||||
import com.ruoyi.system.domain.vo.OrderProProgressReportOverviewVo;
|
||||
import com.ruoyi.system.domain.vo.OrderProProgressReportVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 生产项目进度报表。
|
||||
*/
|
||||
public interface IOrderProProgressReportService {
|
||||
|
||||
TableDataInfo<OrderProProgressReportVo> queryPageList(OrderProProgressReportBo bo, PageQuery pageQuery);
|
||||
|
||||
List<OrderProProgressReportVo> queryList(OrderProProgressReportBo bo);
|
||||
|
||||
OrderProProgressReportOverviewVo queryOverview(OrderProProgressReportBo bo);
|
||||
|
||||
List<OrderProProgressDesignerSummaryVo> queryDesignerSummary(OrderProProgressReportBo bo);
|
||||
}
|
||||
@ -85,6 +85,11 @@ public interface IProcessOrderProService {
|
||||
|
||||
String executDrawing(ProcessOrderProBo orderPro);
|
||||
|
||||
/**
|
||||
* 若出图服务当前结果属于该令号,写入项目最后一次出图状态;否则返回已保存的结果。
|
||||
*/
|
||||
ProcessOrderProVo captureCurrentDrawingState(Long id);
|
||||
|
||||
/**
|
||||
* 上传PDF并生成zip包
|
||||
* @param id 项目ID
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.system.domain.dto.ratio.RatioImportResultDTO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public interface IRatioBomImportService {
|
||||
|
||||
/**
|
||||
* 按机型和速比 Sheet 导入通用件、减速机型号专用件。
|
||||
* 同一机型下的同一个速比会先清掉原有型号和物料明细,再写入本次文件的内容。
|
||||
*/
|
||||
RatioImportResultDTO importExcel(MultipartFile file, String seriesType);
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.system.domain.bo.VmiMaterialBo;
|
||||
import com.ruoyi.system.domain.bo.VmiPurchaseReqLogBo;
|
||||
import com.ruoyi.system.domain.bo.VmiReplenishConfirmBo;
|
||||
import com.ruoyi.system.domain.bo.VmiReplenishPreviewBo;
|
||||
import com.ruoyi.system.domain.bo.VmiStockAlertLogBo;
|
||||
import com.ruoyi.system.domain.vo.VmiMaterialVo;
|
||||
import com.ruoyi.system.domain.vo.VmiPurchaseReqLogVo;
|
||||
import com.ruoyi.system.domain.vo.VmiReplenishPreviewItemVo;
|
||||
import com.ruoyi.system.domain.vo.VmiStockAlertLogVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IVmiMaterialService {
|
||||
|
||||
TableDataInfo<VmiMaterialVo> queryPageList(VmiMaterialBo bo, PageQuery pageQuery);
|
||||
|
||||
VmiMaterialVo queryById(Long id);
|
||||
|
||||
Boolean insertByBo(VmiMaterialBo bo);
|
||||
|
||||
Boolean updateByBo(VmiMaterialBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids);
|
||||
|
||||
int scanLowStock();
|
||||
|
||||
/**
|
||||
* 确认补货前预览:返回库存与建议申请数量,供管理员修改。
|
||||
*/
|
||||
List<VmiReplenishPreviewItemVo> previewReplenish(VmiReplenishPreviewBo bo);
|
||||
|
||||
/**
|
||||
* 确认补货:按供应商合并生成金蝶采购申请(仅保存,不自动审批)。
|
||||
* @return 生成的采购申请单数量
|
||||
*/
|
||||
int confirmReplenish(VmiReplenishConfirmBo bo);
|
||||
|
||||
void ignoreAlert(Long alertId);
|
||||
|
||||
void notifyForecast(Long id);
|
||||
|
||||
TableDataInfo<VmiStockAlertLogVo> queryAlertPage(VmiStockAlertLogBo bo, PageQuery pageQuery);
|
||||
|
||||
TableDataInfo<VmiPurchaseReqLogVo> queryReqPage(VmiPurchaseReqLogBo bo, PageQuery pageQuery);
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.system.domain.bo.WorkAlertGroupPushConfigBo;
|
||||
import com.ruoyi.system.domain.vo.WorkAlertGroupPushConfigVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 指定群预警定时推送配置
|
||||
*/
|
||||
public interface IWorkAlertGroupPushConfigService {
|
||||
|
||||
List<WorkAlertGroupPushConfigVo> queryList();
|
||||
|
||||
WorkAlertGroupPushConfigVo queryById(Long id);
|
||||
|
||||
Boolean insertByBo(WorkAlertGroupPushConfigBo bo);
|
||||
|
||||
Boolean updateByBo(WorkAlertGroupPushConfigBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids);
|
||||
|
||||
void trigger(Long id);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user