diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentController.java deleted file mode 100644 index d6e89ac..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentController.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.yhy.module.core.controller.admin.quota; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import com.yhy.module.core.controller.admin.quota.vo.*; -import com.yhy.module.core.convert.quota.QuotaAdjustmentConvert; -import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDO; -import com.yhy.module.core.service.quota.QuotaAdjustmentService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.math.BigDecimal; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 定额调整设置") -@RestController -@RequestMapping("/core/quota/adjustment") -@Validated -public class QuotaAdjustmentController { - - @Resource - private QuotaAdjustmentService quotaAdjustmentService; - - @PostMapping("/create") - @Operation(summary = "创建定额调整设置") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:create')") - public CommonResult createQuotaAdjustment(@Valid @RequestBody QuotaAdjustmentSaveReqVO createReqVO) { - return success(quotaAdjustmentService.createQuotaAdjustment(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新定额调整设置") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:update')") - public CommonResult updateQuotaAdjustment(@Valid @RequestBody QuotaAdjustmentSaveReqVO updateReqVO) { - quotaAdjustmentService.updateQuotaAdjustment(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除定额调整设置") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:delete')") - public CommonResult deleteQuotaAdjustment(@RequestParam("id") Long id) { - quotaAdjustmentService.deleteQuotaAdjustment(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得定额调整设置") - @Parameter(name = "id", description = "编号", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult getQuotaAdjustment(@RequestParam("id") Long id) { - QuotaAdjustmentDO adjustment = quotaAdjustmentService.getQuotaAdjustment(id); - return success(QuotaAdjustmentConvert.INSTANCE.convert(adjustment)); - } - - @GetMapping("/list") - @Operation(summary = "获得定额调整设置列表") - @Parameter(name = "quotaItemId", description = "定额子目ID", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult> getQuotaAdjustmentList(@RequestParam("quotaItemId") Long quotaItemId) { - List list = quotaAdjustmentService.getQuotaAdjustmentList(quotaItemId); - return success(QuotaAdjustmentConvert.INSTANCE.convertList(list)); - } - - @PostMapping("/swap-sort") - @Operation(summary = "交换排序") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:update')") - public CommonResult swapSort(@Valid @RequestBody QuotaAdjustmentSwapSortReqVO reqVO) { - quotaAdjustmentService.swapSort(reqVO.getId1(), reqVO.getId2()); - return success(true); - } - - @PostMapping("/save-material-items") - @Operation(summary = "保存材料调整明细") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:update')") - public CommonResult saveMaterialItems(@Valid @RequestBody QuotaAdjustmentMaterialItemsReqVO reqVO) { - List> items = new java.util.ArrayList<>(); - for (QuotaAdjustmentMaterialItemVO item : reqVO.getItems()) { - Map itemMap = new java.util.HashMap<>(); - itemMap.put("sortOrder", item.getSortOrder()); - itemMap.put("resourceCode", item.getResourceCode()); - itemMap.put("resourceName", item.getResourceName()); - itemMap.put("adjustValue", item.getAdjustValue()); - itemMap.put("remark", item.getRemark()); - items.add(itemMap); - } - quotaAdjustmentService.saveMaterialItems(reqVO.getAdjustmentId(), items); - return success(true); - } - - @GetMapping("/get-material-items") - @Operation(summary = "获取材料调整明细") - @Parameter(name = "adjustmentId", description = "调整设置ID", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult>> getMaterialItems(@RequestParam("adjustmentId") Long adjustmentId) { - return success(quotaAdjustmentService.getMaterialItems(adjustmentId)); - } - - @GetMapping("/get-available-categories") - @Operation(summary = "获取可配置的类别列表") - @Parameter(name = "quotaItemId", description = "定额子目ID", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult>> getAvailableCategories(@RequestParam("quotaItemId") Long quotaItemId) { - return success(quotaAdjustmentService.getAvailableCategories(quotaItemId)); - } - - @PostMapping("/save-dynamic-config") - @Operation(summary = "保存动态调整配置") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:update')") - public CommonResult saveDynamicConfig(@Valid @RequestBody QuotaAdjustmentDynamicConfigReqVO reqVO) { - List> categories = new java.util.ArrayList<>(); - for (QuotaAdjustmentDynamicCategoryVO category : reqVO.getCategories()) { - Map categoryMap = new java.util.HashMap<>(); - categoryMap.put("categoryId", category.getCategoryId()); - categoryMap.put("categoryName", category.getCategoryName()); - categoryMap.put("categoryType", category.getCategoryType()); - categoryMap.put("coefficient", category.getCoefficient()); - categoryMap.put("minValue", category.getMinValue()); - categoryMap.put("maxValue", category.getMaxValue()); - categoryMap.put("denominator", category.getDenominator()); - categoryMap.put("baseValue", category.getBaseValue()); - categoryMap.put("roundingRule", category.getRoundingRule()); - categories.add(categoryMap); - } - quotaAdjustmentService.saveDynamicConfig(reqVO.getAdjustmentId(), categories); - return success(true); - } - - @GetMapping("/get-dynamic-config") - @Operation(summary = "获取动态调整配置") - @Parameter(name = "adjustmentId", description = "调整设置ID", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult>> getDynamicConfig(@RequestParam("adjustmentId") Long adjustmentId) { - return success(quotaAdjustmentService.getDynamicConfig(adjustmentId)); - } - - @PostMapping("/calculate-dynamic") - @Operation(summary = "计算动态调整结果") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult> calculateDynamic(@Valid @RequestBody QuotaAdjustmentDynamicCalculateReqVO reqVO) { - return success(quotaAdjustmentService.calculateDynamic(reqVO.getAdjustmentId(), reqVO.getInputValues())); - } - - @PostMapping("/save-coefficient-config") - @Operation(summary = "保存系数调整配置") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:update')") - public CommonResult saveCoefficientConfig(@Valid @RequestBody QuotaAdjustmentCoefficientConfigReqVO reqVO) { - List> categories = new java.util.ArrayList<>(); - for (QuotaAdjustmentCoefficientCategoryVO category : reqVO.getCategories()) { - Map categoryMap = new java.util.HashMap<>(); - categoryMap.put("categoryId", category.getCategoryId()); - categoryMap.put("categoryName", category.getCategoryName()); - categoryMap.put("categoryType", category.getCategoryType()); - categoryMap.put("coefficient", category.getCoefficient()); - categories.add(categoryMap); - } - quotaAdjustmentService.saveCoefficientConfig(reqVO.getAdjustmentId(), categories); - return success(true); - } - - @GetMapping("/get-coefficient-config") - @Operation(summary = "获取系数调整配置") - @Parameter(name = "adjustmentId", description = "调整设置ID", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult>> getCoefficientConfig(@RequestParam("adjustmentId") Long adjustmentId) { - return success(quotaAdjustmentService.getCoefficientConfig(adjustmentId)); - } - - @GetMapping("/get-available-resources") - @Operation(summary = "获取可选的工料机列表") - @Parameter(name = "quotaItemId", description = "定额子目ID", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult>> getAvailableResources(@RequestParam("quotaItemId") Long quotaItemId) { - return success(quotaAdjustmentService.getAvailableResources(quotaItemId)); - } - - @PostMapping("/save-merge-config") - @Operation(summary = "保存动态合并配置") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:update')") - public CommonResult saveMergeConfig(@Valid @RequestBody QuotaAdjustmentMergeConfigReqVO reqVO) { - List> resources = new java.util.ArrayList<>(); - for (QuotaAdjustmentMergeResourceVO resource : reqVO.getResources()) { - Map resourceMap = new java.util.HashMap<>(); - resourceMap.put("resourceId", resource.getResourceId()); - resourceMap.put("resourceCode", resource.getResourceCode()); - resourceMap.put("resourceName", resource.getResourceName()); - resourceMap.put("coefficient", resource.getCoefficient()); - resourceMap.put("minValue", resource.getMinValue()); - resourceMap.put("maxValue", resource.getMaxValue()); - resourceMap.put("denominator", resource.getDenominator()); - resourceMap.put("baseValue", resource.getBaseValue()); - resourceMap.put("roundingRule", resource.getRoundingRule()); - resources.add(resourceMap); - } - quotaAdjustmentService.saveMergeConfig(reqVO.getAdjustmentId(), resources); - return success(true); - } - - @GetMapping("/get-merge-config") - @Operation(summary = "获取动态合并配置") - @Parameter(name = "adjustmentId", description = "调整设置ID", required = true, example = "1") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult>> getMergeConfig(@RequestParam("adjustmentId") Long adjustmentId) { - return success(quotaAdjustmentService.getMergeConfig(adjustmentId)); - } - - @PostMapping("/calculate-merge") - @Operation(summary = "计算动态合并结果") - @PreAuthorize("@ss.hasPermission('core:quota:adjustment:query')") - public CommonResult> calculateMerge(@Valid @RequestBody QuotaAdjustmentMergeCalculateReqVO reqVO) { - return success(quotaAdjustmentService.calculateMerge(reqVO.getAdjustmentId(), reqVO.getInputValues())); - } -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentDetailController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentDetailController.java new file mode 100644 index 0000000..fd777e9 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentDetailController.java @@ -0,0 +1,108 @@ +package com.yhy.module.core.controller.admin.quota; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentCombinedRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentDetailRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentDetailSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentDetailSwapSortReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSettingRespVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDetailDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentSettingDO; +import com.yhy.module.core.service.quota.QuotaAdjustmentDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import javax.annotation.Resource; +import javax.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "管理后台 - 定额调整明细") +@RestController +@RequestMapping("/core/quota/adjustment-detail") +@Validated +public class QuotaAdjustmentDetailController { + + @Resource + private QuotaAdjustmentDetailService quotaAdjustmentDetailService; + + @PostMapping("/create") + @Operation(summary = "创建定额调整明细") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:create')") + public CommonResult createQuotaAdjustmentDetail(@Valid @RequestBody QuotaAdjustmentDetailSaveReqVO createReqVO) { + return success(quotaAdjustmentDetailService.createQuotaAdjustmentDetail(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新定额调整明细") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:update')") + public CommonResult updateQuotaAdjustmentDetail(@Valid @RequestBody QuotaAdjustmentDetailSaveReqVO updateReqVO) { + quotaAdjustmentDetailService.updateQuotaAdjustmentDetail(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除定额调整明细") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:delete')") + public CommonResult deleteQuotaAdjustmentDetail(@RequestParam("id") Long id) { + quotaAdjustmentDetailService.deleteQuotaAdjustmentDetail(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得定额调整明细") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:query')") + public CommonResult getQuotaAdjustmentDetail(@RequestParam("id") Long id) { + QuotaAdjustmentDetailDO detail = quotaAdjustmentDetailService.getQuotaAdjustmentDetail(id); + return success(BeanUtils.toBean(detail, QuotaAdjustmentDetailRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得定额调整明细列表") + @Parameter(name = "quotaItemId", description = "定额子目ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:query')") + public CommonResult> getQuotaAdjustmentDetailList(@RequestParam("quotaItemId") Long quotaItemId) { + List list = quotaAdjustmentDetailService.getQuotaAdjustmentDetailListByQuotaItem(quotaItemId); + return success(BeanUtils.toBean(list, QuotaAdjustmentDetailRespVO.class)); + } + + @PostMapping("/swap-sort") + @Operation(summary = "交换排序") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:update')") + public CommonResult swapSort(@Valid @RequestBody QuotaAdjustmentDetailSwapSortReqVO reqVO) { + quotaAdjustmentDetailService.swapSort(reqVO.getId1(), reqVO.getId2()); + return success(true); + } + + @GetMapping("/available-settings") + @Operation(summary = "获取可引用的调整设置列表") + @Parameter(name = "adjustmentSettingId", description = "当前调整设置ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:query')") + public CommonResult> getAvailableSettings(@RequestParam("adjustmentSettingId") Long adjustmentSettingId) { + List list = quotaAdjustmentDetailService.getAvailableSettings(adjustmentSettingId); + return success(BeanUtils.toBean(list, QuotaAdjustmentSettingRespVO.class)); + } + + @GetMapping("/combined-list") + @Operation(summary = "获取调整设置与明细的组合列表") + @Parameter(name = "quotaItemId", description = "定额子目ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-detail:query')") + public CommonResult> getCombinedList(@RequestParam("quotaItemId") Long quotaItemId) { + return success(quotaAdjustmentDetailService.getCombinedList(quotaItemId)); + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentSettingController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentSettingController.java new file mode 100644 index 0000000..d129e42 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaAdjustmentSettingController.java @@ -0,0 +1,88 @@ +package com.yhy.module.core.controller.admin.quota; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSettingRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSettingSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSettingSwapSortReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentSettingDO; +import com.yhy.module.core.service.quota.QuotaAdjustmentSettingService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import javax.annotation.Resource; +import javax.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "管理后台 - 定额调整设置") +@RestController +@RequestMapping("/core/quota/adjustment-setting") +@Validated +public class QuotaAdjustmentSettingController { + + @Resource + private QuotaAdjustmentSettingService quotaAdjustmentSettingService; + + @PostMapping("/create") + @Operation(summary = "创建定额调整设置") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-setting:create')") + public CommonResult createQuotaAdjustmentSetting(@Valid @RequestBody QuotaAdjustmentSettingSaveReqVO createReqVO) { + return success(quotaAdjustmentSettingService.createQuotaAdjustmentSetting(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新定额调整设置") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-setting:update')") + public CommonResult updateQuotaAdjustmentSetting(@Valid @RequestBody QuotaAdjustmentSettingSaveReqVO updateReqVO) { + quotaAdjustmentSettingService.updateQuotaAdjustmentSetting(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除定额调整设置") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-setting:delete')") + public CommonResult deleteQuotaAdjustmentSetting(@RequestParam("id") Long id) { + quotaAdjustmentSettingService.deleteQuotaAdjustmentSetting(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得定额调整设置") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-setting:query')") + public CommonResult getQuotaAdjustmentSetting(@RequestParam("id") Long id) { + QuotaAdjustmentSettingDO setting = quotaAdjustmentSettingService.getQuotaAdjustmentSetting(id); + return success(BeanUtils.toBean(setting, QuotaAdjustmentSettingRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得定额调整设置列表") + @Parameter(name = "quotaItemId", description = "定额子目ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-setting:query')") + public CommonResult> getQuotaAdjustmentSettingList(@RequestParam("quotaItemId") Long quotaItemId) { + List list = quotaAdjustmentSettingService.getQuotaAdjustmentSettingList(quotaItemId); + return success(BeanUtils.toBean(list, QuotaAdjustmentSettingRespVO.class)); + } + + @PostMapping("/swap-sort") + @Operation(summary = "交换排序") + @PreAuthorize("@ss.hasPermission('core:quota:adjustment-setting:update')") + public CommonResult swapSort(@Valid @RequestBody QuotaAdjustmentSettingSwapSortReqVO reqVO) { + quotaAdjustmentSettingService.swapSort(reqVO.getId1(), reqVO.getId2()); + return success(true); + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaCatalogItemController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaCatalogItemController.java index 445cf26..35ba486 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaCatalogItemController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaCatalogItemController.java @@ -1,20 +1,33 @@ package com.yhy.module.core.controller.admin.quota; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import com.yhy.module.core.controller.admin.quota.vo.*; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemBindSpecialtyReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryFullRespVO; import com.yhy.module.core.service.quota.QuotaCatalogItemService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import javax.annotation.Resource; import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.StringUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * 定额目录条目 Controller @@ -55,26 +68,36 @@ public class QuotaCatalogItemController { } @GetMapping("/get") - @Operation(summary = "获得定额目录条目") + @Operation(summary = "获得定额专业节点") @Parameter(name = "id", description = "编号", required = true, example = "1") @PreAuthorize("@ss.hasPermission('core:quota:query')") public CommonResult getQuotaCatalogItem(@RequestParam("id") Long id) { - return success(quotaCatalogItemService.getQuotaCatalogItemList(id).stream().findFirst().orElse(null)); + return success(convertToVO(quotaCatalogItemService.getQuotaCatalogItem(id))); } @GetMapping("/list") - @Operation(summary = "获得定额目录条目列表") - @Parameter(name = "parentId", description = "父节点ID", example = "1") + @Operation(summary = "获得定额专业节点列表(第一层)") @PreAuthorize("@ss.hasPermission('core:quota:query')") - public CommonResult> getQuotaCatalogItemList(@RequestParam(value = "parentId", required = false) Long parentId) { - return success(quotaCatalogItemService.getQuotaCatalogItemList(parentId)); + public CommonResult> getQuotaCatalogItemList() { + return success(quotaCatalogItemService.getQuotaCatalogItemList()); } @GetMapping("/tree") - @Operation(summary = "获得定额目录树") + @Operation(summary = "获得定额专业树结构(第一层)") @PreAuthorize("@ss.hasPermission('core:quota:query')") - public CommonResult> getQuotaCatalogItemTree() { - return success(quotaCatalogItemService.getQuotaCatalogItemTree()); + public CommonResult> getQuotaCatalogItemTree( + @RequestParam(value = "exclude", required = false) + @Parameter(description = "Exclude node types (comma-separated or repeated)", example = "specialty,rate_mode") + List exclude) { + return success(quotaCatalogItemService.getQuotaCatalogItemTree(normalizeExcludeTypes(exclude))); + } + + @PostMapping("/swap-sort") + @Operation(summary = "交换两个节点的排序") + @PreAuthorize("@ss.hasPermission('core:quota:update')") + public CommonResult swapSort(@Valid @RequestBody com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemSwapSortReqVO reqVO) { + quotaCatalogItemService.swapSort(reqVO.getNodeId1(), reqVO.getNodeId2()); + return success(true); } @PostMapping("/bind-specialty") @@ -85,17 +108,46 @@ public class QuotaCatalogItemController { return success(true); } - @PostMapping("/add-directory") - @Operation(summary = "添加目录节点") - @PreAuthorize("@ss.hasPermission('core:quota:create')") - public CommonResult addDirectory(@Valid @RequestBody QuotaCatalogItemSaveReqVO createReqVO) { - return success(quotaCatalogItemService.addDirectory(createReqVO)); + @GetMapping("/{catalogItemId}/categories") + @Operation(summary = "通过费率模式节点获取工料机类别字典(含价格代码)") + @Parameter(name = "catalogItemId", description = "定额目录节点ID(费率模式节点或定额专业节点)", required = true, example = "2005272359865745410") + @PreAuthorize("@ss.hasPermission('core:quota:query')") + public CommonResult> getCategoriesByCatalogItem(@PathVariable("catalogItemId") Long catalogItemId) { + return success(quotaCatalogItemService.getCategoriesByCatalogItem(catalogItemId)); } - @PostMapping("/add-content") - @Operation(summary = "添加内容节点") - @PreAuthorize("@ss.hasPermission('core:quota:create')") - public CommonResult addContent(@Valid @RequestBody QuotaCatalogItemSaveReqVO createReqVO) { - return success(quotaCatalogItemService.addContent(createReqVO)); + // ========== 私有方法 ========== + + private QuotaCatalogItemRespVO convertToVO(com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO catalogItem) { + if (catalogItem == null) { + return null; + } + QuotaCatalogItemRespVO vo = new QuotaCatalogItemRespVO(); + vo.setId(catalogItem.getId()); + vo.setParentId(catalogItem.getParentId()); + vo.setCategoryTreeId(catalogItem.getCategoryTreeId()); + vo.setCode(catalogItem.getCode()); + vo.setName(catalogItem.getName()); + vo.setUnit(catalogItem.getUnit()); + vo.setPath(catalogItem.getPath()); + vo.setSortOrder(catalogItem.getSortOrder()); + vo.setAttributes(catalogItem.getAttributes()); + vo.setCreateTime(catalogItem.getCreateTime()); + vo.setUpdateTime(catalogItem.getUpdateTime()); + vo.setNodeType(catalogItem.getNodeType()); + vo.setSpecialtyLocked(catalogItem.isSpecialtyLocked()); + return vo; + } + + private List normalizeExcludeTypes(List exclude) { + if (exclude == null || exclude.isEmpty()) { + return null; + } + return exclude.stream() + .flatMap(value -> Arrays.stream(value.split(","))) + .map(String::trim) + .filter(StringUtils::hasText) + .distinct() + .collect(Collectors.toList()); } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaCatalogTreeController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaCatalogTreeController.java new file mode 100644 index 0000000..2e84954 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaCatalogTreeController.java @@ -0,0 +1,113 @@ +package com.yhy.module.core.controller.admin.quota; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogTreeRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogTreeSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogTreeSwapSortReqVO; +import com.yhy.module.core.service.quota.QuotaCatalogTreeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import javax.annotation.Resource; +import javax.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "管理后台 - 定额子目树") +@RestController +@RequestMapping("/core/quota/catalog-tree") +@Validated +public class QuotaCatalogTreeController { + + @Resource + private QuotaCatalogTreeService quotaCatalogTreeService; + + @PostMapping("/create") + @Operation(summary = "创建定额子目树节点") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:create')") + public CommonResult createCatalogTree(@Valid @RequestBody QuotaCatalogTreeSaveReqVO createReqVO) { + return success(quotaCatalogTreeService.createCatalogTree(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新定额子目树节点") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:update')") + public CommonResult updateCatalogTree(@Valid @RequestBody QuotaCatalogTreeSaveReqVO updateReqVO) { + quotaCatalogTreeService.updateCatalogTree(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除定额子目树节点") + @Parameter(name = "id", description = "节点ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:delete')") + public CommonResult deleteCatalogTree(@RequestParam("id") Long id) { + quotaCatalogTreeService.deleteCatalogTree(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获取定额子目树节点详情") + @Parameter(name = "id", description = "节点ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:query')") + public CommonResult getCatalogTree(@RequestParam("id") Long id) { + return success(quotaCatalogTreeService.getCatalogTree(id)); + } + + @GetMapping("/list") + @Operation(summary = "获取定额子目树节点列表") + @Parameter(name = "catalogItemId", description = "定额专业节点ID", required = true, example = "1") + @Parameter(name = "parentId", description = "父节点ID", example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:query')") + public CommonResult> getCatalogTreeList( + @RequestParam("catalogItemId") Long catalogItemId, + @RequestParam(value = "parentId", required = false) Long parentId) { + return success(quotaCatalogTreeService.getCatalogTreeList(catalogItemId, parentId)); + } + + @GetMapping("/tree") + @Operation(summary = "获取定额子目树结构") + @Parameter(name = "catalogItemId", description = "定额专业节点ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:query')") + public CommonResult> getCatalogTreeTree( + @RequestParam("catalogItemId") Long catalogItemId) { + return success(quotaCatalogTreeService.getCatalogTreeTree(catalogItemId)); + } + + @PostMapping("/swap-sort") + @Operation(summary = "交换排序") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:update')") + public CommonResult swapSort(@Valid @RequestBody QuotaCatalogTreeSwapSortReqVO swapReqVO) { + quotaCatalogTreeService.swapSort(swapReqVO.getNodeId1(), swapReqVO.getNodeId2()); + return success(true); + } + + @GetMapping("/list-by-rate-item") + @Operation(summary = "根据费率项ID查询绑定的定额子目树") + @Parameter(name = "rateItemId", description = "费率项ID", required = true, example = "3011") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:query')") + public CommonResult> getCatalogTreeByRateItem( + @RequestParam("rateItemId") Long rateItemId) { + return success(quotaCatalogTreeService.getCatalogTreeByRateItem(rateItemId)); + } + + @GetMapping("/list-by-rate-mode-node") + @Operation(summary = "根据费率模式节点ID查询所属定额专业的子目录树") + @Parameter(name = "rateModeNodeId", description = "费率模式节点ID", required = true, example = "1002") + @PreAuthorize("@ss.hasPermission('core:quota:catalog-tree:query')") + public CommonResult> getCatalogTreeByRateModeNode( + @RequestParam("rateModeNodeId") Long rateModeNodeId) { + return success(quotaCatalogTreeService.getCatalogTreeByRateModeNode(rateModeNodeId)); + } +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaFeeItemController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaFeeItemController.java index da66c52..feff095 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaFeeItemController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaFeeItemController.java @@ -1,24 +1,31 @@ package com.yhy.module.core.controller.admin.quota; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemSaveReqVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemSwapSortReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemWithRateRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaFeeItemDO; import com.yhy.module.core.service.quota.QuotaFeeItemService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - +import java.util.List; import javax.annotation.Resource; import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; @Tag(name = "管理后台 - 定额取费项") @RestController @@ -71,6 +78,15 @@ public class QuotaFeeItemController { return success(BeanUtils.toBean(list, QuotaFeeItemRespVO.class)); } + @GetMapping("/list-with-rate") + @Operation(summary = "获取取费项与费率项合并列表") + @Parameter(name = "catalogItemId", description = "模式节点ID", required = true, example = "1002") + @PreAuthorize("@ss.hasPermission('core:quota:fee:query')") + public CommonResult> getFeeItemWithRateList(@RequestParam("catalogItemId") Long catalogItemId) { + List list = quotaFeeItemService.getFeeItemWithRateList(catalogItemId); + return success(list); + } + @PostMapping("/swap-sort") @Operation(summary = "交换排序") @PreAuthorize("@ss.hasPermission('core:quota:fee:update')") diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaItemController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaItemController.java index 76d2a70..0c92f05 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaItemController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaItemController.java @@ -1,5 +1,7 @@ package com.yhy.module.core.controller.admin.quota; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; import com.yhy.module.core.controller.admin.quota.vo.QuotaItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaItemSaveReqVO; @@ -7,15 +9,20 @@ import com.yhy.module.core.service.quota.QuotaItemService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - +import java.util.Collections; +import java.util.List; import javax.annotation.Resource; import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * 定额子目 Controller @@ -65,9 +72,12 @@ public class QuotaItemController { @GetMapping("/list") @Operation(summary = "获得定额子目列表") - @Parameter(name = "catalogItemId", description = "定额条目ID", required = true, example = "1") + @Parameter(name = "catalogItemId", description = "定额条目ID", required = false, example = "1") @PreAuthorize("@ss.hasPermission('core:quota:query')") - public CommonResult> getQuotaItemList(@RequestParam("catalogItemId") Long catalogItemId) { + public CommonResult> getQuotaItemList(@RequestParam(value = "catalogItemId", required = false) Long catalogItemId) { + if (catalogItemId == null) { + return success(Collections.emptyList()); + } return success(quotaItemService.getQuotaItemList(catalogItemId)); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateFieldController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateFieldController.java index 654f2ab..b3815b7 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateFieldController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateFieldController.java @@ -1,5 +1,7 @@ package com.yhy.module.core.controller.admin.quota; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldSaveReqVO; @@ -7,14 +9,17 @@ import com.yhy.module.core.service.quota.QuotaRateFieldService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import javax.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import org.springframework.web.bind.annotation.DeleteMapping; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * 定额费率字段绑定 Controller @@ -43,32 +48,32 @@ public class QuotaRateFieldController { } @GetMapping("/list") - @Operation(summary = "获取费率项的字段绑定列表") - @Parameter(name = "rateItemId", description = "费率项ID", required = true) + @Operation(summary = "获取费率模式节点的字段绑定列表") + @Parameter(name = "catalogItemId", description = "费率模式节点ID", required = true) public CommonResult> getRateFieldList( - @RequestParam("rateItemId") Long rateItemId) { - return success(rateFieldService.getRateFieldList(rateItemId)); + @RequestParam("catalogItemId") Long catalogItemId) { + return success(rateFieldService.getRateFieldList(catalogItemId)); } @PostMapping("/update-binding") @Operation(summary = "更新字段绑定") - @Parameter(name = "rateItemId", description = "费率项ID", required = true) + @Parameter(name = "catalogItemId", description = "费率模式节点ID", required = true) @Parameter(name = "fieldIndex", description = "字段索引", required = true) public CommonResult updateBinding( - @RequestParam("rateItemId") Long rateItemId, + @RequestParam("catalogItemId") Long catalogItemId, @RequestParam("fieldIndex") Integer fieldIndex, @RequestBody Long[] bindingIds) { - rateFieldService.updateBinding(rateItemId, fieldIndex, bindingIds); + rateFieldService.updateBinding(catalogItemId, fieldIndex, bindingIds); return success(true); } @PostMapping("/batch-save") @Operation(summary = "批量保存字段绑定") - @Parameter(name = "rateItemId", description = "费率项ID", required = true) + @Parameter(name = "catalogItemId", description = "费率模式节点ID", required = true) public CommonResult batchSaveRateFields( - @RequestParam("rateItemId") Long rateItemId, + @RequestParam("catalogItemId") Long catalogItemId, @RequestBody List fields) { - rateFieldService.batchSaveRateFields(rateItemId, fields); + rateFieldService.batchSaveRateFields(catalogItemId, fields); return success(true); } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateFieldLabelController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateFieldLabelController.java new file mode 100644 index 0000000..c0db213 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateFieldLabelController.java @@ -0,0 +1,93 @@ +package com.yhy.module.core.controller.admin.quota; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldLabelDO; +import com.yhy.module.core.service.quota.QuotaRateFieldLabelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import javax.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 定额费率字段标签字典 Controller + * + * @author yhy + */ +@Tag(name = "管理后台 - 定额费率字段标签字典") +@RestController +@RequestMapping("/core/quota/rate-field-label") +@RequiredArgsConstructor +@Validated +public class QuotaRateFieldLabelController { + + private final QuotaRateFieldLabelService fieldLabelService; + + @PostMapping("/create") + @Operation(summary = "创建字段标签") + @PreAuthorize("@ss.hasPermission('core:quota:rate:write')") + public CommonResult createFieldLabel(@Valid @RequestBody QuotaRateFieldLabelSaveReqVO createReqVO) { + return success(fieldLabelService.createFieldLabel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新字段标签") + @PreAuthorize("@ss.hasPermission('core:quota:rate:write')") + public CommonResult updateFieldLabel(@Valid @RequestBody QuotaRateFieldLabelSaveReqVO updateReqVO) { + fieldLabelService.updateFieldLabel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除字段标签") + @Parameter(name = "id", description = "标签ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:rate:write')") + public CommonResult deleteFieldLabel(@RequestParam("id") Long id) { + fieldLabelService.deleteFieldLabel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获取字段标签详情") + @Parameter(name = "id", description = "标签ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:rate:read')") + public CommonResult getFieldLabel(@RequestParam("id") Long id) { + QuotaRateFieldLabelDO label = fieldLabelService.getFieldLabel(id); + return success(BeanUtils.toBean(label, QuotaRateFieldLabelRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获取字段标签列表") + @Parameter(name = "catalogItemId", description = "模式节点ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:quota:rate:read')") + public CommonResult> getFieldLabelList(@RequestParam("catalogItemId") Long catalogItemId) { + return success(fieldLabelService.getFieldLabelList(catalogItemId)); + } + + @PostMapping("/swap-sort") + @Operation(summary = "交换排序") + @PreAuthorize("@ss.hasPermission('core:quota:rate:write')") + public CommonResult swapSort( + @RequestParam("labelId1") Long labelId1, + @RequestParam("labelId2") Long labelId2) { + fieldLabelService.swapSort(labelId1, labelId2); + return success(true); + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateItemController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateItemController.java index 43d0750..1b34115 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateItemController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaRateItemController.java @@ -1,23 +1,31 @@ package com.yhy.module.core.controller.admin.quota; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateItemSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateItemSwapSortReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateModeNodeSaveReqVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateValueRulesReqVO; import com.yhy.module.core.service.quota.QuotaRateItemService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; import java.math.BigDecimal; import java.util.List; import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import javax.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * 定额费率项 Controller @@ -31,15 +39,21 @@ public class QuotaRateItemController { private final QuotaRateItemService rateItemService; + @PostMapping("/create-mode-node") + @Operation(summary = "创建费率模式节点") + public CommonResult createRateModeNode(@Valid @RequestBody QuotaRateModeNodeSaveReqVO createReqVO) { + return success(rateItemService.createRateModeNode(createReqVO)); + } + @PostMapping("/create") @Operation(summary = "创建费率项") - public CommonResult createRateItem(@Valid @RequestBody QuotaRateItemSaveReqVO createReqVO) { + public CommonResult createRateItem(@Validated(QuotaRateItemSaveReqVO.CreateGroup.class) @RequestBody QuotaRateItemSaveReqVO createReqVO) { return success(rateItemService.createRateItem(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新费率项") - public CommonResult updateRateItem(@Valid @RequestBody QuotaRateItemSaveReqVO updateReqVO) { + public CommonResult updateRateItem(@Validated(QuotaRateItemSaveReqVO.UpdateGroup.class) @RequestBody QuotaRateItemSaveReqVO updateReqVO) { rateItemService.updateRateItem(updateReqVO); return success(true); } @@ -79,26 +93,30 @@ public class QuotaRateItemController { @PostMapping("/swap-sort") @Operation(summary = "同级交换排序") - @Parameter(name = "nodeId1", description = "节点1 ID", required = true) - @Parameter(name = "nodeId2", description = "节点2 ID", required = true) - public CommonResult swapSort( - @RequestParam("nodeId1") Long nodeId1, - @RequestParam("nodeId2") Long nodeId2) { - rateItemService.swapSort(nodeId1, nodeId2); + public CommonResult swapSort(@Valid @RequestBody QuotaRateItemSwapSortReqVO swapReqVO) { + rateItemService.swapSort(swapReqVO.getNodeId1(), swapReqVO.getNodeId2()); + return success(true); + } + + @PostMapping("/move-node") + @Operation(summary = "移动节点到指定位置", description = "将节点移动到目标节点之前,自动重新计算所有受影响节点的排序值") + public CommonResult moveNode(@Valid @RequestBody QuotaRateItemSwapSortReqVO moveReqVO) { + rateItemService.moveNode(moveReqVO.getNodeId1(), moveReqVO.getNodeId2()); return success(true); } @PostMapping("/config-value-rules") - @Operation(summary = "配置动态取值规则") + @Operation(summary = "配置动态取值规则", description = "阶梯规则(tiers)必填,增量规则(increments)可选") public CommonResult configValueRules(@Valid @RequestBody QuotaRateValueRulesReqVO reqVO) { rateItemService.configValueRules(reqVO); return success(true); } @GetMapping("/calculate-fields") - @Operation(summary = "计算动态字段值") + @Operation(summary = "计算动态字段值(默认使用阶梯规则)", + description = "根据基数值匹配阶梯规则获取字段值,如果配置了增量规则则在基础上叠加") @Parameter(name = "rateItemId", description = "费率项ID", required = true) - @Parameter(name = "baseValue", description = "基数值", required = true) + @Parameter(name = "baseValue", description = "基数值(万元)", required = true) public CommonResult> calculateDynamicFields( @RequestParam("rateItemId") Long rateItemId, @RequestParam("baseValue") BigDecimal baseValue) { diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaResourceController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaResourceController.java index 73affa9..41bf0f8 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaResourceController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/QuotaResourceController.java @@ -1,5 +1,7 @@ package com.yhy.module.core.controller.admin.quota; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceSaveReqVO; @@ -8,15 +10,19 @@ import com.yhy.module.core.service.quota.QuotaResourceService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - +import java.util.List; import javax.annotation.Resource; import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * 定额工料机组成 Controller @@ -73,10 +79,22 @@ public class QuotaResourceController { } @GetMapping("/available-list") - @Operation(summary = "获取可选的工料机列表(已过滤范围)") + @Operation(summary = "获取可选的工料机列表(已过滤范围,支持模糊查询)") @Parameter(name = "quotaItemId", description = "定额子目ID", required = true, example = "1") + @Parameter(name = "code", description = "编码(模糊查询)", required = false, example = "C001") + @Parameter(name = "name", description = "名称(模糊查询)", required = false, example = "水泥") + @Parameter(name = "spec", description = "型号规格(模糊查询)", required = false, example = "P.O 42.5") @PreAuthorize("@ss.hasPermission('core:quota:query')") - public CommonResult> getAvailableResourceItems(@RequestParam("quotaItemId") Long quotaItemId) { - return success(quotaResourceService.getAvailableResourceItems(quotaItemId)); + public CommonResult> getAvailableResourceItems( + @RequestParam("quotaItemId") Long quotaItemId, + @RequestParam(value = "code", required = false) String code, + @RequestParam(value = "name", required = false) String name, + @RequestParam(value = "spec", required = false) String spec) { + // 如果没有任何查询条件,使用原方法(性能更好) + if (code == null && name == null && spec == null) { + return success(quotaResourceService.getAvailableResourceItems(quotaItemId)); + } + // 有查询条件时使用带过滤的方法 + return success(quotaResourceService.getAvailableResourceItemsWithFilter(quotaItemId, code, name, spec)); } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentAvailableCategoryVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentAvailableCategoryVO.java deleted file mode 100644 index e3c1dfa..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentAvailableCategoryVO.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 可配置的类别 VO") -@Data -public class QuotaAdjustmentAvailableCategoryVO { - - @Schema(description = "类别ID", example = "1") - private Long categoryId; - - @Schema(description = "类别名称", example = "人工") - private String categoryName; - - @Schema(description = "类别类型", example = "labor") - private String categoryType; - - @Schema(description = "类别编码", example = "RG") - private String categoryCode; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCoefficientCategoryVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCoefficientCategoryVO.java deleted file mode 100644 index 9062906..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCoefficientCategoryVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; - -@Schema(description = "管理后台 - 系数调整类别配置 VO") -@Data -public class QuotaAdjustmentCoefficientCategoryVO { - - @Schema(description = "类别ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "类别ID不能为空") - private Long categoryId; - - @Schema(description = "类别名称", example = "人工") - private String categoryName; - - @Schema(description = "类别类型", example = "labor") - private String categoryType; - - @Schema(description = "系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.2") - @NotNull(message = "系数不能为空") - private BigDecimal coefficient; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCoefficientConfigReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCoefficientConfigReqVO.java deleted file mode 100644 index 24e9cf0..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCoefficientConfigReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - 保存系数调整配置 Request VO") -@Data -public class QuotaAdjustmentCoefficientConfigReqVO { - - @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "调整设置ID不能为空") - private Long adjustmentId; - - @Schema(description = "类别配置列表", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "类别配置列表不能为空") - @Valid - private List categories; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCombinedRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCombinedRespVO.java new file mode 100644 index 0000000..81a8168 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentCombinedRespVO.java @@ -0,0 +1,67 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import lombok.Data; + +@Schema(description = "管理后台 - 定额调整设置与明细组合 Response VO") +@Data +public class QuotaAdjustmentCombinedRespVO { + + @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "定额子目ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long quotaItemId; + + @Schema(description = "调整名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "人工费调整") + private String name; + + @Schema(description = "定额值", example = "100.00") + private BigDecimal quotaValue; + + @Schema(description = "调整内容", example = "冬季施工增加10%") + private String adjustmentContent; + + @Schema(description = "调整类型(字典)", requiredMode = Schema.RequiredMode.REQUIRED, example = "percentage") + private String adjustmentType; + + @Schema(description = "调整规则(JSON)") + private Map adjustmentRules; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "调整明细列表") + private List details; + + @Schema(description = "调整明细项") + @Data + public static class DetailItem { + + @Schema(description = "明细ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long adjustmentSettingId; + + @Schema(description = "定额调整编号(引用其他调整设置ID)", example = "2") + private Long adjustmentCode; + + @Schema(description = "调整内容", example = "增加10%") + private String adjustment; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailRespVO.java new file mode 100644 index 0000000..d1d2549 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailRespVO.java @@ -0,0 +1,29 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.Data; + +@Schema(description = "管理后台 - 定额调整明细 Response VO") +@Data +public class QuotaAdjustmentDetailRespVO { + + @Schema(description = "主键ID", example = "1") + private Long id; + + @Schema(description = "调整设置ID", example = "1") + private Long adjustmentSettingId; + + @Schema(description = "定额调整编号(引用其他调整设置ID)", example = "2") + private Long adjustmentCode; + + @Schema(description = "调整内容", example = "增加10%") + private String adjustment; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailSaveReqVO.java new file mode 100644 index 0000000..c465c6a --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailSaveReqVO.java @@ -0,0 +1,27 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 定额调整明细创建/更新 Request VO") +@Data +public class QuotaAdjustmentDetailSaveReqVO { + + @Schema(description = "主键ID", example = "1") + private Long id; + + @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "调整设置ID不能为空") + private Long adjustmentSettingId; + + @Schema(description = "定额调整编号(引用其他调整设置ID)", example = "2") + private Long adjustmentCode; + + @Schema(description = "调整内容", example = "增加10%") + private String adjustment; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailSwapSortReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailSwapSortReqVO.java new file mode 100644 index 0000000..5dc68db --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDetailSwapSortReqVO.java @@ -0,0 +1,19 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 定额调整明细交换排序 Request VO") +@Data +public class QuotaAdjustmentDetailSwapSortReqVO { + + @Schema(description = "第一个调整明细ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "第一个调整明细ID不能为空") + private Long id1; + + @Schema(description = "第二个调整明细ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "第二个调整明细ID不能为空") + private Long id2; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicCalculateReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicCalculateReqVO.java deleted file mode 100644 index 528e342..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicCalculateReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; -import java.util.Map; - -@Schema(description = "管理后台 - 计算动态调整结果 Request VO") -@Data -public class QuotaAdjustmentDynamicCalculateReqVO { - - @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "调整设置ID不能为空") - private Long adjustmentId; - - @Schema(description = "输入值映射(类别ID: 输入值)", requiredMode = Schema.RequiredMode.REQUIRED, - example = "{\"1\": 250, \"2\": 150}") - @NotEmpty(message = "输入值映射不能为空") - private Map inputValues; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicCategoryVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicCategoryVO.java deleted file mode 100644 index 6e0fe76..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicCategoryVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; - -@Schema(description = "管理后台 - 动态调整类别配置 VO") -@Data -public class QuotaAdjustmentDynamicCategoryVO { - - @Schema(description = "类别ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "类别ID不能为空") - private Long categoryId; - - @Schema(description = "类别名称", example = "人工") - private String categoryName; - - @Schema(description = "类别类型", example = "labor") - private String categoryType; - - @Schema(description = "系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.2") - @NotNull(message = "系数不能为空") - private BigDecimal coefficient; - - @Schema(description = "最小值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @NotNull(message = "最小值不能为空") - private BigDecimal minValue; - - @Schema(description = "最大值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - @NotNull(message = "最大值不能为空") - private BigDecimal maxValue; - - @Schema(description = "分母值", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - @NotNull(message = "分母值不能为空") - private BigDecimal denominator; - - @Schema(description = "基础值", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") - @NotNull(message = "基础值不能为空") - private BigDecimal baseValue; - - @Schema(description = "取值规则", requiredMode = Schema.RequiredMode.REQUIRED, example = "round_up") - @NotNull(message = "取值规则不能为空") - private String roundingRule; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicConfigReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicConfigReqVO.java deleted file mode 100644 index 7f0324e..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentDynamicConfigReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - 保存动态调整配置 Request VO") -@Data -public class QuotaAdjustmentDynamicConfigReqVO { - - @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "调整设置ID不能为空") - private Long adjustmentId; - - @Schema(description = "类别配置列表", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "类别配置列表不能为空") - @Valid - private List categories; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMaterialItemVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMaterialItemVO.java deleted file mode 100644 index 6f1b9d6..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMaterialItemVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; - -@Schema(description = "管理后台 - 定额调整材料明细 VO") -@Data -public class QuotaAdjustmentMaterialItemVO { - - @Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "序号不能为空") - private Integer sortOrder; - - @Schema(description = "工料机编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "C001") - @NotBlank(message = "工料机编码不能为空") - private String resourceCode; - - @Schema(description = "工料机名称", example = "水泥 P.O 42.5") - private String resourceName; - - @Schema(description = "增减定额消耗量数值", requiredMode = Schema.RequiredMode.REQUIRED, example = "2.5") - @NotNull(message = "增减定额消耗量数值不能为空") - private BigDecimal adjustValue; - - @Schema(description = "备注", example = "增加损耗") - private String remark; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMaterialItemsReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMaterialItemsReqVO.java deleted file mode 100644 index bf944df..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMaterialItemsReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - 保存定额调整材料明细 Request VO") -@Data -public class QuotaAdjustmentMaterialItemsReqVO { - - @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "调整设置ID不能为空") - private Long adjustmentId; - - @Schema(description = "材料明细列表", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "材料明细列表不能为空") - @Valid - private List items; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeCalculateReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeCalculateReqVO.java deleted file mode 100644 index 51ef0c7..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeCalculateReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; -import java.util.Map; - -@Schema(description = "管理后台 - 计算动态合并结果 Request VO") -@Data -public class QuotaAdjustmentMergeCalculateReqVO { - - @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "调整设置ID不能为空") - private Long adjustmentId; - - @Schema(description = "输入值映射(工料机ID: 输入值)", requiredMode = Schema.RequiredMode.REQUIRED, - example = "{\"100\": 250, \"101\": 150}") - @NotEmpty(message = "输入值映射不能为空") - private Map inputValues; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeConfigReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeConfigReqVO.java deleted file mode 100644 index f4a8459..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeConfigReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - 保存动态合并配置 Request VO") -@Data -public class QuotaAdjustmentMergeConfigReqVO { - - @Schema(description = "调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "调整设置ID不能为空") - private Long adjustmentId; - - @Schema(description = "工料机配置列表", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "工料机配置列表不能为空") - @Valid - private List resources; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeResourceVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeResourceVO.java deleted file mode 100644 index f3ed75b..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentMergeResourceVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.math.BigDecimal; - -@Schema(description = "管理后台 - 动态合并工料机配置 VO") -@Data -public class QuotaAdjustmentMergeResourceVO { - - @Schema(description = "工料机ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - @NotNull(message = "工料机ID不能为空") - private Long resourceId; - - @Schema(description = "工料机编码", example = "C001") - private String resourceCode; - - @Schema(description = "工料机名称", example = "水泥 P.O 42.5") - private String resourceName; - - @Schema(description = "系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.2") - @NotNull(message = "系数不能为空") - private BigDecimal coefficient; - - @Schema(description = "最小值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @NotNull(message = "最小值不能为空") - private BigDecimal minValue; - - @Schema(description = "最大值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - @NotNull(message = "最大值不能为空") - private BigDecimal maxValue; - - @Schema(description = "分母值", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - @NotNull(message = "分母值不能为空") - private BigDecimal denominator; - - @Schema(description = "基础值", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") - @NotNull(message = "基础值不能为空") - private BigDecimal baseValue; - - @Schema(description = "取值规则", requiredMode = Schema.RequiredMode.REQUIRED, example = "round_up") - @NotNull(message = "取值规则不能为空") - private String roundingRule; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingRespVO.java similarity index 54% rename from yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentRespVO.java rename to yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingRespVO.java index e4299ea..72ab029 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentRespVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingRespVO.java @@ -1,14 +1,14 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Map; +import lombok.Data; @Schema(description = "管理后台 - 定额调整设置 Response VO") @Data -public class QuotaAdjustmentRespVO { +public class QuotaAdjustmentSettingRespVO { @Schema(description = "主键ID", example = "1") private Long id; @@ -16,19 +16,19 @@ public class QuotaAdjustmentRespVO { @Schema(description = "定额子目ID", example = "1") private Long quotaItemId; - @Schema(description = "调整名称", example = "高温季节调整") + @Schema(description = "调整名称", example = "人工费调整") private String name; - @Schema(description = "调整内容", example = "夏季高温施工增加人工费用") - private String content; + @Schema(description = "定额值", example = "100.00") + private BigDecimal quotaValue; - @Schema(description = "调整类型", example = "percentage") + @Schema(description = "调整内容", example = "冬季施工增加10%") + private String adjustmentContent; + + @Schema(description = "调整类型(字典)", example = "percentage") private String adjustmentType; - @Schema(description = "调整类型名称", example = "百分比调整") - private String adjustmentTypeName; - - @Schema(description = "调整规则", example = "{\"value\": 10, \"operator\": \"increase\"}") + @Schema(description = "调整规则(JSON)") private Map adjustmentRules; @Schema(description = "排序", example = "1") @@ -37,6 +37,4 @@ public class QuotaAdjustmentRespVO { @Schema(description = "创建时间") private LocalDateTime createTime; - @Schema(description = "更新时间") - private LocalDateTime updateTime; } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingSaveReqVO.java similarity index 65% rename from yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSaveReqVO.java rename to yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingSaveReqVO.java index 1a485ab..68ef130 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSaveReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingSaveReqVO.java @@ -1,15 +1,15 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - +import java.math.BigDecimal; +import java.util.Map; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import java.util.Map; +import lombok.Data; @Schema(description = "管理后台 - 定额调整设置创建/更新 Request VO") @Data -public class QuotaAdjustmentSaveReqVO { +public class QuotaAdjustmentSettingSaveReqVO { @Schema(description = "主键ID", example = "1") private Long id; @@ -18,20 +18,24 @@ public class QuotaAdjustmentSaveReqVO { @NotNull(message = "定额子目ID不能为空") private Long quotaItemId; - @Schema(description = "调整名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "高温季节调整") + @Schema(description = "调整名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "人工费调整") @NotBlank(message = "调整名称不能为空") private String name; - @Schema(description = "调整内容", example = "夏季高温施工增加人工费用") - private String content; + @Schema(description = "定额值", example = "100.00") + private BigDecimal quotaValue; - @Schema(description = "调整类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "percentage") + @Schema(description = "调整内容", example = "冬季施工增加10%") + private String adjustmentContent; + + @Schema(description = "调整类型(字典)", requiredMode = Schema.RequiredMode.REQUIRED, example = "percentage") @NotBlank(message = "调整类型不能为空") private String adjustmentType; - @Schema(description = "调整规则", example = "{\"value\": 10, \"operator\": \"increase\"}") + @Schema(description = "调整规则(JSON)") private Map adjustmentRules; @Schema(description = "排序", example = "1") private Integer sortOrder; + } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingSwapSortReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingSwapSortReqVO.java new file mode 100644 index 0000000..b60e626 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSettingSwapSortReqVO.java @@ -0,0 +1,19 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 定额调整设置交换排序 Request VO") +@Data +public class QuotaAdjustmentSettingSwapSortReqVO { + + @Schema(description = "第一个调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "第一个调整设置ID不能为空") + private Long id1; + + @Schema(description = "第二个调整设置ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "第二个调整设置ID不能为空") + private Long id2; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSwapSortReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSwapSortReqVO.java deleted file mode 100644 index fb28d8e..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaAdjustmentSwapSortReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.yhy.module.core.controller.admin.quota.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 定额调整设置交换排序 Request VO") -@Data -public class QuotaAdjustmentSwapSortReqVO { - - @Schema(description = "调整设置ID1", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "调整设置ID1不能为空") - private Long id1; - - @Schema(description = "调整设置ID2", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @NotNull(message = "调整设置ID2不能为空") - private Long id2; -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogItemSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogItemSaveReqVO.java index 2b995a5..b7e4b7a 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogItemSaveReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogItemSaveReqVO.java @@ -37,6 +37,9 @@ public class QuotaCatalogItemSaveReqVO { @Schema(description = "排序", example = "1") private Integer sortOrder; + @Schema(description = "节点类型(region/specialty/rate_mode),不传则尝试从 attributes.node_type 读取", example = "region") + private String nodeType; + @Schema(description = "扩展属性", example = "{\"node_type\":\"specialty\"}") private Map attributes; } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogItemSwapSortReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogItemSwapSortReqVO.java new file mode 100644 index 0000000..5067336 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogItemSwapSortReqVO.java @@ -0,0 +1,23 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotNull; +import lombok.Data; + +/** + * 定额目录条目交换排序 Request VO + * + * @author yhy + */ +@Schema(description = "管理后台 - 定额目录条目交换排序 Request VO") +@Data +public class QuotaCatalogItemSwapSortReqVO { + + @Schema(description = "节点1的ID", required = true, example = "1") + @NotNull(message = "节点1的ID不能为空") + private Long nodeId1; + + @Schema(description = "节点2的ID", required = true, example = "2") + @NotNull(message = "节点2的ID不能为空") + private Long nodeId2; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeRespVO.java new file mode 100644 index 0000000..0fd48ee --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeRespVO.java @@ -0,0 +1,60 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import lombok.Data; + +@Schema(description = "管理后台 - 定额子目树 Response VO") +@Data +public class QuotaCatalogTreeRespVO { + + @Schema(description = "主键", example = "1") + private Long id; + + @Schema(description = "租户ID", example = "1") + private Long tenantId; + + @Schema(description = "关联定额专业节点ID", example = "1") + private Long catalogItemId; + + @Schema(description = "父节点ID", example = "1") + private Long parentId; + + @Schema(description = "编码", example = "01") + private String code; + + @Schema(description = "名称", example = "土石方工程") + private String name; + + @Schema(description = "单位", example = "m²") + private String unit; + + @Schema(description = "内容类型:directory-目录,content-内容", example = "directory") + private String contentType; + + @Schema(description = "是否允许添加子节点", example = "true") + private Boolean allowChildren; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; + + @Schema(description = "树路径") + private String[] path; + + @Schema(description = "层级", example = "1") + private Integer level; + + @Schema(description = "扩展属性") + private Map attributes; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "已被绑定的字段索引列表(用于前端禁用)", example = "[1, 2, 3]") + private List boundFieldIndexes; + + @Schema(description = "子节点列表") + private List children; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeSaveReqVO.java new file mode 100644 index 0000000..b4d28c3 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeSaveReqVO.java @@ -0,0 +1,64 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.Map; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 定额子目树保存 Request VO") +@Data +public class QuotaCatalogTreeSaveReqVO { + + @Schema(description = "主键", example = "1") + private Long id; + + @Schema(description = "关联定额专业节点ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "关联定额专业节点ID不能为空") + private Long catalogItemId; + + @Schema(description = "父节点ID", example = "1") + private Long parentId; + + @Schema(description = "编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "01") + @NotBlank(message = "编码不能为空") + private String code; + + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土石方工程") + @NotBlank(message = "名称不能为空") + private String name; + + @Schema(description = "单位", example = "m²") + private String unit; + + @Schema(description = "内容类型:directory-目录,content-内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "directory") + @NotBlank(message = "内容类型不能为空") + private String contentType; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; + + /** + * 自定义 setter 处理前端传递的无效值 + */ + @com.fasterxml.jackson.annotation.JsonSetter("sortOrder") + public void setSortOrderSafe(Object value) { + if (value == null) { + this.sortOrder = null; + } else if (value instanceof Number) { + // 如果是数字类型,尝试转换为 Integer + long longValue = ((Number) value).longValue(); + // 如果值超出 Integer 范围,设置为 null(让后端自动计算) + if (longValue > Integer.MAX_VALUE || longValue < Integer.MIN_VALUE) { + this.sortOrder = null; + } else { + this.sortOrder = (int) longValue; + } + } else { + this.sortOrder = null; + } + } + + @Schema(description = "扩展属性") + private Map attributes; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeSwapSortReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeSwapSortReqVO.java new file mode 100644 index 0000000..9d0d27d --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaCatalogTreeSwapSortReqVO.java @@ -0,0 +1,18 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 定额子目树交换排序 Request VO") +@Data +public class QuotaCatalogTreeSwapSortReqVO { + + @Schema(description = "节点1 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "节点1 ID不能为空") + private Long nodeId1; + + @Schema(description = "节点2 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "节点2 ID不能为空") + private Long nodeId2; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemRespVO.java index 5eb56f6..149aa98 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemRespVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemRespVO.java @@ -1,10 +1,9 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - import java.time.LocalDateTime; import java.util.Map; +import lombok.Data; @Schema(description = "管理后台 - 定额取费项 Response VO") @Data @@ -19,6 +18,9 @@ public class QuotaFeeItemRespVO { @Schema(description = "模式节点ID", example = "1002") private Long catalogItemId; + @Schema(description = "关联的费率项ID(任意层级节点)", example = "5001") + private Long rateItemId; + @Schema(description = "自定义序号", example = "一") private String customCode; @@ -43,6 +45,12 @@ public class QuotaFeeItemRespVO { @Schema(description = "排序字段", example = "1") private Integer sortOrder; + @Schema(description = "是否隐藏", example = "false") + private Boolean hidden; + + @Schema(description = "是否为变量", example = "false") + private Boolean variable; + @Schema(description = "创建时间") private LocalDateTime createTime; diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemSaveReqVO.java index c172f14..5f577b9 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemSaveReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemSaveReqVO.java @@ -1,11 +1,10 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - +import java.util.Map; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import java.util.Map; +import lombok.Data; @Schema(description = "管理后台 - 定额取费项创建/更新 Request VO") @Data @@ -18,6 +17,9 @@ public class QuotaFeeItemSaveReqVO { @NotNull(message = "模式节点ID不能为空") private Long catalogItemId; + @Schema(description = "关联的费率项ID(任意层级节点,可为空)", example = "5001") + private Long rateItemId; + @Schema(description = "自定义序号", example = "一") private String customCode; @@ -42,4 +44,10 @@ public class QuotaFeeItemSaveReqVO { @Schema(description = "排序字段", example = "1") private Integer sortOrder; + + @Schema(description = "是否隐藏", example = "false") + private Boolean hidden; + + @Schema(description = "是否为变量", example = "false") + private Boolean variable; } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemWithRateRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemWithRateRespVO.java new file mode 100644 index 0000000..0aa615a --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaFeeItemWithRateRespVO.java @@ -0,0 +1,91 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import lombok.Data; + +/** + * 定额取费项(含费率项信息)响应 VO + * + * 用于展示费率项一级节点与取费项的一对一关系 + */ +@Schema(description = "管理后台 - 定额取费项(含费率项信息)响应") +@Data +public class QuotaFeeItemWithRateRespVO { + + // ==================== 费率项信息 ==================== + + @Schema(description = "费率项ID(任意层级节点)", example = "5001") + private Long rateItemId; + + @Schema(description = "费率项名称", example = "分部分项工程费") + private String rateItemName; + + @Schema(description = "费率项自定义序号", example = "1") + private String rateItemCustomCode; + + @Schema(description = "费率代号", example = "FBFX") + private String rateCode; + + @Schema(description = "父节点ID", example = "5000") + private Long parentId; + + @Schema(description = "节点类型", example = "directory") + private String nodeType; + + @Schema(description = "子节点列表") + private List children; + + // ==================== 取费项信息 ==================== + + @Schema(description = "取费项ID", example = "6001") + private Long feeItemId; + + @Schema(description = "取费项名称", example = "企业管理费") + private String feeItemName; + + @Schema(description = "取费项自定义序号", example = "一") + private String feeItemCustomCode; + + @Schema(description = "计算基数") + private Map calcBase; + + @Schema(description = "费率百分比", example = "15") + private String ratePercentage; + + @Schema(description = "代号", example = "QYGLF") + private String code; + + @Schema(description = "费用归属", example = "间接费") + private String feeCategory; + + @Schema(description = "基数说明", example = "按除税基价和含税基价之和计算") + private String baseDescription; + + @Schema(description = "排序字段", example = "1") + private Integer sortOrder; + + @Schema(description = "是否隐藏", example = "false") + private Boolean hidden; + + @Schema(description = "是否为变量", example = "false") + private Boolean variable; + + // ==================== 关联信息 ==================== + + @Schema(description = "模式节点ID", example = "1002") + private Long catalogItemId; + + @Schema(description = "是否已配置取费项", example = "true") + private Boolean hasFeeItem; + + // ==================== 时间信息 ==================== + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + private LocalDateTime updateTime; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldLabelRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldLabelRespVO.java new file mode 100644 index 0000000..a9f3348 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldLabelRespVO.java @@ -0,0 +1,29 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.Data; + +@Schema(description = "管理后台 - 定额费率字段标签 Response VO") +@Data +public class QuotaRateFieldLabelRespVO { + + @Schema(description = "标签ID", example = "1") + private Long id; + + @Schema(description = "模式节点ID", example = "1") + private Long catalogItemId; + + @Schema(description = "标签名称", example = "土石方工程") + private String labelName; + + @Schema(description = "排序字段", example = "1") + private Integer sortOrder; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + private LocalDateTime updateTime; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldLabelSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldLabelSaveReqVO.java new file mode 100644 index 0000000..429a7ba --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldLabelSaveReqVO.java @@ -0,0 +1,26 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 定额费率字段标签保存 Request VO") +@Data +public class QuotaRateFieldLabelSaveReqVO { + + @Schema(description = "标签ID(更新时必填)", example = "1") + private Long id; + + @Schema(description = "模式节点ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "模式节点ID不能为空") + private Long catalogItemId; + + @Schema(description = "标签名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土石方工程") + @NotBlank(message = "标签名称不能为空") + private String labelName; + + @Schema(description = "排序字段", example = "1") + private Integer sortOrder; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldRespVO.java index 27be654..a217799 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldRespVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldRespVO.java @@ -1,11 +1,11 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import lombok.Data; /** * 定额费率字段响应 VO @@ -23,11 +23,14 @@ public class QuotaRateFieldRespVO { @Schema(description = "字段索引") private Integer fieldIndex; - @Schema(description = "字段值") - private Integer fieldValue; + @Schema(description = "字段值(支持小数)") + private BigDecimal fieldValue; - @Schema(description = "字段标签") - private String fieldLabel; + @Schema(description = "字段标签ID", example = "1") + private Long fieldLabelId; + + @Schema(description = "字段标签名称(关联查询)", example = "土石方工程") + private String fieldLabelName; @Schema(description = "绑定的节点ID数组") private Long[] bindingIds; diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldSaveReqVO.java index a02d2e3..e3c1a82 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldSaveReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateFieldSaveReqVO.java @@ -1,9 +1,9 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - +import java.math.BigDecimal; import javax.validation.constraints.NotNull; +import lombok.Data; /** * 定额费率字段保存请求 VO @@ -15,19 +15,22 @@ public class QuotaRateFieldSaveReqVO { @Schema(description = "主键ID(更新时必填)") private Long id; - @Schema(description = "关联费率项ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "费率项ID不能为空") + @Schema(description = "关联费率模式节点ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "费率模式节点ID不能为空") + private Long catalogItemId; + + @Schema(description = "关联费率项ID(保存字段值时必填)") private Long rateItemId; @Schema(description = "字段索引", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "字段索引不能为空") private Integer fieldIndex; - @Schema(description = "字段值", example = "1") - private Integer fieldValue; + @Schema(description = "字段值(支持小数)", example = "0.01") + private BigDecimal fieldValue; - @Schema(description = "字段标签", example = "1区") - private String fieldLabel; + @Schema(description = "字段标签ID", example = "1") + private Long fieldLabelId; @Schema(description = "绑定的节点ID数组") private Long[] bindingIds; diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateItemSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateItemSaveReqVO.java index fe1cbac..d822070 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateItemSaveReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateItemSaveReqVO.java @@ -1,12 +1,11 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.Map; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.Data; /** * 定额费率项保存请求 VO @@ -18,8 +17,8 @@ public class QuotaRateItemSaveReqVO { @Schema(description = "主键ID(更新时必填)") private Long id; - @Schema(description = "关联模式节点ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "模式节点ID不能为空") + @Schema(description = "关联模式节点ID(创建时必填)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "模式节点ID不能为空", groups = {CreateGroup.class}) private Long catalogItemId; @Schema(description = "父节点ID") @@ -28,8 +27,8 @@ public class QuotaRateItemSaveReqVO { @Schema(description = "自定义序号", example = "1.1") private String customCode; - @Schema(description = "费率项名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "人工费") - @NotBlank(message = "费率项名称不能为空") + @Schema(description = "费率项名称(创建时必填)", requiredMode = Schema.RequiredMode.REQUIRED, example = "人工费") + @NotBlank(message = "费率项名称不能为空", groups = {CreateGroup.class}) private String name; @Schema(description = "费率代号", example = "A") @@ -47,10 +46,20 @@ public class QuotaRateItemSaveReqVO { @Schema(description = "扩展设置") private Map settings; - @Schema(description = "节点类型:directory-目录,value-数值", requiredMode = Schema.RequiredMode.REQUIRED, example = "value") - @NotBlank(message = "节点类型不能为空") + @Schema(description = "节点类型(创建时必填):directory-目录,value-数值", requiredMode = Schema.RequiredMode.REQUIRED, example = "value") + @NotBlank(message = "节点类型不能为空", groups = {CreateGroup.class}) private String nodeType; @Schema(description = "排序值") private Integer sortOrder; + + /** + * 创建分组 + */ + public interface CreateGroup {} + + /** + * 更新分组 + */ + public interface UpdateGroup {} } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateItemSwapSortReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateItemSwapSortReqVO.java new file mode 100644 index 0000000..50b34d9 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateItemSwapSortReqVO.java @@ -0,0 +1,18 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 定额费率项交换排序 Request VO") +@Data +public class QuotaRateItemSwapSortReqVO { + + @Schema(description = "节点1 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "节点1 ID不能为空") + private Long nodeId1; + + @Schema(description = "节点2 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "节点2 ID不能为空") + private Long nodeId2; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateModeNodeSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateModeNodeSaveReqVO.java new file mode 100644 index 0000000..ff58899 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateModeNodeSaveReqVO.java @@ -0,0 +1,28 @@ +package com.yhy.module.core.controller.admin.quota.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.Data; + +/** + * 定额费率模式节点保存请求 VO + */ +@Schema(description = "管理后台 - 定额费率模式节点保存请求") +@Data +public class QuotaRateModeNodeSaveReqVO { + + @Schema(description = "父节点ID(定额专业节点ID)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "父节点ID不能为空") + private Long parentId; + + @Schema(description = "节点编码", example = "MODE_001") + private String code; + + @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "一般计税模式") + @NotBlank(message = "节点名称不能为空") + private String name; + + @Schema(description = "排序值", example = "1") + private Integer sortOrder; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateValueRulesReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateValueRulesReqVO.java index 378b9c4..e0453e6 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateValueRulesReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaRateValueRulesReqVO.java @@ -1,12 +1,11 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.List; import java.util.Map; +import javax.validation.constraints.NotNull; +import lombok.Data; /** * 定额费率动态取值规则请求 VO diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaResourceRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaResourceRespVO.java index 43c6a89..4d7afaf 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaResourceRespVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/quota/vo/QuotaResourceRespVO.java @@ -1,11 +1,11 @@ package com.yhy.module.core.controller.admin.quota.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.List; import java.util.Map; +import lombok.Data; /** * 定额工料机组成 Response VO @@ -54,9 +54,91 @@ public class QuotaResourceRespVO { @Schema(description = "资源单位快照", example = "kg") private String resourceUnit; + @Schema(description = "资源型号规格快照", example = "P.O 42.5") + private String resourceSpec; + + @Schema(description = "资源类别ID", example = "1") + private Long resourceCategoryId; + + @Schema(description = "资源类型", example = "material") + private String resourceType; + + @Schema(description = "税率", example = "0.13") + private BigDecimal resourceTaxRate; + + @Schema(description = "除税基价", example = "450.00") + private BigDecimal resourceTaxExclBasePrice; + + @Schema(description = "含税基价", example = "508.50") + private BigDecimal resourceTaxInclBasePrice; + + @Schema(description = "除税编制价", example = "460.00") + private BigDecimal resourceTaxExclCompilePrice; + + @Schema(description = "含税编制价", example = "519.80") + private BigDecimal resourceTaxInclCompilePrice; + + @Schema(description = "计算基数", example = "{\"formula\":\"人机 + 材\",\"variables\":{\"人机\":2,\"材\":3}}") + private Map calcBase; + @Schema(description = "实际消耗量(含损耗)", example = "357.00") private BigDecimal actualDosage; @Schema(description = "金额", example = "17850.00") private BigDecimal amount; + + @Schema(description = "是否复合工料机", example = "false") + private Boolean isMerged; + + @Schema(description = "复合工料机子数据列表") + private List mergedItems; + + /** + * 复合工料机子数据 VO + */ + @Schema(description = "复合工料机子数据") + @Data + public static class MergedResourceItemVO { + @Schema(description = "子数据ID", example = "1") + private Long id; + + @Schema(description = "源工料机ID", example = "100") + private Long resourceItemId; + + @Schema(description = "源工料机编码", example = "C001") + private String resourceCode; + + @Schema(description = "源工料机名称", example = "水泥") + private String resourceName; + + @Schema(description = "源工料机单位", example = "t") + private String resourceUnit; + + @Schema(description = "源工料机型号规格", example = "P.O 42.5") + private String resourceSpec; + + @Schema(description = "源工料机类别ID", example = "1") + private Long resourceCategoryId; + + @Schema(description = "源工料机类型", example = "material") + private String resourceType; + + @Schema(description = "税率", example = "0.13") + private BigDecimal resourceTaxRate; + + @Schema(description = "地区代码", example = "GD") + private String regionCode; + + @Schema(description = "定额消耗量", example = "1.5") + private BigDecimal dosage; + + @Schema(description = "除税市场价", example = "450.00") + private BigDecimal price; + + @Schema(description = "实际消耗量(含损耗)", example = "1.53") + private BigDecimal actualDosage; + + @Schema(description = "金额", example = "688.50") + private BigDecimal amount; + } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceCatalogItemController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceCatalogItemController.java index 80f5a20..b13cffa 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceCatalogItemController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceCatalogItemController.java @@ -1,7 +1,10 @@ package com.yhy.module.core.controller.admin.resource; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; import com.yhy.module.core.controller.admin.resource.vo.CatalogItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceCatalogItemTreeNodeVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceCategorySimpleRespVO; import com.yhy.module.core.controller.admin.resource.vo.SwapSortOrderReqVO; import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO; @@ -9,14 +12,18 @@ import com.yhy.module.core.service.resource.ResourceCatalogItemService; import com.yhy.module.core.service.resource.ResourceCategoryTreeService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - +import java.util.List; import javax.annotation.Resource; import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @Tag(name = "管理后台 - 工料机分类/条目") @RestController @@ -31,8 +38,14 @@ public class ResourceCatalogItemController { private ResourceCategoryTreeService categoryTreeService; @GetMapping("/{catalogId}/tree") - @Operation(summary = "获取分类/条目树") - public CommonResult> tree(@PathVariable("catalogId") Long catalogId) { + @Operation(summary = "获取分类/条目树(树形结构)") + public CommonResult> tree(@PathVariable("catalogId") Long catalogId) { + return success(itemService.getTreeByCatalog(catalogId)); + } + + @GetMapping("/{catalogId}/list") + @Operation(summary = "获取分类/条目列表(扁平结构)") + public CommonResult> list(@PathVariable("catalogId") Long catalogId) { return success(itemService.listByCatalog(catalogId)); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceItemController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceItemController.java index 63324a1..072623b 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceItemController.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceItemController.java @@ -1,23 +1,30 @@ package com.yhy.module.core.controller.admin.resource; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemQueryReqVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceItemWithPricesRespVO; import com.yhy.module.core.controller.admin.resource.vo.ResourcePriceSaveReqVO; import com.yhy.module.core.dal.dataobject.resource.ResourcePriceDO; import com.yhy.module.core.service.resource.ResourceItemService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - +import java.util.List; import javax.annotation.Resource; import javax.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @Tag(name = "管理后台 - 工料机项与价格") @RestController @@ -54,6 +61,12 @@ public class ResourceItemController { return success(true); } + @GetMapping("/{id}/with-prices") + @Operation(summary = "获取工料机项及价格列表(包含完整信息)") + public CommonResult getWithPrices(@PathVariable("id") Long id) { + return success(resourceItemService.getItemWithPrices(id)); + } + // 价格相关 @GetMapping("/{id}/prices") @Operation(summary = "查询工料机价格列表") diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceMergedController.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceMergedController.java new file mode 100644 index 0000000..c522e08 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/ResourceMergedController.java @@ -0,0 +1,76 @@ +package com.yhy.module.core.controller.admin.resource; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedPageReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedRespVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedSaveReqVO; +import com.yhy.module.core.service.resource.ResourceMergedService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import javax.annotation.Resource; +import javax.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "管理后台 - 复合工料机") +@RestController +@RequestMapping("/core/resource/merged") +@Validated +public class ResourceMergedController { + + @Resource + private ResourceMergedService resourceMergedService; + + @PostMapping("/create") + @Operation(summary = "创建复合工料机") + @PreAuthorize("@ss.hasPermission('core:resource:create')") + public CommonResult createResourceMerged(@Valid @RequestBody ResourceMergedSaveReqVO createReqVO) { + return success(resourceMergedService.createResourceMerged(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新复合工料机") + @PreAuthorize("@ss.hasPermission('core:resource:update')") + public CommonResult updateResourceMerged(@Valid @RequestBody ResourceMergedSaveReqVO updateReqVO) { + resourceMergedService.updateResourceMerged(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除复合工料机") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('core:resource:delete')") + public CommonResult deleteResourceMerged(@RequestParam("id") Long id) { + resourceMergedService.deleteResourceMerged(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得复合工料机") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('core:resource:query')") + public CommonResult getResourceMerged(@RequestParam("id") Long id) { + ResourceMergedRespVO respVO = resourceMergedService.getResourceMerged(id); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得复合工料机分页") + @PreAuthorize("@ss.hasPermission('core:resource:query')") + public CommonResult> getResourceMergedPage(@Valid ResourceMergedPageReqVO pageReqVO) { + PageResult pageResult = resourceMergedService.getResourceMergedPage(pageReqVO); + return success(pageResult); + } +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/CatalogItemSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/CatalogItemSaveReqVO.java index 7c68426..8da1b50 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/CatalogItemSaveReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/CatalogItemSaveReqVO.java @@ -1,12 +1,11 @@ package com.yhy.module.core.controller.admin.resource.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; import java.util.List; import java.util.Map; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import lombok.Data; @Data public class CatalogItemSaveReqVO { @@ -29,9 +28,15 @@ public class CatalogItemSaveReqVO { @Schema(description = "单位") private String unit; - @Schema(description = "父路径(不含自身编码),如 ['RM','Concrete']") + @Schema(description = "父节点ID(可选,如果提供则自动查询父路径)") + private Long parentId; + + @Schema(description = "父路径(不含自身编码),如 ['RM','Concrete'],如果提供了parentId则可以不传") private List parentPath; + @Schema(description = "排序号") + private Integer sortOrder; + @Schema(description = "扩展属性") private Map attributes; } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceCatalogItemTreeNodeVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceCatalogItemTreeNodeVO.java new file mode 100644 index 0000000..6c836bd --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceCatalogItemTreeNodeVO.java @@ -0,0 +1,38 @@ +package com.yhy.module.core.controller.admin.resource.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import java.util.Map; +import lombok.Data; + +@Schema(description = "工料机目录树节点(树形结构)") +@Data +public class ResourceCatalogItemTreeNodeVO { + + @Schema(description = "节点ID", example = "1") + private Long id; + + @Schema(description = "工料机机类树节点ID", example = "1") + private Long categoryTreeId; + + @Schema(description = "条目编码", example = "01") + private String code; + + @Schema(description = "条目名称", example = "材料") + private String name; + + @Schema(description = "计量单位", example = "t") + private String unit; + + @Schema(description = "层级路径", example = "[\"01\", \"0101\"]") + private String[] path; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; + + @Schema(description = "扩展属性") + private Map attributes; + + @Schema(description = "子节点") + private List children; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceCategoryFullRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceCategoryFullRespVO.java new file mode 100644 index 0000000..6197fdd --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceCategoryFullRespVO.java @@ -0,0 +1,38 @@ +package com.yhy.module.core.controller.admin.resource.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 工料机类别完整响应VO(包含价格代码) + * + * @author yihuiyong + */ +@Schema(description = "工料机类别完整响应(含价格代码)") +@Data +public class ResourceCategoryFullRespVO { + + @Schema(description = "类别ID", example = "1") + private Long id; + + @Schema(description = "类别代码", example = "人") + private String code; + + @Schema(description = "类别名称", example = "人工费") + private String name; + + @Schema(description = "除税基价代码", example = "DRGF") + private String taxExclBaseCode; + + @Schema(description = "含税基价代码", example = "HDRGF") + private String taxInclBaseCode; + + @Schema(description = "除税编制代码", example = "RGF") + private String taxExclCompileCode; + + @Schema(description = "含税编制代码", example = "HRGF") + private String taxInclCompileCode; + + @Schema(description = "排序", example = "1") + private Integer sortOrder; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemPriceRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemPriceRespVO.java new file mode 100644 index 0000000..3007d96 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemPriceRespVO.java @@ -0,0 +1,32 @@ +package com.yhy.module.core.controller.admin.resource.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import java.time.OffsetDateTime; +import lombok.Data; + +@Schema(description = "管理后台 - 工料机价格响应 VO") +@Data +public class ResourceItemPriceRespVO { + + @Schema(description = "价格ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "地区码", example = "BJ") + private String regionCode; + + @Schema(description = "有效期开始时间") + private OffsetDateTime periodStart; + + @Schema(description = "有效期结束时间") + private OffsetDateTime periodEnd; + + @Schema(description = "不含税价格", example = "450.00") + private BigDecimal priceTaxExcl; + + @Schema(description = "含税价格", example = "508.50") + private BigDecimal priceTaxIncl; + + @Schema(description = "价格来源", example = "市场价") + private String source; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemQueryReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemQueryReqVO.java index 68f6dd5..f4d473f 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemQueryReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemQueryReqVO.java @@ -16,6 +16,9 @@ public class ResourceItemQueryReqVO extends PageParam { @Schema(description = "分类节点ID(查询该节点及其子节点下的工料机)", example = "2") private Long catalogItemId; + @Schema(description = "是否包含子节点(仅当 catalogItemId 不为空时有效)", example = "true") + private Boolean includeChildren; + @Schema(description = "类型 labor/material/machine") private String type; diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemRespVO.java index 3d367fd..44a5066 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemRespVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemRespVO.java @@ -1,10 +1,9 @@ package com.yhy.module.core.controller.admin.resource.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - import java.time.LocalDateTime; import java.util.Map; +import lombok.Data; @Schema(description = "工料机项响应") @Data @@ -16,9 +15,15 @@ public class ResourceItemRespVO { @Schema(description = "库条目ID") private Long catalogItemId; + @Schema(description = "编码(来自目录树)") + private String code; + @Schema(description = "工料机名称") private String name; + @Schema(description = "型号规格") + private String spec; + @Schema(description = "类别ID") private Long categoryId; diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemSaveReqVO.java index f219a04..2e81b00 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemSaveReqVO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemSaveReqVO.java @@ -1,11 +1,10 @@ package com.yhy.module.core.controller.admin.resource.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - +import java.util.Map; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import java.util.Map; +import lombok.Data; @Data public class ResourceItemSaveReqVO { @@ -17,10 +16,17 @@ public class ResourceItemSaveReqVO { @NotNull private Long catalogItemId; + @Schema(description = "工料机编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank + private String code; + @Schema(description = "工料机名称", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank private String name; + @Schema(description = "型号规格") + private String spec; + @Schema(description = "类别ID", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull private Long categoryId; diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemWithPricesRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemWithPricesRespVO.java new file mode 100644 index 0000000..d409015 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceItemWithPricesRespVO.java @@ -0,0 +1,50 @@ +package com.yhy.module.core.controller.admin.resource.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import java.util.List; +import lombok.Data; + +@Schema(description = "管理后台 - 工料机项及价格列表响应 VO") +@Data +public class ResourceItemWithPricesRespVO { + + @Schema(description = "工料机项ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "编码(来自目录树)", requiredMode = Schema.RequiredMode.REQUIRED, example = "C001") + private String code; + + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "水泥 P.O 42.5") + private String name; + + @Schema(description = "型号规格", example = "袋装 50kg") + private String spec; + + @Schema(description = "类别ID", example = "1") + private Long categoryId; + + @Schema(description = "类别名称", example = "水泥") + private String categoryName; + + @Schema(description = "单位", example = "t") + private String unit; + + @Schema(description = "税率", example = "0.13") + private BigDecimal taxRate; + + @Schema(description = "除税基价", example = "450.00") + private BigDecimal taxExclBasePrice; + + @Schema(description = "含税基价", example = "508.50") + private BigDecimal taxInclBasePrice; + + @Schema(description = "除税编制价", example = "460.00") + private BigDecimal taxExclCompilePrice; + + @Schema(description = "含税编制价", example = "519.80") + private BigDecimal taxInclCompilePrice; + + @Schema(description = "价格历史列表") + private List prices; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedPageReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedPageReqVO.java new file mode 100644 index 0000000..a17a7fa --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedPageReqVO.java @@ -0,0 +1,23 @@ +package com.yhy.module.core.controller.admin.resource.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 复合工料机分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ResourceMergedPageReqVO extends PageParam { + + @Schema(description = "复合工料机ID(可选,按复合工料机查询)", example = "1000") + private Long mergedId; + + @Schema(description = "数据源ID(可选,按数据源查询)", example = "101") + private Long sourceId; + + @Schema(description = "地区编码", example = "GD") + private String regionCode; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedRespVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedRespVO.java new file mode 100644 index 0000000..861af21 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedRespVO.java @@ -0,0 +1,88 @@ +package com.yhy.module.core.controller.admin.resource.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Map; +import lombok.Data; + +@Schema(description = "管理后台 - 复合工料机 Response VO") +@Data +public class ResourceMergedRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "复合工料机ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Long mergedId; + + @Schema(description = "数据源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "101") + private Long sourceId; + + @Schema(description = "地区编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "GD") + private String regionCode; + + @Schema(description = "定额消耗量", example = "1.0") + private BigDecimal quotaConsumption; + + @Schema(description = "除税市场价", example = "52.00") + private BigDecimal taxExclMarketPrice; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + // ========== 第四层工料机信息(JOIN 查询) ========== + + @Schema(description = "编码(来自catalog_item)", example = "0001") + private String code; + + @Schema(description = "名称(来自catalog_item)", example = "综合人工") + private String name; + + @Schema(description = "目录树节点ID(来自resource_item)", example = "1001") + private Long catalogItemId; + + @Schema(description = "型号规格(来自resource_item)", example = "技术要求高的工种") + private String spec; + + @Schema(description = "类别ID(来自resource_item)", example = "1") + private Long categoryId; + + @Schema(description = "类别名称(来自resource_category)", example = "人工费") + private String categoryName; + + @Schema(description = "单位(来自catalog_item)", example = "工日") + private String unit; + + @Schema(description = "税率(来自resource_item)", example = "0.09") + private BigDecimal taxRate; + + @Schema(description = "除税基价(来自resource_item)", example = "50.00") + private BigDecimal taxExclBasePrice; + + @Schema(description = "含税基价(来自resource_item)", example = "54.50") + private BigDecimal taxInclBasePrice; + + @Schema(description = "除税编制价(来自resource_item)", example = "55.00") + private BigDecimal taxExclCompilePrice; + + @Schema(description = "含税编制价(来自resource_item)", example = "59.95") + private BigDecimal taxInclCompilePrice; + + @Schema(description = "计算基数(来自resource_item)") + private Map calcBase; + + // ========== 虚拟字段(计算合价) ========== + + @Schema(description = "除税基价合价(虚拟字段)", example = "50.00") + private BigDecimal taxExclBaseTotal; + + @Schema(description = "含税基价合价(虚拟字段)", example = "54.50") + private BigDecimal taxInclBaseTotal; + + @Schema(description = "除税编制价合价(虚拟字段)", example = "55.00") + private BigDecimal taxExclCompileTotal; + + @Schema(description = "含税编制价合价(虚拟字段)", example = "59.95") + private BigDecimal taxInclCompileTotal; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedSaveReqVO.java b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedSaveReqVO.java new file mode 100644 index 0000000..ac40e77 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/controller/admin/resource/vo/ResourceMergedSaveReqVO.java @@ -0,0 +1,31 @@ +package com.yhy.module.core.controller.admin.resource.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import javax.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 复合工料机新增/修改 Request VO") +@Data +public class ResourceMergedSaveReqVO { + + @Schema(description = "主键(更新时必填)", example = "1") + private Long id; + + @Schema(description = "复合工料机ID(引用第四层的复合工料机项,is_merged=1)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + @NotNull(message = "复合工料机ID不能为空") + private Long mergedId; + + @Schema(description = "数据源ID(引用真实的第四层工料机项ID)", requiredMode = Schema.RequiredMode.REQUIRED, example = "101") + @NotNull(message = "数据源ID不能为空") + private Long sourceId; + + @Schema(description = "地区编码(可选)", example = "GD") + private String regionCode; + + @Schema(description = "定额消耗量(可选)", example = "1.0") + private BigDecimal quotaConsumption; + + @Schema(description = "除税市场价(可选)", example = "52.00") + private BigDecimal taxExclMarketPrice; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/convert/quota/QuotaAdjustmentConvert.java b/yhy-module-core/src/main/java/com/yhy/module/core/convert/quota/QuotaAdjustmentConvert.java deleted file mode 100644 index 660c9c3..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/convert/quota/QuotaAdjustmentConvert.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.yhy.module.core.convert.quota; - -import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentRespVO; -import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDO; -import com.yhy.module.core.enums.quota.QuotaAdjustmentTypeEnum; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.List; - -@Mapper -public interface QuotaAdjustmentConvert { - - QuotaAdjustmentConvert INSTANCE = Mappers.getMapper(QuotaAdjustmentConvert.class); - - default QuotaAdjustmentRespVO convert(QuotaAdjustmentDO bean) { - if (bean == null) { - return null; - } - QuotaAdjustmentRespVO vo = new QuotaAdjustmentRespVO(); - vo.setId(bean.getId()); - vo.setQuotaItemId(bean.getQuotaItemId()); - vo.setName(bean.getName()); - vo.setContent(bean.getContent()); - vo.setAdjustmentType(bean.getAdjustmentType()); - vo.setAdjustmentRules(bean.getAdjustmentRules()); - vo.setSortOrder(bean.getSortOrder()); - vo.setCreateTime(bean.getCreateTime()); - vo.setUpdateTime(bean.getUpdateTime()); - - // 设置调整类型名称 - QuotaAdjustmentTypeEnum typeEnum = QuotaAdjustmentTypeEnum.valueOfCode(bean.getAdjustmentType()); - if (typeEnum != null) { - vo.setAdjustmentTypeName(typeEnum.getName()); - } - - return vo; - } - - List convertList(List list); -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentDetailDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentDetailDO.java new file mode 100644 index 0000000..b9aed32 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentDetailDO.java @@ -0,0 +1,54 @@ +package com.yhy.module.core.dal.dataobject.quota; + +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * 定额调整明细 DO + * + * @author 芋道源码 + */ +@TableName("yhy_quota_adjustment_detail") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuotaAdjustmentDetailDO extends TenantBaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 调整设置ID + */ + private Long adjustmentSettingId; + + /** + * 定额调整编号(引用其他调整设置ID) + */ + private Long adjustmentCode; + + /** + * 调整内容 + */ + private String adjustment; + + /** + * 排序 + */ + private Integer sortOrder; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentSettingDO.java similarity index 69% rename from yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentDO.java rename to yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentSettingDO.java index 73d485a..7a75d8a 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaAdjustmentSettingDO.java @@ -1,10 +1,12 @@ package com.yhy.module.core.dal.dataobject.quota; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; +import java.math.BigDecimal; import java.util.Map; import lombok.AllArgsConstructor; import lombok.Builder; @@ -15,20 +17,22 @@ import lombok.ToString; /** * 定额调整设置 DO + * + * @author 芋道源码 */ -@TableName(value = "yhy_quota_adjustment", autoResultMap = true) +@TableName(value = "yhy_quota_adjustment_setting", autoResultMap = true) @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor -public class QuotaAdjustmentDO extends TenantBaseDO { +public class QuotaAdjustmentSettingDO extends TenantBaseDO { /** - * 主键ID(标准库,使用自增ID) + * 主键ID */ - @TableId + @TableId(type = IdType.ASSIGN_ID) private Long id; /** @@ -42,17 +46,22 @@ public class QuotaAdjustmentDO extends TenantBaseDO { private String name; /** - * 调整内容(纯文本) + * 定额值 */ - private String content; + private BigDecimal quotaValue; /** - * 调整类型 + * 调整内容 + */ + private String adjustmentContent; + + /** + * 调整类型(字典) */ private String adjustmentType; /** - * 调整规则(JSON结构) + * 调整规则(JSON) */ @TableField(typeHandler = PostgreSQLJsonbTypeHandler.class) private Map adjustmentRules; @@ -61,4 +70,5 @@ public class QuotaAdjustmentDO extends TenantBaseDO { * 排序 */ private Integer sortOrder; + } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaCatalogItemDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaCatalogItemDO.java index fc25ede..7eb27f1 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaCatalogItemDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaCatalogItemDO.java @@ -1,31 +1,30 @@ package com.yhy.module.core.dal.dataobject.quota; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; import com.yhy.module.core.framework.mybatis.typehandler.StringArrayTypeHandler; +import java.util.Map; +import lombok.AccessLevel; import lombok.Data; import lombok.EqualsAndHashCode; - -import java.util.Map; +import lombok.Getter; /** - * 定额目录条目 DO + * 定额基价第一层 - 定额专业树 DO * - * 对应表 yhy_catalog_item(复用资源库条目表) + * 对应表 yhy_quota_catalog_item(独立表) * - * 三层结构: - * - 第一层:定额目录树(省市/定额专业),通过 attributes.node_type 区分 - * - 第二层:工料机专业节点(通过 categoryTreeId 绑定) - * - 第三层:定额子目树(目录/内容),通过 attributes.content_type 区分 + * 树形结构: + * - 地区节点(node_type='region') + * - 定额专业节点(node_type='specialty') * * @author yhy */ -@TableName(value = "yhy_catalog_item", autoResultMap = true) -@KeySequence("yhy_catalog_item_id_seq") +@TableName(value = "yhy_quota_catalog_item", autoResultMap = true) @Data @EqualsAndHashCode(callSuper = true) public class QuotaCatalogItemDO extends TenantBaseDO { @@ -33,7 +32,7 @@ public class QuotaCatalogItemDO extends TenantBaseDO { /** * 主键 */ - @TableId + @TableId(type = IdType.ASSIGN_ID) private Long id; /** @@ -44,7 +43,7 @@ public class QuotaCatalogItemDO extends TenantBaseDO { /** * 工料机机类树节点ID(引用yhy_resource_category_tree) * - * 只有第一层的"定额专业"节点才会绑定此字段 + * 只有"定额专业"节点才会绑定此字段 */ private Long categoryTreeId; @@ -74,17 +73,19 @@ public class QuotaCatalogItemDO extends TenantBaseDO { */ private Integer sortOrder; + /** + * 节点类型(region-地区节点, specialty-定额专业节点) + */ + private String nodeType; + + /** + * 定额专业是否已绑定工料机专业(绑定后不可修改) + */ + @Getter(AccessLevel.NONE) + private Boolean specialtyLocked; + /** * 扩展属性(JSONB) - * - * 第一层属性: - * - node_type: "province"(省市)| "specialty"(定额专业) - * - specialty_locked: true/false(是否已绑定工料机专业) - * - bind_time: "2024-12-08T10:30:00Z"(绑定时间) - * - * 第三层属性: - * - content_type: "directory"(目录)| "content"(内容) - * - allow_children: true/false(是否允许添加子节点) */ @TableField(typeHandler = PostgreSQLJsonbTypeHandler.class) private Map attributes; @@ -92,70 +93,24 @@ public class QuotaCatalogItemDO extends TenantBaseDO { // ========== 便捷方法 ========== /** - * 获取节点类型(第一层) - */ - public String getNodeType() { - return attributes != null ? (String) attributes.get("node_type") : null; - } - - /** - * 设置节点类型(第一层) - */ - public void setNodeType(String nodeType) { - if (attributes == null) { - attributes = new java.util.HashMap<>(); - } - attributes.put("node_type", nodeType); - } - - /** - * 是否已绑定工料机专业(第一层) + * 是否已绑定工料机专业 */ public Boolean isSpecialtyLocked() { - return attributes != null ? (Boolean) attributes.get("specialty_locked") : false; + return Boolean.TRUE.equals(specialtyLocked); } /** - * 设置绑定状态(第一层) - */ - public void setSpecialtyLocked(Boolean locked) { - if (attributes == null) { - attributes = new java.util.HashMap<>(); - } - attributes.put("specialty_locked", locked); - } - - /** - * 获取内容类型(第三层) - */ - public String getContentType() { - return attributes != null ? (String) attributes.get("content_type") : null; - } - - /** - * 设置内容类型(第三层) - */ - public void setContentType(String contentType) { - if (attributes == null) { - attributes = new java.util.HashMap<>(); - } - attributes.put("content_type", contentType); - } - - /** - * 是否允许添加子节点(第三层) + * 是否允许添加子节点 */ public Boolean isAllowChildren() { - return attributes != null ? (Boolean) attributes.get("allow_children") : true; + // 所有节点都可以添加子节点 + return true; } /** - * 设置是否允许子节点(第三层) + * 获取内容类型(定额专业树没有此概念) */ - public void setAllowChildren(Boolean allow) { - if (attributes == null) { - attributes = new java.util.HashMap<>(); - } - attributes.put("allow_children", allow); + public String getContentType() { + return null; } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaCatalogTreeDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaCatalogTreeDO.java new file mode 100644 index 0000000..61acf72 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaCatalogTreeDO.java @@ -0,0 +1,100 @@ +package com.yhy.module.core.dal.dataobject.quota; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; +import com.yhy.module.core.dal.typehandler.PostgreSQLTextArrayTypeHandler; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * 定额子目树 DO + * 第二层:定额子目树(目录/内容) + * + * @author yhy + */ +@TableName(value = "yhy_quota_catalog_tree", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuotaCatalogTreeDO extends BaseDO { + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 关联定额专业节点ID(第一层) + */ + private Long catalogItemId; + + /** + * 父节点ID(自关联) + */ + private Long parentId; + + /** + * 编码 + */ + private String code; + + /** + * 名称 + */ + private String name; + + /** + * 单位 + */ + private String unit; + + /** + * 内容类型:directory-目录,content-内容 + */ + private String contentType; + + /** + * 是否允许添加子节点 + */ + private Boolean allowChildren; + + /** + * 排序 + */ + private Integer sortOrder; + + /** + * 树路径 + */ + @TableField(typeHandler = PostgreSQLTextArrayTypeHandler.class) + private String[] path; + + /** + * 层级 + */ + private Integer level; + + /** + * 扩展属性 + */ + @TableField(typeHandler = PostgreSQLJsonbTypeHandler.class) + private Map attributes; +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaFeeItemDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaFeeItemDO.java index c293bce..3413d4e 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaFeeItemDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaFeeItemDO.java @@ -1,13 +1,14 @@ package com.yhy.module.core.dal.dataobject.quota; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; +import java.util.Map; import lombok.Data; import lombok.EqualsAndHashCode; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -import java.util.Map; /** * 定额取费项 DO @@ -24,6 +25,7 @@ public class QuotaFeeItemDO extends BaseDO { /** * 主键ID */ + @TableId(type = IdType.ASSIGN_ID) private Long id; /** @@ -36,6 +38,12 @@ public class QuotaFeeItemDO extends BaseDO { */ private Long catalogItemId; + /** + * 关联的费率项ID(任意层级节点,可为空) + * 用于与定额费率项建立一对一关系 + */ + private Long rateItemId; + /** * 自定义序号(字符串,显示用,不参与排序) * 如:"一"、"1.1"、"(1)" @@ -52,20 +60,32 @@ public class QuotaFeeItemDO extends BaseDO { * * 格式: * { - * "formula": "除税基价 + 含税基价 - 除税编制 * 含税编制", + * "formula": "DRGF+HDRGF/DCLF-(DRGF*DCLF+12)", * "variables": { - * "除税基价": "tax_excl_base_price", - * "含税基价": "tax_incl_base_price", - * "除税编制": "tax_excl_compile_price", - * "含税编制": "tax_incl_compile_price" + * "DRGF": { + * "categoryId": 1, + * "priceField": "tax_excl_base_price" + * }, + * "HDRGF": { + * "categoryId": 1, + * "priceField": "tax_incl_base_price" + * }, + * "DCLF": { + * "categoryId": 3, + * "priceField": "tax_excl_base_price" + * } * } * } * - * 价格代码说明: + * 价格字段类型说明: * - tax_excl_base_price: 除税基价 * - tax_incl_base_price: 含税基价 * - tax_excl_compile_price: 除税编制价 * - tax_incl_compile_price: 含税编制价 + * + * variables 中的每个变量包含: + * - categoryId: 类别ID(对应 yhy_resource_category 表的 id) + * - priceField: 价格字段类型(上述4个值之一) */ @TableField(typeHandler = PostgreSQLJsonbTypeHandler.class) private Map calcBase; @@ -97,6 +117,16 @@ public class QuotaFeeItemDO extends BaseDO { */ private Integer sortOrder; + /** + * 是否隐藏(true=隐藏,false=显示) + */ + private Boolean hidden; + + /** + * 是否为变量(true=变量,false=非变量) + */ + private Boolean variable; + // ==================== 便捷方法 ==================== /** @@ -118,14 +148,14 @@ public class QuotaFeeItemDO extends BaseDO { } /** - * 获取变量映射 + * 获取 calcBase 中的变量映射(兼容旧数据) */ @SuppressWarnings("unchecked") - public Map getVariables() { + public Map getCalcBaseVariables() { if (calcBase == null) { return null; } - return (Map) calcBase.get("variables"); + return (Map) calcBase.get("variables"); } // ==================== 常量 ==================== diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaItemDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaItemDO.java index 98e7f4f..21e69d9 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaItemDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaItemDO.java @@ -6,11 +6,10 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; - import java.math.BigDecimal; import java.util.Map; +import lombok.Data; +import lombok.EqualsAndHashCode; /** * 定额子目/基价 DO @@ -34,7 +33,7 @@ public class QuotaItemDO extends TenantBaseDO { private Long id; /** - * 定额条目ID(关联 yhy_catalog_item) + * 定额子目树节点ID(关联 yhy_quota_catalog_tree) * * 只能关联 content_type='content' 的节点 */ diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateFieldDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateFieldDO.java index 39698f7..3aad3b4 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateFieldDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateFieldDO.java @@ -1,17 +1,20 @@ package com.yhy.module.core.dal.dataobject.quota; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLBigintArrayTypeHandler; import lombok.Data; import lombok.EqualsAndHashCode; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; /** * 定额费率字段绑定 DO * - * 存储费率项与定额基价节点的绑定关系 - * 每个费率项可以有多个字段(field_1, field_2, ...),每个字段可以绑定多个定额基价节点 + * 存储费率模式节点与定额基价节点的绑定关系 + * 每个费率模式节点可以有多个字段(field_1, field_2, ...),每个字段可以绑定多个定额基价节点 + * 绑定关系影响该费率模式节点下的所有费率项 */ @TableName(value = "yhy_quota_rate_field", autoResultMap = true) @Data @@ -21,6 +24,7 @@ public class QuotaRateFieldDO extends BaseDO { /** * 主键ID */ + @TableId(type = IdType.ASSIGN_ID) private Long id; /** @@ -29,9 +33,9 @@ public class QuotaRateFieldDO extends BaseDO { private Long tenantId; /** - * 关联费率项ID + * 关联费率模式节点ID(yhy_quota_catalog_item) */ - private Long rateItemId; + private Long catalogItemId; /** * 字段索引(1, 2, 3...) @@ -40,16 +44,16 @@ public class QuotaRateFieldDO extends BaseDO { private Integer fieldIndex; /** - * 字段值(用于标识) + * 字段值(用于标识,支持小数) * 如:1, 2, 3 分别代表 1区, 2区, 3区 + * 或:0.01, 0.224 等小数值 */ - private Integer fieldValue; + private java.math.BigDecimal fieldValue; /** - * 字段标签(显示用) - * 如:"1区"、"2区"、"3区" + * 字段标签ID(引用yhy_quota_rate_field_label) */ - private String fieldLabel; + private Long fieldLabelId; /** * 绑定的定额基价节点ID数组 diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateFieldLabelDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateFieldLabelDO.java new file mode 100644 index 0000000..c36ed51 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateFieldLabelDO.java @@ -0,0 +1,47 @@ +package com.yhy.module.core.dal.dataobject.quota; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 定额费率字段标签字典 DO + * + * @author yhy + */ +@TableName("yhy_quota_rate_field_label") +@KeySequence("yhy_quota_rate_field_label_id_seq") +@Data +@EqualsAndHashCode(callSuper = true) +public class QuotaRateFieldLabelDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 关联模式节点ID + */ + private Long catalogItemId; + + /** + * 字段标签名称 + */ + private String labelName; + + /** + * 排序字段 + */ + private Integer sortOrder; + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateItemDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateItemDO.java index 86d6571..e247976 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateItemDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaRateItemDO.java @@ -1,15 +1,16 @@ package com.yhy.module.core.dal.dataobject.quota; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - import java.math.BigDecimal; import java.util.List; import java.util.Map; +import lombok.Data; +import lombok.EqualsAndHashCode; /** * 定额费率项 DO @@ -26,6 +27,7 @@ public class QuotaRateItemDO extends BaseDO { /** * 主键ID */ + @TableId(type = IdType.ASSIGN_ID) private Long id; /** @@ -87,18 +89,23 @@ public class QuotaRateItemDO extends BaseDO { * "remark": "备注" * } * - * 动态模式: + * 动态模式(默认使用阶梯规则): * { * "valueRules": { - * "tiers": [ + * "tiers": [ // 阶梯规则(必填) * {"seq": 1, "threshold": 50, "compareType": "lte", "fieldValues": {"1": 0.224, "2": 0}}, + * {"seq": 2, "threshold": 100, "compareType": "lte", "fieldValues": {"1": 0.301, "2": 0}}, * ... * ], - * "increments": [ + * "increments": [ // 增量规则(可选,在阶梯基础上叠加) * {"seq": 7, "step": 100, "baseThreshold": 1000, "fieldIncrements": {"1": 0.036, "2": 0.002}} * ] * } * } + * + * 计算逻辑: + * 1. 根据基数值匹配阶梯规则,获取基础字段值 + * 2. 如果配置了增量规则,在基础值上叠加增量 */ @TableField(typeHandler = PostgreSQLJsonbTypeHandler.class) private Map settings; diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaResourceDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaResourceDO.java index 97dc219..aad8e5b 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaResourceDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/quota/QuotaResourceDO.java @@ -6,11 +6,10 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; - import java.math.BigDecimal; import java.util.Map; +import lombok.Data; +import lombok.EqualsAndHashCode; /** * 定额子目工料机组成 DO @@ -161,6 +160,23 @@ public class QuotaResourceDO extends TenantBaseDO { attributes.put("resource_unit", resourceUnit); } + /** + * 获取资源型号规格快照 + */ + public String getResourceSpec() { + return attributes != null ? (String) attributes.get("resource_spec") : null; + } + + /** + * 设置资源型号规格快照 + */ + public void setResourceSpec(String resourceSpec) { + if (attributes == null) { + attributes = new java.util.HashMap<>(); + } + attributes.put("resource_spec", resourceSpec); + } + /** * 计算实际消耗量(含损耗) * diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceItemDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceItemDO.java index 43fc7b3..cbd70d2 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceItemDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceItemDO.java @@ -1,16 +1,16 @@ package com.yhy.module.core.dal.dataobject.resource; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.yhy.module.core.dal.typehandler.PostgreSQLJsonbTypeHandler; +import java.util.Map; import lombok.Data; import lombok.EqualsAndHashCode; -import java.util.Map; - /** * 工料机项 DO,对应 yhy_resource_item。 */ @@ -31,11 +31,21 @@ public class ResourceItemDO extends TenantBaseDO { */ private Long catalogItemId; + /** + * 工料机编码(独立唯一值) + */ + private String code; + /** * 工料机名称 */ private String name; + /** + * 型号规格 + */ + private String spec; + /** * 类别ID,引用 yhy_resource_category.id */ @@ -84,7 +94,9 @@ public class ResourceItemDO extends TenantBaseDO { /** * 复合数据标识符(0=普通,1=复合) + * 注意:此字段由数据库触发器自动维护,应用层不应修改 */ + @TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER) private Integer isMerged; /** diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceMergedDO.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceMergedDO.java index 78b520d..60ec14a 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceMergedDO.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/dataobject/resource/ResourceMergedDO.java @@ -4,17 +4,16 @@ import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import java.math.BigDecimal; import lombok.Data; import lombok.EqualsAndHashCode; -import java.math.BigDecimal; - /** * 复合工料机(第五层,存储多地区价格)DO * * @author yihuiyong */ -@TableName(value = "yhy_resource_merged", autoResultMap = true) +@TableName(value = "yhy_resource_merged") @KeySequence("yhy_resource_merged_id_seq") @Data @EqualsAndHashCode(callSuper = true) @@ -27,10 +26,15 @@ public class ResourceMergedDO extends TenantBaseDO { private Long id; /** - * 工料机项ID(引用yhy_resource_item.id) + * 复合工料机ID(引用第四层的复合工料机项,is_merged=1) */ private Long mergedId; + /** + * 数据源ID(引用真实的第四层工料机项ID) + */ + private Long sourceId; + /** * 地区编码 */ diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentDetailMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentDetailMapper.java new file mode 100644 index 0000000..b082867 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentDetailMapper.java @@ -0,0 +1,46 @@ +package com.yhy.module.core.dal.mysql.quota; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDetailDO; +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +/** + * 定额调整明细 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface QuotaAdjustmentDetailMapper extends BaseMapperX { + + default List selectListBySettingId(Long adjustmentSettingId) { + return selectList(new LambdaQueryWrapperX() + .eq(QuotaAdjustmentDetailDO::getAdjustmentSettingId, adjustmentSettingId) + .orderByAsc(QuotaAdjustmentDetailDO::getSortOrder)); + } + + default List selectListBySettingIds(List adjustmentSettingIds) { + return selectList(new LambdaQueryWrapperX() + .in(QuotaAdjustmentDetailDO::getAdjustmentSettingId, adjustmentSettingIds) + .orderByAsc(QuotaAdjustmentDetailDO::getAdjustmentSettingId) + .orderByAsc(QuotaAdjustmentDetailDO::getSortOrder)); + } + + default QuotaAdjustmentDetailDO selectByIdForUpdate(Long id) { + return selectOne(new LambdaQueryWrapperX() + .eq(QuotaAdjustmentDetailDO::getId, id) + .last("FOR UPDATE")); + } + + default Long countBySettingId(Long adjustmentSettingId) { + return selectCount(new LambdaQueryWrapperX() + .eq(QuotaAdjustmentDetailDO::getAdjustmentSettingId, adjustmentSettingId)); + } + + default Long countByAdjustmentCode(Long adjustmentCode) { + return selectCount(new LambdaQueryWrapperX() + .eq(QuotaAdjustmentDetailDO::getAdjustmentCode, adjustmentCode)); + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentMapper.java deleted file mode 100644 index 99d36df..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.yhy.module.core.dal.mysql.quota; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * 定额调整设置 Mapper - */ -@Mapper -public interface QuotaAdjustmentMapper extends BaseMapperX { - - default List selectListByQuotaItemId(Long quotaItemId) { - return selectList(new LambdaQueryWrapperX() - .eq(QuotaAdjustmentDO::getQuotaItemId, quotaItemId) - .orderByAsc(QuotaAdjustmentDO::getSortOrder)); - } - - default QuotaAdjustmentDO selectByIdForUpdate(Long id) { - return selectOne(new LambdaQueryWrapperX() - .eq(QuotaAdjustmentDO::getId, id) - .last("FOR UPDATE")); - } -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentSettingMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentSettingMapper.java new file mode 100644 index 0000000..bcdda00 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaAdjustmentSettingMapper.java @@ -0,0 +1,29 @@ +package com.yhy.module.core.dal.mysql.quota; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentSettingDO; +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +/** + * 定额调整设置 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface QuotaAdjustmentSettingMapper extends BaseMapperX { + + default List selectListByQuotaItemId(Long quotaItemId) { + return selectList(new LambdaQueryWrapperX() + .eq(QuotaAdjustmentSettingDO::getQuotaItemId, quotaItemId) + .orderByAsc(QuotaAdjustmentSettingDO::getSortOrder)); + } + + default QuotaAdjustmentSettingDO selectByIdForUpdate(Long id) { + return selectOne(new LambdaQueryWrapperX() + .eq(QuotaAdjustmentSettingDO::getId, id) + .last("FOR UPDATE")); + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaCatalogItemMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaCatalogItemMapper.java index fae4559..b3896b5 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaCatalogItemMapper.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaCatalogItemMapper.java @@ -1,12 +1,12 @@ package com.yhy.module.core.dal.mysql.quota; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; +import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import java.util.List; - /** * 定额目录条目 Mapper * @@ -33,7 +33,8 @@ public interface QuotaCatalogItemMapper extends BaseMapperX * 查询根节点列表(parentId 为 null) */ default List selectRootList() { - return selectList("parent_id", null); + return selectList(new LambdaQueryWrapperX() + .isNull(QuotaCatalogItemDO::getParentId)); } /** @@ -57,4 +58,16 @@ public interface QuotaCatalogItemMapper extends BaseMapperX * @return 所有子孙节点 */ List selectDescendants(@Param("id") Long id); + + /** + * 使用行锁查询节点(用于排序交换) + * + * @param id 节点ID + * @return 节点信息 + */ + default QuotaCatalogItemDO selectByIdForUpdate(Long id) { + return selectOne(new LambdaQueryWrapperX() + .eq(QuotaCatalogItemDO::getId, id) + .last("FOR UPDATE")); + } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaCatalogTreeMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaCatalogTreeMapper.java new file mode 100644 index 0000000..58bdc2f --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaCatalogTreeMapper.java @@ -0,0 +1,42 @@ +package com.yhy.module.core.dal.mysql.quota; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO; +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 定额子目树 Mapper + * + * @author yhy + */ +@Mapper +public interface QuotaCatalogTreeMapper extends BaseMapperX { + + /** + * 查询指定定额专业下的所有节点 + */ + default List selectListByCatalogItemId(Long catalogItemId) { + return selectList(QuotaCatalogTreeDO::getCatalogItemId, catalogItemId); + } + + /** + * 查询指定父节点下的子节点 + */ + default List selectListByParentId(Long parentId) { + return selectList(QuotaCatalogTreeDO::getParentId, parentId); + } + + /** + * 查询同级节点(用于排序) + */ + default List selectSiblings(@Param("catalogItemId") Long catalogItemId, + @Param("parentId") Long parentId) { + return selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(QuotaCatalogTreeDO::getCatalogItemId, catalogItemId) + .eq(parentId != null, QuotaCatalogTreeDO::getParentId, parentId) + .isNull(parentId == null, QuotaCatalogTreeDO::getParentId) + .orderByAsc(QuotaCatalogTreeDO::getSortOrder)); + } +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateFieldLabelMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateFieldLabelMapper.java new file mode 100644 index 0000000..775d021 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateFieldLabelMapper.java @@ -0,0 +1,55 @@ +package com.yhy.module.core.dal.mysql.quota; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldLabelDO; +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +/** + * 定额费率字段标签字典 Mapper + * + * @author yhy + */ +@Mapper +public interface QuotaRateFieldLabelMapper extends BaseMapperX { + + /** + * 根据模式节点ID查询标签列表 + */ + default List selectListByCatalogItemId(Long catalogItemId) { + return selectList(new LambdaQueryWrapperX() + .eq(QuotaRateFieldLabelDO::getCatalogItemId, catalogItemId) + .orderByAsc(QuotaRateFieldLabelDO::getSortOrder)); + } + + /** + * 根据模式节点ID和标签名称查询 + */ + default QuotaRateFieldLabelDO selectByCatalogItemIdAndName(Long catalogItemId, String labelName) { + return selectOne(new LambdaQueryWrapperX() + .eq(QuotaRateFieldLabelDO::getCatalogItemId, catalogItemId) + .eq(QuotaRateFieldLabelDO::getLabelName, labelName)); + } + + /** + * 查询最大排序值 + */ + default Integer selectMaxSortOrder(Long catalogItemId) { + QuotaRateFieldLabelDO label = selectOne(new LambdaQueryWrapperX() + .eq(QuotaRateFieldLabelDO::getCatalogItemId, catalogItemId) + .orderByDesc(QuotaRateFieldLabelDO::getSortOrder) + .last("LIMIT 1")); + return label != null ? label.getSortOrder() : 0; + } + + /** + * 更新排序值 + */ + default void updateSortOrder(Long id, Integer sortOrder) { + QuotaRateFieldLabelDO updateObj = new QuotaRateFieldLabelDO(); + updateObj.setId(id); + updateObj.setSortOrder(sortOrder); + updateById(updateObj); + } +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateFieldMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateFieldMapper.java index 708c000..0fdb61f 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateFieldMapper.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateFieldMapper.java @@ -3,11 +3,10 @@ package com.yhy.module.core.dal.mysql.quota; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldDO; +import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; - -import java.util.Collections; -import java.util.List; +import org.apache.ibatis.annotations.Select; /** * 定额费率字段绑定 Mapper @@ -16,29 +15,29 @@ import java.util.List; public interface QuotaRateFieldMapper extends BaseMapperX { /** - * 根据费率项ID查询字段列表 + * 根据费率模式节点ID查询字段列表 */ - default List selectListByRateItemId(Long rateItemId) { + default List selectListByCatalogItemId(Long catalogItemId) { return selectList(new LambdaQueryWrapperX() - .eq(QuotaRateFieldDO::getRateItemId, rateItemId) + .eq(QuotaRateFieldDO::getCatalogItemId, catalogItemId) .orderByAsc(QuotaRateFieldDO::getFieldIndex)); } /** - * 根据费率项ID和字段索引查询 + * 根据费率模式节点ID和字段索引查询 */ - default QuotaRateFieldDO selectByRateItemIdAndFieldIndex(Long rateItemId, Integer fieldIndex) { + default QuotaRateFieldDO selectByCatalogItemIdAndFieldIndex(Long catalogItemId, Integer fieldIndex) { return selectOne(new LambdaQueryWrapperX() - .eq(QuotaRateFieldDO::getRateItemId, rateItemId) + .eq(QuotaRateFieldDO::getCatalogItemId, catalogItemId) .eq(QuotaRateFieldDO::getFieldIndex, fieldIndex)); } /** - * 删除费率项的所有字段 + * 删除费率模式节点的所有字段 */ - default int deleteByRateItemId(Long rateItemId) { + default int deleteByCatalogItemId(Long catalogItemId) { return delete(new LambdaQueryWrapperX() - .eq(QuotaRateFieldDO::getRateItemId, rateItemId)); + .eq(QuotaRateFieldDO::getCatalogItemId, catalogItemId)); } /** @@ -51,22 +50,29 @@ public interface QuotaRateFieldMapper extends BaseMapperX { } /** - * 根据费率项ID列表查询所有字段 + * 统计费率模式节点的字段数量 */ - default List selectListByRateItemIds(@Param("rateItemIds") List rateItemIds) { - if (rateItemIds == null || rateItemIds.isEmpty()) { - return Collections.emptyList(); - } - return selectList(new LambdaQueryWrapperX() - .in(QuotaRateFieldDO::getRateItemId, rateItemIds) - .orderByAsc(QuotaRateFieldDO::getFieldIndex)); + default Long countByCatalogItemId(Long catalogItemId) { + return selectCount(new LambdaQueryWrapperX() + .eq(QuotaRateFieldDO::getCatalogItemId, catalogItemId)); } /** - * 统计费率项的字段数量 + * 查询所有包含指定绑定节点的字段(排除指定字段索引) + * 用于检查节点是否已被其他虚拟字段绑定 + * + * @param catalogItemId 费率模式节点ID + * @param bindingId 要检查的绑定节点ID + * @param excludeFieldIndex 要排除的字段索引(当前正在更新的字段) + * @return 包含该绑定节点的字段列表 */ - default Long countByRateItemId(Long rateItemId) { - return selectCount(new LambdaQueryWrapperX() - .eq(QuotaRateFieldDO::getRateItemId, rateItemId)); - } + @Select("SELECT * FROM yhy_quota_rate_field " + + "WHERE catalog_item_id = #{catalogItemId} " + + "AND field_index != #{excludeFieldIndex} " + + "AND binding_ids @> ARRAY[#{bindingId}]::bigint[] " + + "AND deleted = 0") + List selectByBindingIdExcludeFieldIndex( + @Param("catalogItemId") Long catalogItemId, + @Param("bindingId") Long bindingId, + @Param("excludeFieldIndex") Integer excludeFieldIndex); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateItemMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateItemMapper.java index ca7e042..00fa1dd 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateItemMapper.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/quota/QuotaRateItemMapper.java @@ -3,13 +3,12 @@ package com.yhy.module.core.dal.mysql.quota; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import com.yhy.module.core.dal.dataobject.quota.QuotaRateItemDO; +import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; -import java.util.List; - /** * 定额费率项 Mapper */ @@ -57,8 +56,8 @@ public interface QuotaRateItemMapper extends BaseMapperX { */ @Select("SELECT COALESCE(MAX(sort_order), 0) FROM yhy_quota_rate_item " + "WHERE catalog_item_id = #{catalogItemId} " + - "AND (parent_id = #{parentId} OR (#{parentId} IS NULL AND parent_id IS NULL)) " + - "AND deleted = false") + "AND (parent_id = #{parentId} OR (#{parentId}::bigint IS NULL AND parent_id IS NULL)) " + + "AND deleted = 0") Integer selectMaxSortOrder(@Param("catalogItemId") Long catalogItemId, @Param("parentId") Long parentId); /** diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceCatalogItemMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceCatalogItemMapper.java index 7b65b68..fb7ac7d 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceCatalogItemMapper.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceCatalogItemMapper.java @@ -3,7 +3,28 @@ package com.yhy.module.core.dal.mysql.resource; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; @Mapper public interface ResourceCatalogItemMapper extends BaseMapperX { + + /** + * 检查是否存在子节点 + * 使用PostgreSQL数组切片语法:path[1:array_length(path,1)-1] 表示去掉最后一个元素 + * 使用 CAST 函数进行类型转换,确保类型匹配 + * + * @param categoryTreeId 机类树ID + * @param path 父节点路径 + * @param tenantId 租户ID + * @return 子节点数量 + */ + @Select("SELECT COUNT(*) FROM yhy_catalog_item " + + "WHERE category_tree_id = #{categoryTreeId} " + + "AND path[1:array_length(path,1)-1]::varchar[] = #{path} " + + "AND tenant_id = #{tenantId} " + + "AND deleted = 0") + Long countChildNodes(@Param("categoryTreeId") Long categoryTreeId, + @Param("path") String[] path, + @Param("tenantId") Long tenantId); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceItemMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceItemMapper.java index 540b529..f0cf623 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceItemMapper.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceItemMapper.java @@ -2,9 +2,12 @@ package com.yhy.module.core.dal.mysql.resource; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO; -import org.apache.ibatis.annotations.Mapper; - +import java.math.BigDecimal; import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; @Mapper public interface ResourceItemMapper extends BaseMapperX { @@ -13,8 +16,77 @@ public interface ResourceItemMapper extends BaseMapperX { * 根据工料机机类树节点ID查询工料机列表 * * 用于定额子目工料机的范围过滤 + * 通过 catalog_item_id 关联 yhy_catalog_item 表,再过滤 category_tree_id */ - default List selectByCategoryTreeId(Long categoryTreeId) { - return selectList("category_tree_id", categoryTreeId); + @Select("SELECT ri.* FROM yhy_resource_item ri " + + "INNER JOIN yhy_catalog_item rci ON ri.catalog_item_id = rci.id " + + "WHERE rci.category_tree_id = #{categoryTreeId} " + + "AND ri.deleted = 0 " + + "AND rci.deleted = 0 " + + "ORDER BY ri.id") + List selectByCategoryTreeId(@Param("categoryTreeId") Long categoryTreeId); + + /** + * 根据工料机机类树节点ID和查询条件查询工料机列表(支持模糊查询) + * + * @param categoryTreeId 工料机机类树节点ID + * @param code 编码(模糊查询) + * @param name 名称(模糊查询) + * @param spec 型号规格(模糊查询) + * @return 工料机列表 + */ + @Select("") + List selectByCategoryTreeIdWithFilter( + @Param("categoryTreeId") Long categoryTreeId, + @Param("code") String code, + @Param("name") String name, + @Param("spec") String spec); + + /** + * 批量查询工料机项(根据ID列表) + * + * @param ids ID列表 + * @return 工料机项列表 + */ + default List selectBatchIds(List ids) { + return selectList("id", ids); } + + /** + * 更新工料机的4个价格字段 + * + * @param id 工料机ID + * @param taxExclBasePrice 除税基价 + * @param taxInclBasePrice 含税基价 + * @param taxExclCompilePrice 除税编制价 + * @param taxInclCompilePrice 含税编制价 + */ + @Update("UPDATE yhy_resource_item SET " + + "tax_excl_base_price = #{taxExclBasePrice}, " + + "tax_incl_base_price = #{taxInclBasePrice}, " + + "tax_excl_compile_price = #{taxExclCompilePrice}, " + + "tax_incl_compile_price = #{taxInclCompilePrice}, " + + "update_time = now() " + + "WHERE id = #{id} AND deleted = 0") + void updatePrices(@Param("id") Long id, + @Param("taxExclBasePrice") BigDecimal taxExclBasePrice, + @Param("taxInclBasePrice") BigDecimal taxInclBasePrice, + @Param("taxExclCompilePrice") BigDecimal taxExclCompilePrice, + @Param("taxInclCompilePrice") BigDecimal taxInclCompilePrice); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceMergedMapper.java b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceMergedMapper.java index e33bd5d..dffb122 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceMergedMapper.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/dal/mysql/resource/ResourceMergedMapper.java @@ -1,8 +1,14 @@ package com.yhy.module.core.dal.mysql.resource; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedPageReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedRespVO; import com.yhy.module.core.dal.dataobject.resource.ResourceMergedDO; +import java.util.List; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; /** * 复合工料机 Mapper @@ -11,4 +17,94 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper public interface ResourceMergedMapper extends BaseMapperX { + + /** + * 根据ID查询复合工料机(JOIN 第四层数据) + * + * @param id 主键 + * @return 复合工料机(包含第四层信息) + */ + @Select("SELECT " + + " rm.id, rm.tenant_id, rm.merged_id, rm.source_id, rm.region_code, " + + " rm.quota_consumption, rm.tax_excl_market_price, " + + " rm.creator, rm.create_time, rm.updater, rm.update_time, " + + " ri.code, ri.name, ri.unit, ri.spec, " + + " ri.catalog_item_id, ri.category_id, ri.tax_rate, " + + " ri.tax_excl_base_price, ri.tax_incl_base_price, " + + " ri.tax_excl_compile_price, ri.tax_incl_compile_price, " + + " ri.calc_base, " + + " rc.name AS category_name, " + + " ROUND(COALESCE(ri.tax_excl_base_price, 0) * COALESCE(rm.quota_consumption, 0) / 100, 4) AS tax_excl_base_total, " + + " ROUND(COALESCE(ri.tax_incl_base_price, 0) * COALESCE(rm.quota_consumption, 0) / 100, 4) AS tax_incl_base_total, " + + " ROUND(COALESCE(ri.tax_excl_compile_price, 0) * COALESCE(rm.quota_consumption, 0) / 100, 4) AS tax_excl_compile_total, " + + " ROUND(COALESCE(ri.tax_incl_compile_price, 0) * COALESCE(rm.quota_consumption, 0) / 100, 4) AS tax_incl_compile_total " + + "FROM yhy_resource_merged rm " + + "LEFT JOIN yhy_resource_item ri ON rm.source_id = ri.id AND ri.deleted = 0 " + + "LEFT JOIN yhy_resource_category rc ON ri.category_id = rc.id AND rc.deleted = 0 " + + "WHERE rm.id = #{id} AND rm.deleted = 0") + ResourceMergedRespVO selectByIdWithDetails(@Param("id") Long id); + + /** + * 查询复合工料机分页(JOIN 第四层数据) + * + * @param pageReqVO 分页查询条件 + * @return 复合工料机列表(包含第四层信息) + */ + @Select("") + List selectPageWithDetails(@Param("pageReqVO") ResourceMergedPageReqVO pageReqVO, + @Param("offset") int offset); + + /** + * 查询复合工料机总数 + * + * @param pageReqVO 查询条件 + * @return 总数 + */ + default Long selectCountWithDetails(ResourceMergedPageReqVO pageReqVO) { + return selectCount(new LambdaQueryWrapperX() + .eqIfPresent(ResourceMergedDO::getMergedId, pageReqVO.getMergedId()) + .eqIfPresent(ResourceMergedDO::getSourceId, pageReqVO.getSourceId()) + .eqIfPresent(ResourceMergedDO::getRegionCode, pageReqVO.getRegionCode())); + } + + /** + * 查询指定 merged_id 的所有第五层数据 + * + * @param mergedId 复合工料机ID + * @return 第五层数据列表 + */ + default List selectByMergedId(Long mergedId) { + return selectList(new LambdaQueryWrapperX() + .eq(ResourceMergedDO::getMergedId, mergedId)); + } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/enums/ErrorCodeConstants.java b/yhy-module-core/src/main/java/com/yhy/module/core/enums/ErrorCodeConstants.java index 8ed1bc5..3ac1614 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/enums/ErrorCodeConstants.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/enums/ErrorCodeConstants.java @@ -23,147 +23,193 @@ public interface ErrorCodeConstants { // ========== 工料机机类树模块 1-010-004-000 ========== ErrorCode RESOURCE_CATEGORY_TREE_NOT_EXISTS = new ErrorCode(1_010_004_000, "工料机机类树节点不存在"); ErrorCode RESOURCE_CATEGORY_TREE_NOT_TYPE = new ErrorCode(1_010_004_001, "该节点不是工程类型节点"); + ErrorCode RESOURCE_CATEGORY_TREE_NOT_SPECIALTY = new ErrorCode(1_010_004_002, "该节点不是工料机专业节点"); // ========== 定额目录模块 1-010-005-000 ========== ErrorCode QUOTA_CATALOG_ITEM_NOT_EXISTS = new ErrorCode(1_010_005_000, "定额目录条目不存在"); ErrorCode QUOTA_CATALOG_ITEM_HAS_CHILDREN = new ErrorCode(1_010_005_001, "该节点存在子节点,无法删除"); ErrorCode QUOTA_CATALOG_ITEM_NOT_SPECIALTY = new ErrorCode(1_010_005_002, "该节点不是定额专业节点"); - ErrorCode QUOTA_CATALOG_ITEM_ALREADY_BOUND = new ErrorCode(1_010_005_003, "该定额专业已绑定工料机专业,不可修改"); - ErrorCode QUOTA_CATALOG_ITEM_CONTENT_NO_CHILDREN = new ErrorCode(1_010_005_004, "内容节点不允许添加子节点"); - ErrorCode QUOTA_CATALOG_ITEM_CONTENT_NEED_PARENT = new ErrorCode(1_010_005_005, "内容节点必须有父节点"); - ErrorCode QUOTA_CATALOG_ITEM_PARENT_NOT_DIRECTORY = new ErrorCode(1_010_005_006, "父节点必须是目录节点"); - ErrorCode QUOTA_CATALOG_ITEM_TYPE_MISMATCH = new ErrorCode(1_010_005_007, "节点类型不匹配"); + ErrorCode QUOTA_CATALOG_ITEM_NOT_SAME_LEVEL = new ErrorCode(1_010_005_003, "两个节点不在同一层级,无法交换排序"); + ErrorCode QUOTA_CATALOG_ITEM_ALREADY_BOUND = new ErrorCode(1_010_005_004, "该定额专业已绑定工料机专业,无法重复绑定"); + ErrorCode QUOTA_CATALOG_ITEM_CONTENT_NO_CHILDREN = new ErrorCode(1_010_005_005, "内容节点不允许添加子节点"); + ErrorCode QUOTA_CATALOG_ITEM_CONTENT_NEED_PARENT = new ErrorCode(1_010_005_006, "内容节点必须有父节点"); + ErrorCode QUOTA_CATALOG_ITEM_PARENT_NOT_DIRECTORY = new ErrorCode(1_010_005_007, "父节点必须是目录节点"); + ErrorCode QUOTA_CATALOG_ITEM_TYPE_MISMATCH = new ErrorCode(1_010_005_008, "节点类型不匹配"); + ErrorCode QUOTA_CATALOG_ITEM_PARENT_NOT_EXISTS = new ErrorCode(1_010_005_009, "父节点不存在"); + ErrorCode QUOTA_CATALOG_ITEM_SPECIALTY_NOT_FOUND = new ErrorCode(1_010_005_010, "未找到定额专业节点"); - // ========== 定额子目模块 1-010-006-000 ========== - ErrorCode QUOTA_ITEM_NOT_EXISTS = new ErrorCode(1_010_006_000, "定额子目不存在"); - ErrorCode QUOTA_ITEM_HAS_RESOURCES = new ErrorCode(1_010_006_001, "该定额子目存在工料机组成,无法删除"); - ErrorCode QUOTA_CATALOG_ITEM_NOT_CONTENT = new ErrorCode(1_010_006_002, "只有内容节点才能添加定额子目"); - ErrorCode QUOTA_SPECIALTY_NOT_BOUND = new ErrorCode(1_010_006_003, "定额专业尚未绑定工料机专业"); - ErrorCode QUOTA_SPECIALTY_NOT_FOUND = new ErrorCode(1_010_006_004, "未找到定额专业节点"); + // ========== 定额子目树模块 1-010-006-000 ========== + ErrorCode QUOTA_CATALOG_TREE_NOT_EXISTS = new ErrorCode(1_010_006_000, "定额子目树节点不存在"); + ErrorCode QUOTA_CATALOG_TREE_HAS_CHILDREN = new ErrorCode(1_010_006_001, "该节点存在子节点,无法删除"); + ErrorCode QUOTA_CATALOG_TREE_PARENT_NOT_EXISTS = new ErrorCode(1_010_006_002, "父节点不存在"); + ErrorCode QUOTA_CATALOG_TREE_PARENT_NOT_ALLOW_CHILDREN = new ErrorCode(1_010_006_003, "父节点不允许添加子节点"); + ErrorCode QUOTA_CATALOG_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_006_004, "两个节点不在同一层级"); - // ========== 定额工料机组成模块 1-010-007-000 ========== - ErrorCode QUOTA_RESOURCE_NOT_EXISTS = new ErrorCode(1_010_007_000, "定额工料机组成不存在"); - ErrorCode QUOTA_RESOURCE_OUT_OF_SCOPE = new ErrorCode(1_010_007_001, "该工料机不在定额专业的数据范围内"); + // ========== 定额子目模块 1-010-007-000 ========== + ErrorCode QUOTA_ITEM_NOT_EXISTS = new ErrorCode(1_010_007_000, "定额子目不存在"); + ErrorCode QUOTA_ITEM_HAS_RESOURCES = new ErrorCode(1_010_007_001, "该定额子目存在工料机组成,无法删除"); + ErrorCode QUOTA_CATALOG_ITEM_NOT_CONTENT = new ErrorCode(1_010_007_002, "只有内容节点才能添加定额子目"); + ErrorCode QUOTA_SPECIALTY_NOT_BOUND = new ErrorCode(1_010_007_003, "定额专业尚未绑定工料机专业"); + ErrorCode QUOTA_SPECIALTY_NOT_FOUND = new ErrorCode(1_010_007_004, "未找到定额专业节点"); + + // ========== 定额工料机组成模块 1-010-008-000 ========== + ErrorCode QUOTA_RESOURCE_NOT_EXISTS = new ErrorCode(1_010_008_000, "定额工料机组成不存在"); + ErrorCode QUOTA_RESOURCE_OUT_OF_SCOPE = new ErrorCode(1_010_008_001, "该工料机不在定额专业的数据范围内"); ErrorCode RESOURCE_CATALOG_ITEM_NOT_EXISTS = new ErrorCode(1_010_007_002, "工料机目录条目不存在"); - // ========== 定额费率项模块 1-010-008-000 ========== - ErrorCode QUOTA_RATE_ITEM_NOT_EXISTS = new ErrorCode(1_010_008_000, "定额费率项不存在"); - ErrorCode QUOTA_RATE_ITEM_HAS_CHILDREN = new ErrorCode(1_010_008_001, "该费率项存在子节点,无法删除"); - ErrorCode QUOTA_RATE_ITEM_LEVEL_EXCEED = new ErrorCode(1_010_008_002, "费率项层级不能超过3级"); - ErrorCode QUOTA_RATE_ITEM_NOT_SAME_LEVEL = new ErrorCode(1_010_008_003, "只能在同级节点间交换排序"); - ErrorCode QUOTA_RATE_ITEM_NOT_SAME_MODE = new ErrorCode(1_010_008_004, "只能在同一模式下交换排序"); - ErrorCode QUOTA_RATE_ITEM_NOT_VALUE_NODE = new ErrorCode(1_010_008_005, "只有数值节点才能配置取值规则"); - ErrorCode QUOTA_RATE_ITEM_PARENT_NOT_EXISTS = new ErrorCode(1_010_008_006, "父节点不存在"); - ErrorCode QUOTA_RATE_ITEM_PARENT_NOT_SAME_MODE = new ErrorCode(1_010_008_007, "父节点不属于同一模式"); - ErrorCode QUOTA_RATE_ITEM_PARENT_NOT_DIRECTORY = new ErrorCode(1_010_008_008, "父节点必须是目录节点"); - ErrorCode QUOTA_CATALOG_ITEM_SPECIALTY_NOT_FOUND = new ErrorCode(1_010_008_009, "未找到定额专业节点"); - ErrorCode QUOTA_RATE_ITEM_CATALOG_NOT_RATE_MODE = new ErrorCode(1_010_008_010, "catalogItemId 必须指向 rate_mode 类型的节点"); + // ========== 定额费率项模块 1-010-009-000 ========== + ErrorCode QUOTA_RATE_ITEM_NOT_EXISTS = new ErrorCode(1_010_009_000, "定额费率项不存在"); + ErrorCode QUOTA_RATE_ITEM_HAS_CHILDREN = new ErrorCode(1_010_009_001, "该费率项存在子节点,无法删除"); + ErrorCode QUOTA_RATE_ITEM_LEVEL_EXCEED = new ErrorCode(1_010_009_002, "费率项层级不能超过3级"); + ErrorCode QUOTA_RATE_ITEM_NOT_SAME_LEVEL = new ErrorCode(1_010_009_003, "只能在同级节点间交换排序"); + ErrorCode QUOTA_RATE_ITEM_NOT_SAME_MODE = new ErrorCode(1_010_009_004, "只能在同一模式下交换排序"); + ErrorCode QUOTA_RATE_ITEM_NOT_VALUE_NODE = new ErrorCode(1_010_009_005, "只有数值节点才能配置取值规则"); + ErrorCode QUOTA_RATE_ITEM_PARENT_NOT_EXISTS = new ErrorCode(1_010_009_006, "父节点不存在"); + ErrorCode QUOTA_RATE_ITEM_PARENT_NOT_SAME_MODE = new ErrorCode(1_010_009_007, "父节点不属于同一模式"); + ErrorCode QUOTA_RATE_ITEM_PARENT_NOT_DIRECTORY = new ErrorCode(1_010_009_008, "父节点必须是目录节点"); + ErrorCode QUOTA_RATE_ITEM_NOT_DIRECTORY_CANNOT_SET_EDITABLE = new ErrorCode(1_010_009_009, "只有目录节点才能设置填写属性"); + ErrorCode QUOTA_RATE_ITEM_CATALOG_NOT_RATE_MODE = new ErrorCode(1_010_009_010, "catalogItemId 必须指向 rate_mode 类型的节点"); + ErrorCode QUOTA_RATE_MODE_PARENT_REQUIRED = new ErrorCode(1_010_009_011, "创建费率模式节点必须指定父节点"); + ErrorCode QUOTA_RATE_MODE_PARENT_NOT_SPECIALTY = new ErrorCode(1_010_009_012, "费率模式节点的父节点必须是定额专业节点"); + + // 阶梯规则验证错误码 + ErrorCode QUOTA_RATE_TIER_SEQ_DUPLICATE = new ErrorCode(1_010_009_020, "阶梯规则序号 [{}] 重复"); + ErrorCode QUOTA_RATE_TIER_THRESHOLD_INVALID = new ErrorCode(1_010_009_021, "阶梯规则 [{}] 的阈值必须为正数"); + ErrorCode QUOTA_RATE_TIER_COMPARE_TYPE_INVALID = new ErrorCode(1_010_009_022, "阶梯规则 [{}] 的比较类型无效"); + ErrorCode QUOTA_RATE_TIER_FIELD_VALUES_EMPTY = new ErrorCode(1_010_009_023, "阶梯规则 [{}] 的字段值不能为空"); + ErrorCode QUOTA_RATE_TIER_THRESHOLD_NOT_ASCENDING = new ErrorCode(1_010_009_024, "阶梯规则 [{}] 和 [{}] 的阈值必须递增"); + ErrorCode QUOTA_RATE_TIER_COMPARE_TYPE_MIXED = new ErrorCode(1_010_009_025, "阶梯规则 [{}] 和 [{}] 不能混用不同的比较类型(小于等于/小于 与 大于等于/大于)"); + + // 增量规则验证错误码 + ErrorCode QUOTA_RATE_INCREMENT_SEQ_DUPLICATE = new ErrorCode(1_010_009_030, "增量规则序号 [{}] 重复"); + ErrorCode QUOTA_RATE_INCREMENT_BASE_THRESHOLD_INVALID = new ErrorCode(1_010_009_031, "增量规则 [{}] 的基准阈值必须为正数"); + ErrorCode QUOTA_RATE_INCREMENT_STEP_INVALID = new ErrorCode(1_010_009_032, "增量规则 [{}] 的步长必须为正数"); + ErrorCode QUOTA_RATE_INCREMENT_FIELD_INCREMENTS_EMPTY = new ErrorCode(1_010_009_033, "增量规则 [{}] 的字段增量不能为空"); + ErrorCode QUOTA_RATE_INCREMENT_BASE_THRESHOLD_NOT_ASCENDING = new ErrorCode(1_010_009_034, "增量规则 [{}] 和 [{}] 的基准阈值必须递增"); - // ========== 定额费率字段模块 1-010-009-000 ========== - ErrorCode QUOTA_RATE_FIELD_NOT_EXISTS = new ErrorCode(1_010_009_000, "定额费率字段不存在"); - ErrorCode QUOTA_RATE_FIELD_BINDING_OUT_OF_RANGE = new ErrorCode(1_010_009_001, "绑定的节点不在当前定额专业范围内"); + // ========== 定额费率字段模块 1-010-010-000 ========== + ErrorCode QUOTA_RATE_FIELD_NOT_EXISTS = new ErrorCode(1_010_010_000, "定额费率字段不存在"); + ErrorCode QUOTA_RATE_FIELD_BINDING_OUT_OF_RANGE = new ErrorCode(1_010_010_001, "绑定的节点不在当前定额专业范围内"); + ErrorCode QUOTA_RATE_FIELD_NODE_ALREADY_BOUND = new ErrorCode(1_010_010_002, "该节点已被其他虚拟字段绑定"); - // ========== 定额取费项模块 1-010-010-000 ========== - ErrorCode QUOTA_FEE_ITEM_NOT_EXISTS = new ErrorCode(1_010_010_000, "定额取费项不存在"); - ErrorCode QUOTA_FEE_ITEM_CATALOG_NOT_RATE_MODE = new ErrorCode(1_010_010_001, "catalogItemId 必须指向 rate_mode 类型的节点"); - ErrorCode QUOTA_FEE_ITEM_CUSTOM_CODE_DUPLICATE = new ErrorCode(1_010_010_002, "自定义序号 [{}] 已存在"); - ErrorCode QUOTA_FEE_ITEM_NOT_SAME_CATALOG = new ErrorCode(1_010_010_003, "只能在同一模式下交换排序"); - ErrorCode QUOTA_FEE_CALC_BASE_FORMULA_EMPTY = new ErrorCode(1_010_010_004, "计算基数公式不能为空"); - ErrorCode QUOTA_FEE_CALC_BASE_VARIABLES_EMPTY = new ErrorCode(1_010_010_005, "计算基数变量不能为空"); + // ========== 定额费率字段标签模块 1-010-010-100 ========== + ErrorCode QUOTA_RATE_FIELD_LABEL_NOT_EXISTS = new ErrorCode(1_010_010_100, "定额费率字段标签不存在"); + ErrorCode QUOTA_RATE_FIELD_LABEL_NAME_DUPLICATE = new ErrorCode(1_010_010_101, "字段标签名称已存在"); + ErrorCode QUOTA_RATE_FIELD_LABEL_IN_USE = new ErrorCode(1_010_010_102, "字段标签正在使用中,无法删除"); + ErrorCode QUOTA_RATE_FIELD_LABEL_NOT_SAME_MODE = new ErrorCode(1_010_010_103, "两个标签不属于同一模式节点"); + + // ========== 定额取费项模块 1-010-011-000 ========== + ErrorCode QUOTA_FEE_ITEM_NOT_EXISTS = new ErrorCode(1_010_011_000, "定额取费项不存在"); + ErrorCode QUOTA_FEE_ITEM_CATALOG_NOT_RATE_MODE = new ErrorCode(1_010_011_001, "catalogItemId 必须指向 rate_mode 类型的节点"); + ErrorCode QUOTA_FEE_ITEM_CUSTOM_CODE_DUPLICATE = new ErrorCode(1_010_011_002, "自定义序号 [{}] 已存在"); + ErrorCode QUOTA_FEE_ITEM_NOT_SAME_CATALOG = new ErrorCode(1_010_011_003, "只能在同一模式下交换排序"); + ErrorCode QUOTA_FEE_CALC_BASE_FORMULA_EMPTY = new ErrorCode(1_010_011_004, "计算基数公式不能为空"); + ErrorCode QUOTA_FEE_CALC_BASE_VARIABLES_EMPTY = new ErrorCode(1_010_011_005, "计算基数变量不能为空"); ErrorCode QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE = new ErrorCode(1_010_010_006, "无效的价格代码: {}"); ErrorCode QUOTA_FEE_CALC_BASE_FORMULA_BRACKET_MISMATCH = new ErrorCode(1_010_010_007, "公式括号不匹配"); - ErrorCode QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR = new ErrorCode(1_010_010_008, "公式包含非法字符"); + ErrorCode QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR = new ErrorCode(1_010_010_008, "公式包含非法字符: {}"); - // ========== 定额调整设置模块 1-010-011-000 ========== - ErrorCode QUOTA_ADJUSTMENT_NOT_EXISTS = new ErrorCode(1_010_011_000, "定额调整设置不存在"); - ErrorCode QUOTA_ADJUSTMENT_NOT_SAME_QUOTA_ITEM = new ErrorCode(1_010_011_001, "只能在同一定额子目下交换排序"); - ErrorCode QUOTA_ADJUSTMENT_TYPE_INVALID = new ErrorCode(1_010_011_002, "无效的调整类型"); - ErrorCode QUOTA_ADJUSTMENT_RULES_INVALID = new ErrorCode(1_010_011_003, "调整规则格式不正确"); - ErrorCode QUOTA_ADJUSTMENT_MATERIAL_CODE_DUPLICATE = new ErrorCode(1_010_011_004, "工料机编码 [{}] 重复"); - ErrorCode QUOTA_ADJUSTMENT_CATEGORY_DUPLICATE = new ErrorCode(1_010_011_005, "类别ID [{}] 重复"); - ErrorCode QUOTA_ADJUSTMENT_COEFFICIENT_INVALID = new ErrorCode(1_010_011_006, "系数必须大于0"); - ErrorCode QUOTA_ADJUSTMENT_MIN_MAX_INVALID = new ErrorCode(1_010_011_007, "最小值必须小于最大值"); - ErrorCode QUOTA_ADJUSTMENT_DENOMINATOR_ZERO = new ErrorCode(1_010_011_008, "分母不能为0"); - ErrorCode QUOTA_ADJUSTMENT_BASE_VALUE_OUT_OF_RANGE = new ErrorCode(1_010_011_009, "基础值必须在最小值和最大值之间"); - ErrorCode QUOTA_ADJUSTMENT_ROUNDING_RULE_INVALID = new ErrorCode(1_010_011_010, "无效的取值规则"); - ErrorCode QUOTA_ADJUSTMENT_INPUT_VALUE_OUT_OF_RANGE = new ErrorCode(1_010_011_011, "输入值 [{}] 超出范围 [{}, {}]"); - ErrorCode QUOTA_ADJUSTMENT_RESOURCE_DUPLICATE = new ErrorCode(1_010_011_012, "工料机ID [{}] 重复"); + // ========== 定额调整设置模块(第五层)1-010-011-000 ========== + ErrorCode QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS = new ErrorCode(1_010_011_000, "定额调整设置不存在"); + ErrorCode QUOTA_ADJUSTMENT_SETTING_QUOTA_ITEM_NOT_EXISTS = new ErrorCode(1_010_011_001, "定额子目不存在"); + ErrorCode QUOTA_ADJUSTMENT_SETTING_NAME_DUPLICATE = new ErrorCode(1_010_011_002, "调整设置名称重复"); + ErrorCode QUOTA_ADJUSTMENT_SETTING_TYPE_INVALID = new ErrorCode(1_010_011_003, "调整类型无效"); + ErrorCode QUOTA_ADJUSTMENT_SETTING_RULES_INVALID = new ErrorCode(1_010_011_004, "调整规则格式无效"); + ErrorCode QUOTA_ADJUSTMENT_SETTING_HAS_DETAILS = new ErrorCode(1_010_011_005, "调整设置下存在明细,无法删除"); + ErrorCode QUOTA_ADJUSTMENT_SETTING_REFERENCED = new ErrorCode(1_010_011_006, "调整设置被其他明细引用,无法删除"); + ErrorCode QUOTA_ADJUSTMENT_SETTING_NOT_SAME_QUOTA_ITEM = new ErrorCode(1_010_011_007, "只能在同一定额子目下交换排序"); - // ========== 清单配置目录树模块 1-010-012-000 ========== - ErrorCode BOQ_CATALOG_ITEM_NOT_EXISTS = new ErrorCode(1_010_012_000, "清单配置目录树节点不存在"); - ErrorCode BOQ_CATALOG_ITEM_CODE_DUPLICATE = new ErrorCode(1_010_012_001, "编码 [{}] 已存在"); - ErrorCode BOQ_CATALOG_ITEM_INVALID_NODE_TYPE = new ErrorCode(1_010_012_002, "无效的节点类型"); - ErrorCode BOQ_CATALOG_ITEM_SPECIALTY_LOCKED = new ErrorCode(1_010_012_003, "该清单专业已绑定,不可修改或删除"); - ErrorCode BOQ_CATALOG_ITEM_HAS_CHILDREN = new ErrorCode(1_010_012_004, "该节点存在子节点,无法删除"); - ErrorCode BOQ_CATALOG_ITEM_NOT_SPECIALTY = new ErrorCode(1_010_012_005, "该节点不是清单专业节点"); - ErrorCode BOQ_CATALOG_ITEM_ALREADY_BOUND = new ErrorCode(1_010_012_006, "该清单专业已绑定定额基价"); - ErrorCode BOQ_CATALOG_ITEM_QUOTA_NOT_FIRST_LEVEL = new ErrorCode(1_010_012_007, "只能绑定定额基价第一层节点"); - ErrorCode BOQ_CATALOG_ITEM_PARENT_NOT_EXISTS = new ErrorCode(1_010_012_008, "父节点不存在"); - ErrorCode BOQ_CATALOG_ITEM_NOT_SAME_LEVEL = new ErrorCode(1_010_012_009, "只能在同级节点间交换排序"); - ErrorCode BOQ_CATALOG_ITEM_NOT_BOUND_QUOTA = new ErrorCode(1_010_012_010, "该清单专业尚未绑定定额基价"); + // ========== 定额调整明细模块(第六层)1-010-012-000 ========== + ErrorCode QUOTA_ADJUSTMENT_DETAIL_NOT_EXISTS = new ErrorCode(1_010_012_000, "定额调整明细不存在"); + ErrorCode QUOTA_ADJUSTMENT_DETAIL_SETTING_NOT_EXISTS = new ErrorCode(1_010_012_001, "调整设置不存在"); + ErrorCode QUOTA_ADJUSTMENT_DETAIL_CODE_NOT_EXISTS = new ErrorCode(1_010_012_002, "引用的调整设置不存在"); + ErrorCode QUOTA_ADJUSTMENT_DETAIL_CODE_NOT_SAME_SPECIALTY = new ErrorCode(1_010_012_003, "引用的调整设置不在同一定额专业"); + ErrorCode QUOTA_ADJUSTMENT_DETAIL_CODE_CIRCULAR = new ErrorCode(1_010_012_004, "调整明细引用形成循环"); + ErrorCode QUOTA_ADJUSTMENT_DETAIL_NOT_SAME_SETTING = new ErrorCode(1_010_012_005, "只能在同一调整设置下交换排序"); - // ========== 清单项树模块 1-010-013-000 ========== - ErrorCode BOQ_ITEM_TREE_NOT_EXISTS = new ErrorCode(1_010_013_000, "清单项树节点不存在"); - ErrorCode BOQ_ITEM_TREE_CODE_DUPLICATE = new ErrorCode(1_010_013_001, "编码 [{}] 已存在"); - ErrorCode BOQ_ITEM_TREE_HAS_CHILDREN = new ErrorCode(1_010_013_002, "该节点存在子节点,无法删除"); - ErrorCode BOQ_ITEM_TREE_PARENT_NOT_EXISTS = new ErrorCode(1_010_013_003, "父节点不存在"); - ErrorCode BOQ_ITEM_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_013_004, "只能在同级节点间交换排序"); + // ========== 清单配置目录树模块 1-010-013-000 ========== + ErrorCode BOQ_CATALOG_ITEM_NOT_EXISTS = new ErrorCode(1_010_013_000, "清单配置目录树节点不存在"); + ErrorCode BOQ_CATALOG_ITEM_CODE_DUPLICATE = new ErrorCode(1_010_013_001, "编码 [{}] 已存在"); + ErrorCode BOQ_CATALOG_ITEM_INVALID_NODE_TYPE = new ErrorCode(1_010_013_002, "无效的节点类型"); + ErrorCode BOQ_CATALOG_ITEM_SPECIALTY_LOCKED = new ErrorCode(1_010_013_003, "该清单专业已绑定,不可修改或删除"); + ErrorCode BOQ_CATALOG_ITEM_HAS_CHILDREN = new ErrorCode(1_010_013_004, "该节点存在子节点,无法删除"); + ErrorCode BOQ_CATALOG_ITEM_NOT_SPECIALTY = new ErrorCode(1_010_013_005, "该节点不是清单专业节点"); + ErrorCode BOQ_CATALOG_ITEM_ALREADY_BOUND = new ErrorCode(1_010_013_006, "该清单专业已绑定定额基价"); + ErrorCode BOQ_CATALOG_ITEM_QUOTA_NOT_FIRST_LEVEL = new ErrorCode(1_010_013_007, "只能绑定定额基价第一层节点"); + ErrorCode BOQ_CATALOG_ITEM_PARENT_NOT_EXISTS = new ErrorCode(1_010_013_008, "父节点不存在"); + ErrorCode BOQ_CATALOG_ITEM_NOT_SAME_LEVEL = new ErrorCode(1_010_013_009, "只能在同级节点间交换排序"); + ErrorCode BOQ_CATALOG_ITEM_NOT_BOUND_QUOTA = new ErrorCode(1_010_013_010, "该清单专业尚未绑定定额基价"); - // ========== 清单子项模块 1-010-014-000 ========== - ErrorCode BOQ_SUB_ITEM_NOT_EXISTS = new ErrorCode(1_010_014_000, "清单子项不存在"); - ErrorCode BOQ_SUB_ITEM_CODE_DUPLICATE = new ErrorCode(1_010_014_001, "编码 [{}] 已存在"); - ErrorCode BOQ_SUB_ITEM_NOT_SAME_TREE = new ErrorCode(1_010_014_002, "只能在同一清单项树下交换排序"); + // ========== 清单项树模块 1-010-014-000 ========== + ErrorCode BOQ_ITEM_TREE_NOT_EXISTS = new ErrorCode(1_010_014_000, "清单项树节点不存在"); + ErrorCode BOQ_ITEM_TREE_CODE_DUPLICATE = new ErrorCode(1_010_014_001, "编码 [{}] 已存在"); + ErrorCode BOQ_ITEM_TREE_HAS_CHILDREN = new ErrorCode(1_010_014_002, "该节点存在子节点,无法删除"); + ErrorCode BOQ_ITEM_TREE_PARENT_NOT_EXISTS = new ErrorCode(1_010_014_003, "父节点不存在"); + ErrorCode BOQ_ITEM_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_014_004, "只能在同级节点间交换排序"); - // ========== 清单明细树模块 1-010-015-000 ========== - ErrorCode BOQ_DETAIL_TREE_NOT_EXISTS = new ErrorCode(1_010_015_000, "清单明细树节点不存在"); - ErrorCode BOQ_DETAIL_TREE_CODE_DUPLICATE = new ErrorCode(1_010_015_001, "编码 [{}] 已存在"); - ErrorCode BOQ_DETAIL_TREE_HAS_CHILDREN = new ErrorCode(1_010_015_002, "该节点存在子节点,无法删除"); - ErrorCode BOQ_DETAIL_TREE_PARENT_NOT_DIRECTORY = new ErrorCode(1_010_015_003, "父节点必须是目录类型"); - ErrorCode BOQ_DETAIL_TREE_INVALID_NODE_TYPE = new ErrorCode(1_010_015_004, "无效的节点类型"); - ErrorCode BOQ_DETAIL_TREE_CONTENT_NEED_QUOTA = new ErrorCode(1_010_015_005, "内容节点必须关联定额"); - ErrorCode BOQ_DETAIL_TREE_DIRECTORY_NO_QUOTA = new ErrorCode(1_010_015_006, "目录节点不能关联定额"); - ErrorCode BOQ_DETAIL_TREE_QUOTA_NOT_IN_RANGE = new ErrorCode(1_010_015_007, "该定额不在绑定的定额专业范围内"); - ErrorCode BOQ_DETAIL_TREE_QUOTA_NOT_CONTENT_TYPE = new ErrorCode(1_010_015_008, "只能关联定额基价第三层的内容节点"); - ErrorCode BOQ_DETAIL_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_015_009, "只能在同级节点间交换排序"); + // ========== 清单子项模块 1-010-015-000 ========== + ErrorCode BOQ_SUB_ITEM_NOT_EXISTS = new ErrorCode(1_010_015_000, "清单子项不存在"); + ErrorCode BOQ_SUB_ITEM_CODE_DUPLICATE = new ErrorCode(1_010_015_001, "编码 [{}] 已存在"); + ErrorCode BOQ_SUB_ITEM_NOT_SAME_TREE = new ErrorCode(1_010_015_002, "只能在同一清单项树下交换排序"); - // ========== 信息价树结构模块 1-010-016-000 ========== - ErrorCode INFO_PRICE_TREE_NOT_EXISTS = new ErrorCode(1_010_016_000, "信息价树节点不存在"); - ErrorCode INFO_PRICE_TREE_CODE_DUPLICATE = new ErrorCode(1_010_016_001, "编码 [{}] 已存在"); - ErrorCode INFO_PRICE_TREE_HAS_CHILDREN = new ErrorCode(1_010_016_002, "该节点存在子节点,无法删除"); - ErrorCode INFO_PRICE_TREE_HAS_BOOKS = new ErrorCode(1_010_016_003, "该节点存在信息价册,无法删除"); - ErrorCode INFO_PRICE_TREE_PARENT_NOT_EXISTS = new ErrorCode(1_010_016_004, "父节点不存在"); - ErrorCode INFO_PRICE_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_016_005, "只能在同级节点间交换排序"); - ErrorCode INFO_PRICE_TREE_NOT_SAME_ENUM_TYPE = new ErrorCode(1_010_016_006, "只能在同一枚举类型下交换排序"); - ErrorCode INFO_PRICE_TREE_INVALID_ENUM_TYPE = new ErrorCode(1_010_016_007, "无效的枚举类型"); + // ========== 清单明细树模块 1-010-016-000 ========== + ErrorCode BOQ_DETAIL_TREE_NOT_EXISTS = new ErrorCode(1_010_016_000, "清单明细树节点不存在"); + ErrorCode BOQ_DETAIL_TREE_CODE_DUPLICATE = new ErrorCode(1_010_016_001, "编码 [{}] 已存在"); + ErrorCode BOQ_DETAIL_TREE_HAS_CHILDREN = new ErrorCode(1_010_016_002, "该节点存在子节点,无法删除"); + ErrorCode BOQ_DETAIL_TREE_PARENT_NOT_DIRECTORY = new ErrorCode(1_010_016_003, "父节点必须是目录类型"); + ErrorCode BOQ_DETAIL_TREE_INVALID_NODE_TYPE = new ErrorCode(1_010_016_004, "无效的节点类型"); + ErrorCode BOQ_DETAIL_TREE_CONTENT_NEED_QUOTA = new ErrorCode(1_010_016_005, "内容节点必须关联定额"); + ErrorCode BOQ_DETAIL_TREE_DIRECTORY_NO_QUOTA = new ErrorCode(1_010_016_006, "目录节点不能关联定额"); + ErrorCode BOQ_DETAIL_TREE_QUOTA_NOT_IN_RANGE = new ErrorCode(1_010_016_007, "该定额不在绑定的定额专业范围内"); + ErrorCode BOQ_DETAIL_TREE_QUOTA_NOT_CONTENT_TYPE = new ErrorCode(1_010_016_008, "只能关联定额基价第三层的内容节点"); + ErrorCode BOQ_DETAIL_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_016_009, "只能在同级节点间交换排序"); - // ========== 信息价册模块 1-010-017-000 ========== - ErrorCode INFO_PRICE_BOOK_NOT_EXISTS = new ErrorCode(1_010_017_000, "信息价册不存在"); - ErrorCode INFO_PRICE_BOOK_TIME_OVERLAP = new ErrorCode(1_010_017_001, "时间段与已有数据重叠"); - ErrorCode INFO_PRICE_BOOK_TIME_INVALID = new ErrorCode(1_010_017_002, "结束时间必须大于等于开始时间"); - ErrorCode INFO_PRICE_BOOK_INVALID_PUBLISH_STATUS = new ErrorCode(1_010_017_003, "无效的发布状态"); - ErrorCode INFO_PRICE_BOOK_HAS_ITEMS = new ErrorCode(1_010_017_004, "该信息价册存在条目,无法删除"); + // ========== 信息价树结构模块 1-010-017-000 ========== + ErrorCode INFO_PRICE_TREE_NOT_EXISTS = new ErrorCode(1_010_017_000, "信息价树节点不存在"); + ErrorCode INFO_PRICE_TREE_CODE_DUPLICATE = new ErrorCode(1_010_017_001, "编码 [{}] 已存在"); + ErrorCode INFO_PRICE_TREE_HAS_CHILDREN = new ErrorCode(1_010_017_002, "该节点存在子节点,无法删除"); + ErrorCode INFO_PRICE_TREE_HAS_BOOKS = new ErrorCode(1_010_017_003, "该节点存在信息价册,无法删除"); + ErrorCode INFO_PRICE_TREE_PARENT_NOT_EXISTS = new ErrorCode(1_010_017_004, "父节点不存在"); + ErrorCode INFO_PRICE_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_017_005, "只能在同级节点间交换排序"); + ErrorCode INFO_PRICE_TREE_NOT_SAME_ENUM_TYPE = new ErrorCode(1_010_017_006, "只能在同一枚举类型下交换排序"); + ErrorCode INFO_PRICE_TREE_INVALID_ENUM_TYPE = new ErrorCode(1_010_017_007, "无效的枚举类型"); - // ========== 信息价分类树模块 1-010-018-000 ========== - ErrorCode INFO_PRICE_CATEGORY_TREE_NOT_EXISTS = new ErrorCode(1_010_018_000, "信息价分类树节点不存在"); - ErrorCode INFO_PRICE_CATEGORY_TREE_CODE_DUPLICATE = new ErrorCode(1_010_018_001, "编码 [{}] 已存在"); - ErrorCode INFO_PRICE_CATEGORY_TREE_HAS_CHILDREN = new ErrorCode(1_010_018_002, "该节点存在子节点,无法删除"); - ErrorCode INFO_PRICE_CATEGORY_TREE_HAS_RESOURCES = new ErrorCode(1_010_018_003, "该节点存在工料机信息,无法删除"); - ErrorCode INFO_PRICE_CATEGORY_TREE_PARENT_NOT_EXISTS = new ErrorCode(1_010_018_004, "父节点不存在"); - ErrorCode INFO_PRICE_CATEGORY_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_018_005, "只能在同级节点间交换排序"); - ErrorCode INFO_PRICE_CATEGORY_TREE_CONTENT_NO_CHILDREN = new ErrorCode(1_010_018_006, "内容节点不允许添加子节点"); - ErrorCode INFO_PRICE_CATEGORY_TREE_INVALID_NODE_TYPE = new ErrorCode(1_010_018_007, "无效的节点类型"); + // ========== 信息价册模块 1-010-018-000 ========== + ErrorCode INFO_PRICE_BOOK_NOT_EXISTS = new ErrorCode(1_010_018_000, "信息价册不存在"); + ErrorCode INFO_PRICE_BOOK_TIME_OVERLAP = new ErrorCode(1_010_018_001, "时间段与已有数据重叠"); + ErrorCode INFO_PRICE_BOOK_TIME_INVALID = new ErrorCode(1_010_018_002, "结束时间必须大于等于开始时间"); + ErrorCode INFO_PRICE_BOOK_INVALID_PUBLISH_STATUS = new ErrorCode(1_010_018_003, "无效的发布状态"); + ErrorCode INFO_PRICE_BOOK_HAS_ITEMS = new ErrorCode(1_010_018_004, "该信息价册存在条目,无法删除"); - // ========== 信息价工料机信息模块 1-010-019-000 ========== - ErrorCode INFO_PRICE_RESOURCE_NOT_EXISTS = new ErrorCode(1_010_019_000, "信息价工料机信息不存在"); - ErrorCode INFO_PRICE_RESOURCE_CODE_DUPLICATE = new ErrorCode(1_010_019_001, "编码 [{}] 已存在"); - ErrorCode INFO_PRICE_RESOURCE_ONLY_UNDER_CONTENT_NODE = new ErrorCode(1_010_019_002, "只有内容节点才能添加工料机信息"); - ErrorCode INFO_PRICE_RESOURCE_HAS_PRICES = new ErrorCode(1_010_019_003, "该工料机信息存在价格历史,无法删除"); - ErrorCode INFO_PRICE_RESOURCE_NOT_SAME_CATEGORY = new ErrorCode(1_010_019_004, "只能在同一分类节点下交换排序"); + // ========== 信息价分类树模块 1-010-019-000 ========== + ErrorCode INFO_PRICE_CATEGORY_TREE_NOT_EXISTS = new ErrorCode(1_010_019_000, "信息价分类树节点不存在"); + ErrorCode INFO_PRICE_CATEGORY_TREE_CODE_DUPLICATE = new ErrorCode(1_010_019_001, "编码 [{}] 已存在"); + ErrorCode INFO_PRICE_CATEGORY_TREE_HAS_CHILDREN = new ErrorCode(1_010_019_002, "该节点存在子节点,无法删除"); + ErrorCode INFO_PRICE_CATEGORY_TREE_HAS_RESOURCES = new ErrorCode(1_010_019_003, "该节点存在工料机信息,无法删除"); + ErrorCode INFO_PRICE_CATEGORY_TREE_PARENT_NOT_EXISTS = new ErrorCode(1_010_019_004, "父节点不存在"); + ErrorCode INFO_PRICE_CATEGORY_TREE_NOT_SAME_LEVEL = new ErrorCode(1_010_019_005, "只能在同级节点间交换排序"); + ErrorCode INFO_PRICE_CATEGORY_TREE_CONTENT_NO_CHILDREN = new ErrorCode(1_010_019_006, "内容节点不允许添加子节点"); + ErrorCode INFO_PRICE_CATEGORY_TREE_INVALID_NODE_TYPE = new ErrorCode(1_010_019_007, "无效的节点类型"); - // ========== 信息价工料机价格历史模块 1-010-020-000 ========== - ErrorCode INFO_PRICE_RESOURCE_PRICE_NOT_EXISTS = new ErrorCode(1_010_020_000, "信息价工料机价格历史不存在"); - ErrorCode INFO_PRICE_RESOURCE_PRICE_TIME_OVERLAP = new ErrorCode(1_010_020_001, "时间段与已有数据重叠"); - ErrorCode INFO_PRICE_RESOURCE_PRICE_TIME_INVALID = new ErrorCode(1_010_020_002, "结束时间必须大于等于开始时间"); - ErrorCode INFO_PRICE_RESOURCE_PRICE_TIME_NOT_CONTINUOUS = new ErrorCode(1_010_020_003, "时间段必须与前一条记录连续,期望开始时间为 [{}]"); + // ========== 信息价工料机信息模块 1-010-020-000 ========== + ErrorCode INFO_PRICE_RESOURCE_NOT_EXISTS = new ErrorCode(1_010_020_000, "信息价工料机信息不存在"); + ErrorCode INFO_PRICE_RESOURCE_CODE_DUPLICATE = new ErrorCode(1_010_020_001, "编码 [{}] 已存在"); + ErrorCode INFO_PRICE_RESOURCE_ONLY_UNDER_CONTENT_NODE = new ErrorCode(1_010_020_002, "只有内容节点才能添加工料机信息"); + ErrorCode INFO_PRICE_RESOURCE_HAS_PRICES = new ErrorCode(1_010_020_003, "该工料机信息存在价格历史,无法删除"); + ErrorCode INFO_PRICE_RESOURCE_NOT_SAME_CATEGORY = new ErrorCode(1_010_020_004, "只能在同一分类节点下交换排序"); + + // ========== 信息价工料机价格历史模块 1-010-021-000 ========== + ErrorCode INFO_PRICE_RESOURCE_PRICE_NOT_EXISTS = new ErrorCode(1_010_021_000, "信息价工料机价格历史不存在"); + ErrorCode INFO_PRICE_RESOURCE_PRICE_TIME_OVERLAP = new ErrorCode(1_010_021_001, "时间段与已有数据重叠"); + ErrorCode INFO_PRICE_RESOURCE_PRICE_TIME_INVALID = new ErrorCode(1_010_021_002, "结束时间必须大于等于开始时间"); + ErrorCode INFO_PRICE_RESOURCE_PRICE_TIME_NOT_CONTINUOUS = new ErrorCode(1_010_021_003, "时间段必须与前一条记录连续,期望开始时间为 [{}]"); + + // ========== 复合工料机模块 1-010-021-000 ========== + ErrorCode RESOURCE_MERGED_NOT_EXISTS = new ErrorCode(1_010_021_000, "复合工料机不存在"); + ErrorCode RESOURCE_MERGED_SOURCE_NOT_EXISTS = new ErrorCode(1_010_021_001, "复合工料机引用的数据源不存在"); + ErrorCode RESOURCE_MERGED_CALC_BASE_INVALID = new ErrorCode(1_010_021_002, "计算基数配置无效"); + ErrorCode RESOURCE_MERGED_CALC_BASE_CATEGORY_NOT_FOUND = new ErrorCode(1_010_021_003, "计算基数引用的类别在同组中未找到"); + ErrorCode RESOURCE_MERGED_FORMULA_PARSE_ERROR = new ErrorCode(1_010_021_004, "公式解析失败"); + ErrorCode RESOURCE_MERGED_SELF_REFERENCE = new ErrorCode(1_010_021_005, "不允许自己关联自己"); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/enums/quota/QuotaAdjustmentTypeEnum.java b/yhy-module-core/src/main/java/com/yhy/module/core/enums/quota/QuotaAdjustmentTypeEnum.java deleted file mode 100644 index 06a3662..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/enums/quota/QuotaAdjustmentTypeEnum.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.yhy.module.core.enums.quota; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 定额调整类型枚举 - */ -@Getter -@AllArgsConstructor -public enum QuotaAdjustmentTypeEnum { - - PERCENTAGE(1, "percentage", "百分比调整"), - FIXED_VALUE(2, "fixed_value", "固定值调整"), - FORMULA(3, "formula", "公式计算"), - CONDITIONAL(4, "conditional", "条件调整"), - MATERIAL_ADJUSTMENT(5, "material_adjustment", "增减材料消耗量"), - DYNAMIC_ADJUSTMENT(6, "dynamic_adjustment", "动态调整人才机消耗量"), - COEFFICIENT_ADJUSTMENT(7, "coefficient_adjustment", "调整人才机消耗量"), - DYNAMIC_MERGE_QUOTA(8, "dynamic_merge_quota", "动态合并定额"); - - private final Integer type; - private final String code; - private final String name; - - public static QuotaAdjustmentTypeEnum valueOf(Integer type) { - return Arrays.stream(values()) - .filter(e -> e.getType().equals(type)) - .findFirst() - .orElse(null); - } - - public static QuotaAdjustmentTypeEnum valueOfCode(String code) { - return Arrays.stream(values()) - .filter(e -> e.getCode().equals(code)) - .findFirst() - .orElse(null); - } -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentDetailService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentDetailService.java new file mode 100644 index 0000000..2c195fb --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentDetailService.java @@ -0,0 +1,87 @@ +package com.yhy.module.core.service.quota; + +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentCombinedRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentDetailSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDetailDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentSettingDO; +import java.util.List; +import javax.validation.Valid; + +/** + * 定额调整明细 Service 接口 + * + * @author 芋道源码 + */ +public interface QuotaAdjustmentDetailService { + + /** + * 创建定额调整明细 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createQuotaAdjustmentDetail(@Valid QuotaAdjustmentDetailSaveReqVO createReqVO); + + /** + * 更新定额调整明细 + * + * @param updateReqVO 更新信息 + */ + void updateQuotaAdjustmentDetail(@Valid QuotaAdjustmentDetailSaveReqVO updateReqVO); + + /** + * 删除定额调整明细 + * + * @param id 编号 + */ + void deleteQuotaAdjustmentDetail(Long id); + + /** + * 获得定额调整明细 + * + * @param id 编号 + * @return 定额调整明细 + */ + QuotaAdjustmentDetailDO getQuotaAdjustmentDetail(Long id); + + /** + * 获得定额调整明细列表(按定额子目ID) + * + * @param quotaItemId 定额子目ID + * @return 定额调整明细列表 + */ + List getQuotaAdjustmentDetailListByQuotaItem(Long quotaItemId); + + /** + * 获得定额调整明细列表(按调整设置ID) + * + * @param adjustmentSettingId 调整设置ID + * @return 定额调整明细列表 + */ + List getQuotaAdjustmentDetailList(Long adjustmentSettingId); + + /** + * 交换排序 + * + * @param id1 第一个调整明细ID + * @param id2 第二个调整明细ID + */ + void swapSort(Long id1, Long id2); + + /** + * 获取可引用的调整设置列表(同一定额专业) + * + * @param adjustmentSettingId 当前调整设置ID + * @return 可引用的调整设置列表 + */ + List getAvailableSettings(Long adjustmentSettingId); + + /** + * 获取调整设置与明细的组合列表(按定额子目ID) + * + * @param quotaItemId 定额子目ID + * @return 调整设置与明细的组合列表 + */ + List getCombinedList(Long quotaItemId); + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentService.java deleted file mode 100644 index c65d70a..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentService.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.yhy.module.core.service.quota; - -import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSaveReqVO; -import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDO; - -import javax.validation.Valid; -import java.math.BigDecimal; -import java.util.List; -import java.util.Map; - -/** - * 定额调整设置 Service 接口 - */ -public interface QuotaAdjustmentService { - - /** - * 创建定额调整设置 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createQuotaAdjustment(@Valid QuotaAdjustmentSaveReqVO createReqVO); - - /** - * 更新定额调整设置 - * - * @param updateReqVO 更新信息 - */ - void updateQuotaAdjustment(@Valid QuotaAdjustmentSaveReqVO updateReqVO); - - /** - * 删除定额调整设置 - * - * @param id 编号 - */ - void deleteQuotaAdjustment(Long id); - - /** - * 获得定额调整设置 - * - * @param id 编号 - * @return 定额调整设置 - */ - QuotaAdjustmentDO getQuotaAdjustment(Long id); - - /** - * 获得定额调整设置列表 - * - * @param quotaItemId 定额子目ID - * @return 定额调整设置列表 - */ - List getQuotaAdjustmentList(Long quotaItemId); - - /** - * 交换排序 - * - * @param id1 调整设置ID1 - * @param id2 调整设置ID2 - */ - void swapSort(Long id1, Long id2); - - /** - * 计算调整后的消耗量 - * - * @param quotaItemId 定额子目ID - * @param originalConsumeQty 原始消耗量 - * @return 调整后的消耗量 - */ - BigDecimal calculateAdjustedConsumeQty(Long quotaItemId, BigDecimal originalConsumeQty); - - /** - * 计算指定工料机的调整后消耗量 - * - * @param quotaItemId 定额子目ID - * @param resourceCode 工料机编码 - * @param originalDosage 原始定额消耗量 - * @return 调整后的消耗量 - */ - BigDecimal calculateAdjustedDosage(Long quotaItemId, String resourceCode, BigDecimal originalDosage); - - /** - * 保存材料调整明细 - * - * @param adjustmentId 调整设置ID - * @param items 材料明细列表 - */ - void saveMaterialItems(Long adjustmentId, List> items); - - /** - * 获取材料调整明细 - * - * @param adjustmentId 调整设置ID - * @return 材料明细列表 - */ - List> getMaterialItems(Long adjustmentId); - - /** - * 获取可配置的类别列表 - * - * @param quotaItemId 定额子目ID - * @return 类别列表 - */ - List> getAvailableCategories(Long quotaItemId); - - /** - * 保存动态调整配置 - * - * @param adjustmentId 调整设置ID - * @param categories 类别配置列表 - */ - void saveDynamicConfig(Long adjustmentId, List> categories); - - /** - * 获取动态调整配置 - * - * @param adjustmentId 调整设置ID - * @return 类别配置列表 - */ - List> getDynamicConfig(Long adjustmentId); - - /** - * 计算动态调整结果 - * - * @param adjustmentId 调整设置ID - * @param inputValues 输入值映射(类别ID: 输入值) - * @return 调整结果映射(类别ID: 调整结果) - */ - Map calculateDynamic(Long adjustmentId, Map inputValues); - - /** - * 保存系数调整配置 - * - * @param adjustmentId 调整设置ID - * @param categories 类别配置列表 - */ - void saveCoefficientConfig(Long adjustmentId, List> categories); - - /** - * 获取系数调整配置 - * - * @param adjustmentId 调整设置ID - * @return 类别配置列表 - */ - List> getCoefficientConfig(Long adjustmentId); - - /** - * 获取可选的工料机列表 - * - * @param quotaItemId 定额子目ID - * @return 工料机列表 - */ - List> getAvailableResources(Long quotaItemId); - - /** - * 保存动态合并配置 - * - * @param adjustmentId 调整设置ID - * @param resources 工料机配置列表 - */ - void saveMergeConfig(Long adjustmentId, List> resources); - - /** - * 获取动态合并配置 - * - * @param adjustmentId 调整设置ID - * @return 工料机配置列表 - */ - List> getMergeConfig(Long adjustmentId); - - /** - * 计算动态合并结果 - * - * @param adjustmentId 调整设置ID - * @param inputValues 输入值映射(工料机ID: 输入值) - * @return 调整结果映射(工料机ID: 调整结果) - */ - Map calculateMerge(Long adjustmentId, Map inputValues); -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentSettingService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentSettingService.java new file mode 100644 index 0000000..d159737 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaAdjustmentSettingService.java @@ -0,0 +1,61 @@ +package com.yhy.module.core.service.quota; + +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSettingSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentSettingDO; +import java.util.List; +import javax.validation.Valid; + +/** + * 定额调整设置 Service 接口 + * + * @author 芋道源码 + */ +public interface QuotaAdjustmentSettingService { + + /** + * 创建定额调整设置 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createQuotaAdjustmentSetting(@Valid QuotaAdjustmentSettingSaveReqVO createReqVO); + + /** + * 更新定额调整设置 + * + * @param updateReqVO 更新信息 + */ + void updateQuotaAdjustmentSetting(@Valid QuotaAdjustmentSettingSaveReqVO updateReqVO); + + /** + * 删除定额调整设置 + * + * @param id 编号 + */ + void deleteQuotaAdjustmentSetting(Long id); + + /** + * 获得定额调整设置 + * + * @param id 编号 + * @return 定额调整设置 + */ + QuotaAdjustmentSettingDO getQuotaAdjustmentSetting(Long id); + + /** + * 获得定额调整设置列表 + * + * @param quotaItemId 定额子目ID + * @return 定额调整设置列表 + */ + List getQuotaAdjustmentSettingList(Long quotaItemId); + + /** + * 交换排序 + * + * @param id1 第一个调整设置ID + * @param id2 第二个调整设置ID + */ + void swapSort(Long id1, Long id2); + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaCatalogItemService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaCatalogItemService.java index e5a0949..f5d5e1c 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaCatalogItemService.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaCatalogItemService.java @@ -2,10 +2,10 @@ package com.yhy.module.core.service.quota; import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryFullRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; - -import javax.validation.Valid; import java.util.List; +import javax.validation.Valid; /** * 定额目录条目 Service 接口 @@ -45,26 +45,18 @@ public interface QuotaCatalogItemService { QuotaCatalogItemDO getQuotaCatalogItem(Long id); /** - * 获得定额目录条目列表 + * 获得定额专业节点列表(第一层) * - * @param parentId 父节点ID - * @return 定额目录条目列表 + * @return 定额专业节点列表 */ - List getQuotaCatalogItemList(Long parentId); - - /** - * 获得定额目录树 - * - * @return 定额目录树 - */ - List getQuotaCatalogItemTree(); + List getQuotaCatalogItemList(); /** * 绑定工料机专业 * * 业务规则: * 1. 只有 node_type='specialty' 的节点才能绑定 - * 2. 只能绑定 node_type='type' 的机类树节点 + * 2. 只能绑定 node_type='specialty' 的机类树节点 * 3. 绑定后不可修改(specialty_locked=true) * * @param catalogItemId 定额专业节点ID @@ -72,30 +64,6 @@ public interface QuotaCatalogItemService { */ void bindResourceSpecialty(Long catalogItemId, Long categoryTreeId); - /** - * 添加目录节点 - * - * 业务规则: - * 1. 父节点必须是 content_type='directory' 或根节点 - * 2. 自动设置 content_type='directory', allow_children=true - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long addDirectory(@Valid QuotaCatalogItemSaveReqVO createReqVO); - - /** - * 添加内容节点 - * - * 业务规则: - * 1. 父节点必须是 content_type='directory' - * 2. 自动设置 content_type='content', allow_children=false - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long addContent(@Valid QuotaCatalogItemSaveReqVO createReqVO); - /** * 验证节点类型 * @@ -105,10 +73,31 @@ public interface QuotaCatalogItemService { void validateNodeType(Long id, String expectedType); /** - * 检查节点是否有子节点 + * 获取定额专业树结构(第一层) * - * @param id 节点ID - * @return 是否有子节点 + * @param excludeNodeTypes 要排除的节点类型列表 + * @return 树形结构 */ - boolean hasChildren(Long id); + List getQuotaCatalogItemTree(List excludeNodeTypes); + + /** + * 交换两个节点的排序 + * + * @param nodeId1 节点1的ID + * @param nodeId2 节点2的ID + */ + void swapSort(Long nodeId1, Long nodeId2); + + /** + * 通过费率模式节点获取关联的工料机类别字典(含价格代码) + * + * 业务逻辑: + * 1. 如果传入的是费率模式节点,向上查找定额专业节点 + * 2. 获取定额专业节点绑定的 category_tree_id + * 3. 查询该工料机专业下的所有类别字典(含价格代码) + * + * @param catalogItemId 定额目录节点ID(可以是费率模式节点或定额专业节点) + * @return 类别字典列表(含价格代码) + */ + List getCategoriesByCatalogItem(Long catalogItemId); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaCatalogTreeService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaCatalogTreeService.java new file mode 100644 index 0000000..46bfd24 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaCatalogTreeService.java @@ -0,0 +1,70 @@ +package com.yhy.module.core.service.quota; + +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogTreeRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogTreeSaveReqVO; +import java.util.List; +import javax.validation.Valid; + +/** + * 定额子目树 Service 接口 + * + * @author yhy + */ +public interface QuotaCatalogTreeService { + + /** + * 创建定额子目树节点 + */ + Long createCatalogTree(@Valid QuotaCatalogTreeSaveReqVO createReqVO); + + /** + * 更新定额子目树节点 + */ + void updateCatalogTree(@Valid QuotaCatalogTreeSaveReqVO updateReqVO); + + /** + * 删除定额子目树节点 + */ + void deleteCatalogTree(Long id); + + /** + * 获取定额子目树节点详情 + */ + QuotaCatalogTreeRespVO getCatalogTree(Long id); + + /** + * 获取定额子目树节点列表 + */ + List getCatalogTreeList(Long catalogItemId, Long parentId); + + /** + * 获取定额子目树结构 + */ + List getCatalogTreeTree(Long catalogItemId); + + /** + * 交换排序 + */ + void swapSort(Long nodeId1, Long nodeId2); + + /** + * 根据费率项ID查询绑定的定额子目树 + * + * @param rateItemId 费率项ID + * @return 绑定的定额子目树列表 + */ + List getCatalogTreeByRateItem(Long rateItemId); + + /** + * 根据费率模式节点ID查询所属定额专业的子目录树 + * + * 业务逻辑: + * 1. 验证传入的节点是 rate_mode 类型 + * 2. 向上追溯找到所属的定额专业节点(node_type='specialty') + * 3. 查询该定额专业节点下的所有子目录树(第二层) + * + * @param rateModeNodeId 费率模式节点ID + * @return 子目录树列表 + */ + List getCatalogTreeByRateModeNode(Long rateModeNodeId); +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaFeeItemService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaFeeItemService.java index debdf51..f2f63d2 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaFeeItemService.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaFeeItemService.java @@ -1,10 +1,10 @@ package com.yhy.module.core.service.quota; import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemWithRateRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaFeeItemDO; - -import javax.validation.Valid; import java.util.List; +import javax.validation.Valid; /** * 定额取费项 Service 接口 @@ -36,6 +36,16 @@ public interface QuotaFeeItemService { */ List getFeeItemList(Long catalogItemId); + /** + * 获取取费项与费率项合并列表 + * + * 将费率项树的一级节点与取费项进行一对一关联展示 + * + * @param catalogItemId 模式节点ID + * @return 合并后的列表 + */ + List getFeeItemWithRateList(Long catalogItemId); + /** * 交换排序 */ diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateFieldLabelService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateFieldLabelService.java new file mode 100644 index 0000000..115e98a --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateFieldLabelService.java @@ -0,0 +1,70 @@ +package com.yhy.module.core.service.quota; + +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldLabelDO; +import java.util.List; +import javax.validation.Valid; + +/** + * 定额费率字段标签字典 Service 接口 + * + * @author yhy + */ +public interface QuotaRateFieldLabelService { + + /** + * 创建字段标签 + * + * @param createReqVO 创建信息 + * @return 标签ID + */ + Long createFieldLabel(@Valid QuotaRateFieldLabelSaveReqVO createReqVO); + + /** + * 更新字段标签 + * + * @param updateReqVO 更新信息 + */ + void updateFieldLabel(@Valid QuotaRateFieldLabelSaveReqVO updateReqVO); + + /** + * 删除字段标签 + * + * @param id 标签ID + */ + void deleteFieldLabel(Long id); + + /** + * 获取字段标签 + * + * @param id 标签ID + * @return 字段标签 + */ + QuotaRateFieldLabelDO getFieldLabel(Long id); + + /** + * 获取字段标签列表 + * + * @param catalogItemId 模式节点ID + * @return 字段标签列表 + */ + List getFieldLabelList(Long catalogItemId); + + /** + * 交换排序 + * + * @param labelId1 标签ID1 + * @param labelId2 标签ID2 + */ + void swapSort(Long labelId1, Long labelId2); + + /** + * 验证字段标签存在 + * + * @param id 标签ID + * @return 字段标签 + */ + QuotaRateFieldLabelDO validateFieldLabelExists(Long id); + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateFieldService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateFieldService.java index 10024ff..5fdfa06 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateFieldService.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateFieldService.java @@ -3,7 +3,6 @@ package com.yhy.module.core.service.quota; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldSaveReqVO; import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldDO; - import java.util.List; /** @@ -22,9 +21,9 @@ public interface QuotaRateFieldService { void deleteRateField(Long id); /** - * 删除费率项的所有字段绑定 + * 删除费率模式节点的所有字段绑定 */ - void deleteByRateItemId(Long rateItemId); + void deleteByCatalogItemId(Long catalogItemId); /** * 获取字段绑定 @@ -32,19 +31,19 @@ public interface QuotaRateFieldService { QuotaRateFieldDO getRateField(Long id); /** - * 获取费率项的字段绑定列表 + * 获取费率模式节点的字段绑定列表 */ - List getRateFieldList(Long rateItemId); + List getRateFieldList(Long catalogItemId); /** * 更新字段绑定 */ - void updateBinding(Long rateItemId, Integer fieldIndex, Long[] bindingIds); + void updateBinding(Long catalogItemId, Integer fieldIndex, Long[] bindingIds); /** * 批量保存字段绑定 */ - void batchSaveRateFields(Long rateItemId, List fields); + void batchSaveRateFields(Long catalogItemId, List fields); /** * 验证绑定范围(只能绑定当前定额专业下的节点) diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateItemService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateItemService.java index 49e8bfb..0dfcb77 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateItemService.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaRateItemService.java @@ -2,9 +2,9 @@ package com.yhy.module.core.service.quota; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateItemSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateModeNodeSaveReqVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateValueRulesReqVO; import com.yhy.module.core.dal.dataobject.quota.QuotaRateItemDO; - import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -14,6 +14,11 @@ import java.util.Map; */ public interface QuotaRateItemService { + /** + * 创建费率模式节点(在定额专业下创建 rate_mode 节点) + */ + Long createRateModeNode(QuotaRateModeNodeSaveReqVO createReqVO); + /** * 创建费率项 */ @@ -54,13 +59,34 @@ public interface QuotaRateItemService { */ void swapSort(Long nodeId1, Long nodeId2); + /** + * 移动节点到指定位置(重新计算所有受影响节点的排序值) + * + * @param nodeId 要移动的节点ID + * @param targetNodeId 目标位置节点ID(移动到该节点之前) + */ + void moveNode(Long nodeId, Long targetNodeId); + /** * 配置动态取值规则 + * + * 支持两种规则: + * 1. 阶梯规则(tiers):必填,根据基数值匹配阈值区间,返回对应字段值 + * 2. 增量规则(increments):可选,在阶梯基础上叠加增量计算 */ void configValueRules(QuotaRateValueRulesReqVO reqVO); /** - * 计算动态字段值 + * 计算动态字段值(默认使用阶梯规则) + * + * 计算逻辑: + * 1. 根据基数值匹配阶梯规则,获取基础字段值(必须配置) + * 2. 如果配置了增量规则,在基础值上叠加增量(可选) + * + * 示例: + * - 基数50万以内:字段1=0.224% + * - 基数50-100万:字段1=0.301% + * - 基数超过100万:在0.301%基础上,每增加100万增加0.036% * * @param rateItemId 费率项ID * @param baseValue 基数值(用户输入) diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaResourceService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaResourceService.java index 6eeb5b6..7833c29 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaResourceService.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/QuotaResourceService.java @@ -4,9 +4,8 @@ import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceSaveReqVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO; - -import javax.validation.Valid; import java.util.List; +import javax.validation.Valid; /** * 定额工料机组成 Service 接口 @@ -67,6 +66,19 @@ public interface QuotaResourceService { */ List getAvailableResourceItems(Long quotaItemId); + /** + * 获取可选的工料机列表(已过滤范围,支持模糊查询) + * + * 只返回定额专业绑定的工料机专业下的工料机数据 + * + * @param quotaItemId 定额子目ID + * @param code 编码(模糊查询,可选) + * @param name 名称(模糊查询,可选) + * @param spec 型号规格(模糊查询,可选) + * @return 可选的工料机列表 + */ + List getAvailableResourceItemsWithFilter(Long quotaItemId, String code, String name, String spec); + /** * 验证工料机是否在定额专业的数据范围内 * diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentDetailServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentDetailServiceImpl.java new file mode 100644 index 0000000..730bb88 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentDetailServiceImpl.java @@ -0,0 +1,291 @@ +package com.yhy.module.core.service.quota.impl; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_DETAIL_CODE_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_DETAIL_CODE_NOT_SAME_SPECIALTY; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_DETAIL_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_DETAIL_NOT_SAME_SETTING; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_DETAIL_SETTING_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_SETTING_QUOTA_ITEM_NOT_EXISTS; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentCombinedRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentDetailSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDetailDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentSettingDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaItemDO; +import com.yhy.module.core.dal.mysql.quota.QuotaAdjustmentDetailMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaAdjustmentSettingMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaCatalogTreeMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaItemMapper; +import com.yhy.module.core.service.quota.QuotaAdjustmentDetailService; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * 定额调整明细 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class QuotaAdjustmentDetailServiceImpl implements QuotaAdjustmentDetailService { + + @Resource + private QuotaAdjustmentDetailMapper quotaAdjustmentDetailMapper; + + @Resource + private QuotaAdjustmentSettingMapper quotaAdjustmentSettingMapper; + + @Resource + private QuotaItemMapper quotaItemMapper; + + @Resource + private QuotaCatalogTreeMapper quotaCatalogTreeMapper; + + @Resource + private QuotaCatalogItemMapper quotaCatalogItemMapper; + + @Override + public Long createQuotaAdjustmentDetail(QuotaAdjustmentDetailSaveReqVO createReqVO) { + // 验证调整设置是否存在 + validateAdjustmentSettingExists(createReqVO.getAdjustmentSettingId()); + + // 如果有引用编号,验证引用的调整设置 + if (createReqVO.getAdjustmentCode() != null) { + validateAdjustmentCodeInSameSpecialty(createReqVO.getAdjustmentSettingId(), createReqVO.getAdjustmentCode()); + } + + // 插入 + QuotaAdjustmentDetailDO detail = BeanUtils.toBean(createReqVO, QuotaAdjustmentDetailDO.class); + if (detail.getSortOrder() == null) { + detail.setSortOrder(0); + } + quotaAdjustmentDetailMapper.insert(detail); + return detail.getId(); + } + + @Override + public void updateQuotaAdjustmentDetail(QuotaAdjustmentDetailSaveReqVO updateReqVO) { + // 校验存在 + validateQuotaAdjustmentDetailExists(updateReqVO.getId()); + // 验证调整设置是否存在 + validateAdjustmentSettingExists(updateReqVO.getAdjustmentSettingId()); + + // 如果有引用编号,验证引用的调整设置 + if (updateReqVO.getAdjustmentCode() != null) { + validateAdjustmentCodeInSameSpecialty(updateReqVO.getAdjustmentSettingId(), updateReqVO.getAdjustmentCode()); + } + + // 更新 + QuotaAdjustmentDetailDO updateObj = BeanUtils.toBean(updateReqVO, QuotaAdjustmentDetailDO.class); + quotaAdjustmentDetailMapper.updateById(updateObj); + } + + @Override + public void deleteQuotaAdjustmentDetail(Long id) { + // 校验存在 + validateQuotaAdjustmentDetailExists(id); + // 删除 + quotaAdjustmentDetailMapper.deleteById(id); + } + + @Override + public QuotaAdjustmentDetailDO getQuotaAdjustmentDetail(Long id) { + return quotaAdjustmentDetailMapper.selectById(id); + } + + @Override + public List getQuotaAdjustmentDetailListByQuotaItem(Long quotaItemId) { + // 1. 查询该定额子目下的所有调整设置 + List settings = quotaAdjustmentSettingMapper.selectListByQuotaItemId(quotaItemId); + + if (settings.isEmpty()) { + return Collections.emptyList(); + } + + // 2. 查询这些调整设置下的所有明细 + List settingIds = settings.stream() + .map(QuotaAdjustmentSettingDO::getId) + .collect(java.util.stream.Collectors.toList()); + + return quotaAdjustmentDetailMapper.selectListBySettingIds(settingIds); + } + + @Override + public List getQuotaAdjustmentDetailList(Long adjustmentSettingId) { + return quotaAdjustmentDetailMapper.selectListBySettingId(adjustmentSettingId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void swapSort(Long id1, Long id2) { + // 验证两个调整明细是否存在 + QuotaAdjustmentDetailDO detail1 = quotaAdjustmentDetailMapper.selectByIdForUpdate(id1); + QuotaAdjustmentDetailDO detail2 = quotaAdjustmentDetailMapper.selectByIdForUpdate(id2); + + if (detail1 == null) { + throw exception(QUOTA_ADJUSTMENT_DETAIL_NOT_EXISTS); + } + if (detail2 == null) { + throw exception(QUOTA_ADJUSTMENT_DETAIL_NOT_EXISTS); + } + + // 验证是否在同一调整设置下 + if (!detail1.getAdjustmentSettingId().equals(detail2.getAdjustmentSettingId())) { + throw exception(QUOTA_ADJUSTMENT_DETAIL_NOT_SAME_SETTING); + } + + // 交换排序值 + Integer tempSort = detail1.getSortOrder(); + detail1.setSortOrder(detail2.getSortOrder()); + detail2.setSortOrder(tempSort); + + quotaAdjustmentDetailMapper.updateById(detail1); + quotaAdjustmentDetailMapper.updateById(detail2); + } + + @Override + public List getAvailableSettings(Long adjustmentSettingId) { + // 获取当前调整设置 + QuotaAdjustmentSettingDO currentSetting = quotaAdjustmentSettingMapper.selectById(adjustmentSettingId); + if (currentSetting == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS); + } + + // 获取定额专业ID + Long specialtyId = getSpecialtyIdByQuotaItem(currentSetting.getQuotaItemId()); + + // 查询同一定额专业下的所有定额子目 + List catalogTrees = quotaCatalogTreeMapper.selectListByCatalogItemId(specialtyId); + + // 查询这些定额子目下的所有调整设置 + // 这里简化处理,返回所有调整设置,前端可以根据需要过滤 + // 实际应该根据 catalogTrees 中的 ID 查询对应的 quotaItem,再查询调整设置 + return quotaAdjustmentSettingMapper.selectList(); + } + + @Override + public List getCombinedList(Long quotaItemId) { + // 1. 查询该定额子目下的所有调整设置(第五层) + List settings = quotaAdjustmentSettingMapper.selectListByQuotaItemId(quotaItemId); + + if (settings.isEmpty()) { + return Collections.emptyList(); + } + + // 2. 查询所有调整设置的明细(第六层) + List settingIds = settings.stream() + .map(QuotaAdjustmentSettingDO::getId) + .collect(java.util.stream.Collectors.toList()); + + List allDetails = quotaAdjustmentDetailMapper.selectListBySettingIds(settingIds); + + // 3. 按 adjustmentSettingId 分组 + Map> detailsMap = allDetails.stream() + .collect(java.util.stream.Collectors.groupingBy(QuotaAdjustmentDetailDO::getAdjustmentSettingId)); + + // 4. 组装结果 + return settings.stream() + .map(setting -> { + QuotaAdjustmentCombinedRespVO vo = new QuotaAdjustmentCombinedRespVO(); + vo.setId(setting.getId()); + vo.setQuotaItemId(setting.getQuotaItemId()); + vo.setName(setting.getName()); + vo.setQuotaValue(setting.getQuotaValue()); + vo.setAdjustmentContent(setting.getAdjustmentContent()); + vo.setAdjustmentType(setting.getAdjustmentType()); + vo.setAdjustmentRules(setting.getAdjustmentRules()); + vo.setSortOrder(setting.getSortOrder()); + vo.setCreateTime(setting.getCreateTime()); + + // 获取该调整设置的明细列表 + List details = detailsMap.getOrDefault(setting.getId(), Collections.emptyList()); + List detailItems = details.stream() + .map(detail -> { + QuotaAdjustmentCombinedRespVO.DetailItem item = new QuotaAdjustmentCombinedRespVO.DetailItem(); + item.setId(detail.getId()); + item.setAdjustmentSettingId(detail.getAdjustmentSettingId()); + item.setAdjustmentCode(detail.getAdjustmentCode()); + item.setAdjustment(detail.getAdjustment()); + item.setSortOrder(detail.getSortOrder()); + item.setCreateTime(detail.getCreateTime()); + return item; + }) + .collect(java.util.stream.Collectors.toList()); + + vo.setDetails(detailItems); + return vo; + }) + .collect(java.util.stream.Collectors.toList()); + } + + private void validateQuotaAdjustmentDetailExists(Long id) { + if (quotaAdjustmentDetailMapper.selectById(id) == null) { + throw exception(QUOTA_ADJUSTMENT_DETAIL_NOT_EXISTS); + } + } + + private void validateAdjustmentSettingExists(Long adjustmentSettingId) { + if (quotaAdjustmentSettingMapper.selectById(adjustmentSettingId) == null) { + throw exception(QUOTA_ADJUSTMENT_DETAIL_SETTING_NOT_EXISTS); + } + } + + /** + * 验证 adjustment_code 是否在同一定额专业 + */ + private void validateAdjustmentCodeInSameSpecialty(Long adjustmentSettingId, Long adjustmentCode) { + // 1. 查询当前调整设置 + QuotaAdjustmentSettingDO currentSetting = quotaAdjustmentSettingMapper.selectById(adjustmentSettingId); + if (currentSetting == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS); + } + + // 2. 查询引用的调整设置 + QuotaAdjustmentSettingDO referenceSetting = quotaAdjustmentSettingMapper.selectById(adjustmentCode); + if (referenceSetting == null) { + throw exception(QUOTA_ADJUSTMENT_DETAIL_CODE_NOT_EXISTS); + } + + // 3. 获取两者的定额专业ID + Long currentSpecialtyId = getSpecialtyIdByQuotaItem(currentSetting.getQuotaItemId()); + Long referenceSpecialtyId = getSpecialtyIdByQuotaItem(referenceSetting.getQuotaItemId()); + + // 4. 验证是否在同一定额专业 + if (!currentSpecialtyId.equals(referenceSpecialtyId)) { + throw exception(QUOTA_ADJUSTMENT_DETAIL_CODE_NOT_SAME_SPECIALTY); + } + } + + /** + * 通过定额子目追溯到定额专业 + */ + private Long getSpecialtyIdByQuotaItem(Long quotaItemId) { + // 1. 查询定额子目(第三层) + QuotaItemDO quotaItem = quotaItemMapper.selectById(quotaItemId); + if (quotaItem == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_QUOTA_ITEM_NOT_EXISTS); + } + + // 2. 查询定额子目树节点(第二层) + QuotaCatalogTreeDO catalogTree = quotaCatalogTreeMapper.selectById(quotaItem.getCatalogItemId()); + if (catalogTree == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_QUOTA_ITEM_NOT_EXISTS); + } + + // 3. 返回定额专业节点ID(第一层) + return catalogTree.getCatalogItemId(); + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentServiceImpl.java deleted file mode 100644 index 7b9490c..0000000 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentServiceImpl.java +++ /dev/null @@ -1,977 +0,0 @@ -package com.yhy.module.core.service.quota.impl; - -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; -import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSaveReqVO; -import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentDO; -import com.yhy.module.core.dal.dataobject.quota.QuotaItemDO; -import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; -import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryDO; -import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeDO; -import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO; -import com.yhy.module.core.dal.mysql.quota.QuotaAdjustmentMapper; -import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; -import com.yhy.module.core.dal.mysql.quota.QuotaItemMapper; -import com.yhy.module.core.dal.mysql.quota.QuotaResourceMapper; -import com.yhy.module.core.dal.mysql.resource.ResourceCategoryMapper; -import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMapper; -import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMappingMapper; -import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper; -import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeMappingDO; -import com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO; -import com.yhy.module.core.enums.quota.RoundingRuleEnum; -import com.yhy.module.core.enums.ErrorCodeConstants; -import com.yhy.module.core.enums.quota.QuotaAdjustmentTypeEnum; -import com.yhy.module.core.service.quota.QuotaAdjustmentService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; - -/** - * 定额调整设置 Service 实现类 - */ -@Service -@Validated -@Slf4j -public class QuotaAdjustmentServiceImpl implements QuotaAdjustmentService { - - @Resource - private QuotaAdjustmentMapper quotaAdjustmentMapper; - - @Resource - private QuotaItemMapper quotaItemMapper; - - @Resource - private QuotaCatalogItemMapper quotaCatalogItemMapper; - - @Resource - private ResourceCategoryTreeMapper resourceCategoryTreeMapper; - - @Resource - private ResourceCategoryMapper resourceCategoryMapper; - - @Resource - private ResourceCategoryTreeMappingMapper resourceCategoryTreeMappingMapper; - - @Resource - private QuotaResourceMapper quotaResourceMapper; - - @Resource - private ResourceItemMapper resourceItemMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createQuotaAdjustment(QuotaAdjustmentSaveReqVO createReqVO) { - // 验证定额子目是否存在 - validateQuotaItemExists(createReqVO.getQuotaItemId()); - - // 验证调整类型 - validateAdjustmentType(createReqVO.getAdjustmentType()); - - // 验证调整规则 - validateAdjustmentRules(createReqVO.getAdjustmentType(), createReqVO.getAdjustmentRules()); - - // 插入 - QuotaAdjustmentDO adjustment = QuotaAdjustmentDO.builder() - .quotaItemId(createReqVO.getQuotaItemId()) - .name(createReqVO.getName()) - .content(createReqVO.getContent()) - .adjustmentType(createReqVO.getAdjustmentType()) - .adjustmentRules(createReqVO.getAdjustmentRules()) - .sortOrder(createReqVO.getSortOrder() != null ? createReqVO.getSortOrder() : 0) - .build(); - quotaAdjustmentMapper.insert(adjustment); - return adjustment.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateQuotaAdjustment(QuotaAdjustmentSaveReqVO updateReqVO) { - // 验证存在 - validateQuotaAdjustmentExists(updateReqVO.getId()); - - // 验证定额子目是否存在 - validateQuotaItemExists(updateReqVO.getQuotaItemId()); - - // 验证调整类型 - validateAdjustmentType(updateReqVO.getAdjustmentType()); - - // 验证调整规则 - validateAdjustmentRules(updateReqVO.getAdjustmentType(), updateReqVO.getAdjustmentRules()); - - // 更新 - QuotaAdjustmentDO updateObj = QuotaAdjustmentDO.builder() - .id(updateReqVO.getId()) - .quotaItemId(updateReqVO.getQuotaItemId()) - .name(updateReqVO.getName()) - .content(updateReqVO.getContent()) - .adjustmentType(updateReqVO.getAdjustmentType()) - .adjustmentRules(updateReqVO.getAdjustmentRules()) - .sortOrder(updateReqVO.getSortOrder()) - .build(); - quotaAdjustmentMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteQuotaAdjustment(Long id) { - // 验证存在 - validateQuotaAdjustmentExists(id); - // 删除 - quotaAdjustmentMapper.deleteById(id); - } - - @Override - public QuotaAdjustmentDO getQuotaAdjustment(Long id) { - return quotaAdjustmentMapper.selectById(id); - } - - @Override - public List getQuotaAdjustmentList(Long quotaItemId) { - return quotaAdjustmentMapper.selectListByQuotaItemId(quotaItemId); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void swapSort(Long id1, Long id2) { - // 验证两个调整设置是否存在 - QuotaAdjustmentDO adjustment1 = quotaAdjustmentMapper.selectByIdForUpdate(id1); - QuotaAdjustmentDO adjustment2 = quotaAdjustmentMapper.selectByIdForUpdate(id2); - - if (adjustment1 == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_EXISTS); - } - if (adjustment2 == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_EXISTS); - } - - // 验证是否属于同一个定额子目 - if (!adjustment1.getQuotaItemId().equals(adjustment2.getQuotaItemId())) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_SAME_QUOTA_ITEM); - } - - // 交换排序值 - Integer tempSort = adjustment1.getSortOrder(); - adjustment1.setSortOrder(adjustment2.getSortOrder()); - adjustment2.setSortOrder(tempSort); - - quotaAdjustmentMapper.updateById(adjustment1); - quotaAdjustmentMapper.updateById(adjustment2); - } - - @Override - public BigDecimal calculateAdjustedConsumeQty(Long quotaItemId, BigDecimal originalConsumeQty) { - if (originalConsumeQty == null) { - return BigDecimal.ZERO; - } - - // 获取所有调整设置 - List adjustments = getQuotaAdjustmentList(quotaItemId); - if (adjustments.isEmpty()) { - return originalConsumeQty; - } - - // 依次应用调整规则 - BigDecimal result = originalConsumeQty; - for (QuotaAdjustmentDO adjustment : adjustments) { - result = applyAdjustment(result, adjustment); - } - - return result.setScale(6, RoundingMode.HALF_UP); - } - - @Override - public BigDecimal calculateAdjustedDosage(Long quotaItemId, String resourceCode, BigDecimal originalDosage) { - if (originalDosage == null) { - return BigDecimal.ZERO; - } - if (resourceCode == null || resourceCode.trim().isEmpty()) { - return originalDosage; - } - - // 获取所有调整设置 - List adjustments = getQuotaAdjustmentList(quotaItemId); - if (adjustments.isEmpty()) { - return originalDosage; - } - - // 查找材料调整类型的调整设置 - BigDecimal result = originalDosage; - for (QuotaAdjustmentDO adjustment : adjustments) { - if ("material_adjustment".equals(adjustment.getAdjustmentType())) { - result = applyMaterialAdjustment(result, resourceCode, adjustment); - } - } - - return result.setScale(6, RoundingMode.HALF_UP); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void saveMaterialItems(Long adjustmentId, List> items) { - // 验证调整设置存在 - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_EXISTS); - } - - // 验证调整类型 - if (!"material_adjustment".equals(adjustment.getAdjustmentType())) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_TYPE_INVALID); - } - - // 验证明细数据 - validateMaterialItems(items); - - // 更新调整规则 - Map rules = adjustment.getAdjustmentRules(); - if (rules == null) { - rules = new java.util.HashMap<>(); - } - rules.put("items", items); - - adjustment.setAdjustmentRules(rules); - quotaAdjustmentMapper.updateById(adjustment); - } - - @Override - public List> getMaterialItems(Long adjustmentId) { - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - return new java.util.ArrayList<>(); - } - - Map rules = adjustment.getAdjustmentRules(); - if (rules == null || !rules.containsKey("items")) { - return new java.util.ArrayList<>(); - } - - Object itemsObj = rules.get("items"); - if (itemsObj instanceof List) { - return (List>) itemsObj; - } - - return new java.util.ArrayList<>(); - } - - /** - * 应用单个调整规则 - */ - private BigDecimal applyAdjustment(BigDecimal value, QuotaAdjustmentDO adjustment) { - String type = adjustment.getAdjustmentType(); - Map rules = adjustment.getAdjustmentRules(); - - if (rules == null || rules.isEmpty()) { - return value; - } - - QuotaAdjustmentTypeEnum typeEnum = QuotaAdjustmentTypeEnum.valueOfCode(type); - if (typeEnum == null) { - log.warn("未知的调整类型: {}", type); - return value; - } - - switch (typeEnum) { - case PERCENTAGE: - return applyPercentageAdjustment(value, rules); - case FIXED_VALUE: - return applyFixedValueAdjustment(value, rules); - case FORMULA: - return applyFormulaAdjustment(value, rules); - case CONDITIONAL: - return applyConditionalAdjustment(value, rules); - default: - return value; - } - } - - /** - * 应用百分比调整 - */ - private BigDecimal applyPercentageAdjustment(BigDecimal value, Map rules) { - Object valueObj = rules.get("value"); - Object operatorObj = rules.get("operator"); - - if (valueObj == null || operatorObj == null) { - return value; - } - - BigDecimal percentage = new BigDecimal(valueObj.toString()); - String operator = operatorObj.toString(); - - if ("increase".equals(operator)) { - // 增加:value * (1 + percentage/100) - return value.multiply(BigDecimal.ONE.add(percentage.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP))); - } else if ("decrease".equals(operator)) { - // 减少:value * (1 - percentage/100) - return value.multiply(BigDecimal.ONE.subtract(percentage.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP))); - } - - return value; - } - - /** - * 应用固定值调整 - */ - private BigDecimal applyFixedValueAdjustment(BigDecimal value, Map rules) { - Object valueObj = rules.get("value"); - Object operatorObj = rules.get("operator"); - - if (valueObj == null || operatorObj == null) { - return value; - } - - BigDecimal fixedValue = new BigDecimal(valueObj.toString()); - String operator = operatorObj.toString(); - - if ("increase".equals(operator)) { - return value.add(fixedValue); - } else if ("decrease".equals(operator)) { - return value.subtract(fixedValue); - } - - return value; - } - - /** - * 应用公式调整(简化实现) - */ - private BigDecimal applyFormulaAdjustment(BigDecimal value, Map rules) { - // TODO: 实现复杂的公式计算逻辑 - log.warn("公式调整暂未实现"); - return value; - } - - /** - * 应用条件调整(简化实现) - */ - private BigDecimal applyConditionalAdjustment(BigDecimal value, Map rules) { - // TODO: 实现条件判断逻辑 - log.warn("条件调整暂未实现"); - return value; - } - - /** - * 应用材料调整 - */ - private BigDecimal applyMaterialAdjustment(BigDecimal dosage, String resourceCode, QuotaAdjustmentDO adjustment) { - Map rules = adjustment.getAdjustmentRules(); - if (rules == null || !rules.containsKey("items")) { - return dosage; - } - - Object itemsObj = rules.get("items"); - if (!(itemsObj instanceof List)) { - return dosage; - } - - List> items = (List>) itemsObj; - for (Map item : items) { - String itemCode = (String) item.get("resourceCode"); - if (resourceCode.equals(itemCode)) { - Object adjustValueObj = item.get("adjustValue"); - if (adjustValueObj != null) { - BigDecimal adjustValue = new BigDecimal(adjustValueObj.toString()); - return dosage.add(adjustValue); - } - } - } - - return dosage; - } - - /** - * 验证材料明细 - */ - private void validateMaterialItems(List> items) { - if (items == null || items.isEmpty()) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - - // 检查重复的工料机编码 - java.util.Set codes = new java.util.HashSet<>(); - for (Map item : items) { - String resourceCode = (String) item.get("resourceCode"); - if (resourceCode == null || resourceCode.trim().isEmpty()) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - - if (codes.contains(resourceCode)) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_MATERIAL_CODE_DUPLICATE, resourceCode); - } - codes.add(resourceCode); - - // 验证调整值 - Object adjustValueObj = item.get("adjustValue"); - if (adjustValueObj == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - - try { - new BigDecimal(adjustValueObj.toString()); - } catch (NumberFormatException e) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - } - } - - @Override - public List> getAvailableCategories(Long quotaItemId) { - // 1. 获取定额子目 - QuotaItemDO quotaItem = quotaItemMapper.selectById(quotaItemId); - if (quotaItem == null) { - throw exception(ErrorCodeConstants.QUOTA_ITEM_NOT_EXISTS); - } - - // 2. 获取工料机专业ID - Long categoryTreeId = getCategoryTreeIdByQuotaItem(quotaItemId); - if (categoryTreeId == null) { - throw exception(ErrorCodeConstants.QUOTA_SPECIALTY_NOT_BOUND); - } - - // 3. 获取工料机专业节点 - ResourceCategoryTreeDO categoryTree = resourceCategoryTreeMapper.selectById(categoryTreeId); - if (categoryTree == null) { - throw exception(ErrorCodeConstants.RESOURCE_CATEGORY_TREE_NOT_EXISTS); - } - - // 4. 获取类别列表(通过映射表) - List mappings = resourceCategoryTreeMappingMapper.selectList( - "category_tree_id", categoryTreeId - ); - if (mappings == null || mappings.isEmpty()) { - return new java.util.ArrayList<>(); - } - - List categoryIds = mappings.stream() - .map(ResourceCategoryTreeMappingDO::getCategoryId) - .collect(java.util.stream.Collectors.toList()); - - List categories = resourceCategoryMapper.selectList( - "id", categoryIds - ); - - // 5. 转换为VO - List> result = new java.util.ArrayList<>(); - for (ResourceCategoryDO category : categories) { - Map map = new java.util.HashMap<>(); - map.put("categoryId", category.getId()); - map.put("categoryName", category.getName()); - map.put("categoryType", getCategoryType(category.getCode())); - map.put("categoryCode", category.getCode()); - result.add(map); - } - - return result; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void saveDynamicConfig(Long adjustmentId, List> categories) { - // 验证调整设置存在 - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_EXISTS); - } - - // 验证调整类型 - if (!"dynamic_adjustment".equals(adjustment.getAdjustmentType())) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_TYPE_INVALID); - } - - // 验证配置数据 - validateDynamicConfig(categories); - - // 更新调整规则 - Map rules = adjustment.getAdjustmentRules(); - if (rules == null) { - rules = new java.util.HashMap<>(); - } - rules.put("categories", categories); - - adjustment.setAdjustmentRules(rules); - quotaAdjustmentMapper.updateById(adjustment); - } - - @Override - public List> getDynamicConfig(Long adjustmentId) { - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - return new java.util.ArrayList<>(); - } - - Map rules = adjustment.getAdjustmentRules(); - if (rules == null || !rules.containsKey("categories")) { - return new java.util.ArrayList<>(); - } - - Object categoriesObj = rules.get("categories"); - if (categoriesObj instanceof List) { - return (List>) categoriesObj; - } - - return new java.util.ArrayList<>(); - } - - @Override - public Map calculateDynamic(Long adjustmentId, Map inputValues) { - // 获取动态调整配置 - List> categories = getDynamicConfig(adjustmentId); - if (categories.isEmpty()) { - return new java.util.HashMap<>(); - } - - Map results = new java.util.HashMap<>(); - - for (Map category : categories) { - String categoryId = category.get("categoryId").toString(); - BigDecimal inputValue = inputValues.get(categoryId); - - if (inputValue == null) { - continue; - } - - // 计算调整结果 - BigDecimal result = calculateDynamicForCategory(category, inputValue); - results.put(categoryId, result); - } - - return results; - } - - /** - * 计算单个类别的动态调整结果 - */ - private BigDecimal calculateDynamicForCategory(Map category, BigDecimal inputValue) { - // 获取配置参数 - BigDecimal coefficient = new BigDecimal(category.get("coefficient").toString()); - BigDecimal minValue = new BigDecimal(category.get("minValue").toString()); - BigDecimal maxValue = new BigDecimal(category.get("maxValue").toString()); - BigDecimal denominator = new BigDecimal(category.get("denominator").toString()); - BigDecimal baseValue = new BigDecimal(category.get("baseValue").toString()); - String roundingRule = (String) category.get("roundingRule"); - - // 1. 验证输入值范围 - if (inputValue.compareTo(minValue) < 0 || inputValue.compareTo(maxValue) > 0) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_INPUT_VALUE_OUT_OF_RANGE, - inputValue, minValue, maxValue); - } - - // 2. 计算中间值:(输入值 - 基础值) / 分母 - BigDecimal intermediate = inputValue.subtract(baseValue).divide(denominator, 6, RoundingMode.HALF_UP); - - // 3. 应用取值规则 - BigDecimal rounded = applyRoundingRule(intermediate, roundingRule); - - // 4. 乘以系数 - BigDecimal result = rounded.multiply(coefficient); - - // 5. 负数结果视为0 - if (result.compareTo(BigDecimal.ZERO) < 0) { - result = BigDecimal.ZERO; - } - - return result.setScale(6, RoundingMode.HALF_UP); - } - - /** - * 应用取值规则 - */ - private BigDecimal applyRoundingRule(BigDecimal value, String ruleCode) { - RoundingRuleEnum rule = RoundingRuleEnum.valueOfCode(ruleCode); - if (rule == null) { - return value; - } - - switch (rule) { - case ROUND_UP: - // 向上取整 - return value.setScale(0, RoundingMode.UP); - case ROUND_DOWN: - // 向下取整 - return value.setScale(0, RoundingMode.DOWN); - case ROUND_HALF_UP: - // 四舍五入 - return value.setScale(0, RoundingMode.HALF_UP); - case NO_ROUNDING: - default: - // 不取整 - return value; - } - } - - /** - * 获取定额子目对应的工料机专业ID - */ - private Long getCategoryTreeIdByQuotaItem(Long quotaItemId) { - // 1. 查询定额子目 - QuotaItemDO quotaItem = quotaItemMapper.selectById(quotaItemId); - if (quotaItem == null) { - return null; - } - - // 2. 查询内容节点 - QuotaCatalogItemDO contentNode = quotaCatalogItemMapper.selectById(quotaItem.getCatalogItemId()); - if (contentNode == null) { - return null; - } - - // 3. 向上追溯到定额专业节点 - QuotaCatalogItemDO current = contentNode; - while (current != null) { - Map attributes = current.getAttributes(); - if (attributes != null && "specialty".equals(attributes.get("node_type"))) { - return current.getCategoryTreeId(); - } - if (current.getParentId() == null) { - break; - } - current = quotaCatalogItemMapper.selectById(current.getParentId()); - } - - return null; - } - - /** - * 根据类别代码获取类别类型 - */ - private String getCategoryType(String code) { - if (code == null) { - return "unknown"; - } - // 简单映射,实际可能需要更复杂的逻辑 - if (code.contains("人") || code.equalsIgnoreCase("RG")) { - return "labor"; - } else if (code.contains("材") || code.equalsIgnoreCase("CL")) { - return "material"; - } else if (code.contains("机") || code.equalsIgnoreCase("JX")) { - return "machine"; - } - return "other"; - } - - /** - * 验证动态调整配置 - */ - private void validateDynamicConfig(List> categories) { - if (categories == null || categories.isEmpty()) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - - // 检查重复的类别ID - java.util.Set categoryIds = new java.util.HashSet<>(); - for (Map category : categories) { - String categoryId = category.get("categoryId").toString(); - if (categoryIds.contains(categoryId)) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_CATEGORY_DUPLICATE, categoryId); - } - categoryIds.add(categoryId); - - // 验证参数 - validateDynamicCategoryParams(category); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void saveCoefficientConfig(Long adjustmentId, List> categories) { - // 验证调整设置存在 - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_EXISTS); - } - - // 验证调整类型 - if (!"coefficient_adjustment".equals(adjustment.getAdjustmentType())) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_TYPE_INVALID); - } - - // 验证配置数据 - validateCoefficientConfig(categories); - - // 更新调整规则 - Map rules = adjustment.getAdjustmentRules(); - if (rules == null) { - rules = new java.util.HashMap<>(); - } - rules.put("categories", categories); - - adjustment.setAdjustmentRules(rules); - quotaAdjustmentMapper.updateById(adjustment); - } - - @Override - public List> getCoefficientConfig(Long adjustmentId) { - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - return new java.util.ArrayList<>(); - } - - Map rules = adjustment.getAdjustmentRules(); - if (rules == null || !rules.containsKey("categories")) { - return new java.util.ArrayList<>(); - } - - Object categoriesObj = rules.get("categories"); - if (categoriesObj instanceof List) { - return (List>) categoriesObj; - } - - return new java.util.ArrayList<>(); - } - - @Override - public List> getAvailableResources(Long quotaItemId) { - // 1. 获取定额子目的所有工料机组成 - List quotaResources = quotaResourceMapper.selectListByQuotaItemId(quotaItemId); - if (quotaResources == null || quotaResources.isEmpty()) { - return new java.util.ArrayList<>(); - } - - // 2. 获取工料机详情 - List resourceItemIds = quotaResources.stream() - .map(QuotaResourceDO::getResourceItemId) - .collect(java.util.stream.Collectors.toList()); - - List resourceItems = resourceItemMapper.selectList( - "id", resourceItemIds - ); - - // 3. 转换为VO(从快照中获取编码和名称) - List> result = new java.util.ArrayList<>(); - for (QuotaResourceDO quotaResource : quotaResources) { - Map map = new java.util.HashMap<>(); - map.put("resourceId", quotaResource.getResourceItemId()); - map.put("resourceCode", quotaResource.getResourceCode()); - map.put("resourceName", quotaResource.getResourceName()); - result.add(map); - } - - return result; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void saveMergeConfig(Long adjustmentId, List> resources) { - // 验证调整设置存在 - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_EXISTS); - } - - // 验证调整类型 - if (!"dynamic_merge_quota".equals(adjustment.getAdjustmentType())) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_TYPE_INVALID); - } - - // 验证配置数据 - validateMergeConfig(resources); - - // 更新调整规则 - Map rules = adjustment.getAdjustmentRules(); - if (rules == null) { - rules = new java.util.HashMap<>(); - } - rules.put("resources", resources); - - adjustment.setAdjustmentRules(rules); - quotaAdjustmentMapper.updateById(adjustment); - } - - @Override - public List> getMergeConfig(Long adjustmentId) { - QuotaAdjustmentDO adjustment = getQuotaAdjustment(adjustmentId); - if (adjustment == null) { - return new java.util.ArrayList<>(); - } - - Map rules = adjustment.getAdjustmentRules(); - if (rules == null || !rules.containsKey("resources")) { - return new java.util.ArrayList<>(); - } - - Object resourcesObj = rules.get("resources"); - if (resourcesObj instanceof List) { - return (List>) resourcesObj; - } - - return new java.util.ArrayList<>(); - } - - @Override - public Map calculateMerge(Long adjustmentId, Map inputValues) { - // 获取动态合并配置 - List> resources = getMergeConfig(adjustmentId); - if (resources.isEmpty()) { - return new java.util.HashMap<>(); - } - - Map results = new java.util.HashMap<>(); - - for (Map resource : resources) { - String resourceId = resource.get("resourceId").toString(); - BigDecimal inputValue = inputValues.get(resourceId); - - if (inputValue == null) { - continue; - } - - // 计算调整结果(复用动态调整的计算逻辑) - BigDecimal result = calculateDynamicForCategory(resource, inputValue); - results.put(resourceId, result); - } - - return results; - } - - /** - * 验证系数调整配置 - */ - private void validateCoefficientConfig(List> categories) { - if (categories == null || categories.isEmpty()) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - - // 检查重复的类别ID - java.util.Set categoryIds = new java.util.HashSet<>(); - for (Map category : categories) { - String categoryId = category.get("categoryId").toString(); - if (categoryIds.contains(categoryId)) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_CATEGORY_DUPLICATE, categoryId); - } - categoryIds.add(categoryId); - - // 验证系数 - try { - BigDecimal coefficient = new BigDecimal(category.get("coefficient").toString()); - if (coefficient.compareTo(BigDecimal.ZERO) <= 0) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_COEFFICIENT_INVALID); - } - } catch (NumberFormatException e) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - } - } - - /** - * 验证动态合并配置 - */ - private void validateMergeConfig(List> resources) { - if (resources == null || resources.isEmpty()) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - - // 检查重复的工料机ID - java.util.Set resourceIds = new java.util.HashSet<>(); - for (Map resource : resources) { - String resourceId = resource.get("resourceId").toString(); - if (resourceIds.contains(resourceId)) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RESOURCE_DUPLICATE, resourceId); - } - resourceIds.add(resourceId); - - // 验证参数(与动态调整相同) - validateDynamicCategoryParams(resource); - } - } - - /** - * 验证动态调整类别参数 - */ - private void validateDynamicCategoryParams(Map category) { - try { - BigDecimal coefficient = new BigDecimal(category.get("coefficient").toString()); - BigDecimal minValue = new BigDecimal(category.get("minValue").toString()); - BigDecimal maxValue = new BigDecimal(category.get("maxValue").toString()); - BigDecimal denominator = new BigDecimal(category.get("denominator").toString()); - BigDecimal baseValue = new BigDecimal(category.get("baseValue").toString()); - String roundingRule = (String) category.get("roundingRule"); - - // 系数必须大于0 - if (coefficient.compareTo(BigDecimal.ZERO) <= 0) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_COEFFICIENT_INVALID); - } - - // 最小值必须小于最大值 - if (minValue.compareTo(maxValue) >= 0) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_MIN_MAX_INVALID); - } - - // 分母不能为0 - if (denominator.compareTo(BigDecimal.ZERO) == 0) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_DENOMINATOR_ZERO); - } - - // 基础值必须在范围内 - if (baseValue.compareTo(minValue) < 0 || baseValue.compareTo(maxValue) > 0) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_BASE_VALUE_OUT_OF_RANGE); - } - - // 验证取值规则 - if (RoundingRuleEnum.valueOfCode(roundingRule) == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_ROUNDING_RULE_INVALID); - } - } catch (NumberFormatException e) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - } - - private void validateQuotaAdjustmentExists(Long id) { - if (quotaAdjustmentMapper.selectById(id) == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_NOT_EXISTS); - } - } - - private void validateQuotaItemExists(Long quotaItemId) { - QuotaItemDO quotaItem = quotaItemMapper.selectById(quotaItemId); - if (quotaItem == null) { - throw exception(ErrorCodeConstants.QUOTA_ITEM_NOT_EXISTS); - } - } - - private void validateAdjustmentType(String adjustmentType) { - QuotaAdjustmentTypeEnum typeEnum = QuotaAdjustmentTypeEnum.valueOfCode(adjustmentType); - if (typeEnum == null) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_TYPE_INVALID); - } - } - - private void validateAdjustmentRules(String adjustmentType, Map rules) { - if (rules == null || rules.isEmpty()) { - return; - } - - QuotaAdjustmentTypeEnum typeEnum = QuotaAdjustmentTypeEnum.valueOfCode(adjustmentType); - if (typeEnum == null) { - return; - } - - // 根据不同类型验证规则结构 - switch (typeEnum) { - case PERCENTAGE: - case FIXED_VALUE: - if (!rules.containsKey("value") || !rules.containsKey("operator")) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - break; - case FORMULA: - if (!rules.containsKey("expression")) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - break; - case CONDITIONAL: - if (!rules.containsKey("conditions")) { - throw exception(ErrorCodeConstants.QUOTA_ADJUSTMENT_RULES_INVALID); - } - break; - } - } -} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentSettingServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentSettingServiceImpl.java new file mode 100644 index 0000000..058519f --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaAdjustmentSettingServiceImpl.java @@ -0,0 +1,140 @@ +package com.yhy.module.core.service.quota.impl; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_SETTING_HAS_DETAILS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_SETTING_NOT_SAME_QUOTA_ITEM; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_SETTING_QUOTA_ITEM_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_ADJUSTMENT_SETTING_REFERENCED; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.yhy.module.core.controller.admin.quota.vo.QuotaAdjustmentSettingSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaAdjustmentSettingDO; +import com.yhy.module.core.dal.mysql.quota.QuotaAdjustmentDetailMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaAdjustmentSettingMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaItemMapper; +import com.yhy.module.core.service.quota.QuotaAdjustmentSettingService; +import java.util.List; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * 定额调整设置 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class QuotaAdjustmentSettingServiceImpl implements QuotaAdjustmentSettingService { + + @Resource + private QuotaAdjustmentSettingMapper quotaAdjustmentSettingMapper; + + @Resource + private QuotaAdjustmentDetailMapper quotaAdjustmentDetailMapper; + + @Resource + private QuotaItemMapper quotaItemMapper; + + @Override + public Long createQuotaAdjustmentSetting(QuotaAdjustmentSettingSaveReqVO createReqVO) { + // 验证定额子目是否存在 + validateQuotaItemExists(createReqVO.getQuotaItemId()); + + // 插入 + QuotaAdjustmentSettingDO setting = BeanUtils.toBean(createReqVO, QuotaAdjustmentSettingDO.class); + if (setting.getSortOrder() == null) { + setting.setSortOrder(0); + } + quotaAdjustmentSettingMapper.insert(setting); + return setting.getId(); + } + + @Override + public void updateQuotaAdjustmentSetting(QuotaAdjustmentSettingSaveReqVO updateReqVO) { + // 校验存在 + validateQuotaAdjustmentSettingExists(updateReqVO.getId()); + // 验证定额子目是否存在 + validateQuotaItemExists(updateReqVO.getQuotaItemId()); + + // 更新 + QuotaAdjustmentSettingDO updateObj = BeanUtils.toBean(updateReqVO, QuotaAdjustmentSettingDO.class); + quotaAdjustmentSettingMapper.updateById(updateObj); + } + + @Override + public void deleteQuotaAdjustmentSetting(Long id) { + // 校验存在 + validateQuotaAdjustmentSettingExists(id); + + // 检查是否有明细 + Long detailCount = quotaAdjustmentDetailMapper.countBySettingId(id); + if (detailCount > 0) { + throw exception(QUOTA_ADJUSTMENT_SETTING_HAS_DETAILS); + } + + // 检查是否被其他明细引用 + Long referenceCount = quotaAdjustmentDetailMapper.countByAdjustmentCode(id); + if (referenceCount > 0) { + throw exception(QUOTA_ADJUSTMENT_SETTING_REFERENCED); + } + + // 删除 + quotaAdjustmentSettingMapper.deleteById(id); + } + + @Override + public QuotaAdjustmentSettingDO getQuotaAdjustmentSetting(Long id) { + return quotaAdjustmentSettingMapper.selectById(id); + } + + @Override + public List getQuotaAdjustmentSettingList(Long quotaItemId) { + return quotaAdjustmentSettingMapper.selectListByQuotaItemId(quotaItemId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void swapSort(Long id1, Long id2) { + // 验证两个调整设置是否存在 + QuotaAdjustmentSettingDO setting1 = quotaAdjustmentSettingMapper.selectByIdForUpdate(id1); + QuotaAdjustmentSettingDO setting2 = quotaAdjustmentSettingMapper.selectByIdForUpdate(id2); + + if (setting1 == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS); + } + if (setting2 == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS); + } + + // 验证是否在同一定额子目下 + if (!setting1.getQuotaItemId().equals(setting2.getQuotaItemId())) { + throw exception(QUOTA_ADJUSTMENT_SETTING_NOT_SAME_QUOTA_ITEM); + } + + // 交换排序值 + Integer tempSort = setting1.getSortOrder(); + setting1.setSortOrder(setting2.getSortOrder()); + setting2.setSortOrder(tempSort); + + quotaAdjustmentSettingMapper.updateById(setting1); + quotaAdjustmentSettingMapper.updateById(setting2); + } + + private void validateQuotaAdjustmentSettingExists(Long id) { + if (quotaAdjustmentSettingMapper.selectById(id) == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_NOT_EXISTS); + } + } + + private void validateQuotaItemExists(Long quotaItemId) { + if (quotaItemMapper.selectById(quotaItemId) == null) { + throw exception(QUOTA_ADJUSTMENT_SETTING_QUOTA_ITEM_NOT_EXISTS); + } + } + +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaCatalogItemServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaCatalogItemServiceImpl.java index 7a32935..400b340 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaCatalogItemServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaCatalogItemServiceImpl.java @@ -1,29 +1,37 @@ package com.yhy.module.core.service.quota.impl; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryFullRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryDO; import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeMappingDO; import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; +import com.yhy.module.core.dal.mysql.resource.ResourceCategoryMapper; import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMapper; +import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMappingMapper; import com.yhy.module.core.enums.ErrorCodeConstants; import com.yhy.module.core.service.quota.QuotaCatalogItemService; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - /** - * 定额目录条目 Service 实现类 + * 定额目录条目 Service 实现�? * * @author yhy */ @@ -38,10 +46,16 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { @Resource private ResourceCategoryTreeMapper resourceCategoryTreeMapper; + @Resource + private ResourceCategoryMapper resourceCategoryMapper; + + @Resource + private ResourceCategoryTreeMappingMapper resourceCategoryTreeMappingMapper; + @Override @Transactional(rollbackFor = Exception.class) public Long createQuotaCatalogItem(QuotaCatalogItemSaveReqVO createReqVO) { - // 转换为 DO + // 转换�?DO QuotaCatalogItemDO catalogItem = convertToDO(createReqVO); // 设置路径 @@ -67,7 +81,7 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { // 校验存在 validateExists(updateReqVO.getId()); - // 转换为 DO + // 转换�?DO QuotaCatalogItemDO updateObj = convertToDO(updateReqVO); updateObj.setId(updateReqVO.getId()); @@ -81,8 +95,8 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { // 校验存在 validateExists(id); - // 检查是否有子节点 - if (hasChildren(id)) { + // 检查是否有子节�? + if (quotaCatalogItemMapper.hasChildren(id)) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_HAS_CHILDREN); } @@ -96,27 +110,12 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { } @Override - public List getQuotaCatalogItemList(Long parentId) { - List list; - if (parentId == null) { - list = quotaCatalogItemMapper.selectRootList(); - } else { - list = quotaCatalogItemMapper.selectListByParentId(parentId); - } + public List getQuotaCatalogItemList() { + // 只查询第一层(定额专业节点) + List list = quotaCatalogItemMapper.selectRootList(); return list.stream().map(this::convertToVO).collect(Collectors.toList()); } - @Override - public List getQuotaCatalogItemTree() { - // 查询所有根节点 - List rootList = quotaCatalogItemMapper.selectRootList(); - - // 递归构建树 - return rootList.stream() - .map(this::buildTree) - .collect(Collectors.toList()); - } - @Override @Transactional(rollbackFor = Exception.class) public void bindResourceSpecialty(Long catalogItemId, Long categoryTreeId) { @@ -128,19 +127,19 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_SPECIALTY); } - // 验证是否已绑定 + // 验证是否已绑�? if (Boolean.TRUE.equals(catalogItem.isSpecialtyLocked())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_ALREADY_BOUND); } - // 2. 校验工料机专业节点 + // 2. 校验工料机专业节�? ResourceCategoryTreeDO categoryTree = resourceCategoryTreeMapper.selectById(categoryTreeId); if (categoryTree == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATEGORY_TREE_NOT_EXISTS); } - if (!"type".equals(categoryTree.getNodeType())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATEGORY_TREE_NOT_TYPE); + if (!"specialty".equals(categoryTree.getNodeType())) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATEGORY_TREE_NOT_SPECIALTY); } // 3. 执行绑定 @@ -156,50 +155,6 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { log.info("[bindResourceSpecialty] 绑定成功,catalogItemId={}, categoryTreeId={}", catalogItemId, categoryTreeId); } - @Override - @Transactional(rollbackFor = Exception.class) - public Long addDirectory(QuotaCatalogItemSaveReqVO createReqVO) { - // 验证父节点 - if (createReqVO.getParentId() != null) { - QuotaCatalogItemDO parent = validateExists(createReqVO.getParentId()); - if ("content".equals(parent.getContentType())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_CONTENT_NO_CHILDREN); - } - } - - // 设置目录属性 - if (createReqVO.getAttributes() == null) { - createReqVO.setAttributes(new HashMap<>()); - } - createReqVO.getAttributes().put("content_type", "directory"); - createReqVO.getAttributes().put("allow_children", true); - - return createQuotaCatalogItem(createReqVO); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public Long addContent(QuotaCatalogItemSaveReqVO createReqVO) { - // 验证父节点必须是目录 - if (createReqVO.getParentId() == null) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_CONTENT_NEED_PARENT); - } - - QuotaCatalogItemDO parent = validateExists(createReqVO.getParentId()); - if (!"directory".equals(parent.getContentType())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_PARENT_NOT_DIRECTORY); - } - - // 设置内容属性 - if (createReqVO.getAttributes() == null) { - createReqVO.setAttributes(new HashMap<>()); - } - createReqVO.getAttributes().put("content_type", "content"); - createReqVO.getAttributes().put("allow_children", false); - - return createQuotaCatalogItem(createReqVO); - } - @Override public void validateNodeType(Long id, String expectedType) { QuotaCatalogItemDO catalogItem = validateExists(id); @@ -209,11 +164,6 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { } } - @Override - public boolean hasChildren(Long id) { - return quotaCatalogItemMapper.hasChildren(id); - } - // ========== 私有方法 ========== private QuotaCatalogItemDO validateExists(Long id) { @@ -233,6 +183,10 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { catalogItem.setUnit(vo.getUnit()); catalogItem.setSortOrder(vo.getSortOrder()); catalogItem.setAttributes(vo.getAttributes()); + String nodeType = resolveNodeType(vo, vo.getId() == null); + if (StrUtil.isNotBlank(nodeType)) { + catalogItem.setNodeType(nodeType); + } return catalogItem; } @@ -255,22 +209,165 @@ public class QuotaCatalogItemServiceImpl implements QuotaCatalogItemService { vo.setSpecialtyLocked(catalogItem.isSpecialtyLocked()); vo.setContentType(catalogItem.getContentType()); vo.setAllowChildren(catalogItem.isAllowChildren()); - vo.setHasChildren(hasChildren(catalogItem.getId())); return vo; } - private QuotaCatalogItemRespVO buildTree(QuotaCatalogItemDO node) { - QuotaCatalogItemRespVO vo = convertToVO(node); - - // 查询子节点 - List children = quotaCatalogItemMapper.selectListByParentId(node.getId()); - if (!children.isEmpty()) { - vo.setChildren(children.stream() - .map(this::buildTree) - .collect(Collectors.toList())); + private String resolveNodeType(QuotaCatalogItemSaveReqVO vo, boolean required) { + String nodeType = vo.getNodeType(); + if (StrUtil.isBlank(nodeType) && vo.getAttributes() != null) { + Object attrNodeType = vo.getAttributes().get("node_type"); + if (attrNodeType != null) { + nodeType = String.valueOf(attrNodeType); + } } - + if (required && StrUtil.isBlank(nodeType)) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_TYPE_MISMATCH); + } + return nodeType; + } + + private ResourceCategoryFullRespVO convertToCategoryFullVO(ResourceCategoryDO category) { + ResourceCategoryFullRespVO vo = new ResourceCategoryFullRespVO(); + vo.setId(category.getId()); + vo.setCode(category.getCode()); + vo.setName(category.getName()); + vo.setTaxExclBaseCode(category.getTaxExclBaseCode()); + vo.setTaxInclBaseCode(category.getTaxInclBaseCode()); + vo.setTaxExclCompileCode(category.getTaxExclCompileCode()); + vo.setTaxInclCompileCode(category.getTaxInclCompileCode()); + vo.setSortOrder(category.getSortOrder()); return vo; } + + @Override + public List getQuotaCatalogItemTree(List excludeNodeTypes) { + // 查询所有节点 + List allNodes = quotaCatalogItemMapper.selectList(); + + // 如果有排除类型,先过滤 + if (excludeNodeTypes != null && !excludeNodeTypes.isEmpty()) { + Set excludeSet = excludeNodeTypes.stream() + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + if (!excludeSet.isEmpty()) { + allNodes = allNodes.stream() + .filter(node -> !excludeSet.contains(node.getNodeType())) + .collect(Collectors.toList()); + } + } + + // 转换为VO + List allVOs = allNodes.stream() + .map(this::convertToVO) + .collect(Collectors.toList()); + + // 构建树形结构 + return buildTree(allVOs, null); + } + + /** + * 构建树形结构 + * + * @param nodes 所有节点 + * @param parentId 父节点ID + * @return 树形结构 + */ + private List buildTree(List nodes, Long parentId) { + return nodes.stream() + .filter(node -> Objects.equals(node.getParentId(), parentId)) + .sorted(Comparator.comparing(QuotaCatalogItemRespVO::getSortOrder)) + .peek(node -> { + List children = buildTree(nodes, node.getId()); + node.setChildren(children); + }) + .collect(Collectors.toList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void swapSort(Long nodeId1, Long nodeId2) { + // 使用行锁查询两个节点 + QuotaCatalogItemDO node1 = quotaCatalogItemMapper.selectByIdForUpdate(nodeId1); + QuotaCatalogItemDO node2 = quotaCatalogItemMapper.selectByIdForUpdate(nodeId2); + + // 验证节点存在 + if (node1 == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + if (node2 == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + + // 验证是否同级 + if (!Objects.equals(node1.getParentId(), node2.getParentId())) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_SAME_LEVEL); + } + + // 交换 sort_order + Integer tempSort = node1.getSortOrder(); + node1.setSortOrder(node2.getSortOrder()); + node2.setSortOrder(tempSort); + + // 更新数据库 + quotaCatalogItemMapper.updateById(node1); + quotaCatalogItemMapper.updateById(node2); + } + + @Override + public List getCategoriesByCatalogItem(Long catalogItemId) { + // 1. 查询当前节点 + QuotaCatalogItemDO currentNode = validateExists(catalogItemId); + + // 2. 如果是费率模式节点,向上查找定额专业节点 + QuotaCatalogItemDO specialtyNode = currentNode; + if ("rate_mode".equals(currentNode.getNodeType())) { + if (currentNode.getParentId() == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + specialtyNode = validateExists(currentNode.getParentId()); + } + + // 3. 验证是定额专业节点 + if (!"specialty".equals(specialtyNode.getNodeType())) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_SPECIALTY); + } + + // 4. 获取绑定的工料机专业ID + Long categoryTreeId = specialtyNode.getCategoryTreeId(); + if (categoryTreeId == null) { + log.warn("[getCategoriesByCatalogItem] 定额专业节点未绑定工料机专业,catalogItemId={}", catalogItemId); + return new ArrayList<>(); + } + + // 5. 查询工料机专业下的类别映射关系 + List mappings = resourceCategoryTreeMappingMapper.selectList( + new LambdaQueryWrapper() + .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, categoryTreeId) + ); + + if (mappings.isEmpty()) { + log.warn("[getCategoriesByCatalogItem] 工料机专业下没有关联的类别,categoryTreeId={}", categoryTreeId); + return new ArrayList<>(); + } + + // 6. 提取类别ID列表 + List categoryIds = mappings.stream() + .map(ResourceCategoryTreeMappingDO::getCategoryId) + .collect(Collectors.toList()); + + // 7. 查询类别字典详情(含价格代码) + List categories = resourceCategoryMapper.selectList( + new LambdaQueryWrapper() + .in(ResourceCategoryDO::getId, categoryIds) + .orderByAsc(ResourceCategoryDO::getSortOrder) + ); + + // 8. 转换为VO + return categories.stream() + .map(this::convertToCategoryFullVO) + .collect(Collectors.toList()); + } + + // ========== 私有方法 ========== } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaCatalogTreeServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaCatalogTreeServiceImpl.java new file mode 100644 index 0000000..dfceec5 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaCatalogTreeServiceImpl.java @@ -0,0 +1,408 @@ +package com.yhy.module.core.service.quota.impl; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_PARENT_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_SPECIALTY_NOT_FOUND; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_TREE_HAS_CHILDREN; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_TREE_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_TREE_NOT_SAME_LEVEL; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_TREE_PARENT_NOT_ALLOW_CHILDREN; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_TREE_PARENT_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_NOT_EXISTS; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogTreeRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaCatalogTreeSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateItemDO; +import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaCatalogTreeMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaRateFieldMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaRateItemMapper; +import com.yhy.module.core.service.quota.QuotaCatalogTreeService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * 定额子目树 Service 实现类 + * + * @author yhy + */ +@Service +@Validated +@Slf4j +public class QuotaCatalogTreeServiceImpl implements QuotaCatalogTreeService { + + @Resource + private QuotaCatalogTreeMapper quotaCatalogTreeMapper; + + @Resource + private QuotaRateItemMapper quotaRateItemMapper; + + @Resource + private QuotaCatalogItemMapper quotaCatalogItemMapper; + + @Resource + private QuotaRateFieldMapper rateFieldMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createCatalogTree(QuotaCatalogTreeSaveReqVO createReqVO) { + // 1. 构建DO对象 + QuotaCatalogTreeDO catalogTree = buildCatalogTreeDO(createReqVO); + + // 2. 设置 allowChildren 和 contentType + if ("directory".equals(createReqVO.getContentType())) { + catalogTree.setAllowChildren(true); + } else if ("content".equals(createReqVO.getContentType())) { + catalogTree.setAllowChildren(false); + } + + // 3. 验证父节点(如果有) + if (createReqVO.getParentId() != null) { + QuotaCatalogTreeDO parent = quotaCatalogTreeMapper.selectById(createReqVO.getParentId()); + if (parent == null) { + throw exception(QUOTA_CATALOG_TREE_PARENT_NOT_EXISTS); + } + // 校验父节点是否允许添加子节点 + if (!parent.getAllowChildren()) { + throw exception(QUOTA_CATALOG_TREE_PARENT_NOT_ALLOW_CHILDREN); + } + } + + // 4. 设置排序 + if (catalogTree.getSortOrder() == null) { + catalogTree.setSortOrder(getNextSortOrder(createReqVO.getCatalogItemId(), createReqVO.getParentId())); + } + + // 5. 插入数据库(先插入以获取ID) + quotaCatalogTreeMapper.insert(catalogTree); + + // 6. 计算路径和层级(插入后才有ID) + calculatePathAndLevel(catalogTree, createReqVO.getParentId()); + + // 7. 更新路径和层级 + quotaCatalogTreeMapper.updateById(catalogTree); + + return catalogTree.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCatalogTree(QuotaCatalogTreeSaveReqVO updateReqVO) { + // 1. 校验节点存在 + validateCatalogTreeExists(updateReqVO.getId()); + + // 2. 构建更新对象 + QuotaCatalogTreeDO updateObj = QuotaCatalogTreeDO.builder() + .id(updateReqVO.getId()) + .code(updateReqVO.getCode()) + .name(updateReqVO.getName()) + .unit(updateReqVO.getUnit()) + .sortOrder(updateReqVO.getSortOrder()) + .attributes(updateReqVO.getAttributes()) + .build(); + + // 3. 更新数据库 + quotaCatalogTreeMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteCatalogTree(Long id) { + // 1. 校验节点存在 + validateCatalogTreeExists(id); + + // 2. 校验是否有子节点 + List children = quotaCatalogTreeMapper.selectListByParentId(id); + if (CollUtil.isNotEmpty(children)) { + throw exception(QUOTA_CATALOG_TREE_HAS_CHILDREN); + } + + // 3. 删除节点 + quotaCatalogTreeMapper.deleteById(id); + } + + @Override + public QuotaCatalogTreeRespVO getCatalogTree(Long id) { + QuotaCatalogTreeDO item = quotaCatalogTreeMapper.selectById(id); + if (item == null) { + return null; + } + return convertToRespVO(item); + } + + @Override + public List getCatalogTreeList(Long catalogItemId, Long parentId) { + // 查询指定条件的节点 + List items; + if (parentId != null) { + items = quotaCatalogTreeMapper.selectListByParentId(parentId); + } else { + items = quotaCatalogTreeMapper.selectListByCatalogItemId(catalogItemId); + } + + return items.stream() + .map(this::convertToRespVO) + .collect(Collectors.toList()); + } + + @Override + public List getCatalogTreeTree(Long catalogItemId) { + // 1. 查询所有节点 + List allItems = quotaCatalogTreeMapper.selectListByCatalogItemId(catalogItemId); + + // 2. 转换为VO + List allVOs = allItems.stream() + .map(this::convertToRespVO) + .collect(Collectors.toList()); + + // 3. 构建树形结构 + return buildTree(allVOs, null); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void swapSort(Long nodeId1, Long nodeId2) { + // 1. 查询两个节点(加锁) + QuotaCatalogTreeDO node1 = quotaCatalogTreeMapper.selectById(nodeId1); + QuotaCatalogTreeDO node2 = quotaCatalogTreeMapper.selectById(nodeId2); + + if (node1 == null || node2 == null) { + throw exception(QUOTA_CATALOG_TREE_NOT_EXISTS); + } + + // 2. 校验是否同级 + if (!isSameLevel(node1, node2)) { + throw exception(QUOTA_CATALOG_TREE_NOT_SAME_LEVEL); + } + + // 3. 交换排序值 + Integer tempSort = node1.getSortOrder(); + node1.setSortOrder(node2.getSortOrder()); + node2.setSortOrder(tempSort); + + // 4. 更新数据库 + quotaCatalogTreeMapper.updateById(node1); + quotaCatalogTreeMapper.updateById(node2); + + log.info("[swapSort] 交换节点[{}]和节点[{}]的排序成功", nodeId1, nodeId2); + } + + // ==================== 私有方法 ==================== + + /** + * 校验节点存在 + */ + private QuotaCatalogTreeDO validateCatalogTreeExists(Long id) { + QuotaCatalogTreeDO item = quotaCatalogTreeMapper.selectById(id); + if (item == null) { + throw exception(QUOTA_CATALOG_TREE_NOT_EXISTS); + } + return item; + } + + /** + * 构建DO对象 + */ + private QuotaCatalogTreeDO buildCatalogTreeDO(QuotaCatalogTreeSaveReqVO reqVO) { + return QuotaCatalogTreeDO.builder() + .id(reqVO.getId()) + .catalogItemId(reqVO.getCatalogItemId()) + .parentId(reqVO.getParentId()) + .code(reqVO.getCode()) + .name(reqVO.getName()) + .unit(reqVO.getUnit()) + .contentType(reqVO.getContentType()) + .sortOrder(reqVO.getSortOrder()) + .attributes(reqVO.getAttributes()) + .build(); + } + + /** + * 计算路径和层级 + */ + private void calculatePathAndLevel(QuotaCatalogTreeDO item, Long parentId) { + if (parentId == null) { + // 根节点 + item.setPath(new String[]{item.getId().toString()}); + item.setLevel(1); + } else { + // 子节点 + QuotaCatalogTreeDO parent = quotaCatalogTreeMapper.selectById(parentId); + + // 拼接路径 + String[] parentPath = parent.getPath(); + String[] newPath = ArrayUtil.append(parentPath, item.getId().toString()); + item.setPath(newPath); + item.setLevel(parent.getLevel() + 1); + } + } + + /** + * 获取下一个排序值 + */ + private Integer getNextSortOrder(Long catalogItemId, Long parentId) { + List siblings = quotaCatalogTreeMapper.selectSiblings(catalogItemId, parentId); + if (CollUtil.isEmpty(siblings)) { + return 1; + } + return siblings.stream() + .mapToInt(QuotaCatalogTreeDO::getSortOrder) + .max() + .orElse(0) + 1; + } + + /** + * 转换为RespVO + */ + private QuotaCatalogTreeRespVO convertToRespVO(QuotaCatalogTreeDO item) { + QuotaCatalogTreeRespVO vo = new QuotaCatalogTreeRespVO(); + vo.setId(item.getId()); + vo.setTenantId(item.getTenantId()); + vo.setCatalogItemId(item.getCatalogItemId()); + vo.setParentId(item.getParentId()); + vo.setCode(item.getCode()); + vo.setName(item.getName()); + vo.setUnit(item.getUnit()); + vo.setContentType(item.getContentType()); + vo.setAllowChildren(item.getAllowChildren()); + vo.setSortOrder(item.getSortOrder()); + vo.setPath(item.getPath()); + vo.setLevel(item.getLevel()); + vo.setAttributes(item.getAttributes()); + vo.setCreateTime(item.getCreateTime()); + return vo; + } + + /** + * 构建树形结构 + */ + private List buildTree(List allNodes, Long parentId) { + List result = new ArrayList<>(); + for (QuotaCatalogTreeRespVO node : allNodes) { + if ((parentId == null && node.getParentId() == null) + || (parentId != null && parentId.equals(node.getParentId()))) { + node.setChildren(buildTree(allNodes, node.getId())); + result.add(node); + } + } + return result; + } + + /** + * 判断是否同级 + */ + private boolean isSameLevel(QuotaCatalogTreeDO node1, QuotaCatalogTreeDO node2) { + // 父节点相同且定额专业相同即为同级 + if (!node1.getCatalogItemId().equals(node2.getCatalogItemId())) { + return false; + } + if (node1.getParentId() == null && node2.getParentId() == null) { + return true; + } + if (node1.getParentId() == null || node2.getParentId() == null) { + return false; + } + return node1.getParentId().equals(node2.getParentId()); + } + + @Override + public List getCatalogTreeByRateItem(Long rateItemId) { + // 1. 查询费率项,获取其 catalogItemId(费率模式节点ID) + QuotaRateItemDO rateItem = quotaRateItemMapper.selectById(rateItemId); + if (rateItem == null) { + throw exception(QUOTA_RATE_ITEM_NOT_EXISTS); + } + + // 2. 查询费率模式节点 + QuotaCatalogItemDO rateModeNode = quotaCatalogItemMapper.selectById(rateItem.getCatalogItemId()); + if (rateModeNode == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + + // 3. 获取父节点(定额专业节点) + if (rateModeNode.getParentId() == null) { + throw exception(QUOTA_CATALOG_ITEM_PARENT_NOT_EXISTS); + } + + QuotaCatalogItemDO specialtyNode = quotaCatalogItemMapper.selectById(rateModeNode.getParentId()); + if (specialtyNode == null || !"specialty".equals(specialtyNode.getNodeType())) { + throw exception(QUOTA_CATALOG_ITEM_SPECIALTY_NOT_FOUND); + } + + // 4. 查询定额专业节点对应的定额子目树(第二层) + List catalogTrees = quotaCatalogTreeMapper.selectListByCatalogItemId(specialtyNode.getId()); + + // 5. 转换为VO + return catalogTrees.stream() + .map(this::convertToRespVO) + .collect(Collectors.toList()); + } + + @Override + public List getCatalogTreeByRateModeNode(Long rateModeNodeId) { + // 1. 查询费率模式节点 + QuotaCatalogItemDO rateModeNode = quotaCatalogItemMapper.selectById(rateModeNodeId); + if (rateModeNode == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + + // 2. 验证节点类型必须是 rate_mode + if (!"rate_mode".equals(rateModeNode.getNodeType())) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS, "节点类型必须是 rate_mode"); + } + + // 3. 获取父节点(定额专业节点) + if (rateModeNode.getParentId() == null) { + throw exception(QUOTA_CATALOG_ITEM_PARENT_NOT_EXISTS); + } + + QuotaCatalogItemDO specialtyNode = quotaCatalogItemMapper.selectById(rateModeNode.getParentId()); + if (specialtyNode == null || !"specialty".equals(specialtyNode.getNodeType())) { + throw exception(QUOTA_CATALOG_ITEM_SPECIALTY_NOT_FOUND); + } + + // 4. 查询定额专业节点对应的定额子目树(第二层) + List catalogTrees = quotaCatalogTreeMapper.selectListByCatalogItemId(specialtyNode.getId()); + + // 5. 查询该费率模式节点的所有字段绑定 + List fieldBindings = rateFieldMapper.selectListByCatalogItemId(rateModeNodeId); + + // 6. 构建节点ID到字段索引的映射(哪些节点被哪些字段绑定了) + Map> nodeBindingMap = new HashMap<>(); + for (QuotaRateFieldDO field : fieldBindings) { + if (field.getBindingIds() != null && field.getBindingIds().length > 0) { + for (Long nodeId : field.getBindingIds()) { + nodeBindingMap.computeIfAbsent(nodeId, k -> new ArrayList<>()) + .add(field.getFieldIndex()); + } + } + } + + // 7. 转换为VO并添加绑定信息 + List allVOs = catalogTrees.stream() + .map(tree -> { + QuotaCatalogTreeRespVO vo = convertToRespVO(tree); + // 设置该节点已被哪些字段绑定 + vo.setBoundFieldIndexes(nodeBindingMap.getOrDefault(tree.getId(), new ArrayList<>())); + return vo; + }) + .collect(Collectors.toList()); + + // 8. 构建树形结构 + return buildTree(allVOs, null); + } +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaFeeItemServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaFeeItemServiceImpl.java index 0e0217e..713719f 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaFeeItemServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaFeeItemServiceImpl.java @@ -1,28 +1,41 @@ package com.yhy.module.core.service.quota.impl; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_CALC_BASE_FORMULA_BRACKET_MISMATCH; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_CALC_BASE_FORMULA_EMPTY; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_CALC_BASE_VARIABLES_EMPTY; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_ITEM_CATALOG_NOT_RATE_MODE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_ITEM_CUSTOM_CODE_DUPLICATE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_ITEM_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_FEE_ITEM_NOT_SAME_CATALOG; + import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaFeeItemWithRateRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; import com.yhy.module.core.dal.dataobject.quota.QuotaFeeItemDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateItemDO; import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; import com.yhy.module.core.dal.mysql.quota.QuotaFeeItemMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaRateItemMapper; import com.yhy.module.core.service.quota.QuotaFeeItemService; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.yhy.module.core.enums.ErrorCodeConstants.*; - /** * 定额取费项 Service 实现类 */ @@ -37,29 +50,37 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { @Resource private QuotaCatalogItemMapper quotaCatalogItemMapper; + @Resource + private QuotaRateItemMapper quotaRateItemMapper; + @Override @Transactional(rollbackFor = Exception.class) public Long createFeeItem(QuotaFeeItemSaveReqVO createReqVO) { // 1. 验证模式节点 validateRateModeNode(createReqVO.getCatalogItemId()); - // 2. 验证计算基数 + // 2. 验证费率项ID(如果提供) + if (createReqVO.getRateItemId() != null) { + validateRateItem(createReqVO.getCatalogItemId(), createReqVO.getRateItemId()); + } + + // 3. 验证计算基数 if (createReqVO.getCalcBase() != null) { validateCalcBase(createReqVO.getCalcBase()); } - // 3. 验证自定义序号唯一性 + // 4. 验证自定义序号唯一性 if (StrUtil.isNotBlank(createReqVO.getCustomCode())) { validateCustomCodeUnique(null, createReqVO.getCatalogItemId(), createReqVO.getCustomCode()); } - // 4. 设置排序值 + // 5. 设置排序值 if (createReqVO.getSortOrder() == null) { Integer maxSortOrder = quotaFeeItemMapper.selectMaxSortOrder(createReqVO.getCatalogItemId()); createReqVO.setSortOrder(maxSortOrder + 1); } - // 5. 插入数据 + // 6. 插入数据 QuotaFeeItemDO feeItem = BeanUtils.toBean(createReqVO, QuotaFeeItemDO.class); quotaFeeItemMapper.insert(feeItem); @@ -75,17 +96,22 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { // 2. 验证模式节点 validateRateModeNode(updateReqVO.getCatalogItemId()); - // 3. 验证计算基数 + // 3. 验证费率项ID(如果提供) + if (updateReqVO.getRateItemId() != null) { + validateRateItem(updateReqVO.getCatalogItemId(), updateReqVO.getRateItemId()); + } + + // 4. 验证计算基数 if (updateReqVO.getCalcBase() != null) { validateCalcBase(updateReqVO.getCalcBase()); } - // 4. 验证自定义序号唯一性 + // 5. 验证自定义序号唯一性 if (StrUtil.isNotBlank(updateReqVO.getCustomCode())) { validateCustomCodeUnique(updateReqVO.getId(), updateReqVO.getCatalogItemId(), updateReqVO.getCustomCode()); } - // 5. 更新数据 + // 6. 更新数据 QuotaFeeItemDO updateObj = BeanUtils.toBean(updateReqVO, QuotaFeeItemDO.class); quotaFeeItemMapper.updateById(updateObj); } @@ -110,6 +136,132 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { return quotaFeeItemMapper.selectListByCatalogItemId(catalogItemId); } + @Override + public List getFeeItemWithRateList(Long catalogItemId) { + // 1. 验证模式节点 + validateRateModeNode(catalogItemId); + + // 2. 查询所有费率项节点(不限制层级) + List rateItems = quotaRateItemMapper.selectListByCatalogItemId(catalogItemId); + + // 3. 查询取费项列表 + List feeItems = quotaFeeItemMapper.selectListByCatalogItemId(catalogItemId); + + // 4. 构建取费项Map(优先使用 rate_item_id,其次使用 rateCode) + Map feeItemByRateItemIdMap = feeItems.stream() + .filter(item -> item.getRateItemId() != null) + .collect(Collectors.toMap( + QuotaFeeItemDO::getRateItemId, + item -> item, + (existing, replacement) -> existing + )); + + Map feeItemByRateCodeMap = feeItems.stream() + .filter(item -> StrUtil.isNotBlank(item.getRateCode()) && item.getRateItemId() == null) + .collect(Collectors.toMap( + QuotaFeeItemDO::getRateCode, + item -> item, + (existing, replacement) -> existing + )); + + // 5. 转换为 VO 并合并取费项数据 + List allVos = new ArrayList<>(); + for (QuotaRateItemDO rateItem : rateItems) { + QuotaFeeItemWithRateRespVO vo = new QuotaFeeItemWithRateRespVO(); + + // 设置费率项信息 + vo.setRateItemId(rateItem.getId()); + vo.setRateItemName(rateItem.getName()); + vo.setRateItemCustomCode(rateItem.getCustomCode()); + vo.setRateCode(rateItem.getRateCode()); + vo.setParentId(rateItem.getParentId()); + vo.setNodeType(rateItem.getNodeType()); + vo.setCatalogItemId(catalogItemId); + + // 查找对应的取费项(优先使用 rate_item_id,其次使用 rateCode) + QuotaFeeItemDO feeItem = feeItemByRateItemIdMap.get(rateItem.getId()); + if (feeItem == null && StrUtil.isNotBlank(rateItem.getRateCode())) { + feeItem = feeItemByRateCodeMap.get(rateItem.getRateCode()); + } + + if (feeItem != null) { + // 设置取费项信息 + vo.setFeeItemId(feeItem.getId()); + vo.setFeeItemName(feeItem.getName()); + vo.setFeeItemCustomCode(feeItem.getCustomCode()); + vo.setCalcBase(feeItem.getCalcBase()); + vo.setRatePercentage(feeItem.getRateCode()); + vo.setCode(feeItem.getCode()); + vo.setFeeCategory(feeItem.getFeeCategory()); + vo.setBaseDescription(feeItem.getBaseDescription()); + vo.setSortOrder(feeItem.getSortOrder()); + vo.setHidden(feeItem.getHidden()); + vo.setVariable(feeItem.getVariable()); + vo.setCreateTime(feeItem.getCreateTime()); + vo.setUpdateTime(feeItem.getUpdateTime()); + vo.setHasFeeItem(true); + } else { + vo.setHasFeeItem(false); + } + + allVos.add(vo); + } + + // 6. 构建树形结构 + return buildTree(allVos); + } + + /** + * 构建树形结构 + */ + private List buildTree(List allVos) { + // 1. 构建 ID -> VO 的映射 + Map voMap = allVos.stream() + .collect(Collectors.toMap(QuotaFeeItemWithRateRespVO::getRateItemId, vo -> vo)); + + // 2. 构建树形结构 + List rootNodes = new ArrayList<>(); + for (QuotaFeeItemWithRateRespVO vo : allVos) { + if (vo.getParentId() == null) { + // 根节点 + rootNodes.add(vo); + } else { + // 子节点,添加到父节点的 children 中 + QuotaFeeItemWithRateRespVO parent = voMap.get(vo.getParentId()); + if (parent != null) { + if (parent.getChildren() == null) { + parent.setChildren(new ArrayList<>()); + } + parent.getChildren().add(vo); + } + } + } + + // 3. 对每个节点的子节点按 sortOrder 排序(如果有取费项) + sortTreeNodes(rootNodes); + + return rootNodes; + } + + /** + * 递归排序树节点 + */ + private void sortTreeNodes(List nodes) { + if (CollUtil.isEmpty(nodes)) { + return; + } + + // 不需要排序,保持费率项的原有顺序(已经按 sortOrder 排序) + // 费率项在查询时已经按 sortOrder 排序,这里不再重新排序 + + // 递归排序子节点 + for (QuotaFeeItemWithRateRespVO node : nodes) { + if (CollUtil.isNotEmpty(node.getChildren())) { + sortTreeNodes(node.getChildren()); + } + } + } + @Override @Transactional(rollbackFor = Exception.class) public void swapSort(Long nodeId1, Long nodeId2) { @@ -161,6 +313,23 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { } } + /** + * 验证费率项 + */ + private void validateRateItem(Long catalogItemId, Long rateItemId) { + QuotaRateItemDO rateItem = quotaRateItemMapper.selectById(rateItemId); + if (rateItem == null) { + throw exception(QUOTA_FEE_ITEM_NOT_EXISTS); + } + + // 验证费率项必须属于同一模式节点 + if (!rateItem.getCatalogItemId().equals(catalogItemId)) { + throw exception(QUOTA_FEE_ITEM_NOT_SAME_CATALOG); + } + + // 注意:不再限制必须是一级节点,所有层级的费率项都可以关联取费项 + } + /** * 验证计算基数 */ @@ -172,7 +341,7 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { } @SuppressWarnings("unchecked") - Map variables = (Map) calcBase.get("variables"); + Map variables = (Map) calcBase.get("variables"); if (MapUtil.isEmpty(variables)) { throw exception(QUOTA_FEE_CALC_BASE_VARIABLES_EMPTY); } @@ -185,9 +354,29 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { QuotaFeeItemDO.PRICE_CODE_TAX_INCL_COMPILE ); - for (String priceCode : variables.values()) { - if (!allowedCodes.contains(priceCode)) { - throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, priceCode); + // 遍历 variables,验证每个变量的格式和价格字段类型 + for (Map.Entry entry : variables.entrySet()) { + String varName = entry.getKey(); + Object varValue = entry.getValue(); + + // 验证值必须是 Map 类型 + if (!(varValue instanceof Map)) { + throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, "变量 " + varName + " 的值格式错误"); + } + + @SuppressWarnings("unchecked") + Map varInfo = (Map) varValue; + + // 验证必须包含 categoryId 和 priceField + if (!varInfo.containsKey("categoryId") || !varInfo.containsKey("priceField")) { + throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, + "变量 " + varName + " 缺少 categoryId 或 priceField 字段"); + } + + // 验证 priceField 必须是允许的价格代码 + String priceField = (String) varInfo.get("priceField"); + if (!allowedCodes.contains(priceField)) { + throw exception(QUOTA_FEE_CALC_BASE_INVALID_PRICE_CODE, priceField); } } @@ -199,29 +388,141 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { * 验证公式语法 */ private void validateFormulaSyntax(String formula, Set variableNames) { - // 1. 验证括号匹配 - int leftCount = 0; - int rightCount = 0; - for (char c : formula.toCharArray()) { - if (c == '(') leftCount++; - if (c == ')') rightCount++; + List sortedVarNames = new ArrayList<>(variableNames); + sortedVarNames.sort((a, b) -> Integer.compare(b.length(), a.length())); + + int index = 0; + int parenDepth = 0; + // Simple tokenizer/validator: enforce operator/operand order and parenthesis balance. + boolean expectOperand = true; + boolean hasToken = false; + + while (index < formula.length()) { + char c = formula.charAt(index); + if (Character.isWhitespace(c)) { + index++; + continue; + } + + if (c == '(') { + if (!expectOperand) { + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "缺少运算符('(' 前)"); + } + parenDepth++; + index++; + expectOperand = true; + hasToken = true; + continue; + } + + if (c == ')') { + if (expectOperand) { + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "缺少操作数(')' 前)"); + } + parenDepth--; + if (parenDepth < 0) { + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_BRACKET_MISMATCH); + } + index++; + expectOperand = false; + hasToken = true; + continue; + } + + if (isOperator(c)) { + if (expectOperand) { + if (c == '-') { + // Unary minus is allowed when an operand is expected. + index++; + hasToken = true; + continue; + } + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "运算符位置不合法: " + c); + } + expectOperand = true; + index++; + hasToken = true; + continue; + } + + if (isNumberStart(c)) { + if (!expectOperand) { + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "数字前缺少运算符"); + } + // Consume a numeric literal (integer or decimal). + index = consumeNumber(formula, index); + expectOperand = false; + hasToken = true; + continue; + } + + String matchedVar = matchVariable(formula, index, sortedVarNames); + if (matchedVar != null) { + if (!expectOperand) { + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "变量前缺少运算符"); + } + index += matchedVar.length(); + expectOperand = false; + hasToken = true; + continue; + } + + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, String.valueOf(c)); } - if (leftCount != rightCount) { + + if (parenDepth != 0) { throw exception(QUOTA_FEE_CALC_BASE_FORMULA_BRACKET_MISMATCH); } + if (!hasToken || expectOperand) { + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "公式语法不正确"); + } + } - // 2. 验证只包含允许的字符(数字、运算符、括号、空格、中文) - String allowedPattern = "[0-9+\\-*/()\\s\\u4e00-\\u9fa5]+"; - if (!formula.matches(allowedPattern)) { - throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR); + private boolean isOperator(char c) { + return c == '+' || c == '-' || c == '*' || c == '/'; + } + + private boolean isNumberStart(char c) { + return Character.isDigit(c) || c == '.'; + } + + private int consumeNumber(String formula, int start) { + int index = start; + boolean hasDigit = false; + boolean hasDot = false; + + while (index < formula.length()) { + char c = formula.charAt(index); + if (Character.isDigit(c)) { + hasDigit = true; + index++; + continue; + } + if (c == '.' && !hasDot) { + hasDot = true; + index++; + continue; + } + break; } - // 3. 验证公式中使用的变量都在 variables 中定义 - for (String varName : variableNames) { - if (!formula.contains(varName)) { - log.warn("公式中未使用变量: {}", varName); + if (!hasDigit) { + throw exception(QUOTA_FEE_CALC_BASE_FORMULA_INVALID_CHAR, "数字格式不正确"); + } + + return index; + } + + private String matchVariable(String formula, int index, List sortedVarNames) { + for (String varName : sortedVarNames) { + if (varName.isEmpty()) { + continue; + } + if (formula.startsWith(varName, index)) { + return varName; } } + return null; } /** @@ -234,3 +535,4 @@ public class QuotaFeeItemServiceImpl implements QuotaFeeItemService { } } } + diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaItemServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaItemServiceImpl.java index 018f3c4..c377a07 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaItemServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaItemServiceImpl.java @@ -5,6 +5,7 @@ import com.yhy.module.core.controller.admin.quota.vo.QuotaItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaItemSaveReqVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO; import com.yhy.module.core.dal.dataobject.quota.QuotaItemDO; import com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO; import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; @@ -13,16 +14,15 @@ import com.yhy.module.core.dal.mysql.quota.QuotaResourceMapper; import com.yhy.module.core.enums.ErrorCodeConstants; import com.yhy.module.core.service.quota.QuotaItemService; import com.yhy.module.core.service.quota.QuotaResourceService; +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import javax.annotation.Resource; -import java.math.BigDecimal; -import java.util.List; -import java.util.stream.Collectors; - /** * 定额子目 Service 实现类 * @@ -39,6 +39,9 @@ public class QuotaItemServiceImpl implements QuotaItemService { @Resource private QuotaCatalogItemMapper quotaCatalogItemMapper; + @Resource + private com.yhy.module.core.dal.mysql.quota.QuotaCatalogTreeMapper quotaCatalogTreeMapper; + @Resource private QuotaResourceMapper quotaResourceMapper; @@ -49,7 +52,7 @@ public class QuotaItemServiceImpl implements QuotaItemService { @Transactional(rollbackFor = Exception.class) public Long createQuotaItem(QuotaItemSaveReqVO createReqVO) { // 验证定额条目存在且是内容节点 - QuotaCatalogItemDO catalogItem = validateCatalogItem(createReqVO.getCatalogItemId()); + QuotaCatalogTreeDO catalogItem = validateCatalogItem(createReqVO.getCatalogItemId()); // 转换为 DO QuotaItemDO quotaItem = convertToDO(createReqVO); @@ -192,14 +195,17 @@ public class QuotaItemServiceImpl implements QuotaItemService { // 1. 获取定额子目 QuotaItemDO quotaItem = validateExists(quotaItemId); - // 2. 获取定额条目(第三层) - QuotaCatalogItemDO catalogItem = quotaCatalogItemMapper.selectById(quotaItem.getCatalogItemId()); - if (catalogItem == null) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS); + // 2. 获取定额子目树节点(第二层) + com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO catalogTreeNode = quotaCatalogTreeMapper.selectById(quotaItem.getCatalogItemId()); + if (catalogTreeNode == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_TREE_NOT_EXISTS); } - // 3. 向上追溯到定额专业节点(第一层) - QuotaCatalogItemDO specialtyNode = findSpecialtyNode(catalogItem); + // 3. 获取定额专业节点(第一层) + QuotaCatalogItemDO specialtyNode = quotaCatalogItemMapper.selectById(catalogTreeNode.getCatalogItemId()); + if (specialtyNode == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS); + } // 4. 返回绑定的工料机专业ID if (specialtyNode.getCategoryTreeId() == null) { @@ -219,18 +225,18 @@ public class QuotaItemServiceImpl implements QuotaItemService { return quotaItem; } - private QuotaCatalogItemDO validateCatalogItem(Long catalogItemId) { - QuotaCatalogItemDO catalogItem = quotaCatalogItemMapper.selectById(catalogItemId); - if (catalogItem == null) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS); + private com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO validateCatalogItem(Long catalogItemId) { + com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO catalogTree = quotaCatalogTreeMapper.selectById(catalogItemId); + if (catalogTree == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_TREE_NOT_EXISTS); } // 验证是内容节点 - if (!"content".equals(catalogItem.getContentType())) { + if (!"content".equals(catalogTree.getContentType())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_CONTENT); } - return catalogItem; + return catalogTree; } /** diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateFieldLabelServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateFieldLabelServiceImpl.java new file mode 100644 index 0000000..c33e4b4 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateFieldLabelServiceImpl.java @@ -0,0 +1,151 @@ +package com.yhy.module.core.service.quota.impl; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.*; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldLabelDO; +import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaRateFieldLabelMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaRateFieldMapper; +import com.yhy.module.core.service.quota.QuotaRateFieldLabelService; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 定额费率字段标签字典 Service 实现 + * + * @author yhy + */ +@Service +@RequiredArgsConstructor +public class QuotaRateFieldLabelServiceImpl implements QuotaRateFieldLabelService { + + private final QuotaRateFieldLabelMapper fieldLabelMapper; + private final QuotaRateFieldMapper rateFieldMapper; + private final QuotaCatalogItemMapper catalogItemMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createFieldLabel(QuotaRateFieldLabelSaveReqVO createReqVO) { + // 1. 验证模式节点存在 + validateRateModeNode(createReqVO.getCatalogItemId()); + + // 2. 验证标签名称唯一性 + QuotaRateFieldLabelDO existLabel = fieldLabelMapper.selectByCatalogItemIdAndName( + createReqVO.getCatalogItemId(), createReqVO.getLabelName()); + if (existLabel != null) { + throw exception(QUOTA_RATE_FIELD_LABEL_NAME_DUPLICATE); + } + + // 3. 设置排序值 + Integer sortOrder = createReqVO.getSortOrder(); + if (sortOrder == null) { + sortOrder = fieldLabelMapper.selectMaxSortOrder(createReqVO.getCatalogItemId()) + 1; + } + + // 4. 创建标签 + QuotaRateFieldLabelDO label = BeanUtils.toBean(createReqVO, QuotaRateFieldLabelDO.class); + label.setSortOrder(sortOrder); + fieldLabelMapper.insert(label); + return label.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateFieldLabel(QuotaRateFieldLabelSaveReqVO updateReqVO) { + // 1. 验证存在 + QuotaRateFieldLabelDO existLabel = validateFieldLabelExists(updateReqVO.getId()); + + // 2. 验证标签名称唯一性(排除自己) + QuotaRateFieldLabelDO duplicateLabel = fieldLabelMapper.selectByCatalogItemIdAndName( + existLabel.getCatalogItemId(), updateReqVO.getLabelName()); + if (duplicateLabel != null && !Objects.equals(duplicateLabel.getId(), updateReqVO.getId())) { + throw exception(QUOTA_RATE_FIELD_LABEL_NAME_DUPLICATE); + } + + // 3. 更新 + QuotaRateFieldLabelDO updateObj = BeanUtils.toBean(updateReqVO, QuotaRateFieldLabelDO.class); + fieldLabelMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteFieldLabel(Long id) { + // 1. 验证存在 + validateFieldLabelExists(id); + + // 2. 验证是否被引用 + Long count = rateFieldMapper.selectCount("field_label_id", id); + if (count > 0) { + throw exception(QUOTA_RATE_FIELD_LABEL_IN_USE); + } + + // 3. 删除 + fieldLabelMapper.deleteById(id); + } + + @Override + public QuotaRateFieldLabelDO getFieldLabel(Long id) { + return fieldLabelMapper.selectById(id); + } + + @Override + public List getFieldLabelList(Long catalogItemId) { + List labels = fieldLabelMapper.selectListByCatalogItemId(catalogItemId); + return labels.stream() + .map(label -> BeanUtils.toBean(label, QuotaRateFieldLabelRespVO.class)) + .collect(Collectors.toList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void swapSort(Long labelId1, Long labelId2) { + // 1. 查询两个标签 + QuotaRateFieldLabelDO label1 = validateFieldLabelExists(labelId1); + QuotaRateFieldLabelDO label2 = validateFieldLabelExists(labelId2); + + // 2. 验证同一模式节点 + if (!Objects.equals(label1.getCatalogItemId(), label2.getCatalogItemId())) { + throw exception(QUOTA_RATE_FIELD_LABEL_NOT_SAME_MODE); + } + + // 3. 交换排序值 + Integer temp = label1.getSortOrder(); + fieldLabelMapper.updateSortOrder(labelId1, label2.getSortOrder()); + fieldLabelMapper.updateSortOrder(labelId2, temp); + } + + @Override + public QuotaRateFieldLabelDO validateFieldLabelExists(Long id) { + QuotaRateFieldLabelDO label = fieldLabelMapper.selectById(id); + if (label == null) { + throw exception(QUOTA_RATE_FIELD_LABEL_NOT_EXISTS); + } + return label; + } + + // ==================== 私有方法 ==================== + + /** + * 验证模式节点 + */ + private void validateRateModeNode(Long catalogItemId) { + QuotaCatalogItemDO catalogItem = catalogItemMapper.selectById(catalogItemId); + if (catalogItem == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + + // 验证是否为 rate_mode 节点 + if (!"rate_mode".equals(catalogItem.getNodeType())) { + throw exception(QUOTA_RATE_ITEM_CATALOG_NOT_RATE_MODE); + } + } +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateFieldServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateFieldServiceImpl.java index 81e7389..29f9860 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateFieldServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateFieldServiceImpl.java @@ -1,25 +1,35 @@ package com.yhy.module.core.service.quota.impl; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_SPECIALTY_NOT_FOUND; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_FIELD_BINDING_OUT_OF_RANGE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_FIELD_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_NOT_EXISTS; + import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldSaveReqVO; import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogTreeDO; import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldDO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldLabelDO; import com.yhy.module.core.dal.dataobject.quota.QuotaRateItemDO; import com.yhy.module.core.dal.mysql.quota.QuotaCatalogItemMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaCatalogTreeMapper; +import com.yhy.module.core.dal.mysql.quota.QuotaRateFieldLabelMapper; import com.yhy.module.core.dal.mysql.quota.QuotaRateFieldMapper; import com.yhy.module.core.dal.mysql.quota.QuotaRateItemMapper; +import com.yhy.module.core.enums.ErrorCodeConstants; import com.yhy.module.core.service.quota.QuotaRateFieldService; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.yhy.module.core.enums.ErrorCodeConstants.*; - /** * 定额费率字段绑定 Service 实现 */ @@ -28,39 +38,95 @@ import static com.yhy.module.core.enums.ErrorCodeConstants.*; public class QuotaRateFieldServiceImpl implements QuotaRateFieldService { private final QuotaRateFieldMapper rateFieldMapper; - private final QuotaRateItemMapper rateItemMapper; private final QuotaCatalogItemMapper catalogItemMapper; + private final QuotaCatalogTreeMapper catalogTreeMapper; + private final QuotaRateFieldLabelMapper fieldLabelMapper; + private final QuotaRateItemMapper rateItemMapper; @Override @Transactional(rollbackFor = Exception.class) public Long saveRateField(QuotaRateFieldSaveReqVO saveReqVO) { - // 1. 验证费率项存在 - QuotaRateItemDO rateItem = rateItemMapper.selectById(saveReqVO.getRateItemId()); - if (rateItem == null) { - throw exception(QUOTA_RATE_ITEM_NOT_EXISTS); + // 1. 验证费率模式节点存在 + QuotaCatalogItemDO catalogItem = catalogItemMapper.selectById(saveReqVO.getCatalogItemId()); + if (catalogItem == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); } - // 2. 验证绑定范围 - if (saveReqVO.getBindingIds() != null && saveReqVO.getBindingIds().length > 0) { - validateBindingRange(rateItem.getCatalogItemId(), saveReqVO.getBindingIds()); + // 2. 如果提供了 rateItemId,则保存字段值到费率项的 settings.fieldValues + if (saveReqVO.getRateItemId() != null) { + // 验证费率项存在 + QuotaRateItemDO rateItem = rateItemMapper.selectById(saveReqVO.getRateItemId()); + if (rateItem == null) { + throw exception(QUOTA_RATE_ITEM_NOT_EXISTS); + } + + // 获取或创建 settings + Map settings = rateItem.getSettings(); + if (settings == null) { + settings = new HashMap<>(); + } + + // 获取或创建 fieldValues + @SuppressWarnings("unchecked") + Map fieldValues = (Map) settings.get("fieldValues"); + if (fieldValues == null) { + fieldValues = new HashMap<>(); + settings.put("fieldValues", fieldValues); + } + + // 保存或删除字段值 + String fieldIndexStr = String.valueOf(saveReqVO.getFieldIndex()); + if (saveReqVO.getFieldValue() != null) { + // 保存字段值 + fieldValues.put(fieldIndexStr, saveReqVO.getFieldValue()); + } else { + // 删除字段值(当值为 null 时) + fieldValues.remove(fieldIndexStr); + } + + // 更新费率项 + QuotaRateItemDO updateObj = new QuotaRateItemDO(); + updateObj.setId(saveReqVO.getRateItemId()); + updateObj.setSettings(settings); + rateItemMapper.updateById(updateObj); } - // 3. 查询是否已存在 - QuotaRateFieldDO existField = rateFieldMapper.selectByRateItemIdAndFieldIndex( - saveReqVO.getRateItemId(), saveReqVO.getFieldIndex()); - - if (existField != null) { - // 更新 - QuotaRateFieldDO updateObj = BeanUtils.toBean(saveReqVO, QuotaRateFieldDO.class); - updateObj.setId(existField.getId()); - rateFieldMapper.updateById(updateObj); - return existField.getId(); - } else { - // 新增 - QuotaRateFieldDO insertObj = BeanUtils.toBean(saveReqVO, QuotaRateFieldDO.class); - rateFieldMapper.insert(insertObj); - return insertObj.getId(); + // 3. 保存字段绑定配置(binding_ids, field_label_id)到 yhy_quota_rate_field + if (saveReqVO.getBindingIds() != null || saveReqVO.getFieldLabelId() != null) { + // 验证绑定范围 + if (saveReqVO.getBindingIds() != null && saveReqVO.getBindingIds().length > 0) { + validateBindingRange(saveReqVO.getCatalogItemId(), saveReqVO.getBindingIds()); + } + + // 查询是否已存在 + QuotaRateFieldDO existField = rateFieldMapper.selectByCatalogItemIdAndFieldIndex( + saveReqVO.getCatalogItemId(), saveReqVO.getFieldIndex()); + + if (existField != null) { + // 更新(只更新 binding_ids 和 field_label_id,不更新 field_value) + QuotaRateFieldDO updateObj = new QuotaRateFieldDO(); + updateObj.setId(existField.getId()); + if (saveReqVO.getBindingIds() != null) { + updateObj.setBindingIds(saveReqVO.getBindingIds()); + } + if (saveReqVO.getFieldLabelId() != null) { + updateObj.setFieldLabelId(saveReqVO.getFieldLabelId()); + } + rateFieldMapper.updateById(updateObj); + return existField.getId(); + } else { + // 新增(不保存 field_value) + QuotaRateFieldDO insertObj = new QuotaRateFieldDO(); + insertObj.setCatalogItemId(saveReqVO.getCatalogItemId()); + insertObj.setFieldIndex(saveReqVO.getFieldIndex()); + insertObj.setBindingIds(saveReqVO.getBindingIds()); + insertObj.setFieldLabelId(saveReqVO.getFieldLabelId()); + rateFieldMapper.insert(insertObj); + return insertObj.getId(); + } } + + return null; } @Override @@ -73,8 +139,8 @@ public class QuotaRateFieldServiceImpl implements QuotaRateFieldService { } @Override - public void deleteByRateItemId(Long rateItemId) { - rateFieldMapper.deleteByRateItemId(rateItemId); + public void deleteByCatalogItemId(Long catalogItemId) { + rateFieldMapper.deleteByCatalogItemId(catalogItemId); } @Override @@ -83,32 +149,51 @@ public class QuotaRateFieldServiceImpl implements QuotaRateFieldService { } @Override - public List getRateFieldList(Long rateItemId) { - List fields = rateFieldMapper.selectListByRateItemId(rateItemId); + public List getRateFieldList(Long catalogItemId) { + List fields = rateFieldMapper.selectListByCatalogItemId(catalogItemId); return fields.stream() - .map(field -> BeanUtils.toBean(field, QuotaRateFieldRespVO.class)) + .map(field -> { + QuotaRateFieldRespVO vo = BeanUtils.toBean(field, QuotaRateFieldRespVO.class); + // 关联查询标签名称 + if (field.getFieldLabelId() != null) { + QuotaRateFieldLabelDO label = fieldLabelMapper.selectById(field.getFieldLabelId()); + if (label != null) { + vo.setFieldLabelName(label.getLabelName()); + } + } + return vo; + }) .collect(Collectors.toList()); } @Override @Transactional(rollbackFor = Exception.class) - public void updateBinding(Long rateItemId, Integer fieldIndex, Long[] bindingIds) { - // 1. 验证费率项存在 - QuotaRateItemDO rateItem = rateItemMapper.selectById(rateItemId); - if (rateItem == null) { - throw exception(QUOTA_RATE_ITEM_NOT_EXISTS); + public void updateBinding(Long catalogItemId, Integer fieldIndex, Long[] bindingIds) { + // 1. 验证费率模式节点存在 + QuotaCatalogItemDO catalogItem = catalogItemMapper.selectById(catalogItemId); + if (catalogItem == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); } // 2. 验证绑定范围 if (bindingIds != null && bindingIds.length > 0) { - validateBindingRange(rateItem.getCatalogItemId(), bindingIds); + validateBindingRange(catalogItemId, bindingIds); + + // 3. 验证节点是否已被其他虚拟字段绑定 + for (Long bindingId : bindingIds) { + List existingFields = rateFieldMapper.selectByBindingIdExcludeFieldIndex( + catalogItemId, bindingId, fieldIndex); + if (!existingFields.isEmpty()) { + throw exception(ErrorCodeConstants.QUOTA_RATE_FIELD_NODE_ALREADY_BOUND); + } + } } - // 3. 查询或创建字段 - QuotaRateFieldDO field = rateFieldMapper.selectByRateItemIdAndFieldIndex(rateItemId, fieldIndex); + // 4. 查询或创建字段 + QuotaRateFieldDO field = rateFieldMapper.selectByCatalogItemIdAndFieldIndex(catalogItemId, fieldIndex); if (field == null) { field = new QuotaRateFieldDO(); - field.setRateItemId(rateItemId); + field.setCatalogItemId(catalogItemId); field.setFieldIndex(fieldIndex); field.setBindingIds(bindingIds); rateFieldMapper.insert(field); @@ -120,23 +205,23 @@ public class QuotaRateFieldServiceImpl implements QuotaRateFieldService { @Override @Transactional(rollbackFor = Exception.class) - public void batchSaveRateFields(Long rateItemId, List fields) { - // 1. 验证费率项存在 - QuotaRateItemDO rateItem = rateItemMapper.selectById(rateItemId); - if (rateItem == null) { - throw exception(QUOTA_RATE_ITEM_NOT_EXISTS); + public void batchSaveRateFields(Long catalogItemId, List fields) { + // 1. 验证费率模式节点存在 + QuotaCatalogItemDO catalogItem = catalogItemMapper.selectById(catalogItemId); + if (catalogItem == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); } // 2. 删除原有字段 - rateFieldMapper.deleteByRateItemId(rateItemId); + rateFieldMapper.deleteByCatalogItemId(catalogItemId); // 3. 批量插入新字段 for (QuotaRateFieldSaveReqVO fieldVO : fields) { - fieldVO.setRateItemId(rateItemId); + fieldVO.setCatalogItemId(catalogItemId); // 验证绑定范围 if (fieldVO.getBindingIds() != null && fieldVO.getBindingIds().length > 0) { - validateBindingRange(rateItem.getCatalogItemId(), fieldVO.getBindingIds()); + validateBindingRange(catalogItemId, fieldVO.getBindingIds()); } QuotaRateFieldDO field = BeanUtils.toBean(fieldVO, QuotaRateFieldDO.class); @@ -163,13 +248,22 @@ public class QuotaRateFieldServiceImpl implements QuotaRateFieldService { } // 2. 验证每个绑定节点是否属于同一定额专业 + // 注意:bindingIds 是定额子目树(yhy_quota_catalog_tree)的ID,不是定额专业树的ID for (Long bindingId : bindingIds) { - QuotaCatalogItemDO bindingNode = catalogItemMapper.selectById(bindingId); - if (bindingNode == null) { + // 查询定额子目树节点 + QuotaCatalogTreeDO treeNode = catalogTreeMapper.selectById(bindingId); + if (treeNode == null) { throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); } - Long bindingSpecialtyId = findSpecialtyId(bindingNode); + // 获取该子目树节点关联的定额专业节点 + QuotaCatalogItemDO catalogItem = catalogItemMapper.selectById(treeNode.getCatalogItemId()); + if (catalogItem == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + + // 验证是否属于同一定额专业 + Long bindingSpecialtyId = findSpecialtyId(catalogItem); if (!specialtyId.equals(bindingSpecialtyId)) { throw exception(QUOTA_RATE_FIELD_BINDING_OUT_OF_RANGE); } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateItemServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateItemServiceImpl.java index 916ec0d..6accfd8 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateItemServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaRateItemServiceImpl.java @@ -1,10 +1,36 @@ package com.yhy.module.core.service.quota.impl; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_CATALOG_ITEM_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_INCREMENT_BASE_THRESHOLD_INVALID; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_INCREMENT_BASE_THRESHOLD_NOT_ASCENDING; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_INCREMENT_FIELD_INCREMENTS_EMPTY; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_INCREMENT_SEQ_DUPLICATE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_INCREMENT_STEP_INVALID; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_CATALOG_NOT_RATE_MODE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_HAS_CHILDREN; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_LEVEL_EXCEED; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_NOT_DIRECTORY_CANNOT_SET_EDITABLE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_NOT_SAME_LEVEL; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_NOT_SAME_MODE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_PARENT_NOT_DIRECTORY; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_PARENT_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_ITEM_PARENT_NOT_SAME_MODE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_MODE_PARENT_NOT_SPECIALTY; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_TIER_COMPARE_TYPE_INVALID; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_TIER_COMPARE_TYPE_MIXED; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_TIER_FIELD_VALUES_EMPTY; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_TIER_SEQ_DUPLICATE; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_TIER_THRESHOLD_INVALID; +import static com.yhy.module.core.enums.ErrorCodeConstants.QUOTA_RATE_TIER_THRESHOLD_NOT_ASCENDING; + import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateItemRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateItemSaveReqVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateModeNodeSaveReqVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaRateValueRulesReqVO; import com.yhy.module.core.dal.dataobject.quota.QuotaCatalogItemDO; import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldDO; @@ -14,18 +40,22 @@ import com.yhy.module.core.dal.mysql.quota.QuotaRateFieldMapper; import com.yhy.module.core.dal.mysql.quota.QuotaRateItemMapper; import com.yhy.module.core.service.quota.QuotaRateFieldService; import com.yhy.module.core.service.quota.QuotaRateItemService; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.*; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.yhy.module.core.enums.ErrorCodeConstants.*; - /** * 定额费率项 Service 实现 */ @@ -38,6 +68,45 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { private final QuotaRateFieldService rateFieldService; private final QuotaCatalogItemMapper catalogItemMapper; + @Override + @Transactional(rollbackFor = Exception.class) + public Long createRateModeNode(QuotaRateModeNodeSaveReqVO createReqVO) { + // 1. 验证父节点必须是定额专业节点 + QuotaCatalogItemDO parentNode = catalogItemMapper.selectById(createReqVO.getParentId()); + if (parentNode == null) { + throw exception(QUOTA_CATALOG_ITEM_NOT_EXISTS); + } + + // 验证父节点类型必须是 specialty + if (!"specialty".equals(parentNode.getNodeType())) { + throw exception(QUOTA_RATE_MODE_PARENT_NOT_SPECIALTY); + } + + // 2. 创建 rate_mode 节点 + QuotaCatalogItemDO rateModeNode = new QuotaCatalogItemDO(); + rateModeNode.setParentId(createReqVO.getParentId()); + // 使用 code 字段,如果未提供则自动生成 + rateModeNode.setCode(createReqVO.getCode() != null ? createReqVO.getCode() : "MODE_" + System.currentTimeMillis()); + rateModeNode.setName(createReqVO.getName()); + rateModeNode.setNodeType("rate_mode"); + rateModeNode.setSortOrder(createReqVO.getSortOrder() != null ? createReqVO.getSortOrder() : 0); + + // 设置路径 + String[] parentPath = parentNode.getPath(); + String[] newPath = new String[parentPath.length + 1]; + System.arraycopy(parentPath, 0, newPath, 0, parentPath.length); + newPath[parentPath.length] = parentNode.getId().toString(); + rateModeNode.setPath(newPath); + + // 设置 attributes + Map attributes = new HashMap<>(); + attributes.put("node_type", "rate_mode"); + rateModeNode.setAttributes(attributes); + + catalogItemMapper.insert(rateModeNode); + return rateModeNode.getId(); + } + @Override @Transactional(rollbackFor = Exception.class) public Long createRateItem(QuotaRateItemSaveReqVO createReqVO) { @@ -80,7 +149,15 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { // 1. 验证存在 QuotaRateItemDO existItem = validateRateItemExists(updateReqVO.getId()); - // 2. 如果修改了父节点,需要验证 + // 2. 验证 isEditable 只能在 directory 节点上修改 + if (updateReqVO.getIsEditable() != null && + !Objects.equals(existItem.getIsEditable(), updateReqVO.getIsEditable())) { + if (!existItem.isDirectory()) { + throw exception(QUOTA_RATE_ITEM_NOT_DIRECTORY_CANNOT_SET_EDITABLE); + } + } + + // 3. 如果修改了父节点,需要验证 if (updateReqVO.getParentId() != null && !Objects.equals(existItem.getParentId(), updateReqVO.getParentId())) { validateParent(updateReqVO.getParentId(), existItem.getCatalogItemId()); @@ -92,7 +169,7 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { updateReqVO.setSortOrder(newLevel); } - // 3. 更新 + // 4. 更新 QuotaRateItemDO updateObj = BeanUtils.toBean(updateReqVO, QuotaRateItemDO.class); rateItemMapper.updateById(updateObj); } @@ -108,10 +185,7 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { throw exception(QUOTA_RATE_ITEM_HAS_CHILDREN); } - // 3. 删除字段绑定 - rateFieldService.deleteByRateItemId(id); - - // 4. 删除费率项 + // 3. 删除费率项(字段绑定基于catalog_item_id,不需要删除) rateItemMapper.deleteById(id); } @@ -159,19 +233,82 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { return Collections.emptyList(); } - // 2. 查询所有字段绑定 - List itemIds = allItems.stream().map(QuotaRateItemDO::getId).collect(Collectors.toList()); - List allFields = rateFieldMapper.selectListByRateItemIds(itemIds); - Map> fieldMap = allFields.stream() - .collect(Collectors.groupingBy(QuotaRateFieldDO::getRateItemId)); + // 2. 查询字段绑定配置(基于catalog_item_id,所有费率项共享) + List allFieldConfigs = rateFieldMapper.selectListByCatalogItemId(catalogItemId); // 3. 转换为 VO List allVOs = allItems.stream().map(item -> { QuotaRateItemRespVO vo = BeanUtils.toBean(item, QuotaRateItemRespVO.class); - List fields = fieldMap.get(item.getId()); - if (fields != null) { - vo.setFields(BeanUtils.toBean(fields, QuotaRateFieldRespVO.class)); + + // 构建字段列表 + List fields = new ArrayList<>(); + + // 从 settings.fieldValues 中读取所有字段值 + Map fieldValuesMap = new HashMap<>(); + if (item.getSettings() != null) { + Map settings = item.getSettings(); + Object fieldValuesObj = settings.get("fieldValues"); + if (fieldValuesObj instanceof Map) { + @SuppressWarnings("unchecked") + Map fieldValues = (Map) fieldValuesObj; + + // 遍历所有字段值 + fieldValues.forEach((key, value) -> { + try { + int fieldIndex = Integer.parseInt(key); + if (value != null) { + java.math.BigDecimal decimalValue = null; + if (value instanceof Number) { + decimalValue = new java.math.BigDecimal(value.toString()); + } else if (value instanceof String) { + try { + decimalValue = new java.math.BigDecimal((String) value); + } catch (NumberFormatException e) { + // 忽略无效值 + } + } + if (decimalValue != null) { + fieldValuesMap.put(fieldIndex, decimalValue); + } + } + } catch (NumberFormatException e) { + // 忽略无效的字段索引 + } + }); + } } + + // 如果有字段配置,使用配置信息 + if (!allFieldConfigs.isEmpty()) { + fields = allFieldConfigs.stream().map(config -> { + QuotaRateFieldRespVO fieldVO = new QuotaRateFieldRespVO(); + fieldVO.setId(config.getId()); + fieldVO.setFieldIndex(config.getFieldIndex()); + fieldVO.setFieldLabelId(config.getFieldLabelId()); + fieldVO.setBindingIds(config.getBindingIds()); + + // 从 fieldValuesMap 中获取字段值 + java.math.BigDecimal fieldValue = fieldValuesMap.get(config.getFieldIndex()); + if (fieldValue != null) { + fieldVO.setFieldValue(fieldValue); + } + + return fieldVO; + }).collect(Collectors.toList()); + } else if (!fieldValuesMap.isEmpty()) { + // 如果没有字段配置,但有字段值,创建简单的字段VO + fields = fieldValuesMap.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(entry -> { + QuotaRateFieldRespVO fieldVO = new QuotaRateFieldRespVO(); + fieldVO.setFieldIndex(entry.getKey()); + fieldVO.setFieldValue(entry.getValue()); + return fieldVO; + }).collect(Collectors.toList()); + } + + vo.setFields(fields); + return vo; }).collect(Collectors.toList()); @@ -202,18 +339,87 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { rateItemMapper.updateSortOrder(nodeId2, temp); } + @Override + @Transactional(rollbackFor = Exception.class) + public void moveNode(Long nodeId, Long targetNodeId) { + // 1. 查询两个节点 + QuotaRateItemDO moveNode = validateRateItemExists(nodeId); + QuotaRateItemDO targetNode = validateRateItemExists(targetNodeId); + + // 2. 验证同级(parent_id 相同) + if (!Objects.equals(moveNode.getParentId(), targetNode.getParentId())) { + throw exception(QUOTA_RATE_ITEM_NOT_SAME_LEVEL); + } + + // 3. 验证同一模式(catalog_item_id 相同) + if (!Objects.equals(moveNode.getCatalogItemId(), targetNode.getCatalogItemId())) { + throw exception(QUOTA_RATE_ITEM_NOT_SAME_MODE); + } + + // 4. 查询同级所有节点,按 sort_order 排序 + List siblings; + if (moveNode.getParentId() == null) { + siblings = rateItemMapper.selectRootListByCatalogItemId(moveNode.getCatalogItemId()); + } else { + siblings = rateItemMapper.selectListByParentId(moveNode.getParentId()); + } + + // 按 sort_order 排序 + siblings.sort(Comparator.comparing(QuotaRateItemDO::getSortOrder)); + + // 5. 找到移动节点和目标节点的当前位置 + int moveIndex = -1; + int targetIndex = -1; + for (int i = 0; i < siblings.size(); i++) { + if (siblings.get(i).getId().equals(nodeId)) { + moveIndex = i; + } + if (siblings.get(i).getId().equals(targetNodeId)) { + targetIndex = i; + } + } + + if (moveIndex == -1 || targetIndex == -1) { + throw exception(QUOTA_RATE_ITEM_NOT_EXISTS); + } + + // 6. 如果位置相同或相邻,不需要移动 + if (moveIndex == targetIndex || moveIndex + 1 == targetIndex) { + return; + } + + // 7. 移动节点:从原位置移除,插入到目标位置之前 + QuotaRateItemDO movedNode = siblings.remove(moveIndex); + // 重新计算目标位置(因为移除了一个元素) + if (moveIndex < targetIndex) { + targetIndex--; + } + siblings.add(targetIndex, movedNode); + + // 8. 重新分配 sort_order(从1开始) + for (int i = 0; i < siblings.size(); i++) { + QuotaRateItemDO node = siblings.get(i); + rateItemMapper.updateSortOrder(node.getId(), i + 1); + } + } + @Override @Transactional(rollbackFor = Exception.class) public void configValueRules(QuotaRateValueRulesReqVO reqVO) { // 1. 验证费率项存在 QuotaRateItemDO rateItem = validateRateItemExists(reqVO.getRateItemId()); - // 2. 验证是数值节点 - if (!rateItem.isValue()) { - throw exception(QUOTA_RATE_ITEM_NOT_VALUE_NODE); + // 2. 验证阶梯规则的合理性 + if (reqVO.getTiers() != null && !reqVO.getTiers().isEmpty()) { + validateTierRules(reqVO.getTiers()); } - // 3. 构建规则 + // 3. 验证增量规则的合理性 + if (reqVO.getIncrements() != null && !reqVO.getIncrements().isEmpty()) { + validateIncrementRules(reqVO.getIncrements()); + } + + // 4. 构建规则 Map settings = rateItem.getSettings(); if (settings == null) { settings = new HashMap<>(); @@ -228,7 +434,7 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { } settings.put("valueRules", valueRules); - // 4. 更新 + // 5. 更新 rateItem.setSettings(settings); rateItem.setValueMode(QuotaRateItemDO.VALUE_MODE_DYNAMIC); rateItemMapper.updateById(rateItem); @@ -250,16 +456,16 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { Map result = new HashMap<>(); - // 1. 获取阶梯规则 + // 1. 获取阶梯规则(默认计算方式) List> tiers = (List>) valueRules.get("tiers"); if (tiers == null || tiers.isEmpty()) { return result; } - // 2. 按阈值排序 + // 2. 按阈值排序(从小到大) tiers.sort(Comparator.comparing(t -> new BigDecimal(t.get("threshold").toString()))); - // 3. 找到匹配的阶梯 + // 3. 默认使用阶梯规则:找到第一个满足条件的阶梯 Map matchedTier = null; for (Map tier : tiers) { BigDecimal threshold = new BigDecimal(tier.get("threshold").toString()); @@ -287,12 +493,12 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { } } - // 4. 如果没有匹配,使用最后一个阶梯 + // 4. 如果没有匹配到任何阶梯,使用最后一个阶梯(兜底逻辑) if (matchedTier == null && !tiers.isEmpty()) { matchedTier = tiers.get(tiers.size() - 1); } - // 5. 获取基础字段值 + // 5. 获取阶梯对应的字段值(这是默认的计算结果) if (matchedTier != null) { Map fieldValues = (Map) matchedTier.get("fieldValues"); if (fieldValues != null) { @@ -304,7 +510,8 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { } } - // 6. 计算增量 + // 6. 增量规则(可选,如果配置了则在阶梯基础上叠加) + // 注意:增量规则是可选的,不配置也不影响阶梯规则的使用 List> increments = (List>) valueRules.get("increments"); if (increments != null && !increments.isEmpty()) { for (Map increment : increments) { @@ -313,7 +520,7 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { ? new BigDecimal(increment.get("baseThreshold").toString()) : BigDecimal.ZERO; - // 计算超出部分 + // 计算超出基础阈值的部分 if (baseValue.compareTo(baseThreshold) > 0) { BigDecimal excess = baseValue.subtract(baseThreshold); BigDecimal times = excess.divide(step, 10, RoundingMode.HALF_UP); @@ -325,6 +532,7 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { BigDecimal incrementValue = new BigDecimal(entry.getValue().toString()); BigDecimal addValue = incrementValue.multiply(times); + // 在阶梯基础值上叠加增量 result.merge(fieldIndex, addValue, BigDecimal::add); } } @@ -413,4 +621,132 @@ public class QuotaRateItemServiceImpl implements QuotaRateItemService { return roots; } + + /** + * 验证阶梯规则的合理性 + */ + private void validateTierRules(List tiers) { + if (tiers == null || tiers.isEmpty()) { + return; + } + + // 1. 验证序号唯一性 + Set seqSet = new HashSet<>(); + for (QuotaRateValueRulesReqVO.TierRule tier : tiers) { + if (!seqSet.add(tier.getSeq())) { + throw exception(QUOTA_RATE_TIER_SEQ_DUPLICATE, tier.getSeq()); + } + } + + // 2. 按阈值排序 + List sortedTiers = new ArrayList<>(tiers); + sortedTiers.sort(Comparator.comparing(QuotaRateValueRulesReqVO.TierRule::getThreshold)); + + // 3. 验证阈值的连续性和合理性 + for (int i = 0; i < sortedTiers.size(); i++) { + QuotaRateValueRulesReqVO.TierRule current = sortedTiers.get(i); + + // 验证阈值为正数 + if (current.getThreshold().compareTo(BigDecimal.ZERO) <= 0) { + throw exception(QUOTA_RATE_TIER_THRESHOLD_INVALID, current.getSeq()); + } + + // 验证比较类型 + String compareType = current.getCompareType(); + if (compareType == null || (!compareType.equals("lte") && !compareType.equals("lt") + && !compareType.equals("gte") && !compareType.equals("gt"))) { + throw exception(QUOTA_RATE_TIER_COMPARE_TYPE_INVALID, current.getSeq()); + } + + // 验证字段值不为空 + if (current.getFieldValues() == null || current.getFieldValues().isEmpty()) { + throw exception(QUOTA_RATE_TIER_FIELD_VALUES_EMPTY, current.getSeq()); + } + + // 验证相邻阶梯的阈值关系 + if (i > 0) { + QuotaRateValueRulesReqVO.TierRule previous = sortedTiers.get(i - 1); + + // 如果前一个是 lte/lt,当前应该是 lte/lt,且阈值递增 + if ((previous.getCompareType().equals("lte") || previous.getCompareType().equals("lt")) + && (compareType.equals("lte") || compareType.equals("lt"))) { + if (current.getThreshold().compareTo(previous.getThreshold()) <= 0) { + throw exception(QUOTA_RATE_TIER_THRESHOLD_NOT_ASCENDING, + previous.getSeq(), current.getSeq()); + } + } + + // 如果前一个是 gte/gt,当前应该是 gte/gt,且阈值递增 + if ((previous.getCompareType().equals("gte") || previous.getCompareType().equals("gt")) + && (compareType.equals("gte") || compareType.equals("gt"))) { + if (current.getThreshold().compareTo(previous.getThreshold()) <= 0) { + throw exception(QUOTA_RATE_TIER_THRESHOLD_NOT_ASCENDING, + previous.getSeq(), current.getSeq()); + } + } + + // 不允许混用 lte/lt 和 gte/gt + if ((previous.getCompareType().equals("lte") || previous.getCompareType().equals("lt")) + && (compareType.equals("gte") || compareType.equals("gt"))) { + throw exception(QUOTA_RATE_TIER_COMPARE_TYPE_MIXED, + previous.getSeq(), current.getSeq()); + } + if ((previous.getCompareType().equals("gte") || previous.getCompareType().equals("gt")) + && (compareType.equals("lte") || compareType.equals("lt"))) { + throw exception(QUOTA_RATE_TIER_COMPARE_TYPE_MIXED, + previous.getSeq(), current.getSeq()); + } + } + } + } + + /** + * 验证增量规则的合理性 + */ + private void validateIncrementRules(List increments) { + if (increments == null || increments.isEmpty()) { + return; + } + + // 1. 验证序号唯一性 + Set seqSet = new HashSet<>(); + for (QuotaRateValueRulesReqVO.IncrementRule increment : increments) { + if (!seqSet.add(increment.getSeq())) { + throw exception(QUOTA_RATE_INCREMENT_SEQ_DUPLICATE, increment.getSeq()); + } + } + + // 2. 按基准阈值排序 + List sortedIncrements = new ArrayList<>(increments); + sortedIncrements.sort(Comparator.comparing(QuotaRateValueRulesReqVO.IncrementRule::getBaseThreshold)); + + // 3. 验证增量规则的合理性 + for (int i = 0; i < sortedIncrements.size(); i++) { + QuotaRateValueRulesReqVO.IncrementRule current = sortedIncrements.get(i); + + // 验证基准阈值为正数 + if (current.getBaseThreshold().compareTo(BigDecimal.ZERO) <= 0) { + throw exception(QUOTA_RATE_INCREMENT_BASE_THRESHOLD_INVALID, current.getSeq()); + } + + // 验证步长为正数 + if (current.getStep().compareTo(BigDecimal.ZERO) <= 0) { + throw exception(QUOTA_RATE_INCREMENT_STEP_INVALID, current.getSeq()); + } + + // 验证字段增量不为空 + if (current.getFieldIncrements() == null || current.getFieldIncrements().isEmpty()) { + throw exception(QUOTA_RATE_INCREMENT_FIELD_INCREMENTS_EMPTY, current.getSeq()); + } + + // 验证相邻增量规则的基准阈值递增 + if (i > 0) { + QuotaRateValueRulesReqVO.IncrementRule previous = sortedIncrements.get(i - 1); + if (current.getBaseThreshold().compareTo(previous.getBaseThreshold()) <= 0) { + throw exception(QUOTA_RATE_INCREMENT_BASE_THRESHOLD_NOT_ASCENDING, + previous.getSeq(), current.getSeq()); + } + } + } + } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaResourceServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaResourceServiceImpl.java index d19139e..0ee4e02 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaResourceServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/quota/impl/QuotaResourceServiceImpl.java @@ -5,25 +5,24 @@ import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceRespVO; import com.yhy.module.core.controller.admin.quota.vo.QuotaResourceSaveReqVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO; import com.yhy.module.core.dal.dataobject.quota.QuotaResourceDO; -import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO; import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceMergedDO; import com.yhy.module.core.dal.mysql.quota.QuotaResourceMapper; import com.yhy.module.core.dal.mysql.resource.ResourceCatalogItemMapper; import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper; import com.yhy.module.core.enums.ErrorCodeConstants; import com.yhy.module.core.service.quota.QuotaItemService; import com.yhy.module.core.service.quota.QuotaResourceService; +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import org.springframework.context.annotation.Lazy; - -import javax.annotation.Resource; -import java.util.List; -import java.util.stream.Collectors; - /** * 定额工料机组成 Service 实现类 * @@ -47,13 +46,16 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { @Lazy private QuotaItemService quotaItemService; + @Resource + private com.yhy.module.core.dal.mysql.resource.ResourceMergedMapper resourceMergedMapper; + @Override @Transactional(rollbackFor = Exception.class) public Long createQuotaResource(QuotaResourceSaveReqVO createReqVO) { // 1. 验证工料机是否在范围内 validateResourceInScope(createReqVO.getQuotaItemId(), createReqVO.getResourceItemId()); - // 2. 获取工料机信息并快照 + // 2. 验证工料机是否存在 ResourceItemDO resourceItem = resourceItemMapper.selectById(createReqVO.getResourceItemId()); if (resourceItem == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_ITEM_NOT_EXISTS); @@ -62,10 +64,7 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { // 3. 转换为 DO QuotaResourceDO quotaResource = convertToDO(createReqVO); - // 4. 快照工料机信息 - snapshotResourceInfo(quotaResource, resourceItem); - - // 5. 插入 + // 4. 插入(标准库不需要快照,查询时实时读取) quotaResourceMapper.insert(quotaResource); log.info("[createQuotaResource] 添加定额工料机组成成功,quotaItemId={}, resourceItemId={}", @@ -84,19 +83,28 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { QuotaResourceDO existing = quotaResourceMapper.selectById(updateReqVO.getId()); if (!existing.getResourceItemId().equals(updateReqVO.getResourceItemId())) { validateResourceInScope(updateReqVO.getQuotaItemId(), updateReqVO.getResourceItemId()); + + // 验证新的工料机是否存在 + ResourceItemDO resourceItem = resourceItemMapper.selectById(updateReqVO.getResourceItemId()); + if (resourceItem == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_ITEM_NOT_EXISTS); + } } // 转换为 DO QuotaResourceDO updateObj = convertToDO(updateReqVO); updateObj.setId(updateReqVO.getId()); - // 如果修改了工料机,重新快照 - if (!existing.getResourceItemId().equals(updateReqVO.getResourceItemId())) { - ResourceItemDO resourceItem = resourceItemMapper.selectById(updateReqVO.getResourceItemId()); - snapshotResourceInfo(updateObj, resourceItem); + // 合并 attributes:保留原有字段,只更新传入的字段 + if (existing.getAttributes() != null) { + java.util.Map mergedAttributes = new java.util.HashMap<>(existing.getAttributes()); + if (updateReqVO.getAttributes() != null) { + mergedAttributes.putAll(updateReqVO.getAttributes()); + } + updateObj.setAttributes(mergedAttributes); } - // 更新 + // 更新(标准库不需要快照,查询时实时读取) quotaResourceMapper.updateById(updateObj); } @@ -135,6 +143,21 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { .collect(Collectors.toList()); } + @Override + public List getAvailableResourceItemsWithFilter(Long quotaItemId, String code, String name, String spec) { + // 1. 获取定额专业绑定的工料机专业ID + Long categoryTreeId = quotaItemService.getCategoryTreeIdByQuotaItem(quotaItemId); + + // 2. 查询该工料机专业下的工料机数据(支持模糊查询) + List resourceItems = resourceItemMapper.selectByCategoryTreeIdWithFilter( + categoryTreeId, code, name, spec); + + // 3. 转换为 VO + return resourceItems.stream() + .map(this::convertResourceItemToVO) + .collect(Collectors.toList()); + } + @Override public void validateResourceInScope(Long quotaItemId, Long resourceItemId) { // 1. 获取定额专业绑定的工料机专业ID @@ -146,8 +169,9 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_ITEM_NOT_EXISTS); } - // 3. 获取工料机所属的目录节点 - ResourceCatalogItemDO catalogItem = resourceCatalogItemMapper.selectById(resourceItem.getCatalogItemId()); + // 3. 获取工料机所属的目录节点(yhy_resource_catalog_item 表) + com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO catalogItem = + resourceCatalogItemMapper.selectById(resourceItem.getCatalogItemId()); if (catalogItem == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.RESOURCE_CATALOG_ITEM_NOT_EXISTS); } @@ -184,32 +208,6 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { return quotaResource; } - /** - * 快照工料机信息 - */ - private void snapshotResourceInfo(QuotaResourceDO quotaResource, ResourceItemDO resourceItem) { - // 快照基本信息 - quotaResource.setResourceCode(resourceItem.getName()); // 使用 name 作为 code - quotaResource.setResourceName(resourceItem.getName()); - quotaResource.setResourceUnit(resourceItem.getUnit()); - - // 快照价格(优先使用除税基价) - if (resourceItem.getTaxExclBasePrice() != null) { - quotaResource.setPrice(resourceItem.getTaxExclBasePrice()); - } else if (resourceItem.getTaxInclBasePrice() != null) { - quotaResource.setPrice(resourceItem.getTaxInclBasePrice()); - } - - // 快照资源类型 - if (quotaResource.getAttributes() == null) { - quotaResource.setAttributes(new java.util.HashMap<>()); - } - quotaResource.getAttributes().put("resource_type", resourceItem.getType()); - - log.debug("[snapshotResourceInfo] 快照工料机信息,resourceItemId={}, name={}, unit={}, price={}", - resourceItem.getId(), resourceItem.getName(), resourceItem.getUnit(), quotaResource.getPrice()); - } - private QuotaResourceDO convertToDO(QuotaResourceSaveReqVO vo) { QuotaResourceDO quotaResource = new QuotaResourceDO(); quotaResource.setQuotaItemId(vo.getQuotaItemId()); @@ -231,12 +229,97 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { // 扩展字段 vo.setLossRate(quotaResource.getLossRate()); - vo.setPrice(quotaResource.getPrice()); - vo.setResourceCode(quotaResource.getResourceCode()); - vo.setResourceName(quotaResource.getResourceName()); - vo.setResourceUnit(quotaResource.getResourceUnit()); - vo.setActualDosage(quotaResource.getActualDosage()); - vo.setAmount(quotaResource.getAmount()); + + // 实时查询工料机信息(不使用快照) + ResourceItemDO resourceItem = resourceItemMapper.selectById(quotaResource.getResourceItemId()); + if (resourceItem != null) { + vo.setResourceCode(resourceItem.getCode()); + vo.setResourceName(resourceItem.getName()); + vo.setResourceUnit(resourceItem.getUnit()); + vo.setResourceSpec(resourceItem.getSpec()); + vo.setResourceCategoryId(resourceItem.getCategoryId()); + vo.setResourceType(resourceItem.getType()); + vo.setResourceTaxRate(resourceItem.getTaxRate()); + vo.setResourceTaxExclBasePrice(resourceItem.getTaxExclBasePrice()); + vo.setResourceTaxInclBasePrice(resourceItem.getTaxInclBasePrice()); + vo.setResourceTaxExclCompilePrice(resourceItem.getTaxExclCompilePrice()); + vo.setResourceTaxInclCompilePrice(resourceItem.getTaxInclCompilePrice()); + vo.setCalcBase(resourceItem.getCalcBase()); + + // 价格优先使用除税基价 + if (resourceItem.getTaxExclBasePrice() != null) { + vo.setPrice(resourceItem.getTaxExclBasePrice()); + } else if (resourceItem.getTaxInclBasePrice() != null) { + vo.setPrice(resourceItem.getTaxInclBasePrice()); + } + + // 计算实际消耗量和金额 + vo.setActualDosage(quotaResource.getActualDosage()); + if (vo.getPrice() != null) { + vo.setAmount(vo.getActualDosage().multiply(vo.getPrice())); + } + + // 判断是否为复合工料机 + if (resourceItem.getIsMerged() != null && resourceItem.getIsMerged() == 1) { + vo.setIsMerged(true); + + // 查询复合工料机的子数据 + List mergedList = resourceMergedMapper.selectByMergedId(resourceItem.getId()); + + if (mergedList != null && !mergedList.isEmpty()) { + List mergedItems = mergedList.stream() + .map(merged -> { + QuotaResourceRespVO.MergedResourceItemVO item = new QuotaResourceRespVO.MergedResourceItemVO(); + item.setId(merged.getId()); + item.setResourceItemId(merged.getSourceId()); + item.setRegionCode(merged.getRegionCode()); + item.setDosage(merged.getQuotaConsumption()); + item.setPrice(merged.getTaxExclMarketPrice()); + + // 查询源工料机信息 + if (merged.getSourceId() != null) { + ResourceItemDO sourceItem = resourceItemMapper.selectById(merged.getSourceId()); + if (sourceItem != null) { + item.setResourceCode(sourceItem.getCode()); + item.setResourceName(sourceItem.getName()); + item.setResourceUnit(sourceItem.getUnit()); + item.setResourceSpec(sourceItem.getSpec()); + item.setResourceCategoryId(sourceItem.getCategoryId()); + item.setResourceType(sourceItem.getType()); + item.setResourceTaxRate(sourceItem.getTaxRate()); + } + } + + // 计算实际消耗量和金额 + if (merged.getQuotaConsumption() != null && quotaResource.getDosage() != null) { + // 实际消耗量 = 父定额消耗量 × 子定额消耗量 × (1 + 损耗率) + BigDecimal actualDosage = merged.getQuotaConsumption() + .multiply(quotaResource.getDosage()); + + if (quotaResource.getLossRate() != null) { + actualDosage = actualDosage.multiply( + BigDecimal.ONE.add(quotaResource.getLossRate()) + ); + } + + item.setActualDosage(actualDosage); + + // 金额 = 实际消耗量 × 价格 + if (merged.getTaxExclMarketPrice() != null) { + item.setAmount(actualDosage.multiply(merged.getTaxExclMarketPrice())); + } + } + + return item; + }) + .collect(Collectors.toList()); + + vo.setMergedItems(mergedItems); + } + } else { + vo.setIsMerged(false); + } + } return vo; } @@ -245,7 +328,9 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { ResourceItemRespVO vo = new ResourceItemRespVO(); vo.setId(resourceItem.getId()); vo.setCatalogItemId(resourceItem.getCatalogItemId()); + vo.setCode(resourceItem.getCode()); vo.setName(resourceItem.getName()); + vo.setSpec(resourceItem.getSpec()); vo.setType(resourceItem.getType()); vo.setUnit(resourceItem.getUnit()); vo.setCategoryId(resourceItem.getCategoryId()); @@ -253,6 +338,8 @@ public class QuotaResourceServiceImpl implements QuotaResourceService { vo.setTaxRate(resourceItem.getTaxRate()); vo.setTaxExclBasePrice(resourceItem.getTaxExclBasePrice()); vo.setTaxInclBasePrice(resourceItem.getTaxInclBasePrice()); + vo.setTaxExclCompilePrice(resourceItem.getTaxExclCompilePrice()); + vo.setTaxInclCompilePrice(resourceItem.getTaxInclCompilePrice()); return vo; } } diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceCatalogItemService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceCatalogItemService.java index 93579fb..b7063d4 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceCatalogItemService.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceCatalogItemService.java @@ -1,9 +1,9 @@ package com.yhy.module.core.service.resource; import com.yhy.module.core.controller.admin.resource.vo.CatalogItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceCatalogItemTreeNodeVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceCategorySimpleRespVO; import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO; - import java.util.List; public interface ResourceCatalogItemService { @@ -16,6 +16,14 @@ public interface ResourceCatalogItemService { List listByCatalog(Long catalogId); + /** + * 获取目录树(树形结构) + * + * @param catalogId 工料机机类树节点ID + * @return 树形结构列表 + */ + List getTreeByCatalog(Long catalogId); + /** * 获取目录树节点允许的类别列表 * diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceItemService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceItemService.java index 059d5bb..5ab393d 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceItemService.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceItemService.java @@ -4,9 +4,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemQueryReqVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceItemWithPricesRespVO; import com.yhy.module.core.controller.admin.resource.vo.ResourcePriceSaveReqVO; import com.yhy.module.core.dal.dataobject.resource.ResourcePriceDO; - import java.util.List; public interface ResourceItemService { @@ -19,6 +19,14 @@ public interface ResourceItemService { PageResult pageItems(ResourceItemQueryReqVO reqVO); + /** + * 获取工料机项及价格列表(包含完整信息) + * + * @param resourceId 工料机项ID + * @return 工料机项及价格列表 + */ + ResourceItemWithPricesRespVO getItemWithPrices(Long resourceId); + List listPrices(Long resourceId); Long createPrice(ResourcePriceSaveReqVO reqVO); diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceMergedService.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceMergedService.java new file mode 100644 index 0000000..63ed0e9 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/ResourceMergedService.java @@ -0,0 +1,53 @@ +package com.yhy.module.core.service.resource; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedPageReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedRespVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedSaveReqVO; +import javax.validation.Valid; + +/** + * 复合工料机 Service 接口 + * + * @author yihuiyong + */ +public interface ResourceMergedService { + + /** + * 创建复合工料机 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createResourceMerged(@Valid ResourceMergedSaveReqVO createReqVO); + + /** + * 更新复合工料机 + * + * @param updateReqVO 更新信息 + */ + void updateResourceMerged(@Valid ResourceMergedSaveReqVO updateReqVO); + + /** + * 删除复合工料机 + * + * @param id 编号 + */ + void deleteResourceMerged(Long id); + + /** + * 获得复合工料机 + * + * @param id 编号 + * @return 复合工料机 + */ + ResourceMergedRespVO getResourceMerged(Long id); + + /** + * 获得复合工料机分页(包含第四层完整信息) + * + * @param pageReqVO 分页查询 + * @return 复合工料机分页(包含第四层信息) + */ + PageResult getResourceMergedPage(ResourceMergedPageReqVO pageReqVO); +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCatalogItemServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCatalogItemServiceImpl.java index 8bbe9b9..d88fd90 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCatalogItemServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCatalogItemServiceImpl.java @@ -1,26 +1,29 @@ package com.yhy.module.core.service.resource.impl; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; + import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.yhy.module.core.controller.admin.resource.vo.CatalogItemSaveReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceCatalogItemTreeNodeVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceCategorySimpleRespVO; import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO; import com.yhy.module.core.dal.mysql.resource.ResourceCatalogItemMapper; import com.yhy.module.core.service.resource.ResourceCatalogItemService; import com.yhy.module.core.service.resource.ResourceCategoryTreeService; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; -import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; - @Service public class ResourceCatalogItemServiceImpl implements ResourceCatalogItemService { @@ -41,10 +44,34 @@ public class ResourceCatalogItemServiceImpl implements ResourceCatalogItemServic Long tenantId = TenantContextHolder.getTenantId(); checkCodeUnique(tenantId, reqVO.getCategoryTreeId(), reqVO.getCode(), null); + // 如果是根节点(parentId 为 null),检查是否已存在根节点 + if (reqVO.getParentId() == null) { + checkRootNodeUnique(tenantId, reqVO.getCategoryTreeId()); + } + + // 如果提供了 parentId,自动查询父路径 + List parentPath = reqVO.getParentPath(); + if (reqVO.getParentId() != null) { + ResourceCatalogItemDO parent = catalogItemMapper.selectById(reqVO.getParentId()); + if (parent == null) { + throw exception0(BAD_REQUEST.getCode(), "父节点不存在"); + } + // 使用父节点的完整路径作为 parentPath + parentPath = parent.getPath() != null ? java.util.Arrays.asList(parent.getPath()) : new ArrayList<>(); + } + ResourceCatalogItemDO item = BeanUtils.toBean(reqVO, ResourceCatalogItemDO.class); item.setTenantId(tenantId); - item.setPath(buildPath(reqVO.getParentPath(), reqVO.getCode())); + // 先设置一个临时的 path(空数组),插入后再更新 + item.setPath(new String[0]); catalogItemMapper.insert(item); + + // 插入后获得 id,使用 id 构建 path + List parentPathIds = parentPath; + String[] finalPath = buildPath(parentPathIds, String.valueOf(item.getId())); + item.setPath(finalPath); + catalogItemMapper.updateById(item); + return item.getId(); } @@ -58,10 +85,29 @@ public class ResourceCatalogItemServiceImpl implements ResourceCatalogItemServic Long tenantId = TenantContextHolder.getTenantId(); checkCodeUnique(tenantId, dbItem.getCategoryTreeId(), reqVO.getCode(), dbItem.getId()); + // 如果提供了 parentId,自动查询父路径 + List parentPath = reqVO.getParentPath(); + if (reqVO.getParentId() != null) { + ResourceCatalogItemDO parent = catalogItemMapper.selectById(reqVO.getParentId()); + if (parent == null) { + throw exception0(BAD_REQUEST.getCode(), "父节点不存在"); + } + // 使用父节点的完整路径作为 parentPath + parentPath = parent.getPath() != null ? java.util.Arrays.asList(parent.getPath()) : new ArrayList<>(); + } + ResourceCatalogItemDO item = BeanUtils.toBean(reqVO, ResourceCatalogItemDO.class); item.setTenantId(tenantId); - // 只有 parentPath + code 变化时才重建 path - item.setPath(buildPath(reqVO.getParentPath(), reqVO.getCode())); + + // 如果既没有提供 parentId 也没有提供 parentPath,保持原有的 path 不变 + if (reqVO.getParentId() == null && (reqVO.getParentPath() == null || reqVO.getParentPath().isEmpty())) { + // 保持原有的 path 不变(path 存储的是 id,不需要更新) + item.setPath(dbItem.getPath()); + } else { + // 有提供 parentId 或 parentPath,重新构建 path(使用当前节点的 id) + item.setPath(buildPath(parentPath, String.valueOf(reqVO.getId()))); + } + catalogItemMapper.updateById(item); } @@ -72,9 +118,11 @@ public class ResourceCatalogItemServiceImpl implements ResourceCatalogItemServic return; } // 检查是否有子节点 - Long count = catalogItemMapper.selectCount(new LambdaQueryWrapper() - .eq(ResourceCatalogItemDO::getCategoryTreeId, dbItem.getCategoryTreeId()) - .apply("path[1:array_length(path,1)-1] = #{path}", (Object) dbItem.getPath())); + Long count = catalogItemMapper.countChildNodes( + dbItem.getCategoryTreeId(), + dbItem.getPath(), + TenantContextHolder.getTenantId() + ); if (count != null && count > 0) { throw exception0(BAD_REQUEST.getCode(), "存在子节点,不可删除"); } @@ -86,7 +134,97 @@ public class ResourceCatalogItemServiceImpl implements ResourceCatalogItemServic return catalogItemMapper.selectList(new LambdaQueryWrapper() .eq(ResourceCatalogItemDO::getCategoryTreeId, catalogId) .eq(ResourceCatalogItemDO::getTenantId, TenantContextHolder.getTenantId()) - .orderByAsc(ResourceCatalogItemDO::getPath)); + .orderByAsc(ResourceCatalogItemDO::getPath) + .orderByAsc(ResourceCatalogItemDO::getSortOrder)); + } + + @Override + public List getTreeByCatalog(Long catalogId) { + // 1. 查询所有节点(按path排序) + List allNodes = listByCatalog(catalogId); + if (CollectionUtils.isEmpty(allNodes)) { + return new ArrayList<>(); + } + + // 2. 转换为VO + List allVOs = allNodes.stream() + .map(node -> BeanUtils.toBean(node, ResourceCatalogItemTreeNodeVO.class)) + .collect(Collectors.toList()); + + // 3. 构建树形结构 + return buildTree(allVOs); + } + + /** + * 构建树形结构 + * + * @param allNodes 所有节点(已按path排序) + * @return 树形结构(只返回根节点) + */ + private List buildTree(List allNodes) { + // 1. 创建path到节点的映射 + Map pathMap = new HashMap<>(); + for (ResourceCatalogItemTreeNodeVO node : allNodes) { + // 跳过path为null或空的节点 + if (node.getPath() == null || node.getPath().length == 0) { + continue; + } + String pathKey = String.join("/", node.getPath()); + pathMap.put(pathKey, node); + node.setChildren(new ArrayList<>()); + } + + // 2. 构建父子关系 + List rootNodes = new ArrayList<>(); + for (ResourceCatalogItemTreeNodeVO node : allNodes) { + // 跳过path为null或空的节点 + if (node.getPath() == null || node.getPath().length == 0) { + continue; + } + + if (node.getPath().length == 1) { + // 根节点 + rootNodes.add(node); + } else { + // 非根节点,找到父节点并添加到children + String[] parentPath = new String[node.getPath().length - 1]; + System.arraycopy(node.getPath(), 0, parentPath, 0, parentPath.length); + String parentPathKey = String.join("/", parentPath); + + ResourceCatalogItemTreeNodeVO parent = pathMap.get(parentPathKey); + if (parent != null) { + parent.getChildren().add(node); + } + } + } + + // 3. 对所有节点的子节点按 sortOrder 排序 + sortChildrenBySortOrder(rootNodes); + + return rootNodes; + } + + /** + * 递归对所有节点的子节点按 sortOrder 排序 + */ + private void sortChildrenBySortOrder(List nodes) { + if (nodes == null || nodes.isEmpty()) { + return; + } + + // 对当前层级按 sortOrder 排序 + nodes.sort((a, b) -> { + Integer sortA = a.getSortOrder() != null ? a.getSortOrder() : 0; + Integer sortB = b.getSortOrder() != null ? b.getSortOrder() : 0; + return sortA.compareTo(sortB); + }); + + // 递归对每个节点的子节点排序 + for (ResourceCatalogItemTreeNodeVO node : nodes) { + if (node.getChildren() != null && !node.getChildren().isEmpty()) { + sortChildrenBySortOrder(node.getChildren()); + } + } } private void checkCodeUnique(Long tenantId, Long catalogId, String code, Long excludeId) { @@ -103,6 +241,26 @@ public class ResourceCatalogItemServiceImpl implements ResourceCatalogItemServic } } + /** + * 检查根节点唯一性(一个工料机专业节点只能有一个根目录) + */ + private void checkRootNodeUnique(Long tenantId, Long categoryTreeId) { + // 查询是否已存在根节点(path 长度为 1 的节点) + List allNodes = catalogItemMapper.selectList( + new LambdaQueryWrapper() + .eq(ResourceCatalogItemDO::getTenantId, tenantId) + .eq(ResourceCatalogItemDO::getCategoryTreeId, categoryTreeId) + ); + + // 检查是否已存在根节点 + boolean hasRootNode = allNodes.stream() + .anyMatch(node -> node.getPath() != null && node.getPath().length == 1); + + if (hasRootNode) { + throw exception0(BAD_REQUEST.getCode(), "一个工料机专业节点只能有一个根目录"); + } + } + private String[] buildPath(List parentPath, String code) { List path = new ArrayList<>(); if (!CollectionUtils.isEmpty(parentPath)) { @@ -129,18 +287,16 @@ public class ResourceCatalogItemServiceImpl implements ResourceCatalogItemServic public void swapSortOrder(Long nodeId1, Long nodeId2) { Long tenantId = TenantContextHolder.getTenantId(); - // 1. 查询两个节点(使用行锁) + // 1. 查询两个节点 ResourceCatalogItemDO node1 = catalogItemMapper.selectOne( new LambdaQueryWrapper() .eq(ResourceCatalogItemDO::getId, nodeId1) .eq(ResourceCatalogItemDO::getTenantId, tenantId) - .last("FOR UPDATE") ); ResourceCatalogItemDO node2 = catalogItemMapper.selectOne( new LambdaQueryWrapper() .eq(ResourceCatalogItemDO::getId, nodeId2) .eq(ResourceCatalogItemDO::getTenantId, tenantId) - .last("FOR UPDATE") ); if (node1 == null || node2 == null) { diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryServiceImpl.java index 2c521f7..3400582 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryServiceImpl.java @@ -1,28 +1,38 @@ package com.yhy.module.core.service.resource.impl; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_CATEGORY_CODE_DUPLICATE; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_CATEGORY_NOT_EXISTS; + import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.yhy.module.core.controller.admin.resource.vo.ResourceCategorySaveReqVO; import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeMappingDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO; import com.yhy.module.core.dal.mysql.resource.ResourceCategoryMapper; +import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMappingMapper; +import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper; import com.yhy.module.core.service.resource.ResourceCategoryService; +import java.util.List; +import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; -import static com.yhy.module.core.enums.ErrorCodeConstants.*; - @Service public class ResourceCategoryServiceImpl implements ResourceCategoryService { @Resource private ResourceCategoryMapper categoryMapper; + + @Resource + private ResourceCategoryTreeMappingMapper categoryTreeMappingMapper; + + @Resource + private ResourceItemMapper resourceItemMapper; @Override @Transactional(rollbackFor = Exception.class) @@ -60,8 +70,11 @@ public class ResourceCategoryServiceImpl implements ResourceCategoryService { // 校验类别存在 validateCategoryExists(id); - // TODO: 检查是否有工料机项引用此类别 - // 如果有引用,应该禁止删除或提示用户 + // 检查是否被机类树映射引用 + validateCategoryNotUsedInMapping(id); + + // 检查是否有工料机项引用此类别 + validateCategoryNotUsedInResourceItem(id); categoryMapper.deleteById(id); } @@ -75,6 +88,32 @@ public class ResourceCategoryServiceImpl implements ResourceCategoryService { } } + /** + * 校验类别是否被机类树映射引用 + */ + private void validateCategoryNotUsedInMapping(Long categoryId) { + Long count = categoryTreeMappingMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceCategoryTreeMappingDO::getCategoryId, categoryId) + ); + if (count > 0) { + throw exception0(BAD_REQUEST.getCode(), "该类别已被工料机专业引用,无法删除"); + } + } + + /** + * 校验类别是否被工料机项引用 + */ + private void validateCategoryNotUsedInResourceItem(Long categoryId) { + Long count = resourceItemMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceItemDO::getCategoryId, categoryId) + ); + if (count > 0) { + throw exception0(BAD_REQUEST.getCode(), "该类别已被工料机项引用,无法删除"); + } + } + /** * 校验类别代码是否重复 */ diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryTreeServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryTreeServiceImpl.java index ef3a47e..269b263 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryTreeServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceCategoryTreeServiceImpl.java @@ -1,5 +1,8 @@ package com.yhy.module.core.service.resource.impl; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; + import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -7,24 +10,26 @@ import com.yhy.module.core.controller.admin.resource.vo.ResourceCategorySimpleRe import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryTreeMappingSaveReqVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryTreeNodeVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceCategoryTreeSaveReqVO; +import com.yhy.module.core.dal.dataobject.infoprice.InfoPriceResourceDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceCatalogItemDO; import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryDO; import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeDO; import com.yhy.module.core.dal.dataobject.resource.ResourceCategoryTreeMappingDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO; +import com.yhy.module.core.dal.mysql.infoprice.InfoPriceResourceMapper; +import com.yhy.module.core.dal.mysql.resource.ResourceCatalogItemMapper; import com.yhy.module.core.dal.mysql.resource.ResourceCategoryMapper; import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMapper; import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMappingMapper; +import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper; import com.yhy.module.core.service.resource.ResourceCategoryTreeService; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Resource; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import javax.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; /** * 工料机机类树 Service 实现 @@ -43,18 +48,22 @@ public class ResourceCategoryTreeServiceImpl implements ResourceCategoryTreeServ @Resource private ResourceCategoryMapper categoryMapper; + @Resource + private ResourceItemMapper resourceItemMapper; + + @Resource + private ResourceCatalogItemMapper catalogItemMapper; + + @Resource + private InfoPriceResourceMapper infoPriceResourceMapper; + @Override @Transactional(rollbackFor = Exception.class) public Long createNode(ResourceCategoryTreeSaveReqVO reqVO) { Long tenantId = TenantContextHolder.getTenantId(); - // 检查编码是否重复 - ResourceCategoryTreeDO exists = categoryTreeMapper.selectOne(new LambdaQueryWrapper() - .eq(ResourceCategoryTreeDO::getTenantId, tenantId) - .eq(ResourceCategoryTreeDO::getCode, reqVO.getCode())); - if (exists != null) { - throw exception0(BAD_REQUEST.getCode(), "节点编码已存在"); - } + // 不检查编码唯一性,允许同一父节点下有相同编码 + // 编码由前端自动生成(如:父编码-随机数) ResourceCategoryTreeDO node = BeanUtils.toBean(reqVO, ResourceCategoryTreeDO.class); node.setTenantId(tenantId); @@ -104,17 +113,65 @@ public class ResourceCategoryTreeServiceImpl implements ResourceCategoryTreeServ @Override @Transactional(rollbackFor = Exception.class) public void deleteNode(Long id) { - // 检查是否有子节点 - Long count = categoryTreeMapper.selectCount(new LambdaQueryWrapper() - .eq(ResourceCategoryTreeDO::getParentId, id)); - if (count > 0) { - throw exception0(BAD_REQUEST.getCode(), "存在子节点,无法删除"); + Long tenantId = TenantContextHolder.getTenantId(); + + // 1. 查询节点信息 + ResourceCategoryTreeDO node = categoryTreeMapper.selectById(id); + if (node == null) { + throw exception0(BAD_REQUEST.getCode(), "节点不存在"); } - - // 删除关联关系 + + // 2. 检查第三层:工料机目录树(所有节点都需要检查) + Long catalogItemCount = catalogItemMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceCatalogItemDO::getTenantId, tenantId) + .eq(ResourceCatalogItemDO::getCategoryTreeId, id) + .eq(ResourceCatalogItemDO::getDeleted, 0) + ); + if (catalogItemCount > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("该节点下存在 %d 个工料机目录树节点,无法删除", catalogItemCount)); + } + + // 3. 根据节点类型进行不同的检查 + if ("specialty".equals(node.getNodeType())) { + // 工料机专业节点:检查是否有类别映射 + Long mappingCount = mappingMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceCategoryTreeMappingDO::getTenantId, tenantId) + .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, id) + ); + if (mappingCount > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("该工料机专业下存在 %d 个类别关联,无法删除", mappingCount)); + } + } else if ("region".equals(node.getNodeType())) { + // 省市节点:检查是否有子节点 + Long childCount = categoryTreeMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceCategoryTreeDO::getParentId, id) + ); + if (childCount > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("该省市节点下存在 %d 个子节点,无法删除", childCount)); + } + } else { + // 其他类型节点:通用检查(检查子节点) + Long childCount = categoryTreeMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceCategoryTreeDO::getParentId, id) + ); + if (childCount > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("该节点下存在 %d 个子节点,无法删除", childCount)); + } + } + + // 4. 删除关联关系(如果有) mappingMapper.delete(new LambdaQueryWrapper() .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, id)); - + + // 5. 删除节点 categoryTreeMapper.deleteById(id); } @@ -203,10 +260,54 @@ public class ResourceCategoryTreeServiceImpl implements ResourceCategoryTreeServ @Override @Transactional(rollbackFor = Exception.class) public void removeCategoryMapping(Long treeNodeId, Long categoryId) { + Long tenantId = TenantContextHolder.getTenantId(); + + // 检查映射是否被引用 + checkMappingReference(tenantId, treeNodeId, categoryId); + + // 删除关联关系 mappingMapper.delete(new LambdaQueryWrapper() .eq(ResourceCategoryTreeMappingDO::getCategoryTreeId, treeNodeId) .eq(ResourceCategoryTreeMappingDO::getCategoryId, categoryId)); } + + /** + * 检查映射关系是否被引用 + * + * @param tenantId 租户ID + * @param treeNodeId 机类树节点ID + * @param categoryId 类别ID + * @throws RuntimeException 如果映射被引用则抛出异常 + */ + private void checkMappingReference(Long tenantId, Long treeNodeId, Long categoryId) { + // 1. 检查工料机项是否引用了这个映射关系 + Long resourceItemCount = resourceItemMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceItemDO::getTenantId, tenantId) + .eq(ResourceItemDO::getCategoryTreeId, treeNodeId) + .eq(ResourceItemDO::getCategoryId, categoryId) + .eq(ResourceItemDO::getDeleted, 0) + ); + + if (resourceItemCount > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("该类别映射已被 %d 个工料机项引用,无法删除关联", resourceItemCount)); + } + + // 2. 检查信息价工料机是否引用了这个映射关系 + Long infoPriceCount = infoPriceResourceMapper.selectCount( + new LambdaQueryWrapper() + .eq(InfoPriceResourceDO::getTenantId, tenantId) + .eq(InfoPriceResourceDO::getCategoryTreeId, treeNodeId) + .eq(InfoPriceResourceDO::getCategoryId, categoryId) + .eq(InfoPriceResourceDO::getDeleted, 0) + ); + + if (infoPriceCount > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("该类别映射已被 %d 个信息价工料机引用,无法删除关联", infoPriceCount)); + } + } @Override @Transactional(rollbackFor = Exception.class) diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceItemServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceItemServiceImpl.java index d83c804..ad7d19b 100644 --- a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceItemServiceImpl.java +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceItemServiceImpl.java @@ -1,8 +1,13 @@ package com.yhy.module.core.service.resource.impl; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; + import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemQueryReqVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemRespVO; import com.yhy.module.core.controller.admin.resource.vo.ResourceItemSaveReqVO; @@ -18,19 +23,13 @@ import com.yhy.module.core.dal.mysql.resource.ResourceCategoryTreeMappingMapper; import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper; import com.yhy.module.core.dal.mysql.resource.ResourcePriceMapper; import com.yhy.module.core.service.resource.ResourceItemService; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import java.time.format.DateTimeFormatter; +import java.util.List; +import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import javax.annotation.Resource; -import java.time.format.DateTimeFormatter; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; - @Service public class ResourceItemServiceImpl implements ResourceItemService { @@ -50,32 +49,19 @@ public class ResourceItemServiceImpl implements ResourceItemService { @Override @Transactional(rollbackFor = Exception.class) public Long createItem(ResourceItemSaveReqVO reqVO) { - // 1. 验证目录树节点是否存在 - ResourceCatalogItemDO catalogItem = catalogItemMapper.selectById(reqVO.getCatalogItemId()); - if (catalogItem == null) { - throw exception0(BAD_REQUEST.getCode(), "目录树节点不存在"); - } + Long tenantId = TenantContextHolder.getTenantId(); - // 2. 验证 category_id 是否在机类树节点的允许范围内 - validateCategoryInAllowedRange(catalogItem.getCategoryTreeId(), reqVO.getCategoryId()); - - // 3. 验证计算基数 - validateCalcBase(reqVO, catalogItem.getCategoryTreeId()); - - // 4. 创建工料机项 - ResourceItemDO item = BeanUtils.toBean(reqVO, ResourceItemDO.class); - item.setTenantId(TenantContextHolder.getTenantId()); - resourceItemMapper.insert(item); - return item.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateItem(ResourceItemSaveReqVO reqVO) { - // 1. 验证工料机项是否存在 - ResourceItemDO db = resourceItemMapper.selectById(reqVO.getId()); - if (db == null) { - throw exception0(BAD_REQUEST.getCode(), "工料机项不存在"); + // 1. 验证编码是否重复 + if (reqVO.getCode() != null && !reqVO.getCode().trim().isEmpty()) { + Long count = resourceItemMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceItemDO::getTenantId, tenantId) + .eq(ResourceItemDO::getCode, reqVO.getCode()) + ); + if (count > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("工料机编码 [%s] 已存在,请使用其他编码", reqVO.getCode())); + } } // 2. 验证目录树节点是否存在 @@ -90,9 +76,57 @@ public class ResourceItemServiceImpl implements ResourceItemService { // 4. 验证计算基数 validateCalcBase(reqVO, catalogItem.getCategoryTreeId()); - // 5. 更新工料机项 + // 5. 创建工料机项 ResourceItemDO item = BeanUtils.toBean(reqVO, ResourceItemDO.class); - item.setTenantId(TenantContextHolder.getTenantId()); + item.setTenantId(tenantId); + // 自动设置 category_tree_id(从目录树节点获取) + item.setCategoryTreeId(catalogItem.getCategoryTreeId()); + resourceItemMapper.insert(item); + return item.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateItem(ResourceItemSaveReqVO reqVO) { + Long tenantId = TenantContextHolder.getTenantId(); + + // 1. 验证工料机项是否存在 + ResourceItemDO db = resourceItemMapper.selectById(reqVO.getId()); + if (db == null) { + throw exception0(BAD_REQUEST.getCode(), "工料机项不存在"); + } + + // 2. 验证编码是否重复(排除自己) + if (reqVO.getCode() != null && !reqVO.getCode().trim().isEmpty()) { + Long count = resourceItemMapper.selectCount( + new LambdaQueryWrapper() + .eq(ResourceItemDO::getTenantId, tenantId) + .eq(ResourceItemDO::getCode, reqVO.getCode()) + .ne(ResourceItemDO::getId, reqVO.getId()) + ); + if (count > 0) { + throw exception0(BAD_REQUEST.getCode(), + String.format("工料机编码 [%s] 已存在,请使用其他编码", reqVO.getCode())); + } + } + + // 3. 验证目录树节点是否存在 + ResourceCatalogItemDO catalogItem = catalogItemMapper.selectById(reqVO.getCatalogItemId()); + if (catalogItem == null) { + throw exception0(BAD_REQUEST.getCode(), "目录树节点不存在"); + } + + // 4. 验证 category_id 是否在机类树节点的允许范围内 + validateCategoryInAllowedRange(catalogItem.getCategoryTreeId(), reqVO.getCategoryId()); + + // 5. 验证计算基数 + validateCalcBase(reqVO, catalogItem.getCategoryTreeId()); + + // 6. 更新工料机项 + ResourceItemDO item = BeanUtils.toBean(reqVO, ResourceItemDO.class); + item.setTenantId(tenantId); + // 自动设置 category_tree_id(从目录树节点获取) + item.setCategoryTreeId(catalogItem.getCategoryTreeId()); resourceItemMapper.updateById(item); } @@ -106,28 +140,16 @@ public class ResourceItemServiceImpl implements ResourceItemService { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(ResourceItemDO::getTenantId, TenantContextHolder.getTenantId()); - // 按分类节点查询(包含子节点) + // 按分类节点查询 if (reqVO.getCatalogItemId() != null) { - // 查询该节点及其所有子节点 - ResourceCatalogItemDO catalogItem = catalogItemMapper.selectById(reqVO.getCatalogItemId()); - if (catalogItem != null) { - // 使用 PostgreSQL 数组操作查询该节点及其子节点 - // path @> ARRAY[catalogItemId] 表示 path 包含该节点ID - List nodeAndChildren = catalogItemMapper.selectList( - new LambdaQueryWrapper() - .eq(ResourceCatalogItemDO::getTenantId, TenantContextHolder.getTenantId()) - .eq(ResourceCatalogItemDO::getCategoryTreeId, catalogItem.getCategoryTreeId()) - .apply("path @> ARRAY[{0}]::text[]", reqVO.getCatalogItemId()) - ); - List itemIds = nodeAndChildren.stream() - .map(ResourceCatalogItemDO::getId) - .collect(java.util.stream.Collectors.toList()); - if (!itemIds.isEmpty()) { - wrapper.in(ResourceItemDO::getCatalogItemId, itemIds); - } else { - // 没有匹配的节点,返回空结果 - wrapper.eq(ResourceItemDO::getId, -1L); - } + if (Boolean.TRUE.equals(reqVO.getIncludeChildren())) { + // 查询该节点所属目录树的所有工料机项 + // 先查询节点的 category_tree_id,然后查询该目录树下的所有工料机项 + wrapper.apply("catalog_item_id IN (SELECT id FROM yhy_catalog_item WHERE deleted = 0 AND category_tree_id = (SELECT category_tree_id FROM yhy_catalog_item WHERE id = {0} AND deleted = 0))", + reqVO.getCatalogItemId()); + } else { + // 只查询该节点本身的工料机项 + wrapper.eq(ResourceItemDO::getCatalogItemId, reqVO.getCatalogItemId()); } } else if (reqVO.getCategoryTreeId() != null) { // 按整个机类树查询 @@ -169,6 +191,67 @@ public class ResourceItemServiceImpl implements ResourceItemService { return new PageResult<>(records, page.getTotal()); } + @Override + public com.yhy.module.core.controller.admin.resource.vo.ResourceItemWithPricesRespVO getItemWithPrices(Long resourceId) { + // 1. 查询工料机项 + ResourceItemDO item = resourceItemMapper.selectById(resourceId); + if (item == null) { + throw exception0(BAD_REQUEST.getCode(), "工料机项不存在"); + } + + // 2. 查询类别(获取类别名称) + ResourceCategoryDO category = null; + if (item.getCategoryId() != null) { + category = categoryMapper.selectById(item.getCategoryId()); + } + + // 3. 查询价格列表 + List prices = resourcePriceMapper.selectList( + new LambdaQueryWrapper() + .eq(ResourcePriceDO::getResourceId, resourceId) + .orderByDesc(ResourcePriceDO::getId) + ); + + // 4. 组装响应VO + com.yhy.module.core.controller.admin.resource.vo.ResourceItemWithPricesRespVO respVO = + new com.yhy.module.core.controller.admin.resource.vo.ResourceItemWithPricesRespVO(); + respVO.setId(item.getId()); + respVO.setCode(item.getCode()); + respVO.setName(item.getName()); + respVO.setSpec(item.getSpec()); + respVO.setCategoryId(item.getCategoryId()); + respVO.setCategoryName(category != null ? category.getName() : null); + respVO.setUnit(item.getUnit()); + respVO.setTaxRate(item.getTaxRate()); + respVO.setTaxExclBasePrice(item.getTaxExclBasePrice()); + respVO.setTaxInclBasePrice(item.getTaxInclBasePrice()); + respVO.setTaxExclCompilePrice(item.getTaxExclCompilePrice()); + respVO.setTaxInclCompilePrice(item.getTaxInclCompilePrice()); + + // 转换价格列表 + List priceVOs = + prices.stream().map(this::convertToPriceVO).collect(java.util.stream.Collectors.toList()); + respVO.setPrices(priceVOs); + + return respVO; + } + + /** + * 转换价格DO为VO + */ + private com.yhy.module.core.controller.admin.resource.vo.ResourceItemPriceRespVO convertToPriceVO(ResourcePriceDO price) { + com.yhy.module.core.controller.admin.resource.vo.ResourceItemPriceRespVO vo = + new com.yhy.module.core.controller.admin.resource.vo.ResourceItemPriceRespVO(); + vo.setId(price.getId()); + vo.setRegionCode(price.getRegionCode()); + vo.setPeriodStart(price.getPeriodStart()); + vo.setPeriodEnd(price.getPeriodEnd()); + vo.setPriceTaxExcl(price.getPriceTaxExcl()); + vo.setPriceTaxIncl(price.getPriceTaxIncl()); + vo.setSource(price.getSource()); + return vo; + } + /** * 转换为响应 VO,并填充类别名称和机类树名称 */ diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceMergedServiceImpl.java b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceMergedServiceImpl.java new file mode 100644 index 0000000..d83cf37 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/service/resource/impl/ResourceMergedServiceImpl.java @@ -0,0 +1,338 @@ +package com.yhy.module.core.service.resource.impl; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_MERGED_CALC_BASE_CATEGORY_NOT_FOUND; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_MERGED_CALC_BASE_INVALID; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_MERGED_FORMULA_PARSE_ERROR; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_MERGED_NOT_EXISTS; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_MERGED_SELF_REFERENCE; +import static com.yhy.module.core.enums.ErrorCodeConstants.RESOURCE_MERGED_SOURCE_NOT_EXISTS; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedPageReqVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedRespVO; +import com.yhy.module.core.controller.admin.resource.vo.ResourceMergedSaveReqVO; +import com.yhy.module.core.dal.dataobject.resource.ResourceItemDO; +import com.yhy.module.core.dal.dataobject.resource.ResourceMergedDO; +import com.yhy.module.core.dal.mysql.resource.ResourceItemMapper; +import com.yhy.module.core.dal.mysql.resource.ResourceMergedMapper; +import com.yhy.module.core.service.resource.ResourceMergedService; +import com.yhy.module.core.util.FormulaEvaluator; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * 复合工料机 Service 实现类 + * + * @author yihuiyong + */ +@Service +@Validated +@Slf4j +public class ResourceMergedServiceImpl implements ResourceMergedService { + + @Resource + private ResourceMergedMapper resourceMergedMapper; + + @Resource + private ResourceItemMapper resourceItemMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createResourceMerged(ResourceMergedSaveReqVO createReqVO) { + // 校验不允许自己关联自己 + if (createReqVO.getMergedId() != null && createReqVO.getSourceId() != null + && createReqVO.getMergedId().equals(createReqVO.getSourceId())) { + throw exception(RESOURCE_MERGED_SELF_REFERENCE); + } + + // 插入 + ResourceMergedDO resourceMerged = BeanUtils.toBean(createReqVO, ResourceMergedDO.class); + resourceMergedMapper.insert(resourceMerged); + + // 触发计算 + calculateMergedPrices(resourceMerged.getMergedId()); + + // 返回 + return resourceMerged.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateResourceMerged(ResourceMergedSaveReqVO updateReqVO) { + // 校验存在 + ResourceMergedDO existing = validateResourceMergedExists(updateReqVO.getId()); + + // 校验不允许自己关联自己 + Long mergedId = updateReqVO.getMergedId() != null ? updateReqVO.getMergedId() : existing.getMergedId(); + Long sourceId = updateReqVO.getSourceId() != null ? updateReqVO.getSourceId() : existing.getSourceId(); + if (mergedId != null && sourceId != null && mergedId.equals(sourceId)) { + throw exception(RESOURCE_MERGED_SELF_REFERENCE); + } + + // 更新 + ResourceMergedDO updateObj = BeanUtils.toBean(updateReqVO, ResourceMergedDO.class); + resourceMergedMapper.updateById(updateObj); + + // 触发计算(使用更新后的 mergedId) + calculateMergedPrices(mergedId); + } + + @Override + public void deleteResourceMerged(Long id) { + // 校验存在 + validateResourceMergedExists(id); + // 删除 + resourceMergedMapper.deleteById(id); + } + + private ResourceMergedDO validateResourceMergedExists(Long id) { + ResourceMergedDO merged = resourceMergedMapper.selectById(id); + if (merged == null) { + throw exception(RESOURCE_MERGED_NOT_EXISTS); + } + return merged; + } + + @Override + public ResourceMergedRespVO getResourceMerged(Long id) { + // 查询数据(JOIN 第四层) + ResourceMergedRespVO respVO = resourceMergedMapper.selectByIdWithDetails(id); + if (respVO == null) { + throw exception(RESOURCE_MERGED_NOT_EXISTS); + } + return respVO; + } + + @Override + public PageResult getResourceMergedPage(ResourceMergedPageReqVO pageReqVO) { + // 计算偏移量 + int offset = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize(); + + // 查询数据(JOIN 第四层) + List list = resourceMergedMapper.selectPageWithDetails(pageReqVO, offset); + + // 查询总数 + Long total = resourceMergedMapper.selectCountWithDetails(pageReqVO); + + return new PageResult<>(list, total); + } + + /** + * 计算指定 merged_id 的价格 + * + * @param mergedId 复合工料机ID + */ + private void calculateMergedPrices(Long mergedId) { + log.info("[calculateMergedPrices] 开始计算复合工料机价格,mergedId={}", mergedId); + + // 1. 查询该 merged_id 的所有第五层数据 + List mergedList = resourceMergedMapper.selectByMergedId(mergedId); + if (mergedList.isEmpty()) { + log.warn("[calculateMergedPrices] 未找到第五层数据,mergedId={}", mergedId); + return; + } + + // 2. 查询所有引用的第四层数据 + List sourceIds = mergedList.stream() + .map(ResourceMergedDO::getSourceId) + .filter(id -> id != null) + .distinct() + .collect(Collectors.toList()); + + if (sourceIds.isEmpty()) { + log.warn("[calculateMergedPrices] 所有第五层数据的 source_id 都为空,mergedId={}", mergedId); + return; + } + + Map sourceMap = resourceItemMapper.selectBatchIds(sourceIds).stream() + .collect(Collectors.toMap(ResourceItemDO::getId, item -> item)); + + // 3. 计算4个价格字段的合价 + PriceResult priceResult = calculateTotalPrices(mergedList, sourceMap); + + // 4. 更新第四层(merged_id 对应的工料机)的价格 + resourceItemMapper.updatePrices( + mergedId, + priceResult.taxExclBasePrice, + priceResult.taxInclBasePrice, + priceResult.taxExclCompilePrice, + priceResult.taxInclCompilePrice + ); + + log.info("[calculateMergedPrices] 计算完成,mergedId={}, 除税基价={}, 含税基价={}, 除税编制价={}, 含税编制价={}", + mergedId, priceResult.taxExclBasePrice, priceResult.taxInclBasePrice, + priceResult.taxExclCompilePrice, priceResult.taxInclCompilePrice); + } + + /** + * 计算总价格 + */ + private PriceResult calculateTotalPrices(List mergedList, + Map sourceMap) { + PriceResult result = new PriceResult(); + + // 按类别分组,用于计算基数公式 + Map categoryPriceMap = new HashMap<>(); + + for (ResourceMergedDO merged : mergedList) { + if (merged.getSourceId() == null) { + log.warn("[calculateTotalPrices] source_id 为空,跳过,id={}", merged.getId()); + continue; + } + + ResourceItemDO source = sourceMap.get(merged.getSourceId()); + if (source == null) { + throw exception(RESOURCE_MERGED_SOURCE_NOT_EXISTS); + } + + // 计算单条数据的价格 + PriceResult itemPrice = calculateItemPrice(merged, source, mergedList, sourceMap, categoryPriceMap); + + // 累加到总价 + result.add(itemPrice); + + // 记录类别价格(用于计算基数) + if (source.getCategoryId() != null) { + categoryPriceMap.computeIfAbsent(source.getCategoryId(), k -> new PriceResult()).add(itemPrice); + } + } + + return result; + } + + /** + * 计算单条数据的价格 + */ + private PriceResult calculateItemPrice(ResourceMergedDO merged, + ResourceItemDO source, + List allMergedList, + Map sourceMap, + Map categoryPriceMap) { + PriceResult result = new PriceResult(); + + // 定额消耗量 + BigDecimal quotaConsumption = merged.getQuotaConsumption(); + if (quotaConsumption == null) { + quotaConsumption = BigDecimal.ZERO; + } + + // 判断是否为百分比单位 + boolean isPercentUnit = "%".equals(source.getUnit()); + + if (isPercentUnit && source.getCalcBase() != null) { + // 场景2:单位为"%",使用计算基数公式 + result = calculateWithCalcBase(merged, source, allMergedList, sourceMap, categoryPriceMap); + } else { + // 场景1:普通工料机 + BigDecimal factor = quotaConsumption.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP); + + result.taxExclBasePrice = multiplyOrZero(source.getTaxExclBasePrice(), factor); + result.taxInclBasePrice = multiplyOrZero(source.getTaxInclBasePrice(), factor); + result.taxExclCompilePrice = multiplyOrZero(source.getTaxExclCompilePrice(), factor); + result.taxInclCompilePrice = multiplyOrZero(source.getTaxInclCompilePrice(), factor); + } + + return result; + } + + /** + * 使用计算基数公式计算价格 + */ + @SuppressWarnings("unchecked") + private PriceResult calculateWithCalcBase(ResourceMergedDO merged, + ResourceItemDO source, + List allMergedList, + Map sourceMap, + Map categoryPriceMap) { + PriceResult result = new PriceResult(); + + Map calcBase = source.getCalcBase(); + if (calcBase == null || !calcBase.containsKey("formula") || !calcBase.containsKey("variables")) { + throw exception(RESOURCE_MERGED_CALC_BASE_INVALID); + } + + String formula = (String) calcBase.get("formula"); + Map variablesConfig = (Map) calcBase.get("variables"); + + // 构建变量值映射表(4个价格字段分别计算) + Map taxExclBaseVars = new HashMap<>(); + Map taxInclBaseVars = new HashMap<>(); + Map taxExclCompileVars = new HashMap<>(); + Map taxInclCompileVars = new HashMap<>(); + + for (Map.Entry entry : variablesConfig.entrySet()) { + String varName = entry.getKey(); + Long categoryId = Long.valueOf(entry.getValue().toString()); + + // 从类别价格映射表中获取价格 + PriceResult categoryPrice = categoryPriceMap.get(categoryId); + if (categoryPrice == null) { + throw exception(RESOURCE_MERGED_CALC_BASE_CATEGORY_NOT_FOUND); + } + + taxExclBaseVars.put(varName, categoryPrice.taxExclBasePrice); + taxInclBaseVars.put(varName, categoryPrice.taxInclBasePrice); + taxExclCompileVars.put(varName, categoryPrice.taxExclCompilePrice); + taxInclCompileVars.put(varName, categoryPrice.taxInclCompilePrice); + } + + // 计算公式结果 + try { + BigDecimal taxExclBaseSum = FormulaEvaluator.evaluate(formula, taxExclBaseVars); + BigDecimal taxInclBaseSum = FormulaEvaluator.evaluate(formula, taxInclBaseVars); + BigDecimal taxExclCompileSum = FormulaEvaluator.evaluate(formula, taxExclCompileVars); + BigDecimal taxInclCompileSum = FormulaEvaluator.evaluate(formula, taxInclCompileVars); + + // 乘以 (定额消耗量 / 100) + BigDecimal factor = merged.getQuotaConsumption().divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP); + + result.taxExclBasePrice = taxExclBaseSum.multiply(factor).setScale(4, RoundingMode.HALF_UP); + result.taxInclBasePrice = taxInclBaseSum.multiply(factor).setScale(4, RoundingMode.HALF_UP); + result.taxExclCompilePrice = taxExclCompileSum.multiply(factor).setScale(4, RoundingMode.HALF_UP); + result.taxInclCompilePrice = taxInclCompileSum.multiply(factor).setScale(4, RoundingMode.HALF_UP); + } catch (Exception e) { + log.error("[calculateWithCalcBase] 公式计算失败,formula={}, error={}", formula, e.getMessage()); + throw exception(RESOURCE_MERGED_FORMULA_PARSE_ERROR); + } + + return result; + } + + /** + * 安全乘法(处理 null 值) + */ + private BigDecimal multiplyOrZero(BigDecimal value, BigDecimal factor) { + if (value == null) { + return BigDecimal.ZERO; + } + return value.multiply(factor).setScale(4, RoundingMode.HALF_UP); + } + + /** + * 价格结果对象 + */ + private static class PriceResult { + BigDecimal taxExclBasePrice = BigDecimal.ZERO; + BigDecimal taxInclBasePrice = BigDecimal.ZERO; + BigDecimal taxExclCompilePrice = BigDecimal.ZERO; + BigDecimal taxInclCompilePrice = BigDecimal.ZERO; + + void add(PriceResult other) { + this.taxExclBasePrice = this.taxExclBasePrice.add(other.taxExclBasePrice); + this.taxInclBasePrice = this.taxInclBasePrice.add(other.taxInclBasePrice); + this.taxExclCompilePrice = this.taxExclCompilePrice.add(other.taxExclCompilePrice); + this.taxInclCompilePrice = this.taxInclCompilePrice.add(other.taxInclCompilePrice); + } + } +} diff --git a/yhy-module-core/src/main/java/com/yhy/module/core/util/FormulaEvaluator.java b/yhy-module-core/src/main/java/com/yhy/module/core/util/FormulaEvaluator.java new file mode 100644 index 0000000..d1e1a61 --- /dev/null +++ b/yhy-module-core/src/main/java/com/yhy/module/core/util/FormulaEvaluator.java @@ -0,0 +1,215 @@ +package com.yhy.module.core.util; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.stream.Collectors; + +/** + * 公式计算工具类 + * 支持四则运算、括号、优先级 + * + * @author yihuiyong + */ +public class FormulaEvaluator { + + /** + * 计算公式 + * + * @param formula 公式字符串,如 "(人+机)-(材*0.3)" + * @param variables 变量映射表,key为变量名,value为变量值 + * @return 计算结果 + * @throws IllegalArgumentException 公式解析失败 + */ + public static BigDecimal evaluate(String formula, Map variables) { + if (formula == null || formula.trim().isEmpty()) { + throw new IllegalArgumentException("公式不能为空"); + } + + // 替换变量 + String expression = replaceVariables(formula, variables); + + // 计算表达式 + return calculateExpression(expression); + } + + /** + * 替换公式中的变量 + */ + private static String replaceVariables(String formula, Map variables) { + // 按变量名长度倒序排序,避免短变量名替换长变量名的问题 + // 例如:先替换"人机"再替换"人" + List> sortedEntries = variables.entrySet().stream() + .sorted((e1, e2) -> Integer.compare(e2.getKey().length(), e1.getKey().length())) + .collect(Collectors.toList()); + + String result = formula; + for (Map.Entry entry : sortedEntries) { + String varName = entry.getKey(); + BigDecimal value = entry.getValue(); + result = result.replace(varName, value.toString()); + } + + return result; + } + + /** + * 计算表达式(支持括号和四则运算) + */ + private static BigDecimal calculateExpression(String expression) { + // 去除空格 + expression = expression.replaceAll("\\s+", ""); + + // 验证括号匹配 + if (!isValidParentheses(expression)) { + throw new IllegalArgumentException("括号不匹配"); + } + + // 使用双栈算法计算表达式 + return evaluateWithStacks(expression); + } + + /** + * 验证括号是否匹配 + */ + private static boolean isValidParentheses(String expression) { + int count = 0; + for (char c : expression.toCharArray()) { + if (c == '(') { + count++; + } else if (c == ')') { + count--; + if (count < 0) { + return false; + } + } + } + return count == 0; + } + + /** + * 使用双栈算法计算表达式 + * 一个栈存储数字,一个栈存储运算符 + */ + private static BigDecimal evaluateWithStacks(String expression) { + Stack numbers = new Stack<>(); + Stack operators = new Stack<>(); + + int i = 0; + while (i < expression.length()) { + char c = expression.charAt(i); + + // 跳过空格 + if (c == ' ') { + i++; + continue; + } + + // 处理数字(包括小数) + if (Character.isDigit(c) || c == '.') { + StringBuilder sb = new StringBuilder(); + while (i < expression.length() && + (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) { + sb.append(expression.charAt(i)); + i++; + } + numbers.push(new BigDecimal(sb.toString())); + continue; + } + + // 处理左括号 + if (c == '(') { + operators.push(c); + i++; + continue; + } + + // 处理右括号 + if (c == ')') { + while (!operators.isEmpty() && operators.peek() != '(') { + numbers.push(applyOperator(operators.pop(), numbers.pop(), numbers.pop())); + } + operators.pop(); // 弹出左括号 + i++; + continue; + } + + // 处理运算符 + if (isOperator(c)) { + // 处理负号 + if (c == '-' && (i == 0 || expression.charAt(i - 1) == '(' || isOperator(expression.charAt(i - 1)))) { + // 负号:将其视为 0 - number + numbers.push(BigDecimal.ZERO); + } + + while (!operators.isEmpty() && hasPrecedence(c, operators.peek())) { + numbers.push(applyOperator(operators.pop(), numbers.pop(), numbers.pop())); + } + operators.push(c); + i++; + continue; + } + + throw new IllegalArgumentException("无效字符: " + c); + } + + // 处理剩余的运算符 + while (!operators.isEmpty()) { + numbers.push(applyOperator(operators.pop(), numbers.pop(), numbers.pop())); + } + + return numbers.pop(); + } + + /** + * 判断是否为运算符 + */ + private static boolean isOperator(char c) { + return c == '+' || c == '-' || c == '*' || c == '/'; + } + + /** + * 判断运算符优先级 + * + * @param op1 当前运算符 + * @param op2 栈顶运算符 + * @return true 如果栈顶运算符优先级 >= 当前运算符 + */ + private static boolean hasPrecedence(char op1, char op2) { + if (op2 == '(' || op2 == ')') { + return false; + } + if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) { + return false; + } + return true; + } + + /** + * 应用运算符 + * + * @param operator 运算符 + * @param b 第二个操作数 + * @param a 第一个操作数 + * @return 计算结果 + */ + private static BigDecimal applyOperator(char operator, BigDecimal b, BigDecimal a) { + switch (operator) { + case '+': + return a.add(b); + case '-': + return a.subtract(b); + case '*': + return a.multiply(b); + case '/': + if (b.compareTo(BigDecimal.ZERO) == 0) { + throw new ArithmeticException("除数不能为零"); + } + return a.divide(b, 4, RoundingMode.HALF_UP); + default: + throw new IllegalArgumentException("无效运算符: " + operator); + } + } +} diff --git a/yhy-module-core/src/test/java/com/yhy/module/core/service/quota/QuotaRateFieldLabelServiceTest.java b/yhy-module-core/src/test/java/com/yhy/module/core/service/quota/QuotaRateFieldLabelServiceTest.java new file mode 100644 index 0000000..8d7a218 --- /dev/null +++ b/yhy-module-core/src/test/java/com/yhy/module/core/service/quota/QuotaRateFieldLabelServiceTest.java @@ -0,0 +1,157 @@ +package com.yhy.module.core.service.quota; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelRespVO; +import com.yhy.module.core.controller.admin.quota.vo.QuotaRateFieldLabelSaveReqVO; +import com.yhy.module.core.dal.dataobject.quota.QuotaRateFieldLabelDO; +import java.util.List; +import javax.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * 定额费率字段标签字典 Service 测试 + * + * @author yhy + */ +@SpringBootTest +public class QuotaRateFieldLabelServiceTest { + + @Resource + private QuotaRateFieldLabelService fieldLabelService; + + /** + * 测试创建字段标签 + */ + @Test + public void testCreateFieldLabel() { + // 准备数据 + QuotaRateFieldLabelSaveReqVO createReqVO = new QuotaRateFieldLabelSaveReqVO(); + createReqVO.setCatalogItemId(1002L); // 模式节点ID + createReqVO.setLabelName("土石方工程"); + + // 执行创建 + Long labelId = fieldLabelService.createFieldLabel(createReqVO); + + // 验证结果 + assertNotNull(labelId); + System.out.println("创建标签成功,ID: " + labelId); + + // 查询验证 + QuotaRateFieldLabelDO label = fieldLabelService.getFieldLabel(labelId); + assertNotNull(label); + assertEquals("土石方工程", label.getLabelName()); + assertEquals(1002L, label.getCatalogItemId()); + } + + /** + * 测试更新字段标签 + */ + @Test + public void testUpdateFieldLabel() { + // 先创建 + QuotaRateFieldLabelSaveReqVO createReqVO = new QuotaRateFieldLabelSaveReqVO(); + createReqVO.setCatalogItemId(1002L); + createReqVO.setLabelName("土石方工程"); + Long labelId = fieldLabelService.createFieldLabel(createReqVO); + + // 更新 + QuotaRateFieldLabelSaveReqVO updateReqVO = new QuotaRateFieldLabelSaveReqVO(); + updateReqVO.setId(labelId); + updateReqVO.setCatalogItemId(1002L); + updateReqVO.setLabelName("土方工程"); + fieldLabelService.updateFieldLabel(updateReqVO); + + // 验证 + QuotaRateFieldLabelDO label = fieldLabelService.getFieldLabel(labelId); + assertEquals("土方工程", label.getLabelName()); + System.out.println("更新标签成功,新名称: " + label.getLabelName()); + } + + /** + * 测试查询标签列表 + */ + @Test + public void testGetFieldLabelList() { + // 创建多个标签 + Long catalogItemId = 1002L; + + QuotaRateFieldLabelSaveReqVO label1 = new QuotaRateFieldLabelSaveReqVO(); + label1.setCatalogItemId(catalogItemId); + label1.setLabelName("土石方工程"); + fieldLabelService.createFieldLabel(label1); + + QuotaRateFieldLabelSaveReqVO label2 = new QuotaRateFieldLabelSaveReqVO(); + label2.setCatalogItemId(catalogItemId); + label2.setLabelName("地基处理工程"); + fieldLabelService.createFieldLabel(label2); + + // 查询列表 + List labels = fieldLabelService.getFieldLabelList(catalogItemId); + + // 验证 + assertNotNull(labels); + assertTrue(labels.size() >= 2); + System.out.println("查询到标签数量: " + labels.size()); + labels.forEach(label -> System.out.println(" - " + label.getLabelName())); + } + + /** + * 测试交换排序 + */ + @Test + public void testSwapSort() { + // 创建两个标签 + Long catalogItemId = 1002L; + + QuotaRateFieldLabelSaveReqVO label1 = new QuotaRateFieldLabelSaveReqVO(); + label1.setCatalogItemId(catalogItemId); + label1.setLabelName("标签A"); + Long labelId1 = fieldLabelService.createFieldLabel(label1); + + QuotaRateFieldLabelSaveReqVO label2 = new QuotaRateFieldLabelSaveReqVO(); + label2.setCatalogItemId(catalogItemId); + label2.setLabelName("标签B"); + Long labelId2 = fieldLabelService.createFieldLabel(label2); + + // 获取初始排序 + QuotaRateFieldLabelDO before1 = fieldLabelService.getFieldLabel(labelId1); + QuotaRateFieldLabelDO before2 = fieldLabelService.getFieldLabel(labelId2); + System.out.println("交换前 - 标签A排序: " + before1.getSortOrder() + ", 标签B排序: " + before2.getSortOrder()); + + // 交换排序 + fieldLabelService.swapSort(labelId1, labelId2); + + // 验证排序已交换 + QuotaRateFieldLabelDO after1 = fieldLabelService.getFieldLabel(labelId1); + QuotaRateFieldLabelDO after2 = fieldLabelService.getFieldLabel(labelId2); + System.out.println("交换后 - 标签A排序: " + after1.getSortOrder() + ", 标签B排序: " + after2.getSortOrder()); + + assertEquals(before1.getSortOrder(), after2.getSortOrder()); + assertEquals(before2.getSortOrder(), after1.getSortOrder()); + } + + /** + * 测试删除标签(未被使用) + */ + @Test + public void testDeleteFieldLabel() { + // 创建标签 + QuotaRateFieldLabelSaveReqVO createReqVO = new QuotaRateFieldLabelSaveReqVO(); + createReqVO.setCatalogItemId(1002L); + createReqVO.setLabelName("临时标签"); + Long labelId = fieldLabelService.createFieldLabel(createReqVO); + + // 删除 + fieldLabelService.deleteFieldLabel(labelId); + + // 验证已删除 + QuotaRateFieldLabelDO label = fieldLabelService.getFieldLabel(labelId); + assertNull(label); + System.out.println("删除标签成功"); + } +} diff --git a/yhy-module-core/src/test/java/com/yhy/module/core/service/quota/QuotaRateItemCalculationTest.java b/yhy-module-core/src/test/java/com/yhy/module/core/service/quota/QuotaRateItemCalculationTest.java new file mode 100644 index 0000000..1f364bc --- /dev/null +++ b/yhy-module-core/src/test/java/com/yhy/module/core/service/quota/QuotaRateItemCalculationTest.java @@ -0,0 +1,208 @@ +package com.yhy.module.core.service.quota; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.yhy.module.core.dal.dataobject.quota.QuotaRateItemDO; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * 定额费率项计算逻辑测试 + * + * 测试默认阶梯规则的计算逻辑 + */ +@DisplayName("定额费率项计算逻辑测试") +public class QuotaRateItemCalculationTest { + + @Test + @DisplayName("测试阶梯规则 - 基数50万以内") + public void testTierRule_Under50() { + // 模拟费率项配置 + QuotaRateItemDO rateItem = createRateItemWithTiers(); + + // 基数值:30万 + BigDecimal baseValue = new BigDecimal("30"); + + // 预期结果:字段1=0.224 + Map expected = new HashMap<>(); + expected.put(1, new BigDecimal("0.224")); + + // 验证匹配第一个阶梯 + Map valueRules = rateItem.getValueRules(); + List> tiers = (List>) valueRules.get("tiers"); + + Map matchedTier = findMatchedTier(tiers, baseValue); + assertNotNull(matchedTier); + assertEquals(new BigDecimal("50"), new BigDecimal(matchedTier.get("threshold").toString())); + } + + @Test + @DisplayName("测试阶梯规则 - 基数50-100万之间") + public void testTierRule_Between50And100() { + QuotaRateItemDO rateItem = createRateItemWithTiers(); + + // 基数值:80万 + BigDecimal baseValue = new BigDecimal("80"); + + // 预期结果:字段1=0.301 + Map valueRules = rateItem.getValueRules(); + List> tiers = (List>) valueRules.get("tiers"); + + Map matchedTier = findMatchedTier(tiers, baseValue); + assertNotNull(matchedTier); + assertEquals(new BigDecimal("100"), new BigDecimal(matchedTier.get("threshold").toString())); + } + + @Test + @DisplayName("测试阶梯规则 - 基数超过最大阈值") + public void testTierRule_ExceedMax() { + QuotaRateItemDO rateItem = createRateItemWithTiers(); + + // 基数值:5000万 + BigDecimal baseValue = new BigDecimal("5000"); + + // 预期结果:使用最后一个阶梯 + Map valueRules = rateItem.getValueRules(); + List> tiers = (List>) valueRules.get("tiers"); + + Map matchedTier = findMatchedTier(tiers, baseValue); + // 如果没有匹配,应该使用最后一个阶梯 + if (matchedTier == null) { + matchedTier = tiers.get(tiers.size() - 1); + } + assertNotNull(matchedTier); + } + + @Test + @DisplayName("测试阶梯规则 + 增量规则") + public void testTierWithIncrement() { + QuotaRateItemDO rateItem = createRateItemWithTiersAndIncrements(); + + // 基数值:1200万(超过1000万基础阈值200万) + BigDecimal baseValue = new BigDecimal("1200"); + + // 预期结果: + // 1. 阶梯基础值:字段1=0.301(匹配最后一个阶梯) + // 2. 增量计算:(1200-1000)/100 * 0.036 = 2 * 0.036 = 0.072 + // 3. 最终结果:0.301 + 0.072 = 0.373 + + Map valueRules = rateItem.getValueRules(); + List> tiers = (List>) valueRules.get("tiers"); + List> increments = (List>) valueRules.get("increments"); + + assertNotNull(tiers); + assertNotNull(increments); + assertFalse(increments.isEmpty()); + } + + // ==================== 辅助方法 ==================== + + /** + * 创建只有阶梯规则的费率项 + */ + private QuotaRateItemDO createRateItemWithTiers() { + QuotaRateItemDO rateItem = new QuotaRateItemDO(); + rateItem.setValueMode("dynamic"); + + Map settings = new HashMap<>(); + Map valueRules = new HashMap<>(); + + // 阶梯规则 + List> tiers = new ArrayList<>(); + + // 第1档:≤50万,费率0.224% + Map tier1 = new HashMap<>(); + tier1.put("seq", 1); + tier1.put("threshold", new BigDecimal("50")); + tier1.put("compareType", "lte"); + Map fieldValues1 = new HashMap<>(); + fieldValues1.put("1", new BigDecimal("0.224")); + tier1.put("fieldValues", fieldValues1); + tiers.add(tier1); + + // 第2档:≤100万,费率0.301% + Map tier2 = new HashMap<>(); + tier2.put("seq", 2); + tier2.put("threshold", new BigDecimal("100")); + tier2.put("compareType", "lte"); + Map fieldValues2 = new HashMap<>(); + fieldValues2.put("1", new BigDecimal("0.301")); + tier2.put("fieldValues", fieldValues2); + tiers.add(tier2); + + valueRules.put("tiers", tiers); + settings.put("valueRules", valueRules); + rateItem.setSettings(settings); + + return rateItem; + } + + /** + * 创建包含阶梯规则和增量规则的费率项 + */ + private QuotaRateItemDO createRateItemWithTiersAndIncrements() { + QuotaRateItemDO rateItem = createRateItemWithTiers(); + + Map settings = rateItem.getSettings(); + Map valueRules = (Map) settings.get("valueRules"); + + // 增量规则 + List> increments = new ArrayList<>(); + + Map increment1 = new HashMap<>(); + increment1.put("seq", 1); + increment1.put("step", new BigDecimal("100")); + increment1.put("baseThreshold", new BigDecimal("1000")); + Map fieldIncrements = new HashMap<>(); + fieldIncrements.put("1", new BigDecimal("0.036")); + increment1.put("fieldIncrements", fieldIncrements); + increments.add(increment1); + + valueRules.put("increments", increments); + + return rateItem; + } + + /** + * 查找匹配的阶梯 + */ + private Map findMatchedTier(List> tiers, BigDecimal baseValue) { + // 按阈值排序 + tiers.sort(Comparator.comparing(t -> new BigDecimal(t.get("threshold").toString()))); + + for (Map tier : tiers) { + BigDecimal threshold = new BigDecimal(tier.get("threshold").toString()); + String compareType = (String) tier.getOrDefault("compareType", "lte"); + + boolean matched = false; + switch (compareType) { + case "lte": + matched = baseValue.compareTo(threshold) <= 0; + break; + case "lt": + matched = baseValue.compareTo(threshold) < 0; + break; + case "gte": + matched = baseValue.compareTo(threshold) >= 0; + break; + case "gt": + matched = baseValue.compareTo(threshold) > 0; + break; + } + + if (matched) { + return tier; + } + } + + return null; + } +} diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index 1964ba2..8d5ce79 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -47,14 +47,14 @@ spring: primary: master datasource: master: - url: jdbc:postgresql://192.168.5.118:5432/ruoyi-vue-pro # PostgreSQL 连接配置 + url: jdbc:postgresql://106.55.147.251:35432/dev # PostgreSQL 连接配置 username: postgres - password: 123456 + password: pMnxAcxmHJzG7bnx slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:postgresql://192.168.5.118:5432/ruoyi-vue-pro # PostgreSQL 连接配置 + url: jdbc:postgresql://106.55.147.251:35432/dev # PostgreSQL 连接配置 username: postgres - password: 123456 + password: pMnxAcxmHJzG7bnx # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: