Sfoglia il codice sorgente

预约调度,船舶

fanzherong_v 2 giorni fa
parent
commit
e3029552df
35 ha cambiato i file con 3255 aggiunte e 67 eliminazioni
  1. 32 0
      snowy-admin-web/src/api/biz/bizAppointReplaceApi.js
  2. 28 0
      snowy-admin-web/src/api/biz/bizShipApi.js
  3. 2 2
      snowy-admin-web/src/views/biz/bizappointmentrecord/replace.vue
  4. 108 0
      snowy-admin-web/src/views/biz/bizappointreplace/detail.vue
  5. 94 0
      snowy-admin-web/src/views/biz/bizappointreplace/form.vue
  6. 255 0
      snowy-admin-web/src/views/biz/bizappointreplace/index.vue
  7. 66 0
      snowy-admin-web/src/views/biz/bizappointreplace/review.vue
  8. 137 0
      snowy-admin-web/src/views/biz/bizship/detail.vue
  9. 285 0
      snowy-admin-web/src/views/biz/bizship/form.vue
  10. 230 0
      snowy-admin-web/src/views/biz/bizship/index.vue
  11. 34 64
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointmentrecord/service/impl/BizAppointmentRecordServiceImpl.java
  12. 133 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/controller/BizAppointReplaceController.java
  13. 130 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/entity/BizAppointReplace.java
  14. 34 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/enums/BizAppointReplaceEnum.java
  15. 33 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/mapper/BizAppointReplaceMapper.java
  16. 94 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/mapper/mapping/BizAppointReplaceMapper.xml
  17. 66 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplaceAddParam.java
  18. 75 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplaceEditParam.java
  19. 37 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplaceIdParam.java
  20. 60 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplacePageParam.java
  21. 86 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/service/BizAppointReplaceService.java
  22. 218 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/service/impl/BizAppointReplaceServiceImpl.java
  23. 1 1
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizorder/service/impl/BizOrderServiceImpl.java
  24. 128 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/controller/BizShipController.java
  25. 110 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/entity/BizShip.java
  26. 34 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/enums/BizShipEnum.java
  27. 29 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/mapper/BizShipMapper.java
  28. 13 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/mapper/mapping/BizShipMapper.xml
  29. 111 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipAddParam.java
  30. 116 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipEditParam.java
  31. 35 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipIdParam.java
  32. 66 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipPageParam.java
  33. 10 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipValue.java
  34. 82 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/service/BizShipService.java
  35. 283 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/service/impl/BizShipServiceImpl.java

+ 32 - 0
snowy-admin-web/src/api/biz/bizAppointReplaceApi.js

@@ -0,0 +1,32 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/biz/bizappointreplace/` + url, ...arg)
+
+/**
+ * 沙石预约调度Api接口管理器
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+export default {
+	// 获取沙石预约调度分页
+	bizAppointReplacePage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交沙石预约调度表单 edit为true时为编辑,默认为新增
+	bizAppointReplaceSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除沙石预约调度
+	bizAppointReplaceDelete(data) {
+		return request('delete', data)
+	},
+	// 获取沙石预约调度详情
+	bizAppointReplaceDetail(data) {
+		return request('detail', data, 'get')
+	},
+	//调度审批
+	bizAppointAudit(data){
+		return request('audit',data)
+	}
+}

+ 28 - 0
snowy-admin-web/src/api/biz/bizShipApi.js

@@ -0,0 +1,28 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/biz/bizship/` + url, ...arg)
+
+/**
+ * 船舶管理Api接口管理器
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+export default {
+	// 获取船舶管理分页
+	bizShipPage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交船舶管理表单 edit为true时为编辑,默认为新增
+	bizShipSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除船舶管理
+	bizShipDelete(data) {
+		return request('delete', data)
+	},
+	// 获取船舶管理详情
+	bizShipDetail(data) {
+		return request('detail', data, 'get')
+	}
+}

+ 2 - 2
snowy-admin-web/src/views/biz/bizappointmentrecord/replace.vue

@@ -23,11 +23,11 @@
 				> </a-select>
 			</a-form-item>
 			<a-form-item label="车牌号:" name="licenseNumber">
-				<a-input v-model:value="formData.licenseNumber" placeholder="请输入车牌号" allow-clear />
+				<a-input v-model:value="formData.licenseNumber" placeholder="请输入车牌号" allow-clear disabled/>
 			</a-form-item>
 			<a-form-item label="车辆轴数:" name="overId">
 				<a-select v-model:value="formData.overId" placeholder="请选择车辆轴数"
-						  :options="overIdList"
+						  :options="overIdList" disabled
 				> </a-select>
 			</a-form-item>
 		</a-form>

+ 108 - 0
snowy-admin-web/src/views/biz/bizappointreplace/detail.vue

@@ -0,0 +1,108 @@
+<template>
+	<xn-form-container title="详情" :width="900" :visible="visible" :destroy-on-close="true" @close="onClose">
+		<a-form ref="formRef" :model="formData" :label-col="labelCol4" :wrapper-col="wrapperCol20">
+			<a-divider dashed style="border-color: gray">调度前</a-divider>
+			<a-descriptions :column="4" size="middle" bordered class="mb-2" :label-style="labelStyle" :contentStyle="contentStyle">
+				<a-descriptions-item label="订单编号" :span="2">{{ formOldData.oldOrderNumber }}</a-descriptions-item>
+				<a-descriptions-item label="车牌号" :span="2">{{ formOldData.licenseNumber }}</a-descriptions-item>
+				<a-descriptions-item label="车辆轴数" :span="2">{{ formOldData.axleNumber+'轴' }}</a-descriptions-item>
+				<a-descriptions-item label="提货开始时间" :span="2">{{ formOldData.confStartTime }}</a-descriptions-item>
+				<a-descriptions-item label="提货结束时间" :span="2">{{formOldData.confEndTime }}</a-descriptions-item>
+				<a-descriptions-item label="客户名称" :span="2">{{ formOldData.customerName }}</a-descriptions-item>
+				<a-descriptions-item label="联系人" :span="2">{{ formOldData.customerPhone }}</a-descriptions-item>
+				<a-descriptions-item label="货品名称" :span="2">{{ formOldData.goodsName }}</a-descriptions-item>
+				<a-descriptions-item label="货品编码" :span="2">{{ formOldData.goodsCode }}</a-descriptions-item>
+				<a-descriptions-item label="司机姓名" :span="2">{{ formOldData.driverName }}</a-descriptions-item>
+				<a-descriptions-item label="司机电话" :span="2">{{ formOldData.driverMobile }}</a-descriptions-item>
+				<a-descriptions-item label="装卸点位" :span="2">{{ formOldData.oldLoadPoint }}</a-descriptions-item>
+				<a-descriptions-item label="装卸开始时间" :span="2">{{ formOldData.oldBeginTime }}</a-descriptions-item>
+				<a-descriptions-item label="装卸结束时间" :span="2">{{ formOldData.oldEndTime }}</a-descriptions-item>
+			</a-descriptions>
+
+			<a-divider dashed style="border-color: gray">调度后</a-divider>
+			<a-descriptions :column="4" size="middle" bordered class="mb-2" :label-style="labelStyle" :contentStyle="contentStyle">
+				<a-descriptions-item label="订单编号" :span="2">{{ formNewData.newOrderNumber }}</a-descriptions-item>
+				<a-descriptions-item label="车牌号" :span="2">{{ formNewData.licenseNumber }}</a-descriptions-item>
+				<a-descriptions-item label="车辆轴数" :span="2">{{ formNewData.axleNumber+'轴' }}</a-descriptions-item>
+				<a-descriptions-item label="提货开始时间" :span="2">{{ formNewData.confStartTime }}</a-descriptions-item>
+				<a-descriptions-item label="提货结束时间" :span="2">{{formNewData.confEndTime }}</a-descriptions-item>
+				<a-descriptions-item label="客户名称" :span="2">{{ formNewData.customerName }}</a-descriptions-item>
+				<a-descriptions-item label="联系人" :span="2">{{ formNewData.customerPhone }}</a-descriptions-item>
+				<a-descriptions-item label="货品名称" :span="2">{{ formNewData.goodsName }}</a-descriptions-item>
+				<a-descriptions-item label="货品编码" :span="2">{{ formNewData.goodsCode }}</a-descriptions-item>
+				<a-descriptions-item label="司机姓名" :span="2">{{ formNewData.driverName }}</a-descriptions-item>
+				<a-descriptions-item label="司机电话" :span="2">{{ formNewData.driverMobile }}</a-descriptions-item>
+				<a-descriptions-item label="装卸点位" :span="2">{{ formNewData.newLoadPoint }}</a-descriptions-item>
+				<a-descriptions-item label="装卸开始时间" :span="2">{{ formNewData.newBeginTime }}</a-descriptions-item>
+				<a-descriptions-item label="装卸结束时间" :span="2">{{ formNewData.newEndTime }}</a-descriptions-item>
+
+
+
+
+			</a-descriptions>
+		</a-form>
+	</xn-form-container>
+</template>
+
+<script setup name="recordDoubleForm">
+	import { cloneDeep } from 'lodash-es'
+	import sysConfig from "@/config";
+	import bizAppointReplaceApi from "@/api/biz/bizAppointReplaceApi";
+
+	// 默认是关闭状态
+	const visible = ref(false)
+	const formOldData = ref({})
+	const formNewData = ref({})
+	const table = ref()
+	const resultJson = ref()
+
+	const labelStyle = {
+		width: '20%'
+	}
+	const contentStyle = {
+		width: '30%'
+	}
+
+	const labelCol4 = ref({span: 4, style: 'width: 26%; line-height: 20px; white-space: normal',})
+	const labelCol8 = ref({span: 8, style: 'width: 26%; line-height: 20px; white-space: normal',})
+	const wrapperCol20 = ref({span: 20})
+
+	const fileList = ref([])
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		//getRecordDoubleDetail(record)
+		if(record){
+			let recordData = cloneDeep(record)
+			bizAppointReplaceApi.bizAppointReplaceDetail({id:record.id,flag:"old"}).then((res)=>{
+				formOldData.value = res
+			})
+			bizAppointReplaceApi.bizAppointReplaceDetail({id:record.id,flag:"new"}).then((res)=>{
+				formNewData.value = res
+			})
+
+		}
+
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		resultJson.value = ''
+		visible.value = false
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style scoped>
+	.imgDiv ::v-deep .ant-image {
+		margin-left: 20px;
+		margin-top: 10px;
+	}
+	.imgDiv ::v-deep .ant-image-mask-info {
+		margin-left: 20px;
+		margin-top: 10px;
+	}
+</style>

+ 94 - 0
snowy-admin-web/src/views/biz/bizappointreplace/form.vue

@@ -0,0 +1,94 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑沙石预约调度' : '增加沙石预约调度'"
+		:width="700"
+		v-model:open="open"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+			<a-form-item label="更换前订单id:" name="oldOrderId">
+				<a-input v-model:value="formData.oldOrderId" placeholder="请输入更换前订单id" allow-clear />
+			</a-form-item>
+			<a-form-item label="更换后订单id:" name="newOrderId">
+				<a-input v-model:value="formData.newOrderId" placeholder="请输入更换后订单id" allow-clear />
+			</a-form-item>
+			<a-form-item label="预约id:" name="appointId">
+				<a-input v-model:value="formData.appointId" placeholder="请输入预约id" allow-clear />
+			</a-form-item>
+			<a-form-item label="更换前点位id:" name="oldLoadPointId">
+				<a-input v-model:value="formData.oldLoadPointId" placeholder="请输入更换前点位id" allow-clear />
+			</a-form-item>
+			<a-form-item label="更换后点位id:" name="newLoadPointId">
+				<a-input v-model:value="formData.newLoadPointId" placeholder="请输入更换后点位id" allow-clear />
+			</a-form-item>
+			<a-form-item label="更换前装卸时间id:" name="oldLoadTimeId">
+				<a-input v-model:value="formData.oldLoadTimeId" placeholder="请输入更换前装卸时间id" allow-clear />
+			</a-form-item>
+			<a-form-item label="更换后装卸时间id:" name="newLoadTimeId">
+				<a-input v-model:value="formData.newLoadTimeId" placeholder="请输入更换后装卸时间id" allow-clear />
+			</a-form-item>
+			<a-form-item label="状态 1:待审核   2:审核通过   3:审核通过:" name="status">
+				<a-input v-model:value="formData.status" placeholder="请输入状态 1:待审核   2:审核通过   3:审核通过" allow-clear />
+			</a-form-item>
+		</a-form>
+		<template #footer>
+			<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+			<a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup name="bizAppointReplaceForm">
+	import { cloneDeep } from 'lodash-es'
+	import { required } from '@/utils/formRules'
+	import bizAppointReplaceApi from '@/api/biz/bizAppointReplaceApi'
+	// 抽屉状态
+	const open = ref(false)
+	const emit = defineEmits({ successful: null })
+	const formRef = ref()
+	// 表单数据
+	const formData = ref({})
+	const submitLoading = ref(false)
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		open.value = true
+		if (record) {
+			let recordData = cloneDeep(record)
+			formData.value = Object.assign({}, recordData)
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		formData.value = {}
+		open.value = false
+	}
+	// 默认要校验的
+	const formRules = {
+	}
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				submitLoading.value = true
+				const formDataParam = cloneDeep(formData.value)
+				bizAppointReplaceApi
+					.bizAppointReplaceSubmitForm(formDataParam, formDataParam.id)
+					.then(() => {
+						onClose()
+						emit('successful')
+					})
+					.finally(() => {
+						submitLoading.value = false
+					})
+			})
+			.catch(() => {})
+	}
+	// 抛出函数
+	defineExpose({
+		onOpen
+	})
+</script>

+ 255 - 0
snowy-admin-web/src/views/biz/bizappointreplace/index.vue

@@ -0,0 +1,255 @@
+<template>
+	<a-card :bordered="false" style="margin-bottom: 10px" class="mb-2">
+		<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
+			<a-row :gutter="24">
+
+				<a-col :span="6">
+					<a-form-item label="车牌号" name="licenseNumber">
+						<a-input v-model:value="searchFormState.licenseNumber" placeholder="车牌号码查询" />
+					</a-form-item>
+				</a-col>
+				<a-col :span="6">
+					<a-form-item label="司机名称" name="driverName">
+						<a-input v-model:value="searchFormState.driverName" placeholder="司机名称查询" />
+					</a-form-item>
+				</a-col>
+				<a-col :span="6">
+					<a-form-item label="司机电话" name="driverMobile">
+						<a-input v-model:value="searchFormState.driverMobile" placeholder="司机电话查询" />
+					</a-form-item>
+				</a-col>
+
+
+				<a-col :span="6">
+					<a-button type="primary" @click="tableRef.refresh()">查询</a-button>
+					<a-button style="margin: 0 8px" @click="reset">重置</a-button>
+				</a-col>
+			</a-row>
+		</a-form>
+	</a-card>
+	<a-card :bordered="false">
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			bordered
+			:row-key="(record) => record.id"
+		>
+			<template #operator class="table-operator">
+				<a-space>
+<!--					<a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('bizAppointReplaceAdd')">
+						<template #icon><plus-outlined /></template>
+						新增
+					</a-button>-->
+
+				</a-space>
+			</template>
+			<template #bodyCell="{ column, record }">
+				<template v-if="column.dataIndex === 'action'">
+					<a-space>
+						<a @click="detailRef.onOpen(record)">详情</a>
+						<a-divider type="vertical" v-if="hasPerm('bizAppointReplaceAudit') && record.status =='1'" />
+						<a style="color: #ffaa00" @click="reviewRef.showModal(record.id)" v-if="hasPerm('bizAppointReplaceAudit') && record.status =='1'">审核</a>
+					</a-space>
+				</template>
+				<template v-if="column.dataIndex === 'orderNumber'">
+					<div class="time-list">
+						<p>调度前:{{ record.oldOrderNumber }}</p>
+						<p style="margin-bottom: 0">调度后:{{ record.newOrderNumber }}</p>
+					</div>
+				</template>
+				<template v-if="column.dataIndex === 'loadPointId'">
+					<div class="time-list">
+						<p>调度前:{{ record.oldLoadPoint }}</p>
+						<p style="margin-bottom: 0">调度后:{{ record.newLoadPoint }}</p>
+					</div>
+				</template>
+				<template v-if="column.dataIndex === 'loadTimeId'">
+					<div class="time-list">
+						<p>调度前:{{ record.oldBeginTime+'~'+record.oldEndTime }}</p>
+						<p style="margin-bottom: 0">调度后:{{ record.newBeginTime+'~'+record.newEndTime }}</p>
+					</div>
+				</template>
+				<template v-if="column.dataIndex === 'driverInfo'">
+					<div class="time-list">
+						<p>姓名:{{ record.driverName }}</p>
+						<p style="margin-bottom: 0">电话:{{ record.driverMobile }}</p>
+					</div>
+				</template>
+				<template v-if="column.dataIndex === 'status'">
+					<a-tag
+						:color="
+							record.status === '1'
+								? 'volcano'
+								: record.status === '2'
+								  ? 'cyan'
+								  : 'error'
+						"
+					>
+						{{ $TOOL.dictTypeData('AUDIT_STATUS', record.status) }}
+					</a-tag>
+				</template>
+			</template>
+		</s-table>
+	</a-card>
+	<Form ref="formRef" @successful="tableRef.refresh()" />
+	<Review ref="reviewRef" @successful="tableRef.refresh()" />
+	<Detail ref="detailRef" @successful="tableRef.refresh()" />
+</template>
+
+<script setup name="bizappointreplace">
+	import { cloneDeep } from 'lodash-es'
+	import Form from './form.vue'
+	import bizAppointReplaceApi from '@/api/biz/bizAppointReplaceApi'
+	import Review from './review.vue'
+	import Detail from './detail.vue'
+
+	const tableRef = ref()
+	const formRef = ref()
+	const reviewRef = ref()
+	const detailRef = ref()
+
+	//查询数据
+	const searchFormState = ref({})
+	const searchFormRef = ref()
+
+
+	const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+	const columns = [
+		{
+			title: '车牌号',
+			dataIndex: 'licenseNumber',
+			align: 'center'
+		},
+		{
+			title: '订单号',
+			dataIndex: 'orderNumber',
+		},
+
+		{
+			title: '装卸点位',
+			dataIndex: 'loadPointId',
+		},
+		{
+			title: '装卸时间',
+			dataIndex: 'loadTimeId',
+			align:'center',
+			width:300
+		},
+		{
+			title: '司机信息',
+			dataIndex: 'driverInfo',
+		},
+		{
+			title: '状态',
+			dataIndex: 'status',
+			align:'center',
+		},
+	]
+	// 操作栏通过权限判断是否显示
+	columns.push({
+		title: '操作',
+		dataIndex: 'action',
+		align: 'center',
+		width: 150
+	})
+
+	const selectedRowKeys = ref([])
+	// 列表选择配置
+	const options = {
+		// columns数字类型字段加入 needTotal: true 可以勾选自动算账
+		alert: {
+			show: true,
+			clear: () => {
+				selectedRowKeys.value = ref([])
+			}
+		},
+		rowSelection: {
+			onChange: (selectedRowKey, selectedRows) => {
+				selectedRowKeys.value = selectedRowKey
+			}
+		}
+	}
+	const loadData = (parameter) => {
+		const searchFormParam = cloneDeep(searchFormState.value)
+		return bizAppointReplaceApi.bizAppointReplacePage(Object.assign(parameter, searchFormParam)).then((data) => {
+			return data
+		})
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteBizAppointReplace = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		bizAppointReplaceApi.bizAppointReplaceDelete(params).then(() => {
+			tableRef.value.refresh(true)
+		})
+	}
+	// 批量删除
+	const deleteBatchBizAppointReplace = (params) => {
+		bizAppointReplaceApi.bizAppointReplaceDelete(params).then(() => {
+			tableRef.value.clearRefreshSelected()
+		})
+	}
+</script>
+
+<style lang="less" scoped>
+/** 表头居中 */
+:deep .ant-table-thead > tr > th {
+	text-align: center;
+}
+
+.time-list {
+	-webkit-text-size-adjust: none;
+	font-size: 13px;
+	display: flex;
+	flex-direction: column;
+
+	p {
+		white-space: nowrap;
+		span {
+			display: inline-block;
+			font-weight: 600;
+			height: 16px;
+			line-height: 16px;
+			border-radius: 5px;
+			text-align: center;
+			margin-right: 2px;
+			padding: 0 5px;
+		}
+		.blueTag {
+			color: #1890ff;
+			background: rgba(24, 144, 255, 0.1);
+		}
+		.orangeTag {
+			color: #ff7c18;
+			background: rgba(24, 144, 255, 0.1);
+		}
+		.greenTag {
+			color: rgb(82, 196, 26);
+			background: rgba(82, 196, 26, 0.1);
+		}
+		.purpleTag {
+			color: rgb(77, 26, 196);
+			background: rgba(82, 196, 26, 0.1);
+		}
+		.showNum {
+			display: inline-block;
+			height: 16px;
+			line-height: 16px;
+			width: 45px;
+			border-radius: 5px;
+			text-align: center;
+			margin-right: 2px;
+			text-align: right;
+		}
+	}
+}
+</style>

+ 66 - 0
snowy-admin-web/src/views/biz/bizappointreplace/review.vue

@@ -0,0 +1,66 @@
+<template>
+    <a-modal v-model:visible="visible" title="审核">
+
+        <a-form ref="formRef" :label-col="labelCol" :model="formData" layout="horizontal">
+            <a-form-item v-show="false">
+                <a-input v-model:value="formData.id"></a-input>
+            </a-form-item>
+            <a-form-item
+                    label="审核备注"
+                    name="auditRemark"
+            >
+                <a-textarea v-model:value="formData.auditRemark" placeholder="请输入审核备注"
+                            :auto-size="{ minRows: 3, maxRows: 5 }"/>
+            </a-form-item>
+        </a-form>
+        <template #footer>
+            <a-spin :spinning="submitLoading">
+                <a-button style="margin-right: 8px" @click="onsubmit(false)" type="primary" danger>审核驳回</a-button>
+                <a-button type="primary" @click="onsubmit(true)">审核通过</a-button>
+            </a-spin>
+        </template>
+    </a-modal>
+</template>
+<script setup>
+import {message} from 'ant-design-vue';
+import bizAppointmentRecordApi from '@/api/biz/bizAppointmentRecordApi'
+import bizAppointReplaceApi from "@/api/biz/bizAppointReplaceApi";
+
+const emit = defineEmits({successful: null})
+const visible = ref(false);
+const submitLoading = ref(false)
+const labelCol = ref({span: 4})
+// 表单数据
+const formData = ref({})
+const showModal = (id) => {
+    formData.value.id = id
+    visible.value = true;
+};
+const onClose = () => {
+    formData.value = {}
+    visible.value = false
+};
+const onsubmit = (flag) => {
+    if (flag === false) {
+        if (!formData.value.auditRemark) {
+            message.error('审核驳回时,备注信息不能为空')
+            return
+        }
+    }
+    submitLoading.value = true
+	formData.value.auditFlag = flag
+	bizAppointReplaceApi.bizAppointAudit(formData.value).then(() => {
+        onClose()
+        emit('successful', null)
+    }).finally(() => {
+        submitLoading.value = false
+    })
+}
+// 抛出函数
+defineExpose({
+    showModal
+})
+</script>
+<style scoped>
+
+</style>

+ 137 - 0
snowy-admin-web/src/views/biz/bizship/detail.vue

@@ -0,0 +1,137 @@
+<template>
+	<xn-form-container title="详情" :width="900" :visible="visible" :destroy-on-close="true" @close="onClose">
+		<a-form ref="formRef" :model="formData" :label-col="labelCol4" :wrapper-col="wrapperCol20">
+			<a-descriptions :column="4" size="middle" bordered class="mb-2" :label-style="labelStyle" :contentStyle="contentStyle">
+				<a-descriptions-item label="船舶号" :span="2">{{ formData.shipNumber }}</a-descriptions-item>
+				<a-descriptions-item label="船型" :span="2">{{ formData.shipType }}</a-descriptions-item>
+				<a-descriptions-item label="船舶证书" :span="4">
+					<p v-for="(item, index) in certificateFlie" :key="index">
+						<a @click="downloadMaterial(item.url)" target="_blank">{{ item.name }}</a>
+					</p>
+				</a-descriptions-item>
+
+				<a-descriptions-item label="荷载吨位" :span="2">{{ formData.shipLoad }}</a-descriptions-item>
+				<a-descriptions-item label="船舶所有人" :span="2">{{ formData.shipName }}</a-descriptions-item>
+				<a-descriptions-item label="船舶身份信息" :span="2">{{ formData.shipIdentity }}</a-descriptions-item>
+				<a-descriptions-item label="联系人姓名" :span="2">{{ formData.contactName }}</a-descriptions-item>
+				<a-descriptions-item label="联系人电话" :span="2">{{ formData.contactPhone }}</a-descriptions-item>
+				<a-descriptions-item label="收款单位" :span="2">{{ formData.receiveUnit}}</a-descriptions-item>
+				<a-descriptions-item label="收款银行" :span="2">{{ formData.receiveBank}}</a-descriptions-item>
+				<a-descriptions-item label="收款账户" :span="2">{{ formData.receiveAccount }}</a-descriptions-item>
+				<a-descriptions-item label="安全环保合同" :span="4">
+					<p v-for="(item, index) in contractFlie" :key="index">
+						<a @click="downloadMaterial(item.url)" target="_blank">{{ item.name }}</a>
+					</p>
+				</a-descriptions-item>
+				<a-descriptions-item label="状态" :span="2" v-if="formData.status=='1'">启用</a-descriptions-item>
+				<a-descriptions-item label="状态" :span="2" v-else>关闭</a-descriptions-item>
+				<a-descriptions-item label="船舶注册号码" :span="2">{{ formData.registerNumber}}</a-descriptions-item>
+<!--				<a-descriptions-item label="船主" :span="2">{{ formData.shipName}}</a-descriptions-item>-->
+			</a-descriptions>
+		</a-form>
+	</xn-form-container>
+</template>
+
+<script setup name="recordDoubleForm">
+	import { cloneDeep } from 'lodash-es'
+	import bizOrderSupplierApi from '@/api/biz/bizOrderSupplierApi'
+	import sysConfig from "@/config";
+	// 默认是关闭状态
+	const visible = ref(false)
+	const formData = ref({})
+	const table = ref()
+	const resultJson = ref()
+
+
+	const labelStyle = {
+		width: '20%'
+	}
+	const contentStyle = {
+		width: '30%'
+	}
+
+	const labelCol4 = ref({span: 4, style: 'width: 26%; line-height: 20px; white-space: normal',})
+	const labelCol8 = ref({span: 8, style: 'width: 26%; line-height: 20px; white-space: normal',})
+	const wrapperCol20 = ref({span: 20})
+
+
+	const supplierDataSource = ref([])
+
+	const supplierDataColumns = [
+		{
+			title: '供应商名称',
+			dataIndex: 'supplierName',
+			align: 'center'
+		},
+		{
+			title: '过磅重量(吨)',
+			dataIndex: 'netWeight',
+			align: 'center'
+		},
+		{
+			title: '卸货重量(吨)',
+			dataIndex: 'shippingWeight',
+			align: 'center'
+		},
+	]
+
+	const certificateFlie = ref([])
+
+	const contractFlie = ref([])
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+
+		if(record){
+			let recordData = cloneDeep(record)
+			formData.value = Object.assign({}, recordData)
+
+			if (formData.value.shipCertificatePath) {
+				for (var i = 0; i < formData.value.shipCertificatePath.split(',').length; i++) {
+					certificateFlie.value.push({
+						url: sysConfig.PREVIEW_PATH + formData.value.shipCertificatePath.split(',')[i],
+						name: formData.value.shipCertificateName.split(',')[i]
+					})
+				}
+			}
+
+			if (formData.value.contractPath) {
+				for (var i = 0; i < formData.value.contractPath.split(',').length; i++) {
+					contractFlie.value.push({
+						url: sysConfig.PREVIEW_PATH + formData.value.contractPath.split(',')[i],
+						name: formData.value.contractName.split(',')[i]
+					})
+				}
+			}
+		}
+
+	}
+
+	const downloadMaterial = (url) => {
+		window.open( url)
+	}
+
+	// 关闭抽屉
+	const onClose = () => {
+		resultJson.value = ''
+		certificateFlie.value = []
+		contractFlie.value = []
+		visible.value = false
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style scoped>
+	.imgDiv ::v-deep .ant-image {
+		margin-left: 20px;
+		margin-top: 10px;
+	}
+	.imgDiv ::v-deep .ant-image-mask-info {
+		margin-left: 20px;
+		margin-top: 10px;
+	}
+</style>

+ 285 - 0
snowy-admin-web/src/views/biz/bizship/form.vue

@@ -0,0 +1,285 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑船舶管理' : '增加船舶管理'"
+		:width="700"
+		v-model:open="open"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form ref="formRef" :model="formData" :rules="formRules" :label-col="labelCol" :wrapper-col="wrapperCol">
+			<a-form-item label="船舶号:" name="shipNumber">
+				<a-input v-model:value="formData.shipNumber" placeholder="请输入船舶号" allow-clear />
+			</a-form-item>
+			<a-form-item label="船型:" name="shipType">
+				<a-select v-model:value="formData.shipType" placeholder="请选择船型"
+						  :options="shipTypeList"
+				> </a-select>
+			</a-form-item>
+			<a-form-item label="船舶证书:" name="certificatePathList" :rules="[{ required: true, message: '请上传船舶证书' }]">
+				<a-upload
+					v-model:file-list="certificateList"
+					class="avatar-uploader"
+					list-type="picture"
+					:show-upload-list="true"
+					:custom-request="certificateRequest"
+					:remove="file => removeCertificate(file,index)"
+					:before-upload="beforeUpload"
+					accept="image/png, image/jpeg, image/jpg"
+
+				>
+					<a-button>
+						<upload-outlined></upload-outlined>
+						上传船舶证书
+					</a-button>
+				</a-upload>
+			</a-form-item>
+			<a-form-item label="荷载吨位:" name="shipLoad">
+				<a-input-number v-model:value="formData.shipLoad" style="width:90%"  :precision="2" :min="0.01" :max="9999999"  placeholder="请输入荷载吨位" allow-clear/><span style="margin-left:10px;">吨</span>
+			</a-form-item>
+			<a-form-item label="船舶所有人:" name="shipId">
+				<xn-user-selector
+					:org-tree-api="selectorApiFunction.orgTreeApi"
+					:user-page-api="selectorApiFunction.userPageApi"
+					:radio-model="true"
+					placeholder="请选择船主"
+					v-model:value="formData.shipId"
+				/>
+			</a-form-item>
+			<a-form-item label="船舶注册帐号:" name="registerNumber">
+				<a-input v-model:value="formData.registerNumber" placeholder="请输入船舶注册帐号" allow-clear />
+			</a-form-item>
+
+<!--			<a-form-item label="船舶所有人:" name="shipOwner">
+				<a-input v-model:value="formData.shipOwner" placeholder="请输入船舶所有人" allow-clear />
+			</a-form-item>-->
+			<a-form-item label="船舶身份信息:" name="shipIdentity">
+				<a-input v-model:value="formData.shipIdentity" placeholder="请输入船舶身份信息" allow-clear />
+			</a-form-item>
+<!--			<a-form-item label="联系人姓名:" name="contactName">
+				<a-input v-model:value="formData.contactName" placeholder="请输入联系人姓名" allow-clear />
+			</a-form-item>
+			<a-form-item label="联系人电话:" name="contactPhone">
+				<a-input v-model:value="formData.contactPhone" placeholder="请输入联系人电话" allow-clear />
+			</a-form-item>-->
+			<a-form-item label="收款单位:" name="receiveUnit">
+				<a-input v-model:value="formData.receiveUnit" placeholder="请输入收款单位" allow-clear />
+			</a-form-item>
+			<a-form-item label="收款银行:" name="receiveBank">
+				<a-input v-model:value="formData.receiveBank" placeholder="请输入收款银行" allow-clear />
+			</a-form-item>
+			<a-form-item label="收款账户:" name="receiveAccount">
+				<a-input v-model:value="formData.receiveAccount" placeholder="请输入收款账户" allow-clear />
+			</a-form-item>
+			<a-form-item label="安全环保合同:" name="contractPathList" >
+				<a-upload
+					v-model:file-list="contractList"
+					class="avatar-uploader"
+					list-type="picture"
+					:show-upload-list="true"
+					:custom-request="contractRequest"
+					:remove="file => removeContract(file,index)"
+					:before-upload="beforeUpload"
+					accept="image/png, image/jpeg, image/jpg"
+
+				>
+					<a-button>
+						<upload-outlined></upload-outlined>
+						上传安全环保合同
+					</a-button>
+				</a-upload>
+			</a-form-item>
+
+			<a-form-item label="是否启用:" name="status" v-if="formData.id!=null && formData.id!=''">
+				<a-radio-group button-style="solid" v-model:value="formData.status">
+					<a-radio-button value="1">
+						启用
+					</a-radio-button>
+					<a-radio-button value="2">
+						关闭
+					</a-radio-button>
+				</a-radio-group>
+			</a-form-item>
+
+		</a-form>
+		<template #footer>
+			<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+			<a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup name="bizShipForm">
+	import { cloneDeep } from 'lodash-es'
+	import { required } from '@/utils/formRules'
+	import bizShipApi from '@/api/biz/bizShipApi'
+	import fileApi from "@/api/dev/fileApi";
+	import userApi from '@/api/biz/bizUserApi'
+	import sysConfig from "@/config";
+	import tool from '@/utils/tool'
+
+
+	const shipTypeList = tool.dictList('ship_type')
+
+	// 抽屉状态
+	const open = ref(false)
+	const emit = defineEmits({ successful: null })
+	const formRef = ref()
+	// 表单数据
+	const formData = ref({certificatePathList:[],certificateNameList:[],contractPathList:[],contractNameList:[]})
+	const submitLoading = ref(false)
+
+	const width = ref('calc(45%)')
+	const labelCol = ref({ span: 5 })
+	const wrapperCol = ref({ span: 19 })
+
+
+	const certificateList = ref([])
+	const contractList = ref([])
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		open.value = true
+		if (record) {
+			let recordData = cloneDeep(record)
+			formData.value = Object.assign({}, recordData)
+			formData.value.certificatePathList = []
+			formData.value.certificateNameList = []
+			formData.value.contractPathList = []
+			formData.value.contractNameList = []
+			certificateList.value = []
+			contractList.value = []
+			if (formData.value.shipCertificatePath) {
+				for (var i = 0; i < formData.value.shipCertificatePath.split(',').length; i++) {
+					certificateList.value.push({
+						url: sysConfig.PREVIEW_PATH + formData.value.shipCertificatePath.split(',')[i],
+						name: formData.value.shipCertificateName.split(',')[i]
+					})
+
+					formData.value.certificatePathList.push(formData.value.shipCertificatePath.split(',')[i])
+					formData.value.certificateNameList.push(formData.value.shipCertificateName.split(',')[i])
+				}
+			}
+			if (formData.value.contractPath) {
+				for (var i = 0; i < formData.value.contractPath.split(',').length; i++) {
+					contractList.value.push({
+						url: sysConfig.PREVIEW_PATH + formData.value.contractPath.split(',')[i],
+						name: formData.value.contractName.split(',')[i]
+					})
+
+					formData.value.contractPathList.push(formData.value.contractPath.split(',')[i])
+					formData.value.contractNameList.push(formData.value.contractName.split(',')[i])
+				}
+			}
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		formData.value = {certificatePathList:[],certificateNameList:[],contractPathList:[],contractNameList:[]}
+		certificateList.value = []
+		contractList.value = []
+		open.value = false
+	}
+	// 默认要校验的
+	const formRules = {
+		shipNumber:[required('请输入船舶号')],
+		shipType:[required('请输入船型')],
+		shipId:[required('请选择船主')],
+	}
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				submitLoading.value = true
+				const formDataParam = cloneDeep(formData.value)
+				bizShipApi
+					.bizShipSubmitForm(formDataParam, formDataParam.id)
+					.then(() => {
+						onClose()
+						emit('successful')
+					})
+					.finally(() => {
+						submitLoading.value = false
+					})
+			})
+			.catch(() => {})
+	}
+
+	//上传船舶证书
+	const certificateRequest = (data) => {
+		//保存图片
+		const fileData = new FormData()
+		fileData.append('file', data.file)
+		fileApi
+			.uploadImgMap(fileData)
+			.then((result) => {
+				formData.value.certificatePathList.push(result.imageFile)
+				formData.value.certificateNameList.push(data.file.name)
+			}).finally(()=>{
+			data.onSuccess()
+		})
+
+	}
+
+	//删除船舶证书
+	const removeCertificate = (file) => {
+		certificateList.value.forEach((item,index)=>{
+			if(item.name === file.name){
+
+				certificateList.value.splice(index, 1);
+				formData.value.certificatePathList.splice(index,1)
+				formData.value.certificateNameList.splice(index,1)
+			}
+		})
+	}
+
+
+	//上传安全环保合同
+	const contractRequest = (data) => {
+		//保存图片
+		const fileData = new FormData()
+		fileData.append('file', data.file)
+		fileApi
+			.uploadImgMap(fileData)
+			.then((result) => {
+				formData.value.contractPathList.push(result.imageFile)
+				formData.value.contractNameList.push(data.file.name)
+			}).finally(()=>{
+			data.onSuccess()
+		})
+
+	}
+
+
+	//删除船舶证书
+	const removeContract = (file) => {
+		contractList.value.forEach((item,index)=>{
+			if(item.name === file.name){
+
+				certificateList.value.splice(index, 1);
+				formData.value.contractPathList.splice(index,1)
+				formData.value.contractNameList.splice(index,1)
+			}
+		})
+	}
+
+	// 传递设计器需要的API
+	const selectorApiFunction = {
+		orgTreeApi: (param) => {
+			return userApi.userOrgTreeSelector(param).then((data) => {
+				return Promise.resolve(data)
+			})
+		},
+		userPageApi: (param) => {
+			param.roleName = '船主'
+			return userApi.userSelectorByRole(param).then((data) => {
+				return Promise.resolve(data)
+			})
+		}
+	}
+	// 抛出函数
+	defineExpose({
+		onOpen
+	})
+</script>

+ 230 - 0
snowy-admin-web/src/views/biz/bizship/index.vue

@@ -0,0 +1,230 @@
+<template>
+	<a-card :bordered="false" style="margin-bottom: 10px" class="mb-2">
+		<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
+			<a-row :gutter="24">
+				<a-col :span="6">
+					<a-form-item label="船舶号" name="shipNumber">
+						<a-input v-model:value="searchFormState.shipNumber" placeholder="查询船舶号" />
+					</a-form-item>
+				</a-col>
+				<a-col :span="6">
+					<a-form-item label="船型" name="shipType">
+						<a-select v-model:value="searchFormState.shipType" placeholder="查询船型"
+								  :options="shipTypeList"
+						> </a-select>
+					</a-form-item>
+				</a-col>
+				<a-col :span="6">
+					<a-form-item label="船舶所有人" name="shipOwner">
+						<a-input v-model:value="searchFormState.shipOwner" placeholder="查询船舶所有人" />
+					</a-form-item>
+				</a-col>
+				<template v-if="advanced">
+					<a-col :span="6">
+						<a-form-item label="联系人姓名" name="contactName">
+							<a-input v-model:value="searchFormState.contactName" placeholder="查询联系人姓名" />
+						</a-form-item>
+					</a-col>
+					<a-col :span="6">
+						<a-form-item label="联系人电话" name="contactPhone">
+							<a-input v-model:value="searchFormState.contactPhone" placeholder="查询联系人电话" />
+						</a-form-item>
+					</a-col>
+				</template>
+				<a-col :span="6">
+					<a-button type="primary" @click="tableRef.refresh()">查询</a-button>
+					<a-button style="margin: 0 8px" @click="reset">重置</a-button>
+					<a @click="toggleAdvanced" style="margin-left: 8px">
+						{{ advanced ? '收起' : '展开' }}
+						<component :is="advanced ? 'up-outlined' : 'down-outlined'" />
+					</a>
+				</a-col>
+			</a-row>
+		</a-form>
+	</a-card>
+	<a-card :bordered="false">
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			bordered
+			:row-key="(record) => record.id"
+		>
+			<template #operator class="table-operator">
+				<a-space>
+					<a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('bizShipAdd')">
+						<template #icon><plus-outlined /></template>
+						新增
+					</a-button>
+				</a-space>
+			</template>
+			<template #bodyCell="{ column, record }">
+				<template v-if="column.dataIndex === 'action'">
+					<a-space>
+						<a size="small" type="link" @click="detailRef.onOpen(record)" >详情</a>
+						<a-divider type="vertical"/>
+						<a @click="formRef.onOpen(record)" v-if="hasPerm('bizShipEdit')">编辑</a>
+						<a-divider type="vertical" v-if="hasPerm(['bizShipEdit', 'bizShipDelete'], 'and')" />
+						<a-button type="link" danger size="small" v-if="hasPerm('bizShipDelete')" @click="deleteConfig(record)">删除</a-button>
+					</a-space>
+				</template>
+				<template v-if="column.dataIndex === 'status'">
+					<span v-if="record.status=='1'">启用</span>
+					<span v-else>关闭</span>
+				</template>
+			</template>
+		</s-table>
+	</a-card>
+	<Form ref="formRef" @successful="tableRef.refresh()" />
+	<Detail ref="detailRef" @successful="tableRef.refresh()" />
+</template>
+
+<script setup name="bizship">
+	import { cloneDeep } from 'lodash-es'
+	import Form from './form.vue'
+	import bizShipApi from '@/api/biz/bizShipApi'
+	import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
+	import {Modal} from 'ant-design-vue';
+	import {createVNode} from 'vue';
+	import tool from '@/utils/tool'
+	import Detail from './detail.vue'
+
+	const shipTypeList = tool.dictList('ship_type')
+
+	//查询数据
+	const searchFormState = ref({})
+	const searchFormRef = ref()
+
+	// 查询区域显示更多控制
+	const advanced = ref(false)
+	const toggleAdvanced = () => {
+		advanced.value = !advanced.value
+	}
+
+	const tableRef = ref()
+	const formRef = ref()
+	const detailRef = ref()
+	const submitLoading = ref(false)
+	const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+	const columns = [
+		{
+			title: '船舶号',
+			dataIndex: 'shipNumber',
+			align:"center"
+		},
+		{
+			title: '船型',
+			dataIndex: 'shipType',
+			align:"center"
+		},
+		{
+			title: '荷载吨位',
+			dataIndex: 'shipLoad',
+			align:"center"
+		},
+		{
+			title: '船舶所有人',
+			dataIndex: 'shipName',
+			align:"center"
+		},
+		{
+			title: '船舶身份信息',
+			dataIndex: 'shipIdentity',
+			align:"center"
+		},
+		{
+			title: '联系人姓名',
+			dataIndex: 'contactName',
+			align:"center"
+		},
+		{
+			title: '联系人电话',
+			dataIndex: 'contactPhone',
+			align:"center"
+		},
+		{
+			title: '状态',
+			dataIndex: 'status',
+			align:"center"
+		},
+	]
+	// 操作栏通过权限判断是否显示
+	columns.push({
+		title: '操作',
+		dataIndex: 'action',
+		align: 'center',
+		width: 200
+	})
+
+	const selectedRowKeys = ref([])
+	// 列表选择配置
+	const options = {
+		// columns数字类型字段加入 needTotal: true 可以勾选自动算账
+		alert: {
+			show: true,
+			clear: () => {
+				selectedRowKeys.value = ref([])
+			}
+		},
+		rowSelection: {
+			onChange: (selectedRowKey, selectedRows) => {
+				selectedRowKeys.value = selectedRowKey
+			}
+		}
+	}
+	const loadData = (parameter) => {
+		const searchFormParam = cloneDeep(searchFormState.value)
+		return bizShipApi.bizShipPage(Object.assign(parameter, searchFormParam)).then((data) => {
+			return data
+		})
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteBizShip = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		bizShipApi.bizShipDelete(params).then(() => {
+			tableRef.value.refresh(true)
+		})
+	}
+	// 删除
+	const deleteConfig = (record) => {
+
+		Modal.confirm({
+			title: '确定删除该数据吗?',
+			icon: createVNode(ExclamationCircleOutlined),
+			content: '',
+			onOk() {
+				submitLoading.value = true
+				let params = [
+					{
+						id: record.id
+					}
+				]
+
+				bizShipApi
+					.bizShipDelete(params)
+					.then(() => {
+						tableRef.value.refresh(true)
+					})
+					.finally(() => {
+						submitLoading.value = false
+					})
+			},
+			onCancel() {}
+		})
+	}
+	// 批量删除
+	const deleteBatchBizShip = (params) => {
+		bizShipApi.bizShipDelete(params).then(() => {
+			tableRef.value.clearRefreshSelected()
+		})
+	}
+</script>

+ 34 - 64
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointmentrecord/service/impl/BizAppointmentRecordServiceImpl.java

@@ -39,6 +39,8 @@ import vip.xiaonuo.biz.modular.api.param.WxUserMsgParam;
 import vip.xiaonuo.biz.modular.api.service.ApiService;
 import vip.xiaonuo.biz.modular.bizappointmentrecord.param.*;
 import vip.xiaonuo.biz.modular.bizappointmenttime.service.BizAppointmentTimeService;
+import vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace;
+import vip.xiaonuo.biz.modular.bizappointreplace.service.BizAppointReplaceService;
 import vip.xiaonuo.biz.modular.bizchargestation.entity.BizChargeStation;
 import vip.xiaonuo.biz.modular.bizchargestation.service.BizChargeStationService;
 import vip.xiaonuo.biz.modular.bizconfig.entity.BizConfig;
@@ -158,6 +160,9 @@ public class BizAppointmentRecordServiceImpl extends ServiceImpl<BizAppointmentR
     @Resource
     private BizChargeStationService bizChargeStationService;
 
+    @Resource
+    private BizAppointReplaceService bizAppointReplaceService;
+
     @Override
     public Page<BizAppointmentRecord> page(BizAppointmentRecordPageParam bizAppointmentRecordPageParam) {
         QueryWrapper<BizAppointmentRecord> queryWrapper = getQueryWrapper(bizAppointmentRecordPageParam);
@@ -627,22 +632,20 @@ public class BizAppointmentRecordServiceImpl extends ServiceImpl<BizAppointmentR
         if (!StringUtils.equals(bizAppointmentRecord.getOrderId(), bizAppointmentRecordEditParam.getOrderId())) {
             bizAppointmentRecordEditParam.setTimeId(bizOrderService.queryEntity(bizAppointmentRecordEditParam.getOrderId()).getDeliveryTimeId());
         }
-        BeanUtil.copyProperties(bizAppointmentRecordEditParam, bizAppointmentRecord);
-        if (StringUtils.equals(bizAppointmentRecord.getStatus(), "2")) {
-            //如果当前状态是审核不通过的状态,修改后重新提交审核
-            bizAppointmentRecord.setStatus("1");
-        }
         if(ObjectUtil.isNotEmpty(bizAppointmentRecordEditParam.getDriverId())){
-            BizUser bizUser = bizUserService.getById(bizAppointmentRecordEditParam.getDriverId());
-            if(ObjectUtil.isNotNull(bizUser)){
-                if(ObjectUtil.isEmpty(bizAppointmentRecordEditParam.getDriverName())){
+            if(!StringUtils.equals(bizAppointmentRecordEditParam.getDriverId(),bizAppointmentRecord.getDriverId())){
+                BizUser bizUser = bizUserService.getById(bizAppointmentRecordEditParam.getDriverId());
+                if(ObjectUtil.isNotNull(bizUser)){
                     bizAppointmentRecord.setDriverName(bizUser.getName());
-                }
-                if(ObjectUtil.isEmpty(bizAppointmentRecordEditParam.getDriverMobile())){
                     bizAppointmentRecord.setDriverMobile(bizUser.getPhone());
                 }
             }
         }
+        BeanUtil.copyProperties(bizAppointmentRecordEditParam, bizAppointmentRecord);
+        if (StringUtils.equals(bizAppointmentRecord.getStatus(), "2")) {
+            //如果当前状态是审核不通过的状态,修改后重新提交审核
+            bizAppointmentRecord.setStatus("1");
+        }
         this.updateById(bizAppointmentRecord);
     }
 
@@ -693,61 +696,28 @@ public class BizAppointmentRecordServiceImpl extends ServiceImpl<BizAppointmentR
     public void replaceAppoint(BizAppointmentRecordEditParam bizAppointmentRecordEditParam) {
         BizAppointmentRecord bizAppointmentRecord = this.queryEntity(bizAppointmentRecordEditParam.getId());
         if(!StringUtils.equals(bizAppointmentRecord.getOrderId(),bizAppointmentRecordEditParam.getOrderId())){
-            //查询旧订单预约配置
-            BizOrderConfig oldOrderConfig = bizOrderConfigService.getOne(new QueryWrapper<BizOrderConfig>().lambda().
-                    eq(BizOrderConfig::getOrderId, bizAppointmentRecord.getOrderId()).
-                    last("limit 1"));
-            if(ObjectUtil.isNotNull(oldOrderConfig)){
-                //释放旧订单预约次数
-                oldOrderConfig.setApplyNumberAlready(oldOrderConfig.getApplyNumberAlready()-1);
-                bizOrderConfigService.updateById(oldOrderConfig);
-            }
-
-            //查询新订单预约配置
-            BizOrderConfig newOrderConfig = bizOrderConfigService.getOne(new QueryWrapper<BizOrderConfig>().lambda().
-                    eq(BizOrderConfig::getOrderId, bizAppointmentRecordEditParam.getOrderId()).
-                    last("limit 1"));
-            if(ObjectUtil.isNotNull(newOrderConfig)){
-                newOrderConfig.setApplyNumberAlready(newOrderConfig.getApplyNumberAlready()+1);
-                bizOrderConfigService.updateById(newOrderConfig);
-            }
-        }
-        //判断装卸时间是否一致
-        if(!StringUtils.equals(bizAppointmentRecord.getLoadTimeId(),bizAppointmentRecordEditParam.getLoadTimeId())){
-            //装卸时间段修改
-            //查询旧时间段预约次数
-            BizLoadTime oldTime = bizLoadTimeService.getById(bizAppointmentRecord.getLoadTimeId());
-            if(ObjectUtil.isNotNull(oldTime)){
-                //释放旧时段预约次数
-                oldTime.setAlreadyNumber(oldTime.getAlreadyNumber()-1);
-                bizLoadTimeService.updateById(oldTime);
-            }
-
-            //查询新时段的预约次数
-            BizLoadTime newTime = bizLoadTimeService.getById(bizAppointmentRecordEditParam.getLoadTimeId());
-            if(ObjectUtil.isNotNull(newTime)){
-                //添加新时段预约次数
-                newTime.setAlreadyNumber(newTime.getAlreadyNumber()+1);
-                bizLoadTimeService.updateById(newTime);
-            }
-        }
-        if(!StringUtils.equals(bizAppointmentRecord.getOrderId(),bizAppointmentRecordEditParam.getOrderId())){
-            //订单修改
-            bizAppointmentRecord.setOrderId(bizAppointmentRecordEditParam.getOrderId());
-            //装卸点位
-            bizAppointmentRecord.setLoadPointId(bizAppointmentRecordEditParam.getLoadPointId());
-            //装卸时间
-            bizAppointmentRecord.setLoadTimeId(bizAppointmentRecordEditParam.getLoadTimeId());
-        }
-        //车牌号
-        if(!StringUtils.equals(bizAppointmentRecord.getLicenseNumber(),bizAppointmentRecordEditParam.getLicenseNumber())){
-            bizAppointmentRecord.setLicenseNumber(bizAppointmentRecordEditParam.getLicenseNumber());
-        }
-        //车辆轴数
-        if(!StringUtils.equals(bizAppointmentRecord.getOverId(),bizAppointmentRecordEditParam.getOverId())){
-            bizAppointmentRecord.setOverId(bizAppointmentRecordEditParam.getOverId());
+            //添加调度审批记录
+            BizAppointReplace bizAppointReplace = new BizAppointReplace();
+            //更换前订单id
+            bizAppointReplace.setOldOrderId(bizAppointmentRecord.getOrderId());
+            //更换后订单id
+            bizAppointReplace.setNewOrderId(bizAppointmentRecordEditParam.getOrderId());
+            //更换前装卸点位id
+            bizAppointReplace.setOldLoadPointId(bizAppointmentRecord.getLoadPointId());
+            //更换后装卸点位id
+            bizAppointReplace.setNewLoadPointId(bizAppointmentRecordEditParam.getLoadPointId());
+            //更换前装卸时间id
+            bizAppointReplace.setOldLoadTimeId(bizAppointmentRecord.getLoadTimeId());
+            //更换后装卸时间id
+            bizAppointReplace.setNewLoadTimeId(bizAppointmentRecordEditParam.getLoadTimeId());
+            bizAppointReplace.setAppointId(bizAppointmentRecord.getId());
+            bizAppointReplace.setAppointStatus(bizAppointmentRecord.getStatus());
+            bizAppointReplaceService.save(bizAppointReplace);
+
+            //调度待审批
+            bizAppointmentRecord.setStatus("20");
+            this.updateById(bizAppointmentRecord);
         }
-        this.updateById(bizAppointmentRecord);
     }
 
     @Override

+ 133 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/controller/BizAppointReplaceController.java

@@ -0,0 +1,133 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+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.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceAddParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceEditParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceIdParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplacePageParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.service.BizAppointReplaceService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 沙石预约调度控制器
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ */
+@Tag(name = "沙石预约调度控制器")
+@RestController
+@Validated
+public class BizAppointReplaceController {
+
+    @Resource
+    private BizAppointReplaceService bizAppointReplaceService;
+
+    /**
+     * 获取沙石预约调度分页
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    @Operation(summary = "获取沙石预约调度分页")
+    @SaCheckPermission("/biz/bizappointreplace/page")
+    @GetMapping("/biz/bizappointreplace/page")
+    public CommonResult<Page<BizAppointReplace>> page(BizAppointReplacePageParam bizAppointReplacePageParam) {
+        return CommonResult.data(bizAppointReplaceService.page(bizAppointReplacePageParam));
+    }
+
+    /**
+     * 添加沙石预约调度
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    @Operation(summary = "添加沙石预约调度")
+    @CommonLog("添加沙石预约调度")
+    @PostMapping("/biz/bizappointreplace/add")
+    public CommonResult<String> add(@RequestBody @Valid BizAppointReplaceAddParam bizAppointReplaceAddParam) {
+        bizAppointReplaceService.add(bizAppointReplaceAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑沙石预约调度
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    @Operation(summary = "编辑沙石预约调度")
+    @CommonLog("编辑沙石预约调度")
+    @PostMapping("/biz/bizappointreplace/edit")
+    public CommonResult<String> edit(@RequestBody @Valid BizAppointReplaceEditParam bizAppointReplaceEditParam) {
+        bizAppointReplaceService.edit(bizAppointReplaceEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 审核沙石预约调度
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    @Operation(summary = "审核沙石预约调度")
+    @CommonLog("审核沙石预约调度")
+    @PostMapping("/biz/bizappointreplace/audit")
+    public CommonResult<String> audit(@RequestBody @Valid BizAppointReplaceEditParam bizAppointReplaceEditParam) {
+        bizAppointReplaceService.audit(bizAppointReplaceEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除沙石预约调度
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    @Operation(summary = "删除沙石预约调度")
+    @CommonLog("删除沙石预约调度")
+    @PostMapping("/biz/bizappointreplace/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   List<BizAppointReplaceIdParam> bizAppointReplaceIdParamList) {
+        bizAppointReplaceService.delete(bizAppointReplaceIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取沙石预约调度详情
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    @Operation(summary = "获取沙石预约调度详情")
+    @GetMapping("/biz/bizappointreplace/detail")
+    public CommonResult<BizAppointReplace> detail(@Valid BizAppointReplaceIdParam bizAppointReplaceIdParam) {
+        return CommonResult.data(bizAppointReplaceService.detail(bizAppointReplaceIdParam));
+    }
+}

+ 130 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/entity/BizAppointReplace.java

@@ -0,0 +1,130 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import vip.xiaonuo.common.pojo.CommonEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 沙石预约调度实体
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+@Getter
+@Setter
+@TableName("biz_appoint_replace")
+public class BizAppointReplace extends CommonEntity {
+
+    /** 主键ID */
+    @TableId
+    @Schema(description = "主键ID")
+    private String id;
+
+    /** 更换前订单id */
+    @Schema(description = "更换前订单id")
+    private String oldOrderId;
+
+    /** 更换后订单id */
+    @Schema(description = "更换后订单id")
+    private String newOrderId;
+
+    /** 预约id */
+    @Schema(description = "预约id")
+    private String appointId;
+
+    /** 更换前点位id */
+    @Schema(description = "更换前点位id")
+    private String oldLoadPointId;
+
+    /** 更换后点位id */
+    @Schema(description = "更换后点位id")
+    private String newLoadPointId;
+
+    /** 更换前装卸时间id */
+    @Schema(description = "更换前装卸时间id")
+    private String oldLoadTimeId;
+
+    /** 更换后装卸时间id */
+    @Schema(description = "更换后装卸时间id")
+    private String newLoadTimeId;
+
+    /** 状态 1:待审核   2:审核通过   3:审核通过 */
+    @Schema(description = "状态 1:待审核   2:审核通过   3:审核通过")
+    private String status;
+
+    /**预约状态*/
+    private String appointStatus;
+
+
+    /**旧订单号*/
+    @TableField(exist = false)
+    private String oldOrderNumber;
+
+    /**新订单号*/
+    @TableField(exist = false)
+    private String newOrderNumber;
+    /**旧点位*/
+    @TableField(exist = false)
+    private String oldLoadPoint;
+    /**新点位*/
+    @TableField(exist = false)
+    private String newLoadPoint;
+    /**旧开始时间*/
+    @TableField(exist = false)
+    private String oldBeginTime;
+    /**旧结束时间*/
+    @TableField(exist = false)
+    private String oldEndTime;
+    /**新开始时间*/
+    @TableField(exist = false)
+    private String newBeginTime;
+    /**新结束时间*/
+    @TableField(exist = false)
+    private String newEndTime;
+    /**车牌号*/
+    @TableField(exist = false)
+    private String licenseNumber;
+    /**司机姓名*/
+    @TableField(exist = false)
+    private String driverName;
+    /**司机电话*/
+    @TableField(exist = false)
+    private String driverMobile;
+
+    /**客户名称*/
+    @TableField(exist = false)
+    private String customerName;
+    @TableField(exist = false)
+    private String customerContact;
+    @TableField(exist = false)
+    private String customerPhone;
+    @TableField(exist = false)
+    private String customerAddress;
+    @TableField(exist = false)
+    private String goodsName;
+    @TableField(exist = false)
+    private String goodsCode;
+    @TableField(exist = false)
+    private Date confStartTime;
+    @TableField(exist = false)
+    private Date confEndTime;
+    @TableField(exist = false)
+    private String axleNumber;
+}

+ 34 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/enums/BizAppointReplaceEnum.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.enums;
+
+import lombok.Getter;
+
+/**
+ * 沙石预约调度枚举
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+@Getter
+public enum BizAppointReplaceEnum {
+
+    /** 测试 */
+    TEST("TEST");
+
+    private final String value;
+
+    BizAppointReplaceEnum(String value) {
+        this.value = value;
+    }
+}

+ 33 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/mapper/BizAppointReplaceMapper.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace;
+
+/**
+ * 沙石预约调度Mapper接口
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+public interface BizAppointReplaceMapper extends BaseMapper<BizAppointReplace> {
+    Page<BizAppointReplace> getPage(@Param("page") Page<BizAppointReplace> page, @Param("ew") QueryWrapper<BizAppointReplace> ew);
+
+    BizAppointReplace getOldDetail(@Param("ew") QueryWrapper<BizAppointReplace> ew);
+
+    BizAppointReplace getNewDetail(@Param("ew") QueryWrapper<BizAppointReplace> ew);
+}

+ 94 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/mapper/mapping/BizAppointReplaceMapper.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="vip.xiaonuo.biz.modular.bizappointreplace.mapper.BizAppointReplaceMapper">
+
+    <select id="getPage" resultType="vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace">
+        select
+            br.id,
+            oo.order_number oldOrderNumber,
+            o.order_number newOrderNumber,
+            op.load_point oldLoadPoint,
+            np.load_point newLoadPoint,
+            ot.begin_time oldBeginTime,
+            ot.end_time oldEndTime,
+            nt.begin_time newBeginTime,
+            nt.end_time newEndTime,
+            ba.license_number,
+            ba.driver_name,
+            ba.driver_mobile,
+            br.status
+        from biz_appoint_replace br
+                 left join biz_appointment_record ba on ba.id = br.appoint_id
+                 left join biz_order oo on oo.id = br.old_order_id
+                 left join biz_order o on o.id = br.new_order_id
+                 left join biz_load_point op on op.id = br.old_load_point_id
+                 left join biz_load_point np on np.id = br.new_load_point_id
+                 left join biz_load_time ot on ot.id = br.old_load_time_id
+                 left join biz_load_time nt on nt.id = br.new_load_time_id
+        ${ew.customSqlSegment}
+    </select>
+
+    <select id="getOldDetail" resultType="vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace">
+        select
+            oo.order_number oldOrderNumber,
+            op.load_point oldLoadPoint,
+            ot.begin_time oldBeginTime,
+            ot.end_time oldEndTime,
+            ba.license_number,
+            ba.driver_name,
+            ba.driver_mobile,
+            br.status,
+            bc.`name` customerName,
+            bc.contact customerContact,
+            bc.phone customerPhone,
+            bc.address customerAddress,
+            bg.GOODS_NAME goodsName,
+            bg.GOODS_CODE goodsCode,
+            bgc.CONF_START_TIME confStartTime,
+            bgc.CONF_END_TIME confEndTime,
+            bec.vehicle_axle_number axleNumber
+        from biz_appoint_replace br
+                 left join biz_appointment_record ba on ba.id = br.appoint_id
+                 left join biz_order oo on br.old_order_id = oo.id
+                 left join biz_load_point op on op.id = br.old_load_point_id
+                 left join biz_load_time ot on ot.id = br.old_load_time_id
+                 left join biz_customer bc on bc.id = oo.customer_id
+                 left join biz_goods bg on bg.id = oo.good_id
+                 left join biz_goods_conf bgc on bgc.id = oo.delivery_time_id
+                 left join biz_excess_config bec on bec.id = ba.over_id
+            ${ew.customSqlSegment}
+    </select>
+
+    <select id="getNewDetail" resultType="vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace">
+        SELECT
+            br.id,
+            o.order_number newOrderNumber,
+            np.load_point newLoadPoint,
+            nt.begin_time newBeginTime,
+            nt.end_time newEndTime,
+            ba.license_number,
+            ba.driver_name,
+            ba.driver_mobile,
+            br.STATUS,
+            bc.`name` customerName,
+            bc.contact customerContact,
+            bc.phone customerPhone,
+            bc.address customerAddress,
+            bg.GOODS_NAME goodsName,
+            bg.GOODS_CODE goodsCode,
+            bgc.CONF_START_TIME confStartTime,
+            bgc.CONF_END_TIME confEndTime,
+            bec.vehicle_axle_number axleNumber
+        FROM
+            biz_appoint_replace br
+                LEFT JOIN biz_appointment_record ba ON ba.id = br.appoint_id
+                LEFT JOIN biz_order o ON o.id = br.new_order_id
+                LEFT JOIN biz_load_point np ON np.id = br.new_load_point_id
+                LEFT JOIN biz_load_time nt ON nt.id = br.new_load_time_id
+                left join biz_customer bc on bc.id = o.customer_id
+                left join biz_goods bg on bg.id = o.good_id
+                left join biz_goods_conf bgc on bgc.id = o.delivery_time_id
+                left join biz_excess_config bec on bec.id = ba.over_id
+            ${ew.customSqlSegment}
+    </select>
+</mapper>

+ 66 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplaceAddParam.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 沙石预约调度添加参数
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+@Getter
+@Setter
+public class BizAppointReplaceAddParam {
+
+    /** 更换前订单id */
+    @Schema(description = "更换前订单id")
+    private String oldOrderId;
+
+    /** 更换后订单id */
+    @Schema(description = "更换后订单id")
+    private String newOrderId;
+
+    /** 预约id */
+    @Schema(description = "预约id")
+    private String appointId;
+
+    /** 更换前点位id */
+    @Schema(description = "更换前点位id")
+    private String oldLoadPointId;
+
+    /** 更换后点位id */
+    @Schema(description = "更换后点位id")
+    private String newLoadPointId;
+
+    /** 更换前装卸时间id */
+    @Schema(description = "更换前装卸时间id")
+    private String oldLoadTimeId;
+
+    /** 更换后装卸时间id */
+    @Schema(description = "更换后装卸时间id")
+    private String newLoadTimeId;
+
+    /** 状态 1:待审核   2:审核通过   3:审核通过 */
+    @Schema(description = "状态 1:待审核   2:审核通过   3:审核通过")
+    private String status;
+
+}

+ 75 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplaceEditParam.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 沙石预约调度编辑参数
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+@Getter
+@Setter
+public class BizAppointReplaceEditParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+
+    /** 更换前订单id */
+    @Schema(description = "更换前订单id")
+    private String oldOrderId;
+
+    /** 更换后订单id */
+    @Schema(description = "更换后订单id")
+    private String newOrderId;
+
+    /** 预约id */
+    @Schema(description = "预约id")
+    private String appointId;
+
+    /** 更换前点位id */
+    @Schema(description = "更换前点位id")
+    private String oldLoadPointId;
+
+    /** 更换后点位id */
+    @Schema(description = "更换后点位id")
+    private String newLoadPointId;
+
+    /** 更换前装卸时间id */
+    @Schema(description = "更换前装卸时间id")
+    private String oldLoadTimeId;
+
+    /** 更换后装卸时间id */
+    @Schema(description = "更换后装卸时间id")
+    private String newLoadTimeId;
+
+    /** 状态 1:待审核   2:审核通过   3:审核通过 */
+    @Schema(description = "状态 1:待审核   2:审核通过   3:审核通过")
+    private String status;
+
+    private String auditFlag;
+
+    private String auditRemark;
+
+}

+ 37 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplaceIdParam.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 沙石预约调度Id参数
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+@Getter
+@Setter
+public class BizAppointReplaceIdParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+
+    private String flag;
+}

+ 60 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/param/BizAppointReplacePageParam.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 沙石预约调度查询参数
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+@Getter
+@Setter
+public class BizAppointReplacePageParam {
+
+    /** 当前页 */
+    @Schema(description = "当前页码")
+    private Integer current;
+
+    /** 每页条数 */
+    @Schema(description = "每页条数")
+    private Integer size;
+
+    /** 排序字段 */
+    @Schema(description = "排序字段,字段驼峰名称,如:userName")
+    private String sortField;
+
+    /** 排序方式 */
+    @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+    private String sortOrder;
+
+    /** 关键词 */
+    @Schema(description = "关键词")
+    private String searchKey;
+
+    /**车牌号*/
+    private String licenseNumber;
+
+    /**司机姓名*/
+    private String driverName;
+
+    /**司机电话*/
+    private String driverMobile;
+
+}

+ 86 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/service/BizAppointReplaceService.java

@@ -0,0 +1,86 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceAddParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceEditParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceIdParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplacePageParam;
+
+import java.util.List;
+
+/**
+ * 沙石预约调度Service接口
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+public interface BizAppointReplaceService extends IService<BizAppointReplace> {
+
+    /**
+     * 获取沙石预约调度分页
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    Page<BizAppointReplace> page(BizAppointReplacePageParam bizAppointReplacePageParam);
+
+    /**
+     * 添加沙石预约调度
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    void add(BizAppointReplaceAddParam bizAppointReplaceAddParam);
+
+    /**
+     * 编辑沙石预约调度
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    void edit(BizAppointReplaceEditParam bizAppointReplaceEditParam);
+
+    /**
+     * 调度审核审批
+     * @param bizAppointReplaceEditParam
+     */
+    void audit(BizAppointReplaceEditParam bizAppointReplaceEditParam);
+
+    /**
+     * 删除沙石预约调度
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    void delete(List<BizAppointReplaceIdParam> bizAppointReplaceIdParamList);
+
+    /**
+     * 获取沙石预约调度详情
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     */
+    BizAppointReplace detail(BizAppointReplaceIdParam bizAppointReplaceIdParam);
+
+    /**
+     * 获取沙石预约调度详情
+     *
+     * @author fanzherong
+     * @date  2025/07/11 14:22
+     **/
+    BizAppointReplace queryEntity(String id);
+}

+ 218 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizappointreplace/service/impl/BizAppointReplaceServiceImpl.java

@@ -0,0 +1,218 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizappointreplace.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.Resource;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import vip.xiaonuo.biz.modular.bizappointmentrecord.entity.BizAppointmentRecord;
+import vip.xiaonuo.biz.modular.bizappointmentrecord.service.BizAppointmentRecordService;
+import vip.xiaonuo.biz.modular.bizauditlog.entity.BizAuditLog;
+import vip.xiaonuo.biz.modular.bizauditlog.service.BizAuditLogService;
+import vip.xiaonuo.biz.modular.bizloadtime.entity.BizLoadTime;
+import vip.xiaonuo.biz.modular.bizloadtime.service.BizLoadTimeService;
+import vip.xiaonuo.biz.modular.bizorderconfig.entity.BizOrderConfig;
+import vip.xiaonuo.biz.modular.bizorderconfig.service.BizOrderConfigService;
+import vip.xiaonuo.common.enums.CommonSortOrderEnum;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.page.CommonPageRequest;
+import vip.xiaonuo.biz.modular.bizappointreplace.entity.BizAppointReplace;
+import vip.xiaonuo.biz.modular.bizappointreplace.mapper.BizAppointReplaceMapper;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceAddParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceEditParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplaceIdParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.param.BizAppointReplacePageParam;
+import vip.xiaonuo.biz.modular.bizappointreplace.service.BizAppointReplaceService;
+
+import java.util.List;
+
+/**
+ * 沙石预约调度Service接口实现类
+ *
+ * @author fanzherong
+ * @date  2025/07/11 14:22
+ **/
+@Service
+public class BizAppointReplaceServiceImpl extends ServiceImpl<BizAppointReplaceMapper, BizAppointReplace> implements BizAppointReplaceService {
+
+    @Resource
+    private BizAppointmentRecordService bizAppointmentRecordService;
+
+    @Resource
+    private BizOrderConfigService bizOrderConfigService;
+
+    @Resource
+    private BizLoadTimeService bizLoadTimeService;
+
+    @Resource
+    private BizAuditLogService bizAuditLogService;
+
+    @Override
+    public Page<BizAppointReplace> page(BizAppointReplacePageParam bizAppointReplacePageParam) {
+        QueryWrapper<BizAppointReplace> queryWrapper = new QueryWrapper<BizAppointReplace>().checkSqlInjection();
+        //车牌号查询
+        if(ObjectUtil.isNotEmpty(bizAppointReplacePageParam.getLicenseNumber())){
+            queryWrapper.like("ba.license_number",bizAppointReplacePageParam.getLicenseNumber());
+        }
+        //司机姓名查询
+        if(ObjectUtil.isNotEmpty(bizAppointReplacePageParam.getDriverName())){
+            queryWrapper.like("ba.driver_name",bizAppointReplacePageParam.getDriverName());
+        }
+        //司机电话查询
+        if(ObjectUtil.isNotEmpty(bizAppointReplacePageParam.getDriverMobile())){
+            queryWrapper.like("ba.driver_mobile",bizAppointReplacePageParam.getDriverMobile());
+        }
+        queryWrapper.eq("br.delete_flag","NOT_DELETE");
+        queryWrapper.orderByDesc("br.create_time");
+        return this.getBaseMapper().getPage(CommonPageRequest.defaultPage(), queryWrapper);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void add(BizAppointReplaceAddParam bizAppointReplaceAddParam) {
+        BizAppointReplace bizAppointReplace = BeanUtil.toBean(bizAppointReplaceAddParam, BizAppointReplace.class);
+        this.save(bizAppointReplace);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void edit(BizAppointReplaceEditParam bizAppointReplaceEditParam) {
+        BizAppointReplace bizAppointReplace = this.queryEntity(bizAppointReplaceEditParam.getId());
+        BeanUtil.copyProperties(bizAppointReplaceEditParam, bizAppointReplace);
+        this.updateById(bizAppointReplace);
+    }
+
+    @Transactional
+    @Override
+    public void audit(BizAppointReplaceEditParam bizAppointReplaceEditParam) {
+        BizAppointReplace bizAppointReplace = this.queryEntity(bizAppointReplaceEditParam.getId());
+        String status = "";
+        if(StringUtils.equals(bizAppointReplaceEditParam.getAuditFlag(),"true")){
+            //审核通过
+            bizAppointReplace.setStatus("2");
+
+            //查询旧订单预约配置
+            BizOrderConfig oldOrderConfig = bizOrderConfigService.getOne(new QueryWrapper<BizOrderConfig>().lambda().
+                    eq(BizOrderConfig::getOrderId, bizAppointReplace.getOldOrderId()).
+                    last("limit 1"));
+            if(ObjectUtil.isNotNull(oldOrderConfig)){
+                //释放旧订单预约次数
+                oldOrderConfig.setApplyNumberAlready(oldOrderConfig.getApplyNumberAlready()-1);
+                bizOrderConfigService.updateById(oldOrderConfig);
+            }
+
+            //查询新订单预约配置
+            BizOrderConfig newOrderConfig = bizOrderConfigService.getOne(new QueryWrapper<BizOrderConfig>().lambda().
+                    eq(BizOrderConfig::getOrderId, bizAppointReplace.getNewOrderId()).
+                    last("limit 1"));
+            if(ObjectUtil.isNotNull(newOrderConfig)){
+                newOrderConfig.setApplyNumberAlready(newOrderConfig.getApplyNumberAlready()+1);
+                bizOrderConfigService.updateById(newOrderConfig);
+            }
+
+            //判断装卸时间是否一致
+            if(!StringUtils.equals(bizAppointReplace.getOldLoadTimeId(),bizAppointReplace.getNewLoadTimeId())){
+                //装卸时间段修改
+                //查询旧时间段预约次数
+                BizLoadTime oldTime = bizLoadTimeService.getById(bizAppointReplace.getOldLoadTimeId());
+                if(ObjectUtil.isNotNull(oldTime)){
+                    //释放旧时段预约次数
+                    oldTime.setAlreadyNumber(oldTime.getAlreadyNumber()-1);
+                    bizLoadTimeService.updateById(oldTime);
+                }
+
+                //查询新时段的预约次数
+                BizLoadTime newTime = bizLoadTimeService.getById(bizAppointReplace.getNewLoadTimeId());
+                if(ObjectUtil.isNotNull(newTime)){
+                    //添加新时段预约次数
+                    newTime.setAlreadyNumber(newTime.getAlreadyNumber()+1);
+                    bizLoadTimeService.updateById(newTime);
+                }
+            }
+
+            BizAppointmentRecord bizAppointmentRecord = bizAppointmentRecordService.getById(bizAppointReplace.getAppointId());
+            if(ObjectUtil.isNotNull(bizAppointmentRecord)){
+                //订单修改
+                bizAppointmentRecord.setOrderId(bizAppointReplace.getNewOrderId());
+                //装卸点位
+                bizAppointmentRecord.setLoadPointId(bizAppointReplace.getNewLoadPointId());
+                //装卸时间
+                bizAppointmentRecord.setLoadTimeId(bizAppointReplace.getNewLoadTimeId());
+                bizAppointmentRecord.setStatus(bizAppointReplace.getAppointStatus());
+
+                bizAppointmentRecordService.updateById(bizAppointmentRecord);
+            }
+
+            status= "1";
+
+        }else{
+            //审核驳回
+            bizAppointReplace.setStatus("3");
+
+            BizAppointmentRecord bizAppointmentRecord = bizAppointmentRecordService.getById(bizAppointReplace.getAppointId());
+            if(ObjectUtil.isNotNull(bizAppointmentRecord)){
+                bizAppointmentRecord.setStatus(bizAppointReplace.getAppointStatus());
+
+                bizAppointmentRecordService.updateById(bizAppointmentRecord);
+            }
+            status = "2";
+        }
+        this.updateById(bizAppointReplace);
+
+
+        //插入审核记录
+        BizAuditLog bizAuditLog = new BizAuditLog();
+        bizAuditLog.setType("DDSP");
+        bizAuditLog.setDataId(bizAppointReplace.getAppointId());
+        bizAuditLog.setStatus(status);
+        bizAuditLog.setRemark(bizAppointReplaceEditParam.getAuditRemark());
+        bizAuditLogService.save(bizAuditLog);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void delete(List<BizAppointReplaceIdParam> bizAppointReplaceIdParamList) {
+        // 执行删除
+        this.removeByIds(CollStreamUtil.toList(bizAppointReplaceIdParamList, BizAppointReplaceIdParam::getId));
+    }
+
+    @Override
+    public BizAppointReplace detail(BizAppointReplaceIdParam bizAppointReplaceIdParam) {
+        QueryWrapper<BizAppointReplace> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("br.id",bizAppointReplaceIdParam.getId());
+        if(StringUtils.equals(bizAppointReplaceIdParam.getFlag(),"old")){
+            BizAppointReplace oldDetail = this.getBaseMapper().getOldDetail(queryWrapper);
+            return oldDetail;
+        }else{
+            BizAppointReplace newDetail = this.getBaseMapper().getNewDetail(queryWrapper);
+            return newDetail;
+        }
+    }
+
+    @Override
+    public BizAppointReplace queryEntity(String id) {
+        BizAppointReplace bizAppointReplace = this.getById(id);
+        if(ObjectUtil.isEmpty(bizAppointReplace)) {
+            throw new CommonException("沙石预约调度不存在,id值为:{}", id);
+        }
+        return bizAppointReplace;
+    }
+}

+ 1 - 1
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizorder/service/impl/BizOrderServiceImpl.java

@@ -484,7 +484,7 @@ public class BizOrderServiceImpl extends ServiceImpl<BizOrderMapper, BizOrder> i
         this.updateById(bizOrder);
 
         //上传erp
-        sendOrderForErp(bizOrder);
+        //sendOrderForErp(bizOrder);
     }
 
     @Transactional

+ 128 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/controller/BizShipController.java

@@ -0,0 +1,128 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+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.RestController;
+import vip.xiaonuo.biz.modular.bizship.param.*;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.biz.modular.bizship.entity.BizShip;
+import vip.xiaonuo.biz.modular.bizship.service.BizShipService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 船舶管理控制器
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ */
+@Tag(name = "船舶管理控制器")
+@RestController
+@Validated
+public class BizShipController {
+
+    @Resource
+    private BizShipService bizShipService;
+
+    /**
+     * 获取船舶管理分页
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    @Operation(summary = "获取船舶管理分页")
+    @SaCheckPermission("/biz/bizship/page")
+    @GetMapping("/biz/bizship/page")
+    public CommonResult<Page<BizShip>> page(BizShipPageParam bizShipPageParam) {
+        return CommonResult.data(bizShipService.page(bizShipPageParam));
+    }
+
+    /**
+     * 添加船舶管理
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    @Operation(summary = "添加船舶管理")
+    @CommonLog("添加船舶管理")
+    @PostMapping("/biz/bizship/add")
+    public CommonResult<String> add(@RequestBody @Valid BizShipAddParam bizShipAddParam) {
+        bizShipService.add(bizShipAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑船舶管理
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    @Operation(summary = "编辑船舶管理")
+    @CommonLog("编辑船舶管理")
+    @PostMapping("/biz/bizship/edit")
+    public CommonResult<String> edit(@RequestBody @Valid BizShipEditParam bizShipEditParam) {
+        bizShipService.edit(bizShipEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除船舶管理
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    @Operation(summary = "删除船舶管理")
+    @CommonLog("删除船舶管理")
+    @PostMapping("/biz/bizship/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   List<BizShipIdParam> bizShipIdParamList) {
+        bizShipService.delete(bizShipIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取船舶管理详情
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    @Operation(summary = "获取船舶管理详情")
+    @GetMapping("/biz/bizship/detail")
+    public CommonResult<BizShip> detail(@Valid BizShipIdParam bizShipIdParam) {
+        return CommonResult.data(bizShipService.detail(bizShipIdParam));
+    }
+
+    /**
+     * 获取船型下拉
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    @Operation(summary = "获取船型下拉")
+    @GetMapping("/biz/bizship/getShipTypeList")
+    public CommonResult<List<BizShipValue>> getShipTypeList() {
+        return CommonResult.data(bizShipService.getShipTypeList());
+    }
+}

+ 110 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/entity/BizShip.java

@@ -0,0 +1,110 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import vip.xiaonuo.common.pojo.CommonEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 船舶管理实体
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+@Getter
+@Setter
+@TableName("biz_ship")
+public class BizShip extends CommonEntity {
+
+    /** 主键ID */
+    @TableId
+    @Schema(description = "主键ID")
+    private String id;
+
+    /** 船舶号 */
+    @Schema(description = "船舶号")
+    private String shipNumber;
+
+    /** 船型 */
+    @Schema(description = "船型")
+    private String shipType;
+
+    /** 船舶证书 */
+    @Schema(description = "船舶证书")
+    private String shipCertificateName;
+
+    /** 船舶证书路径 */
+    @Schema(description = "船舶证书路径")
+    private String shipCertificatePath;
+
+    /** 荷载吨位 */
+    @Schema(description = "荷载吨位")
+    private BigDecimal shipLoad;
+
+    /** 船舶所有人 */
+    @Schema(description = "船舶所有人")
+    private String shipOwner;
+
+    /** 船舶身份信息 */
+    @Schema(description = "船舶身份信息")
+    private String shipIdentity;
+
+    /** 联系人姓名 */
+    @Schema(description = "联系人姓名")
+    private String contactName;
+
+    /** 联系人电话 */
+    @Schema(description = "联系人电话")
+    private String contactPhone;
+
+    /** 收款单位 */
+    @Schema(description = "收款单位")
+    private String receiveUnit;
+
+    /** 收款银行 */
+    @Schema(description = "收款银行")
+    private String receiveBank;
+
+    /** 收款账户 */
+    @Schema(description = "收款账户")
+    private String receiveAccount;
+
+    /** 合同名称 */
+    @Schema(description = "合同名称")
+    private String contractName;
+
+    /** 合同路径 */
+    @Schema(description = "合同路径")
+    private String contractPath;
+
+    /** 船主id */
+    @Schema(description = "船主id")
+    private String shipId;
+
+    /** 状态 1:启用   2:停用 */
+    @Schema(description = "状态 1:启用   2:停用")
+    private String status;
+
+    /**船舶注册号码**/
+    private String registerNumber;
+
+    /**船主*/
+    @TableField(exist = false)
+    private String shipName;
+}

+ 34 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/enums/BizShipEnum.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.enums;
+
+import lombok.Getter;
+
+/**
+ * 船舶管理枚举
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+@Getter
+public enum BizShipEnum {
+
+    /** 测试 */
+    TEST("TEST");
+
+    private final String value;
+
+    BizShipEnum(String value) {
+        this.value = value;
+    }
+}

+ 29 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/mapper/BizShipMapper.java

@@ -0,0 +1,29 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import vip.xiaonuo.biz.modular.bizship.entity.BizShip;
+
+/**
+ * 船舶管理Mapper接口
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+public interface BizShipMapper extends BaseMapper<BizShip> {
+    Page<BizShip> getPage(@Param("page") Page<BizShip> page, @Param("ew") QueryWrapper<BizShip> ew);
+}

+ 13 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/mapper/mapping/BizShipMapper.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="vip.xiaonuo.biz.modular.bizship.mapper.BizShipMapper">
+
+    <select id="getPage" resultType="vip.xiaonuo.biz.modular.bizship.entity.BizShip">
+        select
+            bs.*,
+            su.name shipName
+        from biz_ship bs
+        left join sys_user su on su.id = bs.ship_id
+        ${ew.customSqlSegment}
+    </select>
+</mapper>

+ 111 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipAddParam.java

@@ -0,0 +1,111 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 船舶管理添加参数
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+@Getter
+@Setter
+public class BizShipAddParam {
+
+    /** 船舶号 */
+    @Schema(description = "船舶号")
+    private String shipNumber;
+
+    /** 船型 */
+    @Schema(description = "船型")
+    private String shipType;
+
+    /** 船舶证书 */
+    @Schema(description = "船舶证书")
+    private String shipCertificateName;
+
+    /** 船舶证书路径 */
+    @Schema(description = "船舶证书路径")
+    private String shipCertificatePath;
+
+    /** 荷载吨位 */
+    @Schema(description = "荷载吨位")
+    private BigDecimal shipLoad;
+
+    /** 船舶所有人 */
+    @Schema(description = "船舶所有人")
+    private String shipOwner;
+
+    /** 船舶身份信息 */
+    @Schema(description = "船舶身份信息")
+    private String shipIdentity;
+
+    /** 联系人姓名 */
+    @Schema(description = "联系人姓名")
+    private String contactName;
+
+    /** 联系人电话 */
+    @Schema(description = "联系人电话")
+    private String contactPhone;
+
+    /** 收款单位 */
+    @Schema(description = "收款单位")
+    private String receiveUnit;
+
+    /** 收款银行 */
+    @Schema(description = "收款银行")
+    private String receiveBank;
+
+    /** 收款账户 */
+    @Schema(description = "收款账户")
+    private String receiveAccount;
+
+    /** 合同名称 */
+    @Schema(description = "合同名称")
+    private String contractName;
+
+    /** 合同路径 */
+    @Schema(description = "合同路径")
+    private String contractPath;
+
+    /** 船主id */
+    @Schema(description = "船主id")
+    private String shipId;
+
+    /** 状态 1:启用   2:停用 */
+    @Schema(description = "状态 1:启用   2:停用")
+    private String status;
+
+    private List<String> certificatePathList;
+
+    private List<String> certificateNameList;
+
+    private List<String> contractPathList;
+
+    private List<String> contractNameList;
+
+
+    /**船舶注册号码**/
+    private String registerNumber;
+
+}

+ 116 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipEditParam.java

@@ -0,0 +1,116 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 船舶管理编辑参数
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+@Getter
+@Setter
+public class BizShipEditParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+
+    /** 船舶号 */
+    @Schema(description = "船舶号")
+    private String shipNumber;
+
+    /** 船型 */
+    @Schema(description = "船型")
+    private String shipType;
+
+    /** 船舶证书 */
+    @Schema(description = "船舶证书")
+    private String shipCertificateName;
+
+    /** 船舶证书路径 */
+    @Schema(description = "船舶证书路径")
+    private String shipCertificatePath;
+
+    /** 荷载吨位 */
+    @Schema(description = "荷载吨位")
+    private BigDecimal shipLoad;
+
+    /** 船舶所有人 */
+    @Schema(description = "船舶所有人")
+    private String shipOwner;
+
+    /** 船舶身份信息 */
+    @Schema(description = "船舶身份信息")
+    private String shipIdentity;
+
+    /** 联系人姓名 */
+    @Schema(description = "联系人姓名")
+    private String contactName;
+
+    /** 联系人电话 */
+    @Schema(description = "联系人电话")
+    private String contactPhone;
+
+    /** 收款单位 */
+    @Schema(description = "收款单位")
+    private String receiveUnit;
+
+    /** 收款银行 */
+    @Schema(description = "收款银行")
+    private String receiveBank;
+
+    /** 收款账户 */
+    @Schema(description = "收款账户")
+    private String receiveAccount;
+
+    /** 合同名称 */
+    @Schema(description = "合同名称")
+    private String contractName;
+
+    /** 合同路径 */
+    @Schema(description = "合同路径")
+    private String contractPath;
+
+    /** 船主id */
+    @Schema(description = "船主id")
+    private String shipId;
+
+    /** 状态 1:启用   2:停用 */
+    @Schema(description = "状态 1:启用   2:停用")
+    private String status;
+
+
+    private List<String> certificatePathList;
+
+    private List<String> certificateNameList;
+
+    private List<String> contractPathList;
+
+    private List<String> contractNameList;
+
+    /**船舶注册号码**/
+    private String registerNumber;
+
+}

+ 35 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipIdParam.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 船舶管理Id参数
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+@Getter
+@Setter
+public class BizShipIdParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+}

+ 66 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipPageParam.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 船舶管理查询参数
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+@Getter
+@Setter
+public class BizShipPageParam {
+
+    /** 当前页 */
+    @Schema(description = "当前页码")
+    private Integer current;
+
+    /** 每页条数 */
+    @Schema(description = "每页条数")
+    private Integer size;
+
+    /** 排序字段 */
+    @Schema(description = "排序字段,字段驼峰名称,如:userName")
+    private String sortField;
+
+    /** 排序方式 */
+    @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+    private String sortOrder;
+
+    /** 关键词 */
+    @Schema(description = "关键词")
+    private String searchKey;
+
+    //船舶号
+    private String shipNumber;
+
+    //船型
+    private String shipType;
+
+    //船舶所有人
+    private String shipOwner;
+
+    //联系人姓名
+    private String contactName;
+
+    //联系人电话
+    private String contactPhone;
+
+}

+ 10 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/param/BizShipValue.java

@@ -0,0 +1,10 @@
+package vip.xiaonuo.biz.modular.bizship.param;
+
+import lombok.Data;
+
+@Data
+public class BizShipValue {
+    private String label;
+
+    private String value;
+}

+ 82 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/service/BizShipService.java

@@ -0,0 +1,82 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import vip.xiaonuo.biz.modular.bizship.entity.BizShip;
+import vip.xiaonuo.biz.modular.bizship.param.*;
+
+import java.util.List;
+
+/**
+ * 船舶管理Service接口
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+public interface BizShipService extends IService<BizShip> {
+
+    /**
+     * 获取船舶管理分页
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    Page<BizShip> page(BizShipPageParam bizShipPageParam);
+
+    /**
+     * 添加船舶管理
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    void add(BizShipAddParam bizShipAddParam);
+
+    /**
+     * 编辑船舶管理
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    void edit(BizShipEditParam bizShipEditParam);
+
+    /**
+     * 删除船舶管理
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    void delete(List<BizShipIdParam> bizShipIdParamList);
+
+    /**
+     * 获取船舶管理详情
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     */
+    BizShip detail(BizShipIdParam bizShipIdParam);
+
+    /**
+     * 获取船舶管理详情
+     *
+     * @author fanzherong
+     * @date  2025/07/12 15:32
+     **/
+    BizShip queryEntity(String id);
+
+    /**
+     * 获取船型下拉
+     */
+    List<BizShipValue> getShipTypeList();
+}

+ 283 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizship/service/impl/BizShipServiceImpl.java

@@ -0,0 +1,283 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizship.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.PhoneUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.Resource;
+import org.apache.commons.compress.utils.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.biz.modular.bizship.param.*;
+import vip.xiaonuo.biz.modular.user.entity.BizUser;
+import vip.xiaonuo.biz.modular.user.service.BizUserService;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.page.CommonPageRequest;
+import vip.xiaonuo.biz.modular.bizship.entity.BizShip;
+import vip.xiaonuo.biz.modular.bizship.mapper.BizShipMapper;
+import vip.xiaonuo.biz.modular.bizship.service.BizShipService;
+import vip.xiaonuo.dev.modular.dict.entity.DevDict;
+import vip.xiaonuo.dev.modular.dict.service.DevDictService;
+
+import java.util.List;
+
+/**
+ * 船舶管理Service接口实现类
+ *
+ * @author fanzherong
+ * @date  2025/07/12 15:32
+ **/
+@Service
+public class BizShipServiceImpl extends ServiceImpl<BizShipMapper, BizShip> implements BizShipService {
+
+    @Resource
+    private BizUserService bizUserService;
+
+    @Resource
+    private DevDictService devDictService;
+
+    @Override
+    public Page<BizShip> page(BizShipPageParam bizShipPageParam) {
+        QueryWrapper<BizShip> queryWrapper = new QueryWrapper<BizShip>().checkSqlInjection();
+        //船舶号
+        if(ObjectUtil.isNotEmpty(bizShipPageParam.getShipNumber())){
+            queryWrapper.like("bs.ship_number",bizShipPageParam.getShipNumber());
+        }
+        //船型
+        if(ObjectUtil.isNotEmpty(bizShipPageParam.getShipType())){
+            queryWrapper.eq("bs.ship_type",bizShipPageParam.getShipType());
+        }
+        //船舶所有人
+        if(ObjectUtil.isNotEmpty(bizShipPageParam.getShipOwner())){
+            queryWrapper.like("su.name",bizShipPageParam.getShipOwner());
+        }
+        //联系人姓名
+        if(ObjectUtil.isNotEmpty(bizShipPageParam.getContactName())){
+            queryWrapper.like("bs.contact_name",bizShipPageParam.getContactName());
+        }
+        //联系人电话
+        if(ObjectUtil.isNotEmpty(bizShipPageParam.getContactPhone())){
+            queryWrapper.like("bs.contact_name",bizShipPageParam.getContactPhone());
+        }
+        // 校验数据范围
+        List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
+        if(ObjectUtil.isEmpty(loginUserDataScope)) {
+            queryWrapper.eq("bs.ship_id",StpLoginUserUtil.getLoginUser().getId());
+        }
+        queryWrapper.eq("bs.delete_flag","NOT_DELETE");
+        queryWrapper.lambda().orderByDesc(BizShip::getCreateTime);
+        Page<BizShip> page = this.getBaseMapper().getPage(CommonPageRequest.defaultPage(), queryWrapper);
+        return page;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void add(BizShipAddParam bizShipAddParam) {
+        checkParam(bizShipAddParam);
+        BizShip bizShip = BeanUtil.toBean(bizShipAddParam, BizShip.class);
+        if(ObjectUtil.isNotEmpty(bizShipAddParam.getCertificateNameList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String name : bizShipAddParam.getCertificateNameList()){
+                buffer.append(name+",");
+            }
+            bizShip.setShipCertificateName(buffer.substring(0,buffer.length()-1));
+        }
+        if(ObjectUtil.isNotEmpty(bizShipAddParam.getCertificatePathList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String path : bizShipAddParam.getCertificatePathList()){
+                buffer.append(path+",");
+            }
+            bizShip.setShipCertificatePath(buffer.substring(0,buffer.length()-1));
+        }
+        if(ObjectUtil.isNotEmpty(bizShipAddParam.getContractNameList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String name : bizShipAddParam.getContractNameList()){
+                buffer.append(name+",");
+            }
+            bizShip.setContractName(buffer.substring(0,buffer.length()-1));
+        }
+        if(ObjectUtil.isNotEmpty(bizShipAddParam.getContractPathList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String path : bizShipAddParam.getContractPathList()){
+                buffer.append(path+",");
+            }
+            bizShip.setContractPath(buffer.substring(0,buffer.length()-1));
+        }
+        if(ObjectUtil.isNotEmpty(bizShip.getShipId())){
+            BizUser bizUser = bizUserService.getById(bizShip.getShipId());
+            if(ObjectUtil.isNotNull(bizUser)){
+                if(ObjectUtil.isEmpty(bizShip.getContactName())){
+                    bizShip.setContactName(bizUser.getName());
+                }
+                if(ObjectUtil.isEmpty(bizShip.getContactPhone())){
+                    bizShip.setContactPhone(bizUser.getPhone());
+                }
+            }
+        }
+        this.save(bizShip);
+    }
+
+    public void checkParam(BizShipAddParam bizShipAddParam){
+        if(ObjectUtil.isNotEmpty(bizShipAddParam.getContactPhone())){
+            if (!PhoneUtil.isMobile(bizShipAddParam.getContactPhone())) {
+                throw new CommonException("联系人电话:{}格式错误", bizShipAddParam.getContactPhone());
+            }
+        }
+        if(ObjectUtil.isNotEmpty(bizShipAddParam.getShipNumber())){
+            //判断船舶号是否添加过
+            long count = this.count(new QueryWrapper<BizShip>().lambda().
+                    eq(BizShip::getShipNumber, bizShipAddParam.getShipNumber()).
+                    eq(BizShip::getStatus, "1"));
+            if(count>0){
+                throw new CommonException("船舶号已经添加过!");
+            }
+        }
+
+        if(ObjectUtil.isNotEmpty(bizShipAddParam.getShipId())){
+            //判断船主是否添加过船舶
+            long count = this.count(new QueryWrapper<BizShip>().lambda().
+                    eq(BizShip::getShipId, bizShipAddParam.getShipId()).
+                    eq(BizShip::getStatus, "1"));
+            if(count>0){
+                throw new CommonException("船主存在启用的船舶!");
+            }
+        }
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void edit(BizShipEditParam bizShipEditParam) {
+        BizShip bizShip = this.queryEntity(bizShipEditParam.getId());
+        if(ObjectUtil.isNotEmpty(bizShip.getShipId())){
+            if(!StringUtils.equals(bizShipEditParam.getShipId(),bizShip.getShipId())){
+                BizUser bizUser = bizUserService.getById(bizShipEditParam.getShipId());
+                if(ObjectUtil.isNotNull(bizUser)){
+                    bizShip.setContactName(bizUser.getName());
+                    bizShip.setContactPhone(bizUser.getPhone());
+                }
+            }
+        }
+        BeanUtil.copyProperties(bizShipEditParam, bizShip);
+        if(ObjectUtil.isNotEmpty(bizShipEditParam.getCertificateNameList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String name : bizShipEditParam.getCertificateNameList()){
+                buffer.append(name+",");
+            }
+            bizShip.setShipCertificateName(buffer.substring(0,buffer.length()-1));
+        }
+        if(ObjectUtil.isNotEmpty(bizShipEditParam.getCertificatePathList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String path : bizShipEditParam.getCertificatePathList()){
+                buffer.append(path+",");
+            }
+            bizShip.setShipCertificatePath(buffer.substring(0,buffer.length()-1));
+        }
+        if(ObjectUtil.isNotEmpty(bizShipEditParam.getContractNameList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String name : bizShipEditParam.getContractNameList()){
+                buffer.append(name+",");
+            }
+            bizShip.setContractName(buffer.substring(0,buffer.length()-1));
+        }
+        if(ObjectUtil.isNotEmpty(bizShipEditParam.getContractPathList())){
+            StringBuffer buffer = new StringBuffer();
+            for(String path : bizShipEditParam.getContractPathList()){
+                buffer.append(path+",");
+            }
+            bizShip.setContractPath(buffer.substring(0,buffer.length()-1));
+        }
+        this.updateById(bizShip);
+    }
+
+    public void checkParam(BizShip bizShip,BizShipEditParam bizShipEditParam){
+        if(ObjectUtil.isNotEmpty(bizShipEditParam.getContactPhone())){
+            if (!PhoneUtil.isMobile(bizShipEditParam.getContactPhone())) {
+                throw new CommonException("联系人电话:{}格式错误", bizShipEditParam.getContactPhone());
+            }
+        }
+        if(!StringUtils.equals(bizShip.getShipNumber(),bizShipEditParam.getShipNumber())){
+            if(ObjectUtil.isNotEmpty(bizShipEditParam.getShipNumber())){
+                //判断船舶号是否添加过
+                long count = this.count(new QueryWrapper<BizShip>().lambda().
+                        eq(BizShip::getShipNumber, bizShipEditParam.getShipNumber()).
+                        eq(BizShip::getStatus, "1"));
+                if(count>0){
+                    throw new CommonException("船舶号已经添加过!");
+                }
+            }
+        }
+
+        if(!StringUtils.equals(bizShip.getShipId(),bizShipEditParam.getShipId())){
+            if(ObjectUtil.isNotEmpty(bizShipEditParam.getShipId())){
+                //判断船主是否添加过船舶
+                long count = this.count(new QueryWrapper<BizShip>().lambda().
+                        eq(BizShip::getShipId, bizShipEditParam.getShipId()).
+                        eq(BizShip::getStatus, "1"));
+                if(count>0){
+                    throw new CommonException("船主存在启用的船舶!");
+                }
+            }
+        }
+
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void delete(List<BizShipIdParam> bizShipIdParamList) {
+        // 执行删除
+        this.removeByIds(CollStreamUtil.toList(bizShipIdParamList, BizShipIdParam::getId));
+    }
+
+    @Override
+    public BizShip detail(BizShipIdParam bizShipIdParam) {
+        return this.queryEntity(bizShipIdParam.getId());
+    }
+
+    @Override
+    public BizShip queryEntity(String id) {
+        BizShip bizShip = this.getById(id);
+        if(ObjectUtil.isEmpty(bizShip)) {
+            throw new CommonException("船舶管理不存在,id值为:{}", id);
+        }
+        return bizShip;
+    }
+
+    @Override
+    public List<BizShipValue> getShipTypeList() {
+        List<BizShipValue> list = Lists.newArrayList();
+        DevDict devDict = devDictService.getOne(new QueryWrapper<DevDict>().lambda().
+                eq(DevDict::getDictValue, "ship_type").
+                last("limit 1"));
+        if(ObjectUtil.isNotNull(devDict)){
+            List<DevDict> dictList = devDictService.list(new QueryWrapper<DevDict>().lambda().
+                    eq(DevDict::getParentId, devDict.getId()));
+            for(DevDict dict : dictList){
+                BizShipValue bizShipValue = new BizShipValue();
+                bizShipValue.setLabel(dict.getDictLabel());
+                bizShipValue.setValue(dict.getDictValue());
+                list.add(bizShipValue);
+            }
+        }
+        return list;
+    }
+
+
+}