策略优化

parent ecfe8236
......@@ -71,21 +71,43 @@ public class LanuchController {
*/
@PostMapping("/schedule")
@Operation(summary = "运算")
public R<String> schedule(@RequestBody Map<String, String> params) {
String sceneId = params.get("sceneId");
public R<String> schedule(@RequestBody Map<String, Object> params) {
String sceneId = getStringParam(params, "sceneId");
if (sceneId == null || sceneId.trim().isEmpty()) {
throw new IllegalArgumentException("场景ID不能为空");
}
// 如果需要处理时间字段,可以从params中获取并转换
// 例如:开始时间、结束时间等
String startTimeStr = params.get("startTime");
String endTimeStr = params.get("endTime");
String startTimeStr = getStringParam(params, "startTime");
String endTimeStr = getStringParam(params, "endTime");
// 在这里可以添加时间格式转换逻辑
// 根据不同格式解析时间字符串
Chromosome scheduleChromosomes = planResultService.execute2(sceneId);
// 这些参数前端可以不传;不传时后端会根据 sceneId 找场景创建人,再查这个人的策略。
Long userId = getLongParam(params, "userId");
Long baseRuleId = getLongParam(params, "baseRuleId");
if (baseRuleId == null) {
baseRuleId = getLongParam(params, "referenceId");
}
String userRuleId = getStringParam(params, "userRuleId");
String id = getStringParam(params, "id");
if (id != null) {
int index = id.indexOf(":");
if (index > 0 && index < id.length() - 1) {
String source = id.substring(0, index);
String ruleId = id.substring(index + 1);
// 前端如果传的是组合 id:USER:用户策略ID 走用户策略;GLOBAL:基础策略ID 走全局策略。
if ("USER".equalsIgnoreCase(source) && userRuleId == null) {
userRuleId = ruleId;
} else if ("GLOBAL".equalsIgnoreCase(source) && baseRuleId == null) {
baseRuleId = parseLongParam("id", ruleId);
}
}
}
Chromosome scheduleChromosomes = planResultService.execute2(sceneId, userId, baseRuleId, userRuleId);
return R.ok("排产计算成功", "排产计算成功");
}
......@@ -218,4 +240,28 @@ public class LanuchController {
return lanuchService.insertOrder(sceneId, orderCode, materialId,
startDate, endDate, priority, quantity);
}
private String getStringParam(Map<String, ?> params, String key) {
if (params == null || params.get(key) == null) {
return null;
}
String value = String.valueOf(params.get(key)).trim();
return value.isEmpty() ? null : value;
}
private Long getLongParam(Map<String, ?> params, String key) {
String value = getStringParam(params, key);
return parseLongParam(key, value);
}
private Long parseLongParam(String key, String value) {
if (value == null || value.trim().isEmpty()) {
return null;
}
try {
return Long.valueOf(value.trim());
} catch (NumberFormatException e) {
throw new IllegalArgumentException(key + "必须是数字");
}
}
}
......@@ -19,4 +19,6 @@ public interface UserStrategyRuleService extends IService<UserStrategyRule> {
List<Map<String, Object>> getStrategyOptions(Long userId);
List<StrategyScheduling> getEffectiveForwardScheduling(Long userId, Long baseRuleId, String sceneId);
List<StrategyScheduling> getEffectiveForwardScheduling(Long userId, Long baseRuleId, String sceneId, String userRuleId);
}
......@@ -37,7 +37,7 @@ public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMap
String source = getSource(params);
String ruleId = getRuleId(params);
String userRuleId = "USER".equalsIgnoreCase(source) ? ruleId : getString(params, "userRuleId");
Long baseRuleId = "GLOBAL".equalsIgnoreCase(source) ? toLong(ruleId) : getLong(params, "baseRuleId");
Long baseRuleId = resolveBaseRuleId(params, source, ruleId);
UserStrategyRule selectedUserRule = findUserRuleById(userRuleId, userId);
if (selectedUserRule != null) {
......@@ -63,7 +63,7 @@ public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMap
String source = getSource(params);
String ruleId = getRuleId(params);
String userRuleId = "USER".equalsIgnoreCase(source) ? ruleId : getString(params, "userRuleId");
Long baseRuleId = "GLOBAL".equalsIgnoreCase(source) ? toLong(ruleId) : getLong(params, "baseRuleId");
Long baseRuleId = resolveBaseRuleId(params, source, ruleId);
UserStrategyRule userRule = null;
if (StringUtils.hasText(userRuleId)) {
......@@ -116,7 +116,7 @@ public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMap
String source = getSource(params);
String ruleId = getRuleId(params);
String userRuleId = "USER".equalsIgnoreCase(source) ? ruleId : getString(params, "userRuleId");
Long baseRuleId = "GLOBAL".equalsIgnoreCase(source) ? toLong(ruleId) : getLong(params, "baseRuleId");
Long baseRuleId = resolveBaseRuleId(params, source, ruleId);
UserStrategyRule userRule = null;
if (StringUtils.hasText(userRuleId)) {
......@@ -224,10 +224,23 @@ public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMap
@Override
public List<StrategyScheduling> getEffectiveForwardScheduling(Long userId, Long baseRuleId, String sceneId) {
return getEffectiveForwardScheduling(userId, baseRuleId, sceneId, null);
}
@Override
public List<StrategyScheduling> getEffectiveForwardScheduling(Long userId, Long baseRuleId, String sceneId, String userRuleId) {
// 1. 如果 /lanuch/schedule 明确带了 userRuleId,就优先使用这条刚保存的用户策略。
UserStrategyRule selectedUserRule = findUserRuleByIdOptionalUser(userRuleId, userId);
if (selectedUserRule != null) {
return parseForwardScheduling(selectedUserRule.getForwardScheduling());
}
// 2. 没有明确 userRuleId 时,按 userId/baseRuleId 找当前用户可用的策略。
UserStrategyRule userRule = findUserRule(userId, baseRuleId);
if (userRule != null) {
return parseForwardScheduling(userRule.getForwardScheduling());
}
// 3. 用户没有自定义策略时,回退到基础/全局策略,保证排产还能继续跑。
StrategyRule globalRule = findGlobalRule(baseRuleId);
return globalRule == null ? new ArrayList<StrategyScheduling>() : parseForwardScheduling(globalRule.getForwardScheduling());
}
......@@ -238,6 +251,7 @@ public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMap
return null;
}
// 同一个用户可能有多条策略:默认策略优先,其次取最近修改的一条。
rules.sort(Comparator
.comparing((UserStrategyRule rule) -> rule.getIsDefault() == null ? 0L : rule.getIsDefault(), Comparator.reverseOrder())
.thenComparing(rule -> rule.getLastmodificationtime() == null ? LocalDateTime.MIN : rule.getLastmodificationtime(), Comparator.reverseOrder()));
......@@ -256,6 +270,22 @@ public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMap
return rule;
}
private UserStrategyRule findUserRuleByIdOptionalUser(String userRuleId, Long userId) {
if (!StringUtils.hasText(userRuleId)) {
return null;
}
UserStrategyRule rule = this.getById(userRuleId);
if (rule == null || isDeleted(rule.getIsdeleted())) {
return null;
}
// 如果能解析到 userId,则校验策略归属,避免误用其他用户的策略。
if (userId != null && !userId.equals(rule.getUserid())) {
return null;
}
return rule;
}
private List<UserStrategyRule> findUserRules(Long userId, Long baseRuleId) {
if (userId == null) {
return null;
......@@ -362,6 +392,15 @@ public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMap
return toLong(String.valueOf(params.get(key)));
}
private Long resolveBaseRuleId(Map<String, Object> params, String source, String ruleId) {
if ("GLOBAL".equalsIgnoreCase(source)) {
return toLong(ruleId);
}
Long baseRuleId = getLong(params, "baseRuleId");
// 前端保存策略时传的是 referenceId,这里兼容成基础策略 ID。
return baseRuleId == null ? getLong(params, "referenceId") : baseRuleId;
}
private String getString(Map<String, Object> params, String key) {
if (params == null || params.get(key) == null) {
return null;
......
package com.aps.service.plan;
import com.aps.common.util.DateGroupUtil;
import com.aps.entity.Algorithm.OrderSortRule;
import com.aps.entity.ProdSceneConfig;
import com.aps.entity.StrategyScheduling;
import com.aps.entity.basic.Order;
import com.aps.service.ProdSceneConfigService;
import com.aps.service.UserStrategyRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 排产策略服务:负责读取当前排产要使用的策略,并把策略转换成订单排序规则。
*/
@Slf4j
@Service
public class ScheduleStrategyService {
@Autowired
private UserStrategyRuleService userStrategyRuleService;
@Autowired
private ProdSceneConfigService prodSceneConfigService;
/**
* 获取本次排产实际使用的用户。
* 前端传 userId 时直接使用;没传时从场景创建人 createUser 反查。
*/
public Long resolveScheduleUserId(String sceneId, Long userId) {
if (userId != null) {
return userId;
}
try {
ProdSceneConfig sceneConfig = prodSceneConfigService == null ? null : prodSceneConfigService.getById(sceneId);
return parseLongSafely(sceneConfig == null ? null : sceneConfig.getCreateUser());
} catch (Exception e) {
log.warn("读取场景用户失败,sceneId={}", sceneId, e);
return null;
}
}
/**
* 读取 /userStrategyRule/save 保存的用户策略;没有用户策略时由 UserStrategyRuleService 兜底全局策略。
*/
public List<StrategyScheduling> loadEffectiveForwardScheduling(Long userId, Long baseRuleId, String sceneId, String userRuleId) {
try {
List<StrategyScheduling> schedulings =
userStrategyRuleService.getEffectiveForwardScheduling(userId, baseRuleId, sceneId, userRuleId);
return schedulings == null ? Collections.emptyList() : schedulings;
} catch (Exception e) {
log.warn("读取排产策略失败,使用默认优先级排序,sceneId={}, userId={}, baseRuleId={}, userRuleId={}",
sceneId, userId, baseRuleId, userRuleId, e);
return Collections.emptyList();
}
}
public OrderSortRule createMultiConditionRule(List<Order> orders) {
return createMultiConditionRule(orders, Collections.emptyList());
}
/**
* 把 forwardScheduling 中 value=true 的策略项按 sort 顺序转换成 OrderSortRule。
* 如果用户策略和全局策略都没有可用项,则回退到原来的 priority 升序。
*/
public OrderSortRule createMultiConditionRule(List<Order> orders, List<StrategyScheduling> strategySchedulings) {
OrderSortRule rule = new OrderSortRule();
rule.setEnabled(true);
List<OrderSortRule.SortCondition> conditions = new ArrayList<>();
try {
Set<String> fields = new HashSet<>();
List<StrategyScheduling> selecteds = strategySchedulings == null
? Collections.emptyList()
: strategySchedulings.stream()
.filter(StrategyScheduling::isValue)
.sorted(Comparator.comparingInt(StrategyScheduling::getSort))
.collect(Collectors.toList());
int sequence = 1;
for (StrategyScheduling strategy : selecteds) {
OrderSortRule.SortCondition condition = buildSortConditionFromStrategy(orders, strategy, sequence, fields);
if (condition != null) {
conditions.add(condition);
fields.add(condition.getFieldName());
sequence++;
}
}
} catch (Exception e) {
log.warn("策略排序条件生成失败,使用默认优先级排序", e);
conditions.clear();
}
if (conditions.isEmpty()) {
conditions.add(createSortCondition(1, "priority", false));
}
rule.setConditions(conditions);
return rule;
}
/**
* 单个策略项到排序字段的映射。
* 例如 deliveryDate -> dueDate/groupDueDate,startDate -> startDate/groupStartDate。
*/
private OrderSortRule.SortCondition buildSortConditionFromStrategy(
List<Order> orders,
StrategyScheduling strategy,
int sequence,
Set<String> fields) throws Exception {
if (strategy == null || strategy.getName() == null) {
return null;
}
String name = normalizeStrategyName(strategy.getName());
int amplitude = strategy.getAmplitude();
switch (name) {
case "customerduedate":
case "customerdue_date":
case "customer_due_date":
case "deliverydate":
case "delivery_date":
case "duedate":
case "due_date": {
String fieldName = "dueDate";
// amplitude > 0 表示按 N 天跨度分组排序;amplitude <= 0 表示直接按真实交期排序。
if (amplitude > 0) {
DateGroupUtil.calculateGroupDueDate(orders, "dueDate", "groupDueDate", amplitude);
fieldName = "groupDueDate";
}
return createSortCondition(sequence, fieldName, false);
}
case "startddate":
case "startd_date":
case "startdate":
case "start_date": {
String fieldName = "startDate";
// 开始日期策略同样支持按 amplitude 天数分组。
if (amplitude > 0) {
DateGroupUtil.calculateGroupDueDate(orders, "startDate", "groupStartDate", amplitude);
fieldName = "groupStartDate";
}
return createSortCondition(sequence, fieldName, false);
}
case "gatheringseries":
case "gathering_series":
case "serie":
case "series":
return fields.contains("serie") ? null : createSortCondition(sequence, "serie", false);
case "minimumwopriority":
case "minimum_wo_priority":
case "min_wo_priority":
case "priority":
case "priority_asc":
return fields.contains("priority") ? null : createSortCondition(sequence, "priority", false);
case "maximumwopriority":
case "maximum_wo_priority":
case "max_wo_priority":
case "priority_desc":
return fields.contains("priority") ? null : createSortCondition(sequence, "priority", true);
case "materialrise":
case "material_rise":
case "materialcode":
case "material_code":
return fields.contains("materialCode") ? null : createSortCondition(sequence, "materialCode", false);
case "planrise":
case "plan_rise":
case "ordercode_asc":
case "order_code_asc":
return fields.contains("orderCode") ? null : createSortCondition(sequence, "orderCode", false);
case "plandrop":
case "plan_drop":
case "ordercode_desc":
case "order_code_desc":
return fields.contains("orderCode") ? null : createSortCondition(sequence, "orderCode", true);
case "minimizetoolchangeovers":
case "minimize_tool_changeovers":
case "minimizechangeover":
case "minimize_changeover":
return fields.contains("minimize_changeover") ? null : createSortCondition(sequence, "minimize_changeover", false);
default:
log.warn("跳过不支持的排产策略项: {}", strategy.getName());
return null;
}
}
private Long parseLongSafely(String value) {
if (value == null || value.trim().isEmpty()) {
return null;
}
try {
return Long.valueOf(value.trim());
} catch (NumberFormatException e) {
return null;
}
}
private String normalizeStrategyName(String name) {
return name == null ? "" : name.trim().replace("-", "_").replace(" ", "_").toLowerCase(Locale.ROOT);
}
private OrderSortRule.SortCondition createSortCondition(int sequence, String fieldName, boolean reverse) {
OrderSortRule.SortCondition condition = new OrderSortRule.SortCondition();
condition.setSequence(sequence);
condition.setFieldName(fieldName);
condition.setReverse(reverse);
return condition;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment