refactor: 统一字段命名并优化字典树查询逻辑

将字段名从下划线命名法改为驼峰命名法,如 `sort_no` 改为 `sortNo`。同时优化了字典树查询逻辑,使用 `Map<String, Object>` 替代 `Dict` 实体类,简化了数据处理流程。
This commit is contained in:
user 2025-03-21 00:02:14 +08:00
parent 1e9ab24918
commit 77c9943703
9 changed files with 145 additions and 90 deletions

View File

@ -59,7 +59,7 @@ public abstract class BaseRepository {
for (Map.Entry<String, Object> entry : params.entrySet()) {
try {
if (entry.getValue() == null) continue;
String key = entry.getKey();
String key = entry.getKey().replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase();
Map<String, Object> condition;
if (entry.getValue() instanceof String) {
condition = objectMapper.readValue((String) entry.getValue(), Map.class);
@ -102,7 +102,11 @@ public abstract class BaseRepository {
params = preprocessParams(params);
String orderBy = pageable.getSort().isEmpty()
? ""
: pageable.getSort().stream().map(order -> order.getProperty() + " " + order.getDirection()).collect(Collectors.joining(", "));
: pageable
.getSort()
.stream()
.map(order -> order.getProperty().replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase() + " " + order.getDirection())
.collect(Collectors.joining(", "));
long page = pageable.getPageNumber();
long size = pageable.getPageSize();
Map<String, Object> bindMap = new HashMap<>();
@ -113,7 +117,8 @@ public abstract class BaseRepository {
for (Map.Entry<String, Object> entry : params.entrySet()) {
try {
if (entry.getValue() == null) continue;
String key = entry.getKey();
String keyOrg = entry.getKey();
String key = keyOrg.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase();
Map<String, Object> condition;
if (entry.getValue() instanceof String) {
condition = objectMapper.readValue((String) entry.getValue(), Map.class);
@ -128,7 +133,7 @@ public abstract class BaseRepository {
.append(fields.isEmpty() ? "" : ", ")
.append(key)
.append(" as ")
.append(name.toString().isEmpty() ? key : name.toString());
.append(name.toString().isEmpty() ? keyOrg : name.toString());
}
String operator = condition.get("op").toString();
Object value = condition.get("value");
@ -167,6 +172,7 @@ public abstract class BaseRepository {
}
// 执行查询并返回结果
//return executeSpec.fetch().all().map(this::convertKeysToCamelCase);
return executeSpec.fetch().all();
}
}

View File

@ -3,7 +3,9 @@ package com.vxnet.pms.service;
import com.vxnet.pms.domain.Dict;
import com.vxnet.pms.repository.DictRepository;
import com.vxnet.pms.security.SecurityUtils;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
@ -71,35 +73,6 @@ public class DictService {
);
}
public Mono<Long> countDicts(String table, Map<String, Object> params) {
return SecurityUtils.getCurrentOrganization().flatMap(organization -> dictRepository.countRows(organization, table, params));
}
public Flux<Map<String, Object>> getDicts(String table, Map<String, Object> params, Pageable pageable) {
return SecurityUtils.getCurrentOrganization()
.map(organization -> dictRepository.getRows(organization, table, params, pageable))
.flatMapMany(dicts -> dicts);
}
public Mono<Long> countRootDicts(Map<String, Object> params) {
return SecurityUtils.getCurrentOrganization().flatMap(organization -> dictRepository.countRootDicts(organization, params));
}
public Flux<Dict> getDictTree(Map<String, Object> params, Pageable pageable) {
return SecurityUtils.getCurrentOrganization()
.map(organization -> dictRepository.findRootDicts(organization, params, pageable))
.flatMapMany(rootDicts ->
rootDicts.flatMap(rootDict ->
Mono.just(rootDict)
.zipWith(getChildren(rootDict.getOrganization(), rootDict.getNumber()).collectList())
.map(tuple -> {
rootDict.setChildren(tuple.getT2());
return rootDict;
})
)
);
}
private Flux<Dict> getChildren(String organization, String number) {
return dictRepository
.findByParentNumber(organization, number)
@ -112,4 +85,46 @@ public class DictService {
})
);
}
public Mono<Long> countDicts(String table, Map<String, Object> params) {
return SecurityUtils.getCurrentOrganization().flatMap(organization -> dictRepository.countRows(organization, table, params));
}
public Flux<Map<String, Object>> getDicts(String table, Map<String, Object> params, Pageable pageable) {
return SecurityUtils.getCurrentOrganization()
.map(organization -> dictRepository.getRows(organization, table, params, pageable))
.flatMapMany(dicts -> dicts);
}
public Flux<Map<String, Object>> getDictsTree(String table, Map<String, Object> params, Pageable pageable) {
return SecurityUtils.getCurrentOrganization()
.flatMapMany(organization ->
dictRepository
.getRows(organization, table, params, pageable)
.flatMap(dict -> {
params.put("parentNumber[op]", "=");
params.put("parentNumber[value]", dict.get("number").toString());
return Mono.just(dict)
.zipWith(getRowsChildren(organization, table, params).collectList())
.map(tuple -> {
dict.put("children", tuple.getT2());
return dict;
});
})
);
}
private Flux<Map<String, Object>> getRowsChildren(String organization, String table, Map<String, Object> params) {
return dictRepository
.getRows(organization, table, params, PageRequest.of(0, Integer.MAX_VALUE))
.flatMap(dict -> {
params.put("parentNumber[value]", dict.get("number").toString());
return Mono.just(dict)
.zipWith(getRowsChildren(organization, table, params).collectList())
.map(tuple -> {
dict.put("children", tuple.getT2());
return dict;
});
});
}
}

View File

@ -41,7 +41,7 @@ public class CompanyResource {
"property",
"remark",
"status",
"sort_no",
"sortNo",
"createdBy",
"createdDate",
"updatedBy",

View File

@ -32,8 +32,8 @@ public class DictResource {
"property",
"remark",
"status",
"parent_number",
"sort_no",
"parentNumber",
"sortNo",
"createdBy",
"createdDate",
"updatedBy",
@ -82,16 +82,18 @@ public class DictResource {
}
@GetMapping("/dicts/tree")
public Mono<ResponseEntity<Flux<Dict>>> getDictTree(
public Mono<ResponseEntity<Flux<Map<String, Object>>>> getDictsTree(
@RequestParam Map<String, Object> params,
@org.springdoc.core.annotations.ParameterObject Pageable pageable
) {
String table = "jhi_dict";
if (!onlyContainsAllowedProperties(pageable)) {
return Mono.just(ResponseEntity.badRequest().build());
}
return dictService
.countRootDicts(params)
.map(total -> ResponseEntity.ok().header("X-Total-Count", String.valueOf(total)).body(dictService.getDictTree(params, pageable))
.countDicts(table, params)
.map(total ->
ResponseEntity.ok().header("X-Total-Count", String.valueOf(total)).body(dictService.getDictsTree(table, params, pageable))
);
}
}

View File

@ -39,8 +39,8 @@ public class StockResource {
"property",
"remark",
"status",
"parent_number",
"sort_no",
"parentNumber",
"sortNo",
"createdBy",
"createdDate",
"updatedBy",

View File

@ -41,18 +41,18 @@ export default defineComponent({
const [parentDictsRes, statusDictsRes] = await Promise.all([
axios.get('api/dicts', {
params: {
number: { name: 'number' },
name: { name: 'name' },
parent_number: { op: 'IFNULL', value: '' },
number: { name: '' },
name: { name: '' },
parentNumber: { op: 'IFNULL', value: '' },
status: { op: '=', value: '1' },
},
}),
axios.get('api/dicts', {
params: {
number: { name: 'number' },
name: { name: 'name' },
property: { name: 'property' },
parent_number: { op: '=', value: 'StatusType' },
number: { name: '' },
name: { name: '' },
property: { name: '' },
parentNumber: { op: '=', value: 'StatusType' },
status: { op: '=', value: '1' },
},
}),

View File

@ -26,12 +26,23 @@ export default defineComponent({
const statusDicts = ref([]);
const showFilter = ref(false);
const filterParams = ref({
number: null,
name: null,
property: null,
status: null,
number: { op: '=', value: null },
name: { op: '=', value: null },
property: { op: '=', value: null },
status: { op: '=', value: null },
});
const operatorSelect = [
{ value: '=', text: t$('entity.operator.equal') },
{ value: '>', text: t$('entity.operator.greaterThan') },
{ value: '<', text: t$('entity.operator.lessThan') },
{ value: '>=', text: t$('entity.operator.greaterOrEqual') },
{ value: '<=', text: t$('entity.operator.lessOrEqual') },
{ value: '!=', text: t$('entity.operator.notEqual') },
{ value: 'like', text: t$('entity.operator.like') },
{ value: 'not like', text: t$('entity.operator.notLike') },
];
// 父级字典选项
const numberOptions = ref<SelectOption[]>([]);
const isLoadingNumberOptions = ref(false);
@ -94,9 +105,9 @@ export default defineComponent({
isLoadingNumberOptions.value = true;
try {
const params = {
number: { name: 'number' },
name: { name: 'name' },
parent_number: { op: 'IFNULL', value: '' },
number: { name: '' },
name: { name: '' },
parentNumber: { op: 'IFNULL', value: '' },
status: { op: '=', value: '1' },
};
//// 如果有查询条件,添加到请求参数中
@ -126,7 +137,7 @@ export default defineComponent({
handleSyncList();
};
const propOrder = ref('sort_no');
const propOrder = ref('sortNo');
const reverse = ref(false);
const sort = () => {
@ -151,30 +162,40 @@ export default defineComponent({
const handleSyncList = async () => {
isFetching.value = true;
try {
const processedParams = showFilter.value
? Object.entries(filterParams.value).reduce((acc, [key, value]) => {
acc[key] = value === '' ? null : value;
return acc;
}, {})
: {};
let params = Object.entries(dict.value).reduce((acc, [key]) => {
acc[`${key}[name]`] = '';
if (key === 'parentNumber') {
acc[`${key}[op]`] = 'IFNULL';
acc[`${key}[value]`] = '';
}
return acc;
}, {});
if (showFilter.value) {
params = Object.entries(filterParams.value).reduce((acc, [key, filterParam]) => {
if (filterParam.value !== null && filterParam.value !== '') {
acc[`${key}[op]`] = filterParam.op;
acc[`${key}[value]`] = filterParam.value;
}
return acc;
}, params);
}
console.log('params', params);
// 获取分页数据
const paginationQuery = {
page: page.value - 1,
size: itemsPerPage.value,
sort: sort(),
};
// 获取状态字典列表
const [dictsRes, statusDictsRes] = await Promise.all([
axios.get(`api/dicts/tree?${buildPaginationQuery(paginationQuery)}`, {
params: processedParams,
params: params,
}),
axios.get('api/dicts', {
params: {
number: { name: 'number' },
name: { name: 'name' },
parent_number: { op: '=', value: 'StatusType' },
number: { name: '' },
name: { name: '' },
parentNumber: { op: '=', value: 'StatusType' },
status: { op: '=', value: '1' },
},
}),
@ -246,6 +267,7 @@ export default defineComponent({
propOrder,
reverse,
changeOrder,
operatorSelect,
handlePageSizeChange,
};
},

View File

@ -31,32 +31,38 @@
<vue-select
class="form-control"
id="filter-number"
v-model="filterParams.number"
v-model="filterParams.number.value"
:options="numberOptions"
:is-loading="isLoadingNumberOptions"
:placeholder="$t('entity.action.select')"
@search="loadNumberOptions"
@select="option => (filterParams.number = option.value)"
@select="option => (filterParams.number.value = option.value)"
/>
</div>
<div class="form-group">
<label class="form-control-label" for="filter-name">{{ $t('jewpmsApp.dict.name') }}</label>
<input type="text" class="form-control" id="filter-name" v-model="filterParams.name" />
<b-input-group class="form-control-group">
<b-form-select id="filter-op" v-model="filterParams.name.op" :options="operatorSelect"></b-form-select>
<b-form-input id="filter-name" v-model="filterParams.name.value"></b-form-input>
</b-input-group>
</div>
<div class="form-group">
<label class="form-control-label" for="filter-property">{{ $t('jewpmsApp.dict.property') }}</label>
<input type="text" class="form-control" id="filter-property" v-model="filterParams.property" />
<b-input-group class="form-control-group">
<b-form-select id="filter-op" v-model="filterParams.property.op" :options="operatorSelect"></b-form-select>
<b-form-input id="filter-property" v-model="filterParams.property.value"></b-form-input>
</b-input-group>
</div>
<div class="form-group">
<label class="form-control-label">{{ $t('jewpmsApp.dict.status') }}</label>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="status-active" :value="1" v-model="filterParams.status" />
<label class="form-check-label" for="status-active">生效</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="status-inactive" :value="0" v-model="filterParams.status" />
<label class="form-check-label" for="status-inactive">失效</label>
</div>
<b-input-group class="form-control-group">
<b-form-select id="filter-op" v-model="filterParams.status.op" :options="operatorSelect"></b-form-select>
<b-form-select id="filter-status" v-model="filterParams.status.value">
<option :value="null">{{ $t('entity.action.select') }}</option>
<option :value="1">生效</option>
<option :value="0">失效</option>
</b-form-select>
</b-input-group>
</div>
</div>
</div>

View File

@ -70,23 +70,27 @@ export default defineComponent({
const handleSyncList = async () => {
isFetching.value = true;
try {
const processedParams = showFilter.value
? Object.entries(filterParams.value).reduce((acc, [key, filterParam]) => {
if (filterParam.value !== null && filterParam.value !== '') {
acc[`${key}[op]`] = filterParam.op;
acc[`${key}[value]`] = filterParam.value;
}
return acc;
}, {})
: {};
let params = Object.entries(stock.value).reduce((acc, [key]) => {
acc[`${key}[name]`] = '';
return acc;
}, {});
if (showFilter.value) {
params = Object.entries(filterParams.value).reduce((acc, [key, filterParam]) => {
if (filterParam.value !== null && filterParam.value !== '') {
acc[`${key}[op]`] = filterParam.op;
acc[`${key}[value]`] = filterParam.value;
}
return acc;
}, params);
}
const stocksRes = await axios.get(
`api/stocks?${buildPaginationQuery({
page: page.value - 1,
size: itemsPerPage.value,
sort: sort(),
...processedParams,
})}`,
{ params: params },
);
stocks.value = stocksRes.data;
totalItems.value = stocksRes.headers['x-total-count'] || stocksRes.data.length;