refactor(仓库): 重构订单和款式仓库以支持通用数据查询

将订单和款式仓库的查询逻辑重构为通用的数据查询接口,支持分页和过滤条件。新增了`countRows`和`getRows`方法,简化了数据查询的重复代码,并提高了代码的可维护性。
This commit is contained in:
user 2025-04-24 13:00:21 +08:00
parent 3e9e5f3904
commit 59dff2b1bf
11 changed files with 108 additions and 60 deletions

View File

@ -3,34 +3,32 @@ package com.vxnet.pms.repository;
import com.vxnet.pms.domain.Order;
import java.util.Map;
import org.springframework.data.domain.Pageable;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* Spring Data R2DBC repository for the Order entity.
*/
public interface OrderRepository extends R2dbcRepository<Order, Long> {
@Query(
"SELECT * FROM jhi_order WHERE (:#{#organization} = '*' OR organization = :#{#organization}) " +
"AND (:#{#params.get('number')} IS NULL OR number = :#{#params.get('number')}) " +
"AND (:#{#params.get('name')} IS NULL OR name = :#{#params.get('name')}) " +
"AND (:#{#params.get('customerNumber')} IS NULL OR customer_number = :#{#params.get('customerNumber')}) " +
"AND (:#{#params.get('customerPo')} IS NULL OR customer_po = :#{#params.get('customerPo')}) " +
"AND (:#{#params.get('customerOrder')} IS NULL OR customer_order = :#{#params.get('customerOrder')}) " +
"AND (:#{#params.get('styleNo')} IS NULL OR style_no LIKE CONCAT('%', :#{#params.get('styleNo')}, '%')) " +
"AND (:#{#params.get('material')} IS NULL OR material = :#{#params.get('material')}) " +
"AND (:#{#params.get('sizeType')} IS NULL OR size_type = :#{#params.get('sizeType')}) " +
"AND (:#{#params.get('platingType')} IS NULL OR plating_type = :#{#params.get('platingType')}) " +
"AND (:#{#params.get('stampMark')} IS NULL OR stamp_mark = :#{#params.get('stampMark')}) " +
"AND (:#{#params.get('quality')} IS NULL OR quality = :#{#params.get('quality')}) " +
"AND (:#{#params.get('customsType')} IS NULL OR customs_type = :#{#params.get('customsType')}) " +
"AND (:#{#params.get('originalOrder')} IS NULL OR original_order = :#{#params.get('originalOrder')}) " +
"AND (:#{#params.get('property')} IS NULL OR property LIKE CONCAT('%', :#{#params.get('property')}, '%')) " +
"AND (:#{#params.get('imageUrl')} IS NULL OR image_url LIKE CONCAT('%', :#{#params.get('imageUrl')}, '%')) " +
"AND (:#{#params.get('status')} IS NULL OR status = :#{#params.get('status')}) " +
"ORDER BY sort_no ASC"
)
Flux<Order> findOrders(@Param("organization") String organization, @Param("params") Map<String, Object> params, Pageable pageable);
@Repository
public interface OrderRepository extends R2dbcRepository<Order, Long>, OrderRepositoryInternal {}
interface OrderRepositoryInternal {
Mono<Long> countRows(String organization, String table, Map<String, Object> params);
Flux<Map<String, Object>> getRows(String organization, String table, Map<String, Object> params, Pageable pageable);
}
@Component
class OrderRepositoryInternalImpl extends BaseRepository implements OrderRepositoryInternal {
public OrderRepositoryInternalImpl(DatabaseClient db, R2dbcEntityTemplate r2dbcEntityTemplate, R2dbcConverter r2dbcConverter) {
super(db, r2dbcEntityTemplate, r2dbcConverter);
}
}

View File

@ -3,23 +3,32 @@ package com.vxnet.pms.repository;
import com.vxnet.pms.domain.Style;
import java.util.Map;
import org.springframework.data.domain.Pageable;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* Spring Data R2DBC repository for the Style entity.
*/
public interface StyleRepository extends R2dbcRepository<Style, Long> {
@Query(
"SELECT * FROM jhi_style WHERE (:#{#organization} = '*' OR organization = :#{#organization}) " +
"AND (:#{#params['number']} IS NULL OR number = :#{#params['number']}) " +
"AND (:#{#params['name']} IS NULL OR name LIKE CONCAT('%', :#{#params['name']}, '%')) " +
"AND (:#{#params['customerNumber']} IS NULL OR customer_number = :#{#params['customerNumber']}) " +
"AND (:#{#params['productType']} IS NULL OR product_type = :#{#params['productType']}) " +
"AND (:#{#params['status']} IS NULL OR status = :#{#params['status']}) " +
"ORDER BY number ASC"
)
Flux<Style> findStyles(@Param("organization") String organization, @Param("params") Map<String, Object> params, Pageable pageable);
@Repository
public interface StyleRepository extends R2dbcRepository<Style, Long>, StyleRepositoryInternal {}
interface StyleRepositoryInternal {
Mono<Long> countRows(String organization, String table, Map<String, Object> params);
Flux<Map<String, Object>> getRows(String organization, String table, Map<String, Object> params, Pageable pageable);
}
@Component
class StyleRepositoryInternalImpl extends BaseRepository implements StyleRepositoryInternal {
public StyleRepositoryInternalImpl(DatabaseClient db, R2dbcEntityTemplate r2dbcEntityTemplate, R2dbcConverter r2dbcConverter) {
super(db, r2dbcEntityTemplate, r2dbcConverter);
}
}

View File

@ -74,9 +74,13 @@ public class OrderService {
return orderRepository.findById(id);
}
public Flux<Order> getOrders(Map<String, Object> params, Pageable pageable) {
public Mono<Long> countOrders(String table, Map<String, Object> params) {
return SecurityUtils.getCurrentOrganization().flatMap(organization -> orderRepository.countRows(organization, table, params));
}
public Flux<Map<String, Object>> getOrders(String table, Map<String, Object> params, Pageable pageable) {
return SecurityUtils.getCurrentOrganization()
.map(organization -> orderRepository.findOrders(organization, params, pageable))
.map(organization -> orderRepository.getRows(organization, table, params, pageable))
.flatMapMany(orders -> orders);
}
}

View File

@ -64,9 +64,13 @@ public class StyleService {
return styleRepository.findById(id);
}
public Flux<Style> getStyles(Map<String, Object> params, Pageable pageable) {
public Mono<Long> countStyles(String table, Map<String, Object> params) {
return SecurityUtils.getCurrentOrganization().flatMap(organization -> styleRepository.countRows(organization, table, params));
}
public Flux<Map<String, Object>> getStyles(String table, Map<String, Object> params, Pageable pageable) {
return SecurityUtils.getCurrentOrganization()
.map(organization -> styleRepository.findStyles(organization, params, pageable))
.map(organization -> styleRepository.getRows(organization, table, params, pageable))
.flatMapMany(styles -> styles);
}
}

View File

@ -29,6 +29,8 @@ public class OrderResource {
private final OrderService orderService;
private String table = "jhi_order";
public OrderResource(OrderService orderService) {
this.orderService = orderService;
}
@ -107,13 +109,17 @@ public class OrderResource {
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body all orders.
*/
@GetMapping("/orders")
public Mono<ResponseEntity<Flux<Order>>> getOrders(
public Mono<ResponseEntity<Flux<Map<String, Object>>>> getOrders(
@RequestParam Map<String, Object> params,
@ParameterObject @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable
) {
if (!onlyContainsAllowedProperties(pageable)) {
return Mono.just(ResponseEntity.badRequest().build());
}
return Mono.just(ResponseEntity.ok().body(orderService.getOrders(params, pageable)));
return orderService
.countOrders(table, params)
.map(total ->
ResponseEntity.ok().header("X-Total-Count", String.valueOf(total)).body(orderService.getOrders(table, params, pageable))
);
}
}

View File

@ -7,8 +7,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@ -25,8 +23,6 @@ import reactor.core.publisher.Mono;
@RequestMapping("/api")
public class StyleDtlResource {
private static final Logger LOG = LoggerFactory.getLogger(StyleDtlResource.class);
private final StyleDtlService styleDtlService;
private String table = "jhi_styledtl";

View File

@ -29,6 +29,8 @@ public class StyleResource {
private final StyleService styleService;
private String table = "jhi_style";
public StyleResource(StyleService styleService) {
this.styleService = styleService;
}
@ -76,13 +78,17 @@ public class StyleResource {
}
@GetMapping("/styles")
public Mono<ResponseEntity<Flux<Style>>> getStyles(
public Mono<ResponseEntity<Flux<Map<String, Object>>>> getStyles(
@RequestParam Map<String, Object> params,
@ParameterObject @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable
) {
if (!onlyContainsAllowedProperties(pageable)) {
return Mono.just(ResponseEntity.badRequest().build());
}
return Mono.just(ResponseEntity.ok().body(styleService.getStyles(params, pageable)));
return styleService
.countStyles(table, params)
.map(total ->
ResponseEntity.ok().header("X-Total-Count", String.valueOf(total)).body(styleService.getStyles(table, params, pageable))
);
}
}

View File

@ -7,6 +7,7 @@ import axios from 'axios';
import { StreamBarcodeReader } from 'vue-barcode-reader';
import type { VxeToolbarInstance, VxeTableInstance, VxePagerEvents } from 'vxe-table';
import debounce from 'lodash/debounce';
import dayjs from 'dayjs';
export default defineComponent({
compatConfig: { MODE: 3 },
@ -80,6 +81,12 @@ export default defineComponent({
];
const columns = [
{
title: 'jewpmsApp.order.id',
field: 'id',
sortable: false,
visible: false,
},
{
field: 'imageUrl',
title: 'jewpmsApp.order.imageUrl',
@ -192,16 +199,17 @@ export default defineComponent({
sort: sort(),
})}`,
{
params: Object.fromEntries(
showFilter.value
params: Object.fromEntries([
...columns.filter(col => col.field !== undefined).map(col => [`${col.field}[name]`, '']),
...(showFilter.value
? Object.entries(filterParams.value)
.filter(([, filterParam]) => filterParam.value !== null && filterParam.value !== '')
.flatMap(([key, filterParam]) => [
[`${key}[op]`, filterParam.op],
[`${key}[value]`, filterParam.value],
])
: [],
),
: []),
]),
},
),
axios.get('api/dicts', {
@ -352,6 +360,10 @@ export default defineComponent({
showDelete,
removeRow,
columns,
formatDate: (date: string | Date): string => {
if (!date) return '';
return dayjs(date).format('YYYY-MM-DD');
},
};
},
});

View File

@ -137,6 +137,15 @@
<template #default="{ row }" v-if="column.field === 'quality'">
{{ qualityDicts.find(s => s.number === String(row.quality))?.name || row.quality }}
</template>
<template #default="{ row }" v-else-if="column.field === 'orderDate'">
{{ formatDate(row.orderDate) }}
</template>
<template #default="{ row }" v-else-if="column.field === 'finishDate'">
{{ formatDate(row.finishDate) }}
</template>
<template #default="{ row }" v-else-if="column.field === 'deliveryDate'">
{{ formatDate(row.deliveryDate) }}
</template>
<template #default="{ row }" v-else-if="column.field === 'imageUrl'">
<vxe-image v-if="row.imageUrl" :src="row.imageUrl" :preview-src="row.imageUrl" :width="65" :height="65" alt="Order Image" />
<span v-else>-</span>

View File

@ -49,6 +49,12 @@ export default defineComponent({
];
const columns = [
{
title: 'jewpmsApp.cost.id',
field: 'id',
sortable: false,
visible: false,
},
{
field: 'imageUrl',
title: 'jewpmsApp.style.imageUrl',
@ -72,13 +78,10 @@ export default defineComponent({
field: 'productType',
title: 'jewpmsApp.style.productType',
sortable: true,
slots: {
default: ({ row }) => getProductTypeName(row.productType),
},
},
{
field: 'customerUnit',
title: 'jewpmsApp.style.customerUnit',
field: 'customsUnit',
title: 'jewpmsApp.style.customsUnit',
sortable: true,
},
{
@ -108,7 +111,7 @@ export default defineComponent({
name: { op: '=', value: null },
customerNumber: { op: '=', value: null },
productType: { op: '=', value: null },
customerUnit: { op: '=', value: null },
customsUnit: { op: '=', value: null },
property: { op: '=', value: null },
});
@ -144,16 +147,17 @@ export default defineComponent({
sort: sort(),
})}`,
{
params: Object.fromEntries(
showFilter.value
params: Object.fromEntries([
...columns.filter(col => col.field !== undefined).map(col => [`${col.field}[name]`, '']),
...(showFilter.value
? Object.entries(filterParams.value)
.filter(([, filterParam]) => filterParam.value !== null && filterParam.value !== '')
.flatMap(([key, filterParam]) => [
[`${key}[op]`, filterParam.op],
[`${key}[value]`, filterParam.value],
])
: [],
),
: []),
]),
},
),
axios.get('api/dicts', {

View File

@ -58,7 +58,7 @@
"customerNumber": "客户款号",
"customerName": "客户名称",
"productType": "产品类别",
"customerUnit": "单位",
"customsUnit": "报关单位",
"property": "属性",
"group00": "组别0",
"group01": "组别1",