增加查询上游的菜单

This commit is contained in:
wuweihua
2026-10-13 09:29:26 +08:00
parent 3456a0bf7f
commit 26bdd3ba87
9 changed files with 665 additions and 0 deletions
@@ -0,0 +1,76 @@
package com.bms.web.controller.monitor;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.common.constant.HttpStatus;
import com.bms.common.core.controller.BaseController;
import com.bms.common.core.page.PageDomain;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.core.page.TableSupport;
import com.bms.common.utils.StringUtils;
import com.bms.price.domain.vo.TabOrderMonitorQuery;
import com.bms.price.domain.vo.TabOrderMonitorVo;
import com.bms.price.oracle.TabOrderMonitorService;
/**
* 订单数据源:查询上游 Oracle TAB_ORDER。
*/
@RestController
@RequestMapping("/monitor/orderSource")
public class TabOrderMonitorController extends BaseController
{
@Autowired
private TabOrderMonitorService tabOrderMonitorService;
@PreAuthorize("@ss.hasPermi('monitor:orderSource:list')")
@GetMapping("/list")
public TableDataInfo list(TabOrderMonitorQuery query)
{
TableDataInfo rsp = new TableDataInfo();
rsp.setCode(HttpStatus.SUCCESS);
rsp.setMsg("\u67e5\u8be2\u6210\u529f");
if (!hasRequiredFilters(query))
{
rsp.setRows(Collections.emptyList());
rsp.setTotal(0L);
return rsp;
}
PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
long total = tabOrderMonitorService.count(query);
List<TabOrderMonitorVo> rows = total <= 0L
? Collections.emptyList()
: tabOrderMonitorService.selectPage(query, pageNum, pageSize);
rsp.setRows(rows);
rsp.setTotal(total);
return rsp;
}
private static boolean hasRequiredFilters(TabOrderMonitorQuery query)
{
if (query == null)
{
return false;
}
boolean hasCustomer = false;
if (query.getCustomerCodes() != null)
{
for (String code : query.getCustomerCodes())
{
if (StringUtils.isNotEmpty(code))
{
hasCustomer = true;
break;
}
}
}
boolean hasTime = StringUtils.isNotEmpty(query.getBeginTime()) && StringUtils.isNotEmpty(query.getEndTime());
return hasCustomer && hasTime;
}
}
@@ -0,0 +1,28 @@
package com.bms.price.domain.vo;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
/**
* 订单数据源(TAB_ORDER)监控查询条件。
* 必须传客户 + 提货时间范围,避免全表扫描。
*/
@Data
public class TabOrderMonitorQuery implements Serializable
{
private static final long serialVersionUID = 1L;
/** 客户代码列表 */
private List<String> customerCodes;
/** 提货时间起(yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss) */
private String beginTime;
/** 提货时间止(yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss) */
private String endTime;
/** 客户订单号 ORDER_ID */
private String orderId;
/** 运单号 ORG_ORDER_ID */
private String orgOrderId;
/** 转单号 TRANSFER_ORDER_ID */
private String transferOrderId;
}
@@ -0,0 +1,50 @@
package com.bms.price.domain.vo;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
/** 订单数据源(TAB_ORDER)列表行。 */
@Data
public class TabOrderMonitorVo implements Serializable
{
private static final long serialVersionUID = 1L;
/** 客户订单号 */
private String orderId;
/** 运单号 */
private String orgOrderId;
/** 转单号 */
private String transferOrderId;
/** 轨迹ID */
private String trackId;
/** 客户代码 */
private String customerCode;
/** 客户名称 */
private String customerName;
/** 产品代码 */
private String productCode;
/** 产品名称 */
private String productName;
/** 产品底级代码 */
private String productFloorCode;
/** 国家代码 */
private String country;
/** 国家名称 */
private String countryName;
/** 收件人邮编 */
private String consigneePostcode;
/** 材积系数/计费重 */
private BigDecimal dimensionalWeightFactor;
/** 体积重 */
private BigDecimal volumeWeight;
/** 实重 */
private BigDecimal weight;
/** 订单状态 ORDER_STATUS */
private Integer orderStatus;
/** 提货时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date pickupDate;
}
@@ -1,7 +1,9 @@
package com.bms.price.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import com.bms.price.domain.vo.TabOrderMonitorQuery;
/** Read upstream main order from Oracle (SLAVE datasource). */
public interface OracleUpstreamOrderMapper
@@ -17,4 +19,12 @@ public interface OracleUpstreamOrderMapper
Map<String, Object> selectByBarcode(@Param("orderTable") String orderTable, @Param("trackingNo") String trackingNo);
Map<String, Object> selectByCustomerTrackId(@Param("orderTable") String orderTable, @Param("trackingNo") String trackingNo);
long countTabOrderMonitor(@Param("orderTable") String orderTable, @Param("query") TabOrderMonitorQuery query);
List<Map<String, Object>> selectTabOrderMonitorPage(
@Param("orderTable") String orderTable,
@Param("query") TabOrderMonitorQuery query,
@Param("startRow") long startRow,
@Param("endRow") long endRow);
}
@@ -0,0 +1,292 @@
package com.bms.price.oracle;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.bms.common.annotation.DataSource;
import com.bms.common.enums.DataSourceType;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.StringUtils;
import com.bms.price.config.BillingProperties;
import com.bms.price.domain.vo.TabOrderMonitorQuery;
import com.bms.price.domain.vo.TabOrderMonitorVo;
import com.bms.price.mapper.OracleUpstreamOrderMapper;
/**
* 订单数据源:从 Oracle TAB_ORDER 分页查询(SLAVE)。
*/
@Service
public class TabOrderMonitorService
{
private static final Logger log = LoggerFactory.getLogger(TabOrderMonitorService.class);
@Autowired
private OracleUpstreamOrderMapper oracleUpstreamOrderMapper;
@Autowired
private BillingProperties billingProperties;
@DataSource(DataSourceType.SLAVE)
public long count(TabOrderMonitorQuery query)
{
validate(query);
String table = resolveTable();
try
{
PageHelper.clearPage();
return oracleUpstreamOrderMapper.countTabOrderMonitor(table, query);
}
catch (RuntimeException e)
{
throw new ServiceException("Oracle TAB_ORDER count failed: " + e.getMessage());
}
finally
{
PageHelper.clearPage();
}
}
@DataSource(DataSourceType.SLAVE)
public List<TabOrderMonitorVo> selectPage(TabOrderMonitorQuery query, int pageNum, int pageSize)
{
validate(query);
String table = resolveTable();
int pn = pageNum < 1 ? 1 : pageNum;
int ps = pageSize < 1 ? 10 : pageSize;
if (ps > 200)
{
ps = 200;
}
long endRow = (long) pn * ps;
long startRow = endRow - ps;
try
{
PageHelper.clearPage();
long t0 = System.currentTimeMillis();
List<Map<String, Object>> rows = oracleUpstreamOrderMapper.selectTabOrderMonitorPage(table, query, startRow, endRow);
log.info("oracle tab_order page customerSize={} begin={} end={} page={}/{} rows={} costMs={}",
query.getCustomerCodes() == null ? 0 : query.getCustomerCodes().size(),
query.getBeginTime(), query.getEndTime(), pn, ps,
rows == null ? 0 : rows.size(), System.currentTimeMillis() - t0);
if (rows == null || rows.isEmpty())
{
return Collections.emptyList();
}
List<TabOrderMonitorVo> list = new ArrayList<TabOrderMonitorVo>(rows.size());
for (Map<String, Object> row : rows)
{
list.add(toVo(row));
}
return list;
}
catch (RuntimeException e)
{
throw new ServiceException("Oracle TAB_ORDER list failed: " + e.getMessage());
}
finally
{
PageHelper.clearPage();
}
}
private void validate(TabOrderMonitorQuery query)
{
if (query == null)
{
throw new ServiceException("请先选择客户和提货时间");
}
List<String> codes = query.getCustomerCodes();
if (codes == null || codes.isEmpty())
{
throw new ServiceException("请选择客户");
}
List<String> cleaned = new ArrayList<String>();
for (String c : codes)
{
if (StringUtils.isNotEmpty(c))
{
cleaned.add(c.trim());
}
}
if (cleaned.isEmpty())
{
throw new ServiceException("请选择客户");
}
query.setCustomerCodes(cleaned);
String begin = normalizeTime(query.getBeginTime(), true);
String end = normalizeTime(query.getEndTime(), false);
if (begin == null || end == null)
{
throw new ServiceException("请选择提货时间范围");
}
query.setBeginTime(begin);
query.setEndTime(end);
query.setOrderId(trimToNull(query.getOrderId()));
query.setOrgOrderId(trimToNull(query.getOrgOrderId()));
query.setTransferOrderId(trimToNull(query.getTransferOrderId()));
}
private static String trimToNull(String s)
{
if (StringUtils.isEmpty(s))
{
return null;
}
String t = s.trim();
return t.isEmpty() ? null : t;
}
private String resolveTable()
{
String table = billingProperties.getOracle().getOrderTable();
if (StringUtils.isEmpty(table))
{
throw new ServiceException("bms.billing.oracle.order-table is empty");
}
if (!table.matches("[A-Za-z0-9_\\.]+"))
{
throw new ServiceException("invalid oracle order table name");
}
return table;
}
private static String normalizeTime(String raw, boolean begin)
{
if (StringUtils.isEmpty(raw))
{
return null;
}
String s = raw.trim();
if (s.length() == 10)
{
return begin ? (s + " 00:00:00") : (s + " 23:59:59");
}
if (s.length() >= 19)
{
return s.substring(0, 19);
}
return s;
}
private static TabOrderMonitorVo toVo(Map<String, Object> row)
{
TabOrderMonitorVo vo = new TabOrderMonitorVo();
vo.setOrderId(str(row, "ORDER_ID"));
vo.setOrgOrderId(str(row, "ORG_ORDER_ID"));
vo.setTransferOrderId(str(row, "TRANSFER_ORDER_ID"));
vo.setTrackId(str(row, "TRACK_ID"));
vo.setCustomerCode(str(row, "CUSTOMER_CODE"));
vo.setCustomerName(str(row, "CUSTOMER_NAME"));
vo.setProductCode(str(row, "PRODUCT_CODE"));
vo.setProductName(str(row, "PRODUCT_NAME"));
vo.setProductFloorCode(str(row, "PRODUCT_FLOOR_CODE"));
vo.setCountry(str(row, "COUNTRY"));
vo.setCountryName(str(row, "COUNTRY_NAME"));
vo.setConsigneePostcode(str(row, "CONSIGNEE_POSTCODE"));
vo.setDimensionalWeightFactor(decimal(row, "DIMENSIONAL_WEIGHT_FACTOR"));
vo.setVolumeWeight(decimal(row, "VOLUME_WEIGHT"));
vo.setWeight(decimal(row, "WEIGHT"));
vo.setOrderStatus(integer(row, "ORDER_STATUS"));
vo.setPickupDate(date(row, "PICKUP_DATE"));
return vo;
}
private static String str(Map<String, Object> row, String key)
{
Object v = value(row, key);
return v == null ? null : String.valueOf(v).trim();
}
private static Integer integer(Map<String, Object> row, String key)
{
Object v = value(row, key);
if (v == null)
{
return null;
}
if (v instanceof Number)
{
return Integer.valueOf(((Number) v).intValue());
}
try
{
return Integer.valueOf(String.valueOf(v).trim());
}
catch (Exception e)
{
return null;
}
}
private static BigDecimal decimal(Map<String, Object> row, String key)
{
Object v = value(row, key);
if (v == null)
{
return null;
}
if (v instanceof BigDecimal)
{
return (BigDecimal) v;
}
if (v instanceof Number)
{
return BigDecimal.valueOf(((Number) v).doubleValue());
}
try
{
return new BigDecimal(String.valueOf(v).trim());
}
catch (Exception e)
{
return null;
}
}
private static Date date(Map<String, Object> row, String key)
{
Object v = value(row, key);
if (v == null)
{
return null;
}
if (v instanceof Timestamp)
{
return new Date(((Timestamp) v).getTime());
}
if (v instanceof Date)
{
return (Date) v;
}
return null;
}
private static Object value(Map<String, Object> row, String key)
{
if (row == null || key == null)
{
return null;
}
if (row.containsKey(key))
{
return row.get(key);
}
for (Map.Entry<String, Object> e : row.entrySet())
{
if (e.getKey() != null && e.getKey().equalsIgnoreCase(key))
{
return e.getValue();
}
}
return null;
}
}
@@ -44,4 +44,67 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT * FROM ${orderTable} WHERE CUSTOMER_TRACK_ID = #{trackingNo}
) WHERE ROWNUM &lt;= 1
</select>
<sql id="tabOrderMonitorWhere">
o.BL_PICKUP = 1
AND o.ORDER_STATUS IN (3, 4, 5, 6, 7, 8, 11)
AND o.CUSTOMER_CODE IN
<foreach collection="query.customerCodes" item="code" open="(" separator="," close=")">
#{code}
</foreach>
AND o.PICKUP_DATE &gt;= TO_TIMESTAMP(#{query.beginTime}, 'YYYY-MM-DD HH24:MI:SS')
AND o.PICKUP_DATE &lt;= TO_TIMESTAMP(#{query.endTime}, 'YYYY-MM-DD HH24:MI:SS')
<if test="query.orderId != null and query.orderId != ''">
AND o.ORDER_ID = #{query.orderId}
</if>
<if test="query.orgOrderId != null and query.orgOrderId != ''">
AND o.ORG_ORDER_ID = #{query.orgOrderId}
</if>
<if test="query.transferOrderId != null and query.transferOrderId != ''">
AND o.TRANSFER_ORDER_ID = #{query.transferOrderId}
</if>
</sql>
<sql id="tabOrderMonitorColumns">
o.ORDER_ID,
o.ORG_ORDER_ID,
o.TRANSFER_ORDER_ID,
o.TRACK_ID,
o.CUSTOMER_CODE,
o.CUSTOMER_NAME,
o.PRODUCT_CODE,
o.PRODUCT_NAME,
o.PRODUCT_FLOOR_CODE,
o.COUNTRY,
o.COUNTRY_NAME,
o.CONSIGNEE_POSTCODE,
o.DIMENSIONAL_WEIGHT_FACTOR,
o.VOLUME_WEIGHT,
o.WEIGHT,
o.ORDER_STATUS,
o.PICKUP_DATE
</sql>
<select id="countTabOrderMonitor" resultType="long">
SELECT COUNT(1)
FROM ${orderTable} o
WHERE
<include refid="tabOrderMonitorWhere"/>
</select>
<select id="selectTabOrderMonitorPage" resultType="java.util.HashMap">
SELECT * FROM (
SELECT inner_q.*, ROWNUM AS rn FROM (
SELECT
<include refid="tabOrderMonitorColumns"/>
FROM ${orderTable} o
WHERE
<include refid="tabOrderMonitorWhere"/>
ORDER BY o.PICKUP_DATE DESC, o.ORDER_ID DESC
) inner_q
WHERE ROWNUM &lt;= #{endRow}
)
WHERE rn &gt; #{startRow}
</select>
</mapper>
@@ -0,0 +1,14 @@
-- 订单数据源 under system monitor (parent_id = 2)
SET NAMES utf8mb4;
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
SELECT 2325, N'订单数据源', 2, 9, 'orderSource', 'monitor/orderSource/index', '', 'OrderSource', 1, 0, 'C', '0', '0', 'monitor:orderSource:list', 'list', 'admin', sysdate(), N'查询上游 Oracle TAB_ORDER,需客户+提货时间筛选'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2325 OR perms = 'monitor:orderSource:list');
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
SELECT 2326, N'查询', 2325, 1, '', NULL, 1, 0, 'F', '0', '0', 'monitor:orderSource:list', '#', 'admin', sysdate()
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2326);
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT 1, m.menu_id FROM (SELECT 2325 AS menu_id UNION SELECT 2326) m
WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id);
+10
View File
@@ -0,0 +1,10 @@
import request from '@/utils/request'
export function listOrderSource(query) {
return request({
url: '/monitor/orderSource/list',
method: 'get',
params: query,
timeout: 60000
})
}
@@ -0,0 +1,122 @@
<template>
<div class="app-container">
<basic-table
ref="tableRef"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
row-key="orderId"
:auto-load="false"
search-action-align="center"
:search-per-row="4"
>
<template #search-customerCodes="{ model }">
<customer-select
v-model="model.customerCodes"
type="customer"
value-key="customerCode"
label-key="customerName"
code-key="customerCode"
placeholder="请选择客户"
multiple
/>
</template>
<template #col-product="{ row }">
{{ row.productName || '-' }} ({{ row.productCode || '-' }})
</template>
<template #col-country="{ row }">
{{ row.countryName || '-' }} ({{ row.country || '-' }})
</template>
<template #col-orderStatus="{ row }">
{{ orderStatusLabel(row.orderStatus) }}
</template>
</basic-table>
</div>
</template>
<script setup name="OrderSource">
import { ElMessage } from 'element-plus'
import { listOrderSource } from '@/api/monitor/orderSource'
const tableRef = ref()
const searchColumns = [
{ label: '客户', prop: 'customerCodes', placeholder: '客户代码' },
{
label: '提货时间',
prop: 'dateRange',
component: 'datetimerange',
colSpan: 2,
props: {
clearable: true,
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
}
},
{ label: '订单号', prop: 'orderId', component: 'input', placeholder: '订单号', width: '180px' },
{ label: '运单号', prop: 'orgOrderId', component: 'input', placeholder: '运单号', width: '180px' },
{ label: '转单号', prop: 'transferOrderId', component: 'input', placeholder: '转单号', width: '180px' }
]
const columns = [
{ label: '客户订单号', prop: 'orderId', minWidth: 160, showOverflowTooltip: true },
{ label: '运单号', prop: 'orgOrderId', minWidth: 150, showOverflowTooltip: true },
{ label: '转单号', prop: 'transferOrderId', minWidth: 160, showOverflowTooltip: true },
{ label: '轨迹ID', prop: 'trackId', minWidth: 140, showOverflowTooltip: true },
{ label: '订单状态', prop: 'orderStatus', align: 'center', width: 110, slot: 'orderStatus' },
{ label: '客户代码', prop: 'customerCode', minWidth: 110, showOverflowTooltip: true },
{ label: '客户名称', prop: 'customerName', minWidth: 140, showOverflowTooltip: true },
{ label: '产品', prop: 'product', minWidth: 180, slot: 'product', showOverflowTooltip: true },
{ label: '产品底级代码', prop: 'productFloorCode', minWidth: 130, showOverflowTooltip: true },
{ label: '国家', prop: 'country', minWidth: 140, slot: 'country', showOverflowTooltip: true },
{ label: '收件人邮编', prop: 'consigneePostcode', minWidth: 120, showOverflowTooltip: true },
{ label: '计费重', prop: 'dimensionalWeightFactor', align: 'center', width: 100 },
{ label: '体积重', prop: 'volumeWeight', align: 'center', width: 100 },
{ label: '实重', prop: 'weight', align: 'center', width: 100 },
{ label: '提货时间', prop: 'pickupDate', align: 'center', width: 170 }
]
const orderStatusMap = {
0: '预报订单',
1: '确认订单',
2: '已中转',
3: '已收货',
4: '已装箱',
5: '已出库',
6: '转运中',
7: '已上网',
8: '已签收',
9: '已退件',
10: '已删除'
}
function orderStatusLabel(status) {
if (status === null || status === undefined || status === '') return '-'
const key = Number(status)
if (Object.prototype.hasOwnProperty.call(orderStatusMap, key)) {
return orderStatusMap[key]
}
return String(status)
}
function hasRequiredFilters(params) {
const codes = params && params.customerCodes
const hasCustomer = Array.isArray(codes) && codes.some(v => v != null && String(v).trim() !== '')
const range = params && params.dateRange
const hasTime = Array.isArray(range) && !!(range[0] && range[1])
return hasCustomer && hasTime
}
function queryList(params) {
if (!hasRequiredFilters(params)) {
ElMessage.warning('请先选择客户和提货时间')
return Promise.resolve({ rows: [], total: 0 })
}
const { dateRange, ...rest } = params
const query = { ...rest }
if (Array.isArray(dateRange) && dateRange[0] && dateRange[1]) {
query.beginTime = dateRange[0]
query.endTime = dateRange[1]
}
return listOrderSource(query)
}
</script>