增强客户编码管理功能,新增客户编码唯一性校验,确保同租户内客户编码不重复。更新相关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.aspect.annotation.AutoLog;
|
||||||
import org.jeecg.common.system.base.controller.JeecgController;
|
import org.jeecg.common.system.base.controller.JeecgController;
|
||||||
import org.jeecg.common.system.query.QueryGenerator;
|
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.constant.MesXslCustomerBizStatus;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslCustomer;
|
import org.jeecg.modules.xslmes.entity.MesXslCustomer;
|
||||||
import org.jeecg.modules.xslmes.service.IMesXslCustomerService;
|
import org.jeecg.modules.xslmes.service.IMesXslCustomerService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
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.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.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MES 客户管理
|
* MES 客户管理
|
||||||
@@ -53,6 +63,14 @@ public class MesXslCustomerController extends JeecgController<MesXslCustomer, IM
|
|||||||
@RequiresPermissions("xslmes:mes_xsl_customer:add")
|
@RequiresPermissions("xslmes:mes_xsl_customer:add")
|
||||||
@PostMapping(value = "/add")
|
@PostMapping(value = "/add")
|
||||||
public Result<String> add(@RequestBody MesXslCustomer mesXslCustomer) {
|
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 对齐
|
// 新增默认启用(0);空白或未传时写入启用,并与 iz_enable 对齐
|
||||||
String st = mesXslCustomer.getStatus();
|
String st = mesXslCustomer.getStatus();
|
||||||
if (st != null) {
|
if (st != null) {
|
||||||
@@ -72,6 +90,17 @@ public class MesXslCustomerController extends JeecgController<MesXslCustomer, IM
|
|||||||
@RequiresPermissions("xslmes:mes_xsl_customer:edit")
|
@RequiresPermissions("xslmes:mes_xsl_customer:edit")
|
||||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||||
public Result<String> edit(@RequestBody MesXslCustomer mesXslCustomer) {
|
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) {
|
if (mesXslCustomer.getStatus() != null) {
|
||||||
String s = mesXslCustomer.getStatus().trim();
|
String s = mesXslCustomer.getStatus().trim();
|
||||||
mesXslCustomer.setStatus(s.isEmpty() ? null : s);
|
mesXslCustomer.setStatus(s.isEmpty() ? null : s);
|
||||||
@@ -130,6 +159,20 @@ public class MesXslCustomerController extends JeecgController<MesXslCustomer, IM
|
|||||||
return Result.OK("批量删除成功!");
|
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查询")
|
@Operation(summary = "MES客户管理-通过id查询")
|
||||||
@GetMapping(value = "/queryById")
|
@GetMapping(value = "/queryById")
|
||||||
public Result<MesXslCustomer> queryById(@RequestParam(name = "id", required = true) String id) {
|
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")
|
@RequiresPermissions("xslmes:mes_xsl_customer:importExcel")
|
||||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
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;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Excel(name = "客户编码", width = 20)
|
@Excel(name = "客户编码", width = 20)
|
||||||
@Schema(description = "客户编码")
|
@Schema(description = "客户编码(同租户内唯一,不可重复)")
|
||||||
private String customerCode;
|
private String customerCode;
|
||||||
|
|
||||||
@Excel(name = "客户名称", width = 28)
|
@Excel(name = "客户名称", width = 28)
|
||||||
|
|||||||
@@ -12,4 +12,9 @@ public interface IMesXslCustomerService extends IService<MesXslCustomer> {
|
|||||||
* 按业务状态同步 izEnable:停用(字典值 1)为 0,其余为 1。删除仅 del_flag,与 status 无关。
|
* 按业务状态同步 izEnable:停用(字典值 1)为 0,其余为 1。删除仅 del_flag,与 status 无关。
|
||||||
*/
|
*/
|
||||||
void syncIzEnableWithStatus(MesXslCustomer entity);
|
void syncIzEnableWithStatus(MesXslCustomer entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同租户下是否已存在相同客户编码(编辑时传 excludeId 排除自身;依赖多租户 SQL 只查当前租户)
|
||||||
|
*/
|
||||||
|
boolean existsSameCustomerCode(String customerCode, String excludeId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.jeecg.modules.xslmes.service.impl;
|
package org.jeecg.modules.xslmes.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
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.constant.MesXslCustomerBizStatus;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslCustomer;
|
import org.jeecg.modules.xslmes.entity.MesXslCustomer;
|
||||||
import org.jeecg.modules.xslmes.mapper.MesXslCustomerMapper;
|
import org.jeecg.modules.xslmes.mapper.MesXslCustomerMapper;
|
||||||
@@ -29,4 +30,16 @@ public class MesXslCustomerServiceImpl extends ServiceImpl<MesXslCustomerMapper,
|
|||||||
entity.setIzEnable(1);
|
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 {
|
enum Api {
|
||||||
list = '/xslmes/mesXslCustomer/list',
|
list = '/xslmes/mesXslCustomer/list',
|
||||||
|
checkCustomerCode = '/xslmes/mesXslCustomer/checkCustomerCode',
|
||||||
queryById = '/xslmes/mesXslCustomer/queryById',
|
queryById = '/xslmes/mesXslCustomer/queryById',
|
||||||
save = '/xslmes/mesXslCustomer/add',
|
save = '/xslmes/mesXslCustomer/add',
|
||||||
edit = '/xslmes/mesXslCustomer/edit',
|
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 });
|
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) => {
|
export const deleteOne = (params, handleSuccess) => {
|
||||||
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
|
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
|
||||||
handleSuccess();
|
handleSuccess();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||||
|
import { checkCustomerCode } from './MesXslCustomer.api';
|
||||||
|
|
||||||
export const columns: BasicColumn[] = [
|
export const columns: BasicColumn[] = [
|
||||||
{ title: 'ID', align: 'center', dataIndex: 'id', width: 280, ellipsis: true, defaultHidden: true },
|
{ title: 'ID', align: 'center', dataIndex: 'id', width: 280, ellipsis: true, defaultHidden: true },
|
||||||
@@ -48,7 +49,26 @@ export const formSchema: FormSchema[] = [
|
|||||||
field: 'customerCode',
|
field: 'customerCode',
|
||||||
required: true,
|
required: true,
|
||||||
component: 'Input',
|
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: '客户名称',
|
label: '客户名称',
|
||||||
|
|||||||
Reference in New Issue
Block a user