From 2bac7d1648c1873d7485e893869c8974a011bc12 Mon Sep 17 00:00:00 2001 From: liyongde <1419499670@qq.com> Date: Wed, 22 Jul 2026 09:31:40 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E8=BD=BD=E5=85=B7=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/wms/enums/ErrorCodeConstants.java | 3 + .../StorageVehicleInfoController.java | 104 +++++ .../vo/StorageVehicleInfoPageReqVO.java | 60 +++ .../vo/StorageVehicleInfoRespVO.java | 90 +++++ .../vo/StorageVehicleInfoSaveReqVO.java | 60 +++ .../StorageVehicleInfoDO.java | 89 +++++ .../StorageVehicleInfoMapper.java | 39 ++ .../StorageVehicleInfoService.java | 62 +++ .../StorageVehicleInfoServiceImpl.java | 85 ++++ .../mapper/groupplate/GroupPlateMapper.xml | 12 + .../StorageVehicleInfoMapper.xml | 12 + .../apps/web-antdv-next/.env.production | 4 +- .../src/api/wms/storagevehicleinfo/index.ts | 67 ++++ .../src/views/wms/storagevehicleinfo/data.ts | 374 ++++++++++++++++++ .../views/wms/storagevehicleinfo/index.vue | 186 +++++++++ .../wms/storagevehicleinfo/modules/form.vue | 82 ++++ 前端Jenkins部署指南.md | 347 ++++++++++++++++ 17 files changed, 1674 insertions(+), 2 deletions(-) create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/StorageVehicleInfoController.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoPageReqVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoRespVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoSaveReqVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/storagevehicleinfo/StorageVehicleInfoDO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/storagevehicleinfo/StorageVehicleInfoMapper.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoService.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoServiceImpl.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/resources/mapper/groupplate/GroupPlateMapper.xml create mode 100644 nl-module-wms/nl-module-wms-server/src/main/resources/mapper/storagevehicleinfo/StorageVehicleInfoMapper.xml create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/storagevehicleinfo/index.ts create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/data.ts create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/index.vue create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/modules/form.vue create mode 100644 前端Jenkins部署指南.md diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java index d0d40c5e..51876fd8 100644 --- a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java @@ -19,4 +19,7 @@ public interface ErrorCodeConstants { // ================ 组盘记录相关错误码 ================= ErrorCode GROUP_PLATE_NOT_EXISTS = new ErrorCode(9, "组盘记录不存在"); + + // ========== 载具信息相关错误码 ========== + ErrorCode STORAGE_VEHICLE_INFO_NOT_EXISTS = new ErrorCode(11, "载具信息不存在"); } diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/StorageVehicleInfoController.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/StorageVehicleInfoController.java new file mode 100644 index 00000000..e252e130 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/StorageVehicleInfoController.java @@ -0,0 +1,104 @@ +package cn.code.nl.module.wms.controller.admin.storagevehicleinfo; + +import org.springframework.web.bind.annotation.*; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import jakarta.validation.constraints.*; +import jakarta.validation.*; +import jakarta.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.code.nl.framework.common.pojo.PageParam; +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.framework.common.util.object.BeanUtils; +import static cn.code.nl.framework.common.pojo.CommonResult.success; + +import cn.code.nl.framework.excel.core.util.ExcelUtils; + +import cn.code.nl.framework.apilog.core.annotation.ApiAccessLog; +import static cn.code.nl.framework.apilog.core.enums.OperateTypeEnum.*; + +import cn.code.nl.module.wms.controller.admin.storagevehicleinfo.vo.*; +import cn.code.nl.module.wms.dal.dataobject.storagevehicleinfo.StorageVehicleInfoDO; +import cn.code.nl.module.wms.service.storagevehicleinfo.StorageVehicleInfoService; + +@Tag(name = "管理后台 - 载具信息") +@RestController +@RequestMapping("/wms/storage-vehicle-info") +@Validated +public class StorageVehicleInfoController { + + @Resource + private StorageVehicleInfoService storageVehicleInfoService; + + @PostMapping("/create") + @Operation(summary = "创建载具信息") + @PreAuthorize("@ss.hasPermission('wms:storage-vehicle-info:create')") + public CommonResult createStorageVehicleInfo(@Valid @RequestBody StorageVehicleInfoSaveReqVO createReqVO) { + return success(storageVehicleInfoService.createStorageVehicleInfo(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新载具信息") + @PreAuthorize("@ss.hasPermission('wms:storage-vehicle-info:update')") + public CommonResult updateStorageVehicleInfo(@Valid @RequestBody StorageVehicleInfoSaveReqVO updateReqVO) { + storageVehicleInfoService.updateStorageVehicleInfo(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除载具信息") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('wms:storage-vehicle-info:delete')") + public CommonResult deleteStorageVehicleInfo(@RequestParam("id") Long id) { + storageVehicleInfoService.deleteStorageVehicleInfo(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Parameter(name = "ids", description = "编号", required = true) + @Operation(summary = "批量删除载具信息") + @PreAuthorize("@ss.hasPermission('wms:storage-vehicle-info:delete')") + public CommonResult deleteStorageVehicleInfoList(@RequestParam("ids") List ids) { + storageVehicleInfoService.deleteStorageVehicleInfoListByIds(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得载具信息") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('wms:storage-vehicle-info:query')") + public CommonResult getStorageVehicleInfo(@RequestParam("id") Long id) { + StorageVehicleInfoDO storageVehicleInfo = storageVehicleInfoService.getStorageVehicleInfo(id); + return success(BeanUtils.toBean(storageVehicleInfo, StorageVehicleInfoRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得载具信息分页") + @PreAuthorize("@ss.hasPermission('wms:storage-vehicle-info:query')") + public CommonResult> getStorageVehicleInfoPage(@Valid StorageVehicleInfoPageReqVO pageReqVO) { + PageResult pageResult = storageVehicleInfoService.getStorageVehicleInfoPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, StorageVehicleInfoRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出载具信息 Excel") + @PreAuthorize("@ss.hasPermission('wms:storage-vehicle-info:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportStorageVehicleInfoExcel(@Valid StorageVehicleInfoPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = storageVehicleInfoService.getStorageVehicleInfoPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "载具信息.xls", "数据", StorageVehicleInfoRespVO.class, + BeanUtils.toBean(list, StorageVehicleInfoRespVO.class)); + } + +} \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoPageReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoPageReqVO.java new file mode 100644 index 00000000..6e2dfa21 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoPageReqVO.java @@ -0,0 +1,60 @@ +package cn.code.nl.module.wms.controller.admin.storagevehicleinfo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.code.nl.framework.common.pojo.PageParam; +import java.math.BigDecimal; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.code.nl.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 载具信息分页 Request VO") +@Data +public class StorageVehicleInfoPageReqVO extends PageParam { + + @Schema(description = "载具编码") + private String storageVehicleCode; + + @Schema(description = "载具名称") + private String storageVehicleName; + + @Schema(description = "一维码") + private String oneCode; + + @Schema(description = "二维码") + private String twoCode; + + @Schema(description = "是否启用") + private Boolean isUsed; + + @Schema(description = "载具类型") + private String storageVehicleType; + + @Schema(description = "载具宽度") + private Integer vehicleWidth; + + @Schema(description = "载具长度") + private Integer vehicleLong; + + @Schema(description = "载具高度") + private Integer vehicleHeight; + + @Schema(description = "托盘重量") + private BigDecimal weigth; + + @Schema(description = "载具是否超仓位") + private String overStructType; + + @Schema(description = "占仓位数") + private Integer occupyStructQty; + + @Schema(description = "木箱号") + private String boxNo; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoRespVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoRespVO.java new file mode 100644 index 00000000..e2bbf772 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoRespVO.java @@ -0,0 +1,90 @@ +package cn.code.nl.module.wms.controller.admin.storagevehicleinfo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.idev.excel.annotation.*; + +@Schema(description = "管理后台 - 载具信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class StorageVehicleInfoRespVO { + + private Long storageVehicleId; + + @Schema(description = "载具编码", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("载具编码") + private String storageVehicleCode; + + @Schema(description = "载具名称") + @ExcelProperty("载具名称") + private String storageVehicleName; + + @Schema(description = "一维码") + @ExcelProperty("一维码") + private String oneCode; + + @Schema(description = "二维码") + @ExcelProperty("二维码") + private String twoCode; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("是否启用") + private Boolean isUsed; + + @Schema(description = "载具类型", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("载具类型") + private String storageVehicleType; + + @Schema(description = "载具宽度") + @ExcelProperty("载具宽度") + private Integer vehicleWidth; + + @Schema(description = "载具长度") + @ExcelProperty("载具长度") + private Integer vehicleLong; + + @Schema(description = "载具高度") + @ExcelProperty("载具高度") + private Integer vehicleHeight; + + @Schema(description = "托盘重量") + @ExcelProperty("托盘重量") + private BigDecimal weigth; + + @Schema(description = "载具是否超仓位", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("载具是否超仓位") + private String overStructType; + + @Schema(description = "占仓位数", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("占仓位数") + private Integer occupyStructQty; + + @Schema(description = "木箱号", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("木箱号") + private String boxNo; + + @Schema(description = "外部标识") + @ExcelProperty("外部标识") + private String extId; + + @Schema(description = "创建者") + @ExcelProperty("创建者") + private String creator; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新者") + @ExcelProperty("更新者") + private String updater; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("更新时间") + private LocalDateTime updateTime; + +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoSaveReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoSaveReqVO.java new file mode 100644 index 00000000..3a18f05f --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/storagevehicleinfo/vo/StorageVehicleInfoSaveReqVO.java @@ -0,0 +1,60 @@ +package cn.code.nl.module.wms.controller.admin.storagevehicleinfo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import jakarta.validation.constraints.*; +import java.math.BigDecimal; + +@Schema(description = "管理后台 - 载具信息新增/修改 Request VO") +@Data +public class StorageVehicleInfoSaveReqVO { + + private Long storageVehicleId; + + @Schema(description = "载具编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "载具编码不能为空") + private String storageVehicleCode; + + @Schema(description = "载具名称") + private String storageVehicleName; + + @Schema(description = "一维码") + private String oneCode; + + @Schema(description = "二维码") + private String twoCode; + + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "是否启用不能为空") + private Boolean isUsed; + + @Schema(description = "载具类型", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "载具类型不能为空") + private String storageVehicleType; + + @Schema(description = "载具宽度") + private Integer vehicleWidth; + + @Schema(description = "载具长度") + private Integer vehicleLong; + + @Schema(description = "载具高度") + private Integer vehicleHeight; + + @Schema(description = "托盘重量") + private BigDecimal weigth; + + @Schema(description = "载具是否超仓位", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "载具是否超仓位不能为空") + private String overStructType; + + @Schema(description = "占仓位数", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "占仓位数不能为空") + private Integer occupyStructQty; + + @Schema(description = "木箱号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "木箱号不能为空") + private String boxNo; + +} \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/storagevehicleinfo/StorageVehicleInfoDO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/storagevehicleinfo/StorageVehicleInfoDO.java new file mode 100644 index 00000000..db7f6606 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/dataobject/storagevehicleinfo/StorageVehicleInfoDO.java @@ -0,0 +1,89 @@ +package cn.code.nl.module.wms.dal.dataobject.storagevehicleinfo; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.code.nl.framework.mybatis.core.dataobject.BaseDO; + +/** + * 载具信息 DO + * + * @author 诺力管理员 + */ +@TableName("wms_storage_vehicle_info") +@KeySequence("wms_storage_vehicle_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class StorageVehicleInfoDO extends BaseDO { + + /** + * 载具标识 + */ + @TableId + private Long storageVehicleId; + /** + * 载具编码 + */ + private String storageVehicleCode; + /** + * 载具名称 + */ + private String storageVehicleName; + /** + * 一维码 + */ + private String oneCode; + /** + * 二维码 + */ + private String twoCode; + /** + * 是否启用 + */ + private Boolean isUsed; + /** + * 载具类型 + */ + private String storageVehicleType; + /** + * 载具宽度 + */ + private Integer vehicleWidth; + /** + * 载具长度 + */ + private Integer vehicleLong; + /** + * 载具高度 + */ + private Integer vehicleHeight; + /** + * 托盘重量 + */ + private BigDecimal weigth; + /** + * 载具是否超仓位 + */ + private String overStructType; + /** + * 占仓位数 + */ + private Integer occupyStructQty; + /** + * 木箱号 + */ + private String boxNo; + /** + * 外部标识 + */ + private String extId; + + +} \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/storagevehicleinfo/StorageVehicleInfoMapper.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/storagevehicleinfo/StorageVehicleInfoMapper.java new file mode 100644 index 00000000..4a29fa40 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/storagevehicleinfo/StorageVehicleInfoMapper.java @@ -0,0 +1,39 @@ +package cn.code.nl.module.wms.dal.mysql.storagevehicleinfo; + +import java.util.*; + +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX; +import cn.code.nl.module.wms.dal.dataobject.storagevehicleinfo.StorageVehicleInfoDO; +import org.apache.ibatis.annotations.Mapper; +import cn.code.nl.module.wms.controller.admin.storagevehicleinfo.vo.*; + +/** + * 载具信息 Mapper + * + * @author 诺力管理员 + */ +@Mapper +public interface StorageVehicleInfoMapper extends BaseMapperX { + + default PageResult selectPage(StorageVehicleInfoPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(StorageVehicleInfoDO::getStorageVehicleCode, reqVO.getStorageVehicleCode()) + .likeIfPresent(StorageVehicleInfoDO::getStorageVehicleName, reqVO.getStorageVehicleName()) + .eqIfPresent(StorageVehicleInfoDO::getOneCode, reqVO.getOneCode()) + .eqIfPresent(StorageVehicleInfoDO::getTwoCode, reqVO.getTwoCode()) + .eqIfPresent(StorageVehicleInfoDO::getIsUsed, reqVO.getIsUsed()) + .eqIfPresent(StorageVehicleInfoDO::getStorageVehicleType, reqVO.getStorageVehicleType()) + .eqIfPresent(StorageVehicleInfoDO::getVehicleWidth, reqVO.getVehicleWidth()) + .eqIfPresent(StorageVehicleInfoDO::getVehicleLong, reqVO.getVehicleLong()) + .eqIfPresent(StorageVehicleInfoDO::getVehicleHeight, reqVO.getVehicleHeight()) + .eqIfPresent(StorageVehicleInfoDO::getWeigth, reqVO.getWeigth()) + .eqIfPresent(StorageVehicleInfoDO::getOverStructType, reqVO.getOverStructType()) + .eqIfPresent(StorageVehicleInfoDO::getOccupyStructQty, reqVO.getOccupyStructQty()) + .eqIfPresent(StorageVehicleInfoDO::getBoxNo, reqVO.getBoxNo()) + .betweenIfPresent(StorageVehicleInfoDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(StorageVehicleInfoDO::getStorageVehicleId)); + } + +} \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoService.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoService.java new file mode 100644 index 00000000..23aefcbc --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoService.java @@ -0,0 +1,62 @@ +package cn.code.nl.module.wms.service.storagevehicleinfo; + +import java.util.*; +import jakarta.validation.*; +import cn.code.nl.module.wms.controller.admin.storagevehicleinfo.vo.*; +import cn.code.nl.module.wms.dal.dataobject.storagevehicleinfo.StorageVehicleInfoDO; +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.framework.common.pojo.PageParam; + +/** + * 载具信息 Service 接口 + * + * @author 诺力管理员 + */ +public interface StorageVehicleInfoService { + + /** + * 创建载具信息 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStorageVehicleInfo(@Valid StorageVehicleInfoSaveReqVO createReqVO); + + /** + * 更新载具信息 + * + * @param updateReqVO 更新信息 + */ + void updateStorageVehicleInfo(@Valid StorageVehicleInfoSaveReqVO updateReqVO); + + /** + * 删除载具信息 + * + * @param id 编号 + */ + void deleteStorageVehicleInfo(Long id); + + /** + * 批量删除载具信息 + * + * @param ids 编号 + */ + void deleteStorageVehicleInfoListByIds(List ids); + + /** + * 获得载具信息 + * + * @param id 编号 + * @return 载具信息 + */ + StorageVehicleInfoDO getStorageVehicleInfo(Long id); + + /** + * 获得载具信息分页 + * + * @param pageReqVO 分页查询 + * @return 载具信息分页 + */ + PageResult getStorageVehicleInfoPage(StorageVehicleInfoPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoServiceImpl.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoServiceImpl.java new file mode 100644 index 00000000..4b2581fb --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/storagevehicleinfo/StorageVehicleInfoServiceImpl.java @@ -0,0 +1,85 @@ +package cn.code.nl.module.wms.service.storagevehicleinfo; + +import cn.hutool.core.collection.CollUtil; +import org.springframework.stereotype.Service; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.code.nl.module.wms.controller.admin.storagevehicleinfo.vo.*; +import cn.code.nl.module.wms.dal.dataobject.storagevehicleinfo.StorageVehicleInfoDO; +import cn.code.nl.framework.common.pojo.PageResult; +import cn.code.nl.framework.common.pojo.PageParam; +import cn.code.nl.framework.common.util.object.BeanUtils; + +import cn.code.nl.module.wms.dal.mysql.storagevehicleinfo.StorageVehicleInfoMapper; + +import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.code.nl.framework.common.util.collection.CollectionUtils.convertList; +import static cn.code.nl.framework.common.util.collection.CollectionUtils.diffList; +import static cn.code.nl.module.wms.enums.ErrorCodeConstants.*; + +/** + * 载具信息 Service 实现类 + * + * @author 诺力管理员 + */ +@Service +@Validated +public class StorageVehicleInfoServiceImpl implements StorageVehicleInfoService { + + @Resource + private StorageVehicleInfoMapper storageVehicleInfoMapper; + + @Override + public Long createStorageVehicleInfo(StorageVehicleInfoSaveReqVO createReqVO) { + // 插入 + StorageVehicleInfoDO storageVehicleInfo = BeanUtils.toBean(createReqVO, StorageVehicleInfoDO.class); + storageVehicleInfoMapper.insert(storageVehicleInfo); + + // 返回 + return storageVehicleInfo.getStorageVehicleId(); + } + + @Override + public void updateStorageVehicleInfo(StorageVehicleInfoSaveReqVO updateReqVO) { + // 校验存在 + validateStorageVehicleInfoExists(updateReqVO.getStorageVehicleId()); + // 更新 + StorageVehicleInfoDO updateObj = BeanUtils.toBean(updateReqVO, StorageVehicleInfoDO.class); + storageVehicleInfoMapper.updateById(updateObj); + } + + @Override + public void deleteStorageVehicleInfo(Long id) { + // 校验存在 + validateStorageVehicleInfoExists(id); + // 删除 + storageVehicleInfoMapper.deleteById(id); + } + + @Override + public void deleteStorageVehicleInfoListByIds(List ids) { + // 删除 + storageVehicleInfoMapper.deleteByIds(ids); + } + + + private void validateStorageVehicleInfoExists(Long id) { + if (storageVehicleInfoMapper.selectById(id) == null) { + throw exception(STORAGE_VEHICLE_INFO_NOT_EXISTS); + } + } + + @Override + public StorageVehicleInfoDO getStorageVehicleInfo(Long id) { + return storageVehicleInfoMapper.selectById(id); + } + + @Override + public PageResult getStorageVehicleInfoPage(StorageVehicleInfoPageReqVO pageReqVO) { + return storageVehicleInfoMapper.selectPage(pageReqVO); + } + +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/groupplate/GroupPlateMapper.xml b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/groupplate/GroupPlateMapper.xml new file mode 100644 index 00000000..21bd592d --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/groupplate/GroupPlateMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/storagevehicleinfo/StorageVehicleInfoMapper.xml b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/storagevehicleinfo/StorageVehicleInfoMapper.xml new file mode 100644 index 00000000..104b0180 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/storagevehicleinfo/StorageVehicleInfoMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/.env.production b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/.env.production index a8f3d29a..28a7c86c 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/.env.production +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/.env.production @@ -3,7 +3,7 @@ VITE_BASE=/ # 请求路径 VITE_BASE_URL=http://127.0.0.1:48080 # 接口地址 -VITE_GLOB_API_URL=http://127.0.0.1:48080/admin-api +VITE_GLOB_API_URL=/admin-api # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务 VITE_UPLOAD_TYPE=server @@ -23,4 +23,4 @@ VITE_INJECT_APP_LOADING=true VITE_ARCHIVER=true # 验证码的开关 -VITE_APP_CAPTCHA_ENABLE=true \ No newline at end of file +VITE_APP_CAPTCHA_ENABLE=true diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/storagevehicleinfo/index.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/storagevehicleinfo/index.ts new file mode 100644 index 00000000..dbf9f062 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/storagevehicleinfo/index.ts @@ -0,0 +1,67 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace WmsStorageVehicleInfoApi { + /** 载具信息信息 */ + export interface StorageVehicleInfo { + storageVehicleId?: string; + storageVehicleCode?: string; // 载具编码 + storageVehicleName: string; // 载具名称 + oneCode: string; // 一维码 + twoCode: string; // 二维码 + isUsed?: boolean; // 是否启用 + storageVehicleType?: string; // 载具类型 + vehicleWidth: number; // 载具宽度 + vehicleLong: number; // 载具长度 + vehicleHeight: number; // 载具高度 + weigth: number; // 托盘重量 + overStructType?: string; // 载具是否超仓位 + occupyStructQty?: number; // 占仓位数 + boxNo?: string; // 木箱号 + } +} + +/** 查询载具信息分页 */ +export function getStorageVehicleInfoPage(params: PageParam) { + return requestClient.get>( + '/wms/storage-vehicle-info/page', + { params }, + ); +} + +/** 查询载具信息详情 */ +export function getStorageVehicleInfo(id: number) { + return requestClient.get( + `/wms/storage-vehicle-info/get?id=${id}`, + ); +} + +/** 新增载具信息 */ +export function createStorageVehicleInfo(data: WmsStorageVehicleInfoApi.StorageVehicleInfo) { + return requestClient.post('/wms/storage-vehicle-info/create', data); +} + +/** 修改载具信息 */ +export function updateStorageVehicleInfo(data: WmsStorageVehicleInfoApi.StorageVehicleInfo) { + return requestClient.put('/wms/storage-vehicle-info/update', data); +} + +/** 删除载具信息 */ +export function deleteStorageVehicleInfo(id: number) { + return requestClient.delete(`/wms/storage-vehicle-info/delete?id=${id}`); +} + +/** 批量删除载具信息 */ +export function deleteStorageVehicleInfoList(ids: number[]) { + return requestClient.delete( + `/wms/storage-vehicle-info/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出载具信息 */ +export function exportStorageVehicleInfo(params: any) { + return requestClient.download('/wms/storage-vehicle-info/export-excel', { params }); +} + + diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/data.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/data.ts new file mode 100644 index 00000000..f7493ae3 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/data.ts @@ -0,0 +1,374 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { WmsStorageVehicleInfoApi } from '#/api/wms/storagevehicleinfo'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'storageVehicleId', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'storageVehicleCode', + label: '载具编码', + component: 'Input', + componentProps: { + disabled: true, + placeholder: '后台自动生成', + }, + }, + { + fieldName: 'storageVehicleName', + label: '载具名称', + component: 'Input', + componentProps: { + placeholder: '请输入载具名称', + }, + }, + { + fieldName: 'oneCode', + label: '一维码', + component: 'Input', + componentProps: { + placeholder: '请输入一维码', + }, + }, + { + fieldName: 'twoCode', + label: '二维码', + component: 'Input', + componentProps: { + placeholder: '请输入二维码', + }, + }, + { + fieldName: 'isUsed', + label: '是否启用', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: [], + buttonStyle: 'solid', + optionType: 'button', + }, + }, + { + fieldName: 'storageVehicleType', + label: '载具类型', + rules: 'required', + component: 'Select', + componentProps: { + options: [], + placeholder: '请选择载具类型', + }, + }, + { + fieldName: 'vehicleWidth', + label: '载具宽度', + component: 'Input', + componentProps: { + placeholder: '请输入载具宽度', + }, + }, + { + fieldName: 'vehicleLong', + label: '载具长度', + component: 'Input', + componentProps: { + placeholder: '请输入载具长度', + }, + }, + { + fieldName: 'vehicleHeight', + label: '载具高度', + component: 'Input', + componentProps: { + placeholder: '请输入载具高度', + }, + }, + { + fieldName: 'weigth', + label: '托盘重量', + component: 'Input', + componentProps: { + placeholder: '请输入托盘重量', + }, + }, + { + fieldName: 'overStructType', + label: '载具是否超仓位', + rules: 'required', + component: 'Select', + componentProps: { + options: [], + placeholder: '请选择载具是否超仓位', + }, + }, + { + fieldName: 'occupyStructQty', + label: '占仓位数', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入占仓位数', + }, + }, + { + fieldName: 'boxNo', + label: '木箱号', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入木箱号', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'storageVehicleCode', + label: '载具编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入载具编码', + }, + }, + { + fieldName: 'storageVehicleName', + label: '载具名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入载具名称', + }, + }, + { + fieldName: 'oneCode', + label: '一维码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入一维码', + }, + }, + { + fieldName: 'twoCode', + label: '二维码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入二维码', + }, + }, + { + fieldName: 'isUsed', + label: '是否启用', + component: 'Select', + componentProps: { + allowClear: true, + options: [], + placeholder: '请选择是否启用', + }, + }, + { + fieldName: 'storageVehicleType', + label: '载具类型', + component: 'Select', + componentProps: { + allowClear: true, + options: [], + placeholder: '请选择载具类型', + }, + }, + { + fieldName: 'vehicleWidth', + label: '载具宽度', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入载具宽度', + }, + }, + { + fieldName: 'vehicleLong', + label: '载具长度', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入载具长度', + }, + }, + { + fieldName: 'vehicleHeight', + label: '载具高度', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入载具高度', + }, + }, + { + fieldName: 'weigth', + label: '托盘重量', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入托盘重量', + }, + }, + { + fieldName: 'overStructType', + label: '载具是否超仓位', + component: 'Select', + componentProps: { + allowClear: true, + options: [], + placeholder: '请选择载具是否超仓位', + }, + }, + { + fieldName: 'occupyStructQty', + label: '占仓位数', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入占仓位数', + }, + }, + { + fieldName: 'boxNo', + label: '木箱号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入木箱号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'storageVehicleCode', + title: '载具编码', + minWidth: 120, + }, + { + field: 'storageVehicleName', + title: '载具名称', + minWidth: 120, + }, + { + field: 'oneCode', + title: '一维码', + minWidth: 120, + }, + { + field: 'twoCode', + title: '二维码', + minWidth: 120, + }, + { + field: 'isUsed', + title: '是否启用', + minWidth: 120, + }, + { + field: 'storageVehicleType', + title: '载具类型', + minWidth: 120, + }, + { + field: 'vehicleWidth', + title: '载具宽度', + minWidth: 120, + }, + { + field: 'vehicleLong', + title: '载具长度', + minWidth: 120, + }, + { + field: 'vehicleHeight', + title: '载具高度', + minWidth: 120, + }, + { + field: 'weigth', + title: '托盘重量', + minWidth: 120, + }, + { + field: 'overStructType', + title: '载具是否超仓位', + minWidth: 120, + }, + { + field: 'occupyStructQty', + title: '占仓位数', + minWidth: 120, + }, + { + field: 'boxNo', + title: '木箱号', + minWidth: 120, + }, + { + field: 'extId', + title: '外部标识', + minWidth: 120, + }, + { + field: 'creator', + title: '创建者', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'updater', + title: '更新者', + minWidth: 120, + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/index.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/index.vue new file mode 100644 index 00000000..fa5db193 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/index.vue @@ -0,0 +1,186 @@ + + + \ No newline at end of file diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/modules/form.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/modules/form.vue new file mode 100644 index 00000000..e165c5a4 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/storagevehicleinfo/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/前端Jenkins部署指南.md b/前端Jenkins部署指南.md new file mode 100644 index 00000000..6016897f --- /dev/null +++ b/前端Jenkins部署指南.md @@ -0,0 +1,347 @@ +# 前端 Jenkins 部署指南 + +## 一、项目概况 + +| 项目 | 说明 | +|------|------| +| 源码路径 | `nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/` | +| 框架 | Vue 3 + Vite + Ant Design Vue 4(基于 vue-vben-admin 5.7.0) | +| 包管理器 | pnpm 11.7.0(强制,不可用 npm/yarn) | +| Node 版本 | >= 22.18.0(`.node-version` 指定 24.16.0) | +| 构建工具 | Turbo(monorepo 编排)+ Vite(打包) | +| 构建产物 | `apps/web-antdv-next/dist/` | +| 路由模式 | hash 模式(`VITE_ROUTER_HISTORY=hash`) | + +--- + +## 二、环境变量说明 + +构建时通过 `.env.production` 控制关键配置,可按环境覆盖。 + +**文件位置:** `apps/web-antdv-next/.env.production` + +| 变量 | 默认值 | 说明 | +|------|--------|------| +| `VITE_BASE` | `/` | 部署路径前缀,部署在子路径时改为 `/admin/` | +| `VITE_GLOB_API_URL` | `http://127.0.0.1:48080/admin-api` | 后端 API 地址(完整 URL) | +| `VITE_ROUTER_HISTORY` | `hash` | 路由模式,hash 模式无需 nginx 特殊配置 | +| `VITE_COMPRESS` | `none` | 压缩方式(none / gzip / brotli) | +| `VITE_ARCHIVER` | `true` | 是否生成 `dist.zip` | +| `VITE_PWA` | `false` | 是否启用 PWA | + +**多环境覆盖方式**:在 Jenkins 构建步骤中创建 `.env.production.local` 文件覆盖 `VITE_GLOB_API_URL` 等变量,Vite 会优先读取 local 文件。 + +--- + +## 三、Jenkins 流水线(Pipeline) + +### 3.1 声明式流水线(推荐,使用 Docker 镜像自带 Node 环境) + +```groovy +pipeline { + // 使用 Docker 镜像,自带 Node 24 + pnpm,不需要 Jenkins 宿主机装 Node + agent { + docker { + image 'node:24-slim' + args '-u root --memory=4g' + } + } + + // 构建参数,支持按环境选择 + parameters { + choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: '部署环境') + string(name: 'API_BASE_URL', defaultValue: 'http://127.0.0.1:48080/admin-api', description: '后端 API 地址') + } + + environment { + // 源码子目录(仓库根目录下的相对路径) + SOURCE_DIR = 'nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben' + // pnpm 缓存目录,挂到宿主机避免重复下载 + PNPM_HOME = '/root/.local/share/pnpm' + } + + stages { + + // ==================== 第一步:拉取代码 ==================== + stage('Checkout') { + steps { + checkout scm + } + } + + // ==================== 第二步:安装依赖 ==================== + stage('Setup') { + steps { + dir(env.SOURCE_DIR) { + sh 'corepack enable' + sh 'corepack prepare pnpm@11.7.0 --activate' + // 安装依赖(锁定版本) + sh 'pnpm install --frozen-lockfile' + } + } + } + + // ==================== 第三步:代码检查(可选) ==================== + stage('Lint') { + steps { + dir(env.SOURCE_DIR) { + sh 'pnpm run lint || true' + } + } + } + + // ==================== 第四步:构建 ==================== + stage('Build') { + steps { + dir(env.SOURCE_DIR) { + script { + // 根据部署环境覆盖 API 地址 + def apiUrl = params.API_BASE_URL + if (params.DEPLOY_ENV == 'prod') { + apiUrl = 'https://api.your-domain.com/admin-api' + } else if (params.DEPLOY_ENV == 'staging') { + apiUrl = 'https://staging-api.your-domain.com/admin-api' + } + + // 写入环境变量(Vite 构建时读取) + writeFile file: 'apps/web-antdv-next/.env.production.local', text: """ + VITE_GLOB_API_URL=${apiUrl} + """.stripIndent().trim() + + // 执行构建 + sh '''#!/bin/bash + export NODE_OPTIONS="--max-old-space-size=8192" + pnpm run build --filter=@vben/web-antdv-next + ''' + } + } + } + } + + // ==================== 第五步:打包产物 ==================== + stage('Archive') { + steps { + dir(env.SOURCE_DIR) { + script { + def distPath = 'apps/web-antdv-next/dist' + sh "tar -czf dist-${params.DEPLOY_ENV}.tar.gz -C ${distPath} ." + archiveArtifacts artifacts: "dist-${params.DEPLOY_ENV}.tar.gz", fingerprint: true + } + } + } + } + + // ==================== 第六步:部署 ==================== + stage('Deploy') { + when { + expression { params.DEPLOY_ENV == 'dev' || params.DEPLOY_ENV == 'staging' || params.DEPLOY_ENV == 'prod' } + } + steps { + dir(env.SOURCE_DIR) { + script { + def serverIp = '' + def deployPath = '/usr/share/nginx/html/admin' + + if (params.DEPLOY_ENV == 'dev') { serverIp = '192.168.1.10' } + else if (params.DEPLOY_ENV == 'staging') { serverIp = '192.168.1.20' } + else if (params.DEPLOY_ENV == 'prod') { serverIp = '192.168.1.30' } + + sshagent(['deploy-ssh-key']) { + sh """ + ssh root@${serverIp} 'mkdir -p ${deployPath}' + scp dist-${params.DEPLOY_ENV}.tar.gz root@${serverIp}:/tmp/ + ssh root@${serverIp} ' + rm -rf ${deployPath}/* + tar -xzf /tmp/dist-${params.DEPLOY_ENV}.tar.gz -C ${deployPath} + rm -f /tmp/dist-${params.DEPLOY_ENV}.tar.gz + nginx -t && nginx -s reload + ' + """ + } + } + } + } + } + } + + post { + success { echo "构建成功!环境:${params.DEPLOY_ENV}" } + failure { echo "构建失败!请检查日志。" } + always { cleanWs() } + } +} +``` + +### 3.2 简化版流水线(仅构建 + 归档,手动部署) + +```groovy +pipeline { + agent { + docker { + image 'node:24-slim' + args '-u root --memory=4g' + } + } + + parameters { + string(name: 'API_BASE_URL', defaultValue: 'http://127.0.0.1:48080/admin-api', description: '后端 API 地址') + string(name: 'VITE_BASE', defaultValue: '/', description: '部署子路径') + } + + environment { + SOURCE_DIR = 'nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben' + } + + stages { + stage('Checkout') { steps { checkout scm } } + + stage('Install & Build') { + steps { + dir(env.SOURCE_DIR) { + sh 'corepack enable && corepack prepare pnpm@11.7.0 --activate' + sh 'pnpm install --frozen-lockfile' + + writeFile file: 'apps/web-antdv-next/.env.production.local', text: """ + VITE_GLOB_API_URL=${params.API_BASE_URL} + VITE_BASE=${params.VITE_BASE} + """.stripIndent().trim() + + sh ''' + export NODE_OPTIONS="--max-old-space-size=8192" + pnpm run build --filter=@vben/web-antdv-next + ''' + } + } + } + + stage('Package') { + steps { + dir("${env.SOURCE_DIR}/apps/web-antdv-next") { + sh 'tar -czf dist.tar.gz -C dist .' + archiveArtifacts artifacts: 'dist.tar.gz', fingerprint: true + } + } + } + } + + post { + always { cleanWs() } + } +} +``` + +--- + +## 四、Nginx 部署配置 + +项目使用 hash 路由,nginx 配置很简单: + +```nginx +server { + listen 80; + server_name admin.your-domain.com; + + # 前端静态文件 + root /usr/share/nginx/html/admin; + index index.html; + + # hash 路由模式,单页应用标准配置 + location / { + try_files $uri $uri/ /index.html; + } + + # API 反向代理到后端网关 + location /admin-api/ { + proxy_pass http://backend-gateway:48080/admin-api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # 静态资源缓存 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} +``` + +--- + +## 五、Docker 部署(可选) + +项目自带 Dockerfile 但默认构建的是 playground 应用,需要调整。推荐新建一个: + +```dockerfile +# Dockerfile 放在 yudao-ui-admin-vben/ 目录下 +# ============ 构建阶段 ============ +FROM node:24-slim AS builder + +RUN npm install -g pnpm@11.7.0 + +WORKDIR /app +COPY pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json .npmrc ./ +COPY internal/ internal/ +COPY packages/ packages/ +COPY apps/web-antdv-next/ apps/web-antdv-next/ + +RUN pnpm install --frozen-lockfile + +ARG VITE_GLOB_API_URL=http://127.0.0.1:48080/admin-api +ENV VITE_GLOB_API_URL=${VITE_GLOB_API_URL} + +RUN NODE_OPTIONS="--max-old-space-size=8192" pnpm run build --filter=@vben/web-antdv-next + +# ============ 运行阶段 ============ +FROM nginx:stable-alpine + +COPY --from=builder /app/apps/web-antdv-next/dist /usr/share/nginx/html + +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` + +构建镜像: + +```bash +docker build \ + --build-arg VITE_GLOB_API_URL=https://api.your-domain.com/admin-api \ + -t nl-admin-ui:latest \ + . +``` + +--- + +## 六、Jenkins 配置清单 + +在 Jenkins 上配置前需要准备: + +| 配置项 | 说明 | +|--------|------| +| **Node.js 插件** | 安装 Jenkins NodeJS Plugin,添加 Node.js 24.x 安装 | +| **SSH 凭据** | 添加目标服务器的 SSH 私钥(ID 为 `deploy-ssh-key`) | +| **Git 仓库** | 确保 Jenkins 能访问代码仓库 | +| **构建机资源** | 内存 >= 4GB(`NODE_OPTIONS=--max-old-space-size=8192`) | +| **网络** | 构建机能访问 `registry.npmmirror.com`(或换内网 npm 镜像) | + +--- + +## 七、常见问题 + +### Q1:构建报 `pnpm: command not found` + +Jenkins 环境中 `corepack enable` 可能不生效,改用 `npm install -g pnpm@11.7.0`。 + +### Q2:构建 OOM(内存溢出) + +调大 Node 内存限制:`NODE_OPTIONS="--max-old-space-size=8192"`,流水线中已默认设置。 + +### Q3:API 地址不对 + +检查 `.env.production.local` 是否在构建前正确写入,且 `VITE_GLOB_API_URL` 为完整 URL(如 `https://api.your-domain.com/admin-api`)。 + +### Q4:页面 404 白屏 + +确认 nginx 配置了 `try_files $uri $uri/ /index.html`,以及 `VITE_BASE` 与部署路径匹配。