增强客户编码管理功能,新增客户编码唯一性校验,确保同租户内客户编码不重复。更新相关API和前端表单验证逻辑,优化Excel导入功能以处理客户编码的有效性和唯一性。添加数据库约束以维护数据完整性。
This commit is contained in:
@@ -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<MesXslCustomer, IM
|
||||
@RequiresPermissions("xslmes:mes_xsl_customer:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> 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<MesXslCustomer, IM
|
||||
@RequiresPermissions("xslmes:mes_xsl_customer:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||
public Result<String> 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<MesXslCustomer, IM
|
||||
return Result.OK("批量删除成功!");
|
||||
}
|
||||
|
||||
@Operation(summary = "校验客户编码是否重复(同租户,dataId 为编辑时当前主键)")
|
||||
@GetMapping(value = "/checkCustomerCode")
|
||||
public Result<String> 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<MesXslCustomer> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||
@@ -149,6 +192,75 @@ public class MesXslCustomerController extends JeecgController<MesXslCustomer, IM
|
||||
@RequiresPermissions("xslmes:mes_xsl_customer:importExcel")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
return super.importExcel(request, response, MesXslCustomer.class);
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
|
||||
for (Map.Entry<String, MultipartFile> ent : fileMap.entrySet()) {
|
||||
MultipartFile file = ent.getValue();
|
||||
ImportParams params = new ImportParams();
|
||||
params.setTitleRows(2);
|
||||
params.setHeadRows(1);
|
||||
params.setNeedSave(true);
|
||||
try {
|
||||
List<MesXslCustomer> list = ExcelImportUtil.importExcel(file.getInputStream(), MesXslCustomer.class, params);
|
||||
if (list == null) {
|
||||
list = List.of();
|
||||
}
|
||||
// 1)客户编码非空、2)导入文件内不重复、3)与库中同租户数据不重复
|
||||
Set<String> 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("文件导入失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -12,4 +12,9 @@ public interface IMesXslCustomerService extends IService<MesXslCustomer> {
|
||||
* 按业务状态同步 izEnable:停用(字典值 1)为 0,其余为 1。删除仅 del_flag,与 status 无关。
|
||||
*/
|
||||
void syncIzEnableWithStatus(MesXslCustomer entity);
|
||||
|
||||
/**
|
||||
* 同租户下是否已存在相同客户编码(编辑时传 excludeId 排除自身;依赖多租户 SQL 只查当前租户)
|
||||
*/
|
||||
boolean existsSameCustomerCode(String customerCode, String excludeId);
|
||||
}
|
||||
|
||||
@@ -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<MesXslCustomerMapper,
|
||||
entity.setIzEnable(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsSameCustomerCode(String customerCode, String excludeId) {
|
||||
if (StringUtils.isBlank(customerCode)) {
|
||||
return false;
|
||||
}
|
||||
String code = customerCode.trim();
|
||||
return this.lambdaQuery()
|
||||
.eq(MesXslCustomer::getCustomerCode, code)
|
||||
.ne(StringUtils.isNotBlank(excludeId), MesXslCustomer::getId, excludeId)
|
||||
.count() > 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`);
|
||||
@@ -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();
|
||||
|
||||
@@ -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: '客户名称',
|
||||
|
||||
Reference in New Issue
Block a user