fix: 完善日计划开始确认与取消入口

This commit is contained in:
zhouz
2026-08-14 16:09:32 +08:00
parent 777de97085
commit a693d528cc
10 changed files with 78 additions and 9 deletions

View File

@@ -426,7 +426,7 @@ git commit -m "feat: 增加LMS日计划管理接口"
- [ ] **步骤 4实现开始订单和详情**
开始订单弹窗展示当前数量并允许填写追加数量。详情抽屉展示三类状态时间和按时间倒序的操作记录。
开始订单弹窗展示原计划量、确认计划量、已有追加、本次追加和开始后总量确认计划量为必填正整数ERP 与人工来源都可在开始时确认。服务端在同一事务内原子更新计划量、累计追加量和订单状态,并分别记录数量调整、追加和开始日志。管理页为未开始订单提供填写原因的取消入口。详情抽屉展示三类状态时间和按时间倒序的操作记录。
- [ ] **步骤 5运行局部 ESLint**

View File

@@ -31,6 +31,7 @@
- 人工新增日计划。
- 编辑未开始的人工日计划。
- 对未开始订单执行“开始订单”。
- 对未开始订单执行“取消订单”,填写原因后可从作业页恢复。
- 已结束订单只在本页面查询,不进入作业页面。
查询条件在桌面端一行多项展示,包括日计划编号或 ERP 订单号、计划日期范围、管芯编码或名称、来源和订单状态。
@@ -136,7 +137,7 @@ ERP 推送不允许直接修改顺序、权重、当前完成数量或作业状
人工创建字段包括计划日期、人工订单号、管芯物料、计划数量、装箱信息、顺序和权重。人工订单号允许手填;未填写时使用 LMS 日计划编号作为订单号。
只有未开始的人工来源日计划允许编辑基本信息。ERP 来源计划通过 ERP 幂等推送维护,管理页面只读。开始订单时允许再次确认计划数量,并可填写追加数量;开始成功后进入日计划作业页面。
只有未开始的人工来源日计划允许编辑基本信息。ERP 来源计划通过 ERP 幂等推送维护,管理页面只读。开始订单时 ERP 和人工来源均允许再次确认计划数量,并可填写追加数量;计划数量、累计追加数量和开始状态在同一事务中原子更新,开始成功后进入日计划作业页面。
追加数量仅允许订单进行中时执行,必须大于零,按累计值保存并记录每次追加明细。

View File

@@ -9,6 +9,10 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class DailyPlanStartReqVO extends DailyPlanVersionReqVO {
@NotNull(message = "确认计划数量不能为空")
@Min(value = 1, message = "确认计划数量必须为正整数")
private Integer planQty;
@NotNull(message = "追加数量不能为空")
@Min(value = 0, message = "追加数量不能小于0")
private Integer appendQty;

View File

@@ -94,6 +94,9 @@ public interface DailyPlanMapper extends BaseMapperX<DailyPlanDO> {
*/
int updateStateWithVersion(@Param("plan") DailyPlanDO plan);
/** 原子确认数量并开始未开始订单。 */
int startOrderWithVersion(@Param("plan") DailyPlanDO plan);
/**
* 乐观锁恢复已取消的订单状态,并清空前一订单状态。
*/

View File

@@ -359,19 +359,32 @@ public class DailyPlanServiceImpl implements DailyPlanService {
checkAffected(dailyPlanMapper.updateNotStartedPlan(update));
}
/** 开始订单并写入追加数量。 */
/** 开始订单,同时确认计划数量并写入本次追加数量。 */
@Override
@Transactional(rollbackFor = Exception.class)
public void startOrder(DailyPlanStartReqVO reqVO) {
DailyPlanDO current = validateVersion(reqVO.getId(), reqVO.getVersion());
validateOrderStatus(current, DailyPlanOrderStatusEnum.NOT_STARTED, "开始订单");
DailyPlanDO update = newUpdate(current);
int confirmedPlanQty = reqVO.getPlanQty();
int appendQty = safeAddQuantity(current.getAppendQty(), reqVO.getAppendQty());
safeAddQuantity(confirmedPlanQty, appendQty); // 校验开始后的需求总量不会溢出。
update.setPlanQty(confirmedPlanQty);
update.setOrderStatus(DailyPlanOrderStatusEnum.IN_PROGRESS.getCode());
update.setOrderStartTime(LocalDateTime.now());
update.setAppendQty(safeAddQuantity(current.getAppendQty(), reqVO.getAppendQty()));
checkAffected(dailyPlanMapper.updateStateWithVersion(update));
update.setAppendQty(appendQty);
checkAffected(dailyPlanMapper.startOrderWithVersion(update));
if (!current.getPlanQty().equals(confirmedPlanQty)) {
insertLog(current.getDailyPlanId(), TARGET_QUANTITY, TYPE_UPDATE,
String.valueOf(current.getPlanQty()), String.valueOf(confirmedPlanQty),
confirmedPlanQty - current.getPlanQty(), null);
}
if (reqVO.getAppendQty() > 0) {
insertLog(current.getDailyPlanId(), TARGET_QUANTITY, TYPE_APPEND,
String.valueOf(current.getAppendQty()), String.valueOf(appendQty), reqVO.getAppendQty(), null);
}
insertLog(current.getDailyPlanId(), TARGET_ORDER, TYPE_START, String.valueOf(current.getOrderStatus()),
String.valueOf(update.getOrderStatus()), reqVO.getAppendQty(), null);
String.valueOf(update.getOrderStatus()), null, null);
}
/** 追加进行中订单的数量。 */

View File

@@ -246,6 +246,20 @@
AND deleted = b'0'
</update>
<update id="startOrderWithVersion">
UPDATE lms_daily_plan
SET plan_qty = #{plan.planQty},
append_qty = #{plan.appendQty},
order_status = 1,
order_start_time = #{plan.orderStartTime},
updater = #{plan.updater},
version = version + 1
WHERE daily_plan_id = #{plan.dailyPlanId}
AND version = #{plan.version}
AND order_status = 0
AND deleted = b'0'
</update>
<update id="updateSortWithVersion">
UPDATE lms_daily_plan
SET sort_seq = #{sortSeq}, updater = #{updater}, version = version + 1

View File

@@ -84,6 +84,7 @@ export namespace LmsDailyPlanApi {
export interface StartReq extends VersionReq {
appendQty: number;
planQty: number;
}
export interface AppendReq extends VersionReq {

View File

@@ -7,6 +7,7 @@ import { Page, useVbenDrawer, useVbenModal } from '@vben/common-ui';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getDailyPlanManagementPage } from '#/api/lms/dailyplan';
import Operation from '../work/modules/operation.vue';
import { useGridColumns, useGridFormSchema } from './data';
import Detail from './modules/detail.vue';
import Form from './modules/form.vue';
@@ -27,6 +28,10 @@ const [StartOrderModal, startOrderModalApi] = useVbenModal({
connectedComponent: StartOrder,
destroyOnClose: true,
});
const [OperationModal, operationModalApi] = useVbenModal({
connectedComponent: Operation,
destroyOnClose: true,
});
function handleRefresh() {
gridApi.query();
@@ -48,6 +53,10 @@ function handleStart(row: LmsDailyPlanApi.DailyPlan) {
startOrderModalApi.setData(row).open();
}
function handleCancel(row: LmsDailyPlanApi.DailyPlan) {
operationModalApi.setData({ row, type: 'cancelOrder' }).open();
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
@@ -82,6 +91,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormDrawer @success="handleRefresh" />
<DetailDrawer />
<StartOrderModal @success="handleRefresh" />
<OperationModal @success="handleRefresh" />
<Grid table-title="日计划管理">
<template #toolbar-tools>
<TableAction
@@ -126,6 +136,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
ifShow: row.orderStatus === ORDER_NOT_STARTED,
onClick: handleStart.bind(null, row),
},
{
label: '取消订单',
type: 'link',
danger: true,
auth: ['lms:daily-plan:operate'],
ifShow: row.orderStatus === ORDER_NOT_STARTED,
onClick: handleCancel.bind(null, row),
},
]"
/>
</template>

View File

@@ -11,12 +11,17 @@ import { startDailyPlanOrder } from '#/api/lms/dailyplan';
const emit = defineEmits(['success']);
const row = ref<LmsDailyPlanApi.DailyPlan>();
const planQty = ref(1);
const appendQty = ref(0);
const totalAfterStart = computed(() => (row.value?.totalQty ?? 0) + appendQty.value);
const totalAfterStart = computed(() => planQty.value + (row.value?.appendQty ?? 0) + appendQty.value);
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
if (!row.value) return;
if (!Number.isInteger(planQty.value) || planQty.value <= 0) {
message.warning('确认计划数量必须为正整数');
return;
}
if (!Number.isInteger(appendQty.value) || appendQty.value < 0) {
message.warning('追加数量必须为不小于 0 的整数');
return;
@@ -26,6 +31,7 @@ const [Modal, modalApi] = useVbenModal({
await startDailyPlanOrder({
id: row.value.dailyPlanId,
version: row.value.version,
planQty: planQty.value,
appendQty: appendQty.value,
});
await modalApi.close();
@@ -38,10 +44,12 @@ const [Modal, modalApi] = useVbenModal({
onOpenChange(isOpen: boolean) {
if (!isOpen) {
row.value = undefined;
planQty.value = 1;
appendQty.value = 0;
return;
}
row.value = modalApi.getData<LmsDailyPlanApi.DailyPlan>();
planQty.value = row.value.planQty;
appendQty.value = 0;
},
});
@@ -52,7 +60,10 @@ const [Modal, modalApi] = useVbenModal({
<Descriptions v-if="row" bordered :column="1" size="small">
<DescriptionsItem label="日计划编号">{{ row.planCode }}</DescriptionsItem>
<DescriptionsItem label="管芯">{{ row.materialCode }} / {{ row.materialName }}</DescriptionsItem>
<DescriptionsItem label="当前计划数量">{{ row.planQty }}</DescriptionsItem>
<DescriptionsItem label="计划数量">{{ row.planQty }}</DescriptionsItem>
<DescriptionsItem label="确认计划数量">
<InputNumber v-model:value="planQty" :min="1" :precision="0" class="w-full" />
</DescriptionsItem>
<DescriptionsItem label="已有追加数量">{{ row.appendQty }}</DescriptionsItem>
<DescriptionsItem label="本次追加数量">
<InputNumber v-model:value="appendQty" :min="0" :precision="0" class="w-full" />

View File

@@ -56,6 +56,7 @@ CROSS JOIN (
UNION ALL SELECT 4, '日计划启动', 'lms:daily-plan:start'
UNION ALL SELECT 5, '日计划排程', 'lms:daily-plan:schedule'
UNION ALL SELECT 6, '日计划策略', 'lms:daily-plan:strategy'
UNION ALL SELECT 7, '日计划操作', 'lms:daily-plan:operate'
) permission
WHERE menu.id = COALESCE(
(SELECT fixed.id FROM system_menu fixed WHERE fixed.id=2093000000000000100 AND fixed.deleted=b'0'),
@@ -63,7 +64,10 @@ WHERE menu.id = COALESCE(
WHERE fallback.component='lms/daily-plan/management/index' AND fallback.deleted=b'0')
)
AND menu.deleted=b'0'
AND NOT EXISTS (SELECT 1 FROM system_menu existing WHERE existing.permission=permission.code AND existing.deleted=b'0');
AND NOT EXISTS (
SELECT 1 FROM system_menu existing
WHERE existing.parent_id=menu.id AND existing.permission=permission.code AND existing.deleted=b'0'
);
-- 作业页面独立挂载自身实际使用的按钮权限,避免仅授权作业菜单时按钮全部不可见。
INSERT INTO system_menu