diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslCustomerController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslCustomerController.java index afc3520..2e7d0a4 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslCustomerController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslCustomerController.java @@ -13,15 +13,25 @@ import org.jeecg.common.api.vo.Result; import org.jeecg.common.aspect.annotation.AutoLog; import org.jeecg.common.system.base.controller.JeecgController; import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.util.oConvertUtils; import org.jeecg.modules.xslmes.constant.MesXslCustomerBizStatus; import org.jeecg.modules.xslmes.entity.MesXslCustomer; import org.jeecg.modules.xslmes.service.IMesXslCustomerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.servlet.ModelAndView; +import org.jeecgframework.poi.excel.ExcelImportUtil; +import org.jeecgframework.poi.excel.entity.ImportParams; +import java.io.IOException; import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; /** * MES 客户管理 @@ -53,6 +63,14 @@ public class MesXslCustomerController extends JeecgController add(@RequestBody MesXslCustomer mesXslCustomer) { + if (oConvertUtils.isEmpty(mesXslCustomer.getCustomerCode())) { + return Result.error("客户编码不能为空"); + } + String customerCode = mesXslCustomer.getCustomerCode().trim(); + mesXslCustomer.setCustomerCode(customerCode); + if (mesXslCustomerService.existsSameCustomerCode(customerCode, null)) { + return Result.error("客户编码已存在,不允许重复"); + } // 新增默认启用(0);空白或未传时写入启用,并与 iz_enable 对齐 String st = mesXslCustomer.getStatus(); if (st != null) { @@ -72,6 +90,17 @@ public class MesXslCustomerController extends JeecgController edit(@RequestBody MesXslCustomer mesXslCustomer) { + if (oConvertUtils.isEmpty(mesXslCustomer.getId())) { + return Result.error("主键不能为空"); + } + if (oConvertUtils.isEmpty(mesXslCustomer.getCustomerCode())) { + return Result.error("客户编码不能为空"); + } + String customerCode = mesXslCustomer.getCustomerCode().trim(); + mesXslCustomer.setCustomerCode(customerCode); + if (mesXslCustomerService.existsSameCustomerCode(customerCode, mesXslCustomer.getId())) { + return Result.error("客户编码已存在,不允许重复"); + } if (mesXslCustomer.getStatus() != null) { String s = mesXslCustomer.getStatus().trim(); mesXslCustomer.setStatus(s.isEmpty() ? null : s); @@ -130,6 +159,20 @@ public class MesXslCustomerController extends JeecgController checkCustomerCode( + @RequestParam(name = "customerCode", required = true) String customerCode, + @RequestParam(name = "dataId", required = false) String dataId) { + if (oConvertUtils.isEmpty(customerCode) || customerCode.trim().isEmpty()) { + return Result.OK("该值可用!"); + } + if (mesXslCustomerService.existsSameCustomerCode(customerCode, dataId)) { + return Result.error("该客户编码已存在"); + } + return Result.OK("该值可用!"); + } + @Operation(summary = "MES客户管理-通过id查询") @GetMapping(value = "/queryById") public Result queryById(@RequestParam(name = "id", required = true) String id) { @@ -149,6 +192,75 @@ public class MesXslCustomerController extends JeecgController importExcel(HttpServletRequest request, HttpServletResponse response) { - return super.importExcel(request, response, MesXslCustomer.class); + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + Map fileMap = multipartRequest.getFileMap(); + for (Map.Entry ent : fileMap.entrySet()) { + MultipartFile file = ent.getValue(); + ImportParams params = new ImportParams(); + params.setTitleRows(2); + params.setHeadRows(1); + params.setNeedSave(true); + try { + List list = ExcelImportUtil.importExcel(file.getInputStream(), MesXslCustomer.class, params); + if (list == null) { + list = List.of(); + } + // 1)客户编码非空、2)导入文件内不重复、3)与库中同租户数据不重复 + Set codesInFile = new HashSet<>(); + for (int i = 0; i < list.size(); i++) { + MesXslCustomer row = list.get(i); + int rowNo = i + 1; + if (row == null) { + return Result.error("文件导入失败:第 " + rowNo + " 条数据无效(空行)"); + } + String code = row.getCustomerCode(); + if (code != null) { + code = code.trim(); + } + if (oConvertUtils.isEmpty(code)) { + return Result.error("文件导入失败:第 " + rowNo + " 条客户编码不能为空"); + } + row.setCustomerCode(code); + if (!codesInFile.add(code)) { + return Result.error("文件导入失败:客户编码【" + code + "】在导入文件中重复"); + } + if (mesXslCustomerService.existsSameCustomerCode(code, null)) { + return Result.error("文件导入失败:第 " + rowNo + " 条客户编码【" + code + "】已存在,不允许重复"); + } + } + for (MesXslCustomer row : list) { + if (row == null) { + continue; + } + String st = row.getStatus(); + if (st != null) { + st = st.trim(); + row.setStatus(st.isEmpty() ? null : st); + } + if (row.getStatus() == null || row.getStatus().isEmpty()) { + row.setStatus(MesXslCustomerBizStatus.ENABLED); + } + mesXslCustomerService.syncIzEnableWithStatus(row); + } + long start = System.currentTimeMillis(); + mesXslCustomerService.saveBatch(list); + log.info("客户Excel导入完成,耗时" + (System.currentTimeMillis() - start) + "ms,行数=" + list.size()); + return Result.ok("文件导入成功!数据行数:" + list.size()); + } catch (Exception e) { + String msg = e.getMessage(); + log.error(msg, e); + if (msg != null && msg.indexOf("Duplicate entry") >= 0) { + return Result.error("文件导入失败: 存在重复数据(客户编码在系统中需唯一)"); + } + return Result.error("文件导入失败:" + e.getMessage()); + } finally { + try { + file.getInputStream().close(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + return Result.error("文件导入失败!"); } } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslCustomer.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslCustomer.java index a698b00..0a63b04 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslCustomer.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslCustomer.java @@ -27,7 +27,7 @@ public class MesXslCustomer extends JeecgEntity implements Serializable { private static final long serialVersionUID = 1L; @Excel(name = "客户编码", width = 20) - @Schema(description = "客户编码") + @Schema(description = "客户编码(同租户内唯一,不可重复)") private String customerCode; @Excel(name = "客户名称", width = 28) diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslCustomerService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslCustomerService.java index ee17e54..e46e16e 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslCustomerService.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslCustomerService.java @@ -12,4 +12,9 @@ public interface IMesXslCustomerService extends IService { * 按业务状态同步 izEnable:停用(字典值 1)为 0,其余为 1。删除仅 del_flag,与 status 无关。 */ void syncIzEnableWithStatus(MesXslCustomer entity); + + /** + * 同租户下是否已存在相同客户编码(编辑时传 excludeId 排除自身;依赖多租户 SQL 只查当前租户) + */ + boolean existsSameCustomerCode(String customerCode, String excludeId); } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslCustomerServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslCustomerServiceImpl.java index 9462b76..b34c881 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslCustomerServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslCustomerServiceImpl.java @@ -1,6 +1,7 @@ package org.jeecg.modules.xslmes.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.lang3.StringUtils; import org.jeecg.modules.xslmes.constant.MesXslCustomerBizStatus; import org.jeecg.modules.xslmes.entity.MesXslCustomer; import org.jeecg.modules.xslmes.mapper.MesXslCustomerMapper; @@ -29,4 +30,16 @@ public class MesXslCustomerServiceImpl extends ServiceImpl 0L; + } } diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_30__mes_xsl_customer_code_unique_per_tenant.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_30__mes_xsl_customer_code_unique_per_tenant.sql new file mode 100644 index 0000000..4e1eab3 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_30__mes_xsl_customer_code_unique_per_tenant.sql @@ -0,0 +1,4 @@ +-- 客户编码在同租户内唯一(tenant_id 与多租户拦截一致;NULL 已归 0 见 V3.9.2_20) +-- 若执行失败,请先排查:SELECT tenant_id, customer_code, COUNT(*) c FROM mes_xsl_customer GROUP BY tenant_id, customer_code HAVING c > 1; +ALTER TABLE `mes_xsl_customer` + ADD UNIQUE KEY `uk_mes_xsl_customer_tenant_code` (`tenant_id`, `customer_code`); diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.api.ts b/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.api.ts index 32b0e48..82113bb 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.api.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.api.ts @@ -5,6 +5,7 @@ const { createConfirm } = useMessage(); enum Api { list = '/xslmes/mesXslCustomer/list', + checkCustomerCode = '/xslmes/mesXslCustomer/checkCustomerCode', queryById = '/xslmes/mesXslCustomer/queryById', save = '/xslmes/mesXslCustomer/add', edit = '/xslmes/mesXslCustomer/edit', @@ -22,6 +23,10 @@ export const list = (params) => defHttp.get({ url: Api.list, params }); export const queryById = (params: { id: string }) => defHttp.get({ url: Api.queryById, params }); +/** 客户编码唯一性校验(同租户;编辑传 dataId) */ +export const checkCustomerCode = (params: { customerCode: string; dataId?: string }) => + defHttp.get({ url: Api.checkCustomerCode, params }); + export const deleteOne = (params, handleSuccess) => { return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => { handleSuccess(); diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.data.ts b/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.data.ts index 6e011e3..c7ed236 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.data.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslCustomer/MesXslCustomer.data.ts @@ -1,4 +1,5 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; +import { checkCustomerCode } from './MesXslCustomer.api'; export const columns: BasicColumn[] = [ { title: 'ID', align: 'center', dataIndex: 'id', width: 280, ellipsis: true, defaultHidden: true }, @@ -48,7 +49,26 @@ export const formSchema: FormSchema[] = [ field: 'customerCode', required: true, component: 'Input', - componentProps: { placeholder: '请输入客户编码' }, + componentProps: { placeholder: '同租户内唯一,不可与已有客户重复' }, + dynamicRules: ({ model }) => [ + { required: true, message: '请输入客户编码' }, + { + validator: async (_rule, value) => { + const v = value == null ? '' : String(value).trim(); + if (!v) { + return Promise.resolve(); + } + try { + await checkCustomerCode({ customerCode: v, dataId: model?.id }); + return Promise.resolve(); + } catch (e: any) { + const msg = e?.response?.data?.message || e?.message || '该客户编码已存在'; + return Promise.reject(msg); + } + }, + trigger: 'blur', + }, + ], }, { label: '客户名称',