|
|
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.ListUtil;
|
|
|
import cn.hutool.core.util.IdUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
import com.fs.company.cache.ICompanyCacheService;
|
|
|
@@ -17,6 +18,7 @@ import com.fs.hisStore.param.FsStoreProductAddEditParam;
|
|
|
import com.fs.hisStore.service.IFsPlatformProductScrmService;
|
|
|
import com.fs.hisStore.utils.StoreAuditLogUtil;
|
|
|
import com.fs.hisStore.vo.FsPlatformProductListVO;
|
|
|
+import com.fs.statis.dto.ProductAuditDTO;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.ObjectUtils;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
@@ -152,8 +154,13 @@ public class FsPlatformProductScrmServiceImpl implements IFsPlatformProductScrmS
|
|
|
copyProductInfo.setProductId(null);
|
|
|
copyProductInfo.setCreateTime(new Date());
|
|
|
copyProductInfo.setUpdateTime(new Date());
|
|
|
- copyProductInfo.setIsAudit("1");
|
|
|
- copyProductInfo.setIsShow(1);
|
|
|
+ // 复制商品统一为草稿,不进待审核;保存后再按审核规则流转
|
|
|
+ copyProductInfo.setIsShow(9);
|
|
|
+ copyProductInfo.setIsAudit(null);
|
|
|
+ if (!isPlatformProductAuditEnabled()) {
|
|
|
+ // 审核开关关闭时保持现网:复制后仍视为已通过,待运营编辑保存后上架
|
|
|
+ copyProductInfo.setIsAudit("1");
|
|
|
+ }
|
|
|
|
|
|
//插入复制商品
|
|
|
copyProductInfo.setProductId(FsProductUtils.createId());
|
|
|
@@ -210,38 +217,21 @@ public class FsPlatformProductScrmServiceImpl implements IFsPlatformProductScrmS
|
|
|
//这里是更新
|
|
|
if(param.getProductId() != null && param.getProductId() > 0){
|
|
|
FsPlatformProductScrm oldFsStoreProduct = fsPlatformProductScrmMapper.selectFsPlatformProductById(product.getProductId());
|
|
|
- Boolean isAudit = configUtil.generateConfigByKey("medicalMall.func.switch").getBoolean("isAudit");
|
|
|
- try {
|
|
|
- if (isAudit != null && isAudit && param.getIsDrug() != 1) {
|
|
|
- if (oldFsStoreProduct.getIsAudit() != null && "1".equals(oldFsStoreProduct.getIsAudit())) {
|
|
|
- Map<String, Object> diff = getDiff(oldFsStoreProduct, product);
|
|
|
- Set<String> diff_columns = diff.keySet();
|
|
|
- JSONArray productColumns = configUtil.generateConfigByKey("medicalMall.func.switch").getJSONArray("productColumns");
|
|
|
- if(com.fs.common.utils.StringUtils.isNotEmpty(productColumns)){
|
|
|
- //判断diff_columns是否在productColumns中,不是则将isAudit设置为0
|
|
|
- for (String column : diff_columns) {
|
|
|
- if (!productColumns.contains(column)) {
|
|
|
- product.setIsAudit("0");
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }else{
|
|
|
- product.setIsAudit("0");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (IllegalAccessException e) {
|
|
|
- log.error("获取diff出错", e);
|
|
|
+ if (oldFsStoreProduct == null) {
|
|
|
+ return R.error("商品数据不存在!");
|
|
|
}
|
|
|
- //只要是编辑,都待审核,通过审核sql 去处理audit和show字段。
|
|
|
- if(oldFsStoreProduct.getIsAudit() != null){
|
|
|
- product.setIsAudit("1");
|
|
|
+ if (isPendingAuditLocked(oldFsStoreProduct)) {
|
|
|
+ return R.error("审核中的商品无法修改");
|
|
|
+ }
|
|
|
+ R auditResult = applyAuditStatusForUpdate(oldFsStoreProduct, product);
|
|
|
+ if (auditResult != null) {
|
|
|
+ return auditResult;
|
|
|
}
|
|
|
fsPlatformProductScrmMapper.updateFsPlatformProduct(product);
|
|
|
} else{
|
|
|
- //总后台商品如果是上架就默认通过审核
|
|
|
- if(product.getIsShow() == 1){
|
|
|
- product.setIsAudit("1");
|
|
|
+ R auditResult = applyAuditStatusForInsert(product);
|
|
|
+ if (auditResult != null) {
|
|
|
+ return auditResult;
|
|
|
}
|
|
|
//复制新的id
|
|
|
product.setProductId(FsProductUtils.createId());
|
|
|
@@ -250,6 +240,9 @@ public class FsPlatformProductScrmServiceImpl implements IFsPlatformProductScrmS
|
|
|
storeAuditLogUtil.addOperLog(product.getProductId());
|
|
|
//处理多规格
|
|
|
handleProductAttributes(param, product, null);
|
|
|
+ if (isPlatformProductAuditEnabled() && "0".equals(product.getIsAudit())) {
|
|
|
+ return R.ok("保存成功,已提交审核");
|
|
|
+ }
|
|
|
return R.ok();
|
|
|
}
|
|
|
|
|
|
@@ -265,6 +258,16 @@ public class FsPlatformProductScrmServiceImpl implements IFsPlatformProductScrmS
|
|
|
if(copyProductInfo==null){
|
|
|
return R.error("复制,商品数据不存在!");
|
|
|
}
|
|
|
+ if (isPlatformProductAuditEnabled()) {
|
|
|
+ if (!"1".equals(copyProductInfo.getIsAudit())
|
|
|
+ || copyProductInfo.getIsShow() == null
|
|
|
+ || copyProductInfo.getIsShow() != 1) {
|
|
|
+ return R.error("总库商品未审核通过或未上线,无法入库");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (copyProductInfo.getIsShow() != null && copyProductInfo.getIsShow() == 9) {
|
|
|
+ return R.error("草稿商品无法入库,请先完善并保存商品");
|
|
|
+ }
|
|
|
//存在,这个商品是否已经在我店铺存在了
|
|
|
boolean existFlag = fsStoreProductMapper.productNameExist(fsStoreProduct.getProductName(), copyProductInfo.getCommonName(),fsStoreProduct.getStoreId());
|
|
|
if(existFlag){
|
|
|
@@ -284,6 +287,8 @@ public class FsPlatformProductScrmServiceImpl implements IFsPlatformProductScrmS
|
|
|
fsStoreProductScrm.setUpdateTime(new Date());
|
|
|
fsStoreProductScrm.setPlatformProductId(platformProductId);
|
|
|
fsStoreProductScrm.setStoreId(fsStoreProduct.getStoreId());
|
|
|
+ // 打上来源标识,方便以后分清哪些是从总库调过来的
|
|
|
+ fsStoreProductScrm.setSourceMark(buildStorageSourceMark(platformProductId));
|
|
|
int insertRowNum = fsStoreProductMapper.insertFsStoreProduct(fsStoreProductScrm);
|
|
|
boolean flag = insertRowNum > 0;
|
|
|
if(flag){
|
|
|
@@ -306,6 +311,13 @@ public class FsPlatformProductScrmServiceImpl implements IFsPlatformProductScrmS
|
|
|
return flag?R.ok():R.error("添加失败!");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 一键入库的来源标识:库调 - 总库商品ID
|
|
|
+ */
|
|
|
+ private String buildStorageSourceMark(Long platformProductId) {
|
|
|
+ return "库调 - " + platformProductId;
|
|
|
+ }
|
|
|
+
|
|
|
private void handleProductAttributes(FsPlatFormProductAddEditParam param, FsPlatformProductScrm product, Long storeId) {
|
|
|
if (param.getSpecType().equals(0)) {
|
|
|
ProductArrtDTO fromatDetailDto = ProductArrtDTO.builder()
|
|
|
@@ -508,6 +520,219 @@ public class FsPlatformProductScrmServiceImpl implements IFsPlatformProductScrmS
|
|
|
return diff;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 总库商品是否开启审核。读失败默认关闭,保证现网行为不变。
|
|
|
+ */
|
|
|
+ private boolean isPlatformProductAuditEnabled() {
|
|
|
+ try {
|
|
|
+ JSONObject switchConfig = configUtil.generateConfigByKey("medicalMall.func.switch");
|
|
|
+ if (switchConfig == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return Boolean.TRUE.equals(switchConfig.getBoolean("isPlatformProductAudit"));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("读取总库审核开关 isPlatformProductAudit 失败,按关闭处理", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 待审核商品锁定:草稿仍可编辑,审核中不可改。
|
|
|
+ */
|
|
|
+ private boolean isPendingAuditLocked(FsPlatformProductScrm product) {
|
|
|
+ if (!isPlatformProductAuditEnabled() || product == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (product.getIsShow() != null && product.getIsShow() == 9) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return "0".equals(product.getIsAudit());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增时的审核状态:开关关闭走现网;打开则保存即待审核,商品上下架以用户选择为准。
|
|
|
+ */
|
|
|
+ private R applyAuditStatusForInsert(FsPlatformProductScrm product) {
|
|
|
+ if (!isPlatformProductAuditEnabled()) {
|
|
|
+ if (product.getIsShow() != null && product.getIsShow() == 1) {
|
|
|
+ product.setIsAudit("1");
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ product.setIsAudit("0");
|
|
|
+ normalizeShowStatus(product);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 编辑时的审核状态。
|
|
|
+ * 上架商品再改、或改了关键字段:进入待审核,但保留用户选择的上下架。
|
|
|
+ */
|
|
|
+ private R applyAuditStatusForUpdate(FsPlatformProductScrm oldProduct, FsPlatformProductScrm product) {
|
|
|
+ if (!isPlatformProductAuditEnabled()) {
|
|
|
+ if (oldProduct.getIsAudit() != null) {
|
|
|
+ product.setIsAudit("1");
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ boolean oldOnline = oldProduct.getIsShow() != null && oldProduct.getIsShow() == 1;
|
|
|
+ boolean passed = "1".equals(oldProduct.getIsAudit());
|
|
|
+ boolean needReAudit = oldOnline || !passed || hasSensitiveChange(oldProduct, product);
|
|
|
+ if (needReAudit) {
|
|
|
+ product.setIsAudit("0");
|
|
|
+ normalizeShowStatus(product);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ product.setIsAudit("1");
|
|
|
+ normalizeShowStatus(product);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保留上架/下架选择,仅把草稿统一成下架。
|
|
|
+ */
|
|
|
+ private void normalizeShowStatus(FsPlatformProductScrm product) {
|
|
|
+ if (product.getIsShow() == null || product.getIsShow() == 9) {
|
|
|
+ product.setIsShow(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是否存在需要重新审核的字段变更。药品与非药品同一套规则。
|
|
|
+ */
|
|
|
+ private boolean hasSensitiveChange(FsPlatformProductScrm oldProduct, FsPlatformProductScrm product) {
|
|
|
+ Set<String> ignoreFields = new HashSet<String>(Arrays.asList(
|
|
|
+ "isAudit", "isShow", "reviewAudit", "serialVersionUID",
|
|
|
+ "updateTime", "createTime", "params", "searchValue",
|
|
|
+ "beginTime", "endTime", "createBy", "updateBy", "remark",
|
|
|
+ "productId", "storeId", "storeProductId", "browse", "sales"
|
|
|
+ ));
|
|
|
+ Set<String> exemptFields = resolvePlatformExemptAuditFields();
|
|
|
+ try {
|
|
|
+ Field[] fields = oldProduct.getClass().getDeclaredFields();
|
|
|
+ for (int i = 0; i < fields.length; i++) {
|
|
|
+ Field field = fields[i];
|
|
|
+ String column = field.getName();
|
|
|
+ if (ignoreFields.contains(column) || exemptFields.contains(column)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ field.setAccessible(true);
|
|
|
+ Object oldVal = field.get(oldProduct);
|
|
|
+ Object newVal = field.get(product);
|
|
|
+ if (isPlatformAuditValueEqual(oldVal, newVal)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ log.info("总库商品触发重新审核, productId={}, field={}", oldProduct.getProductId(), column);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
+ log.error("获取总库商品diff出错", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isPlatformAuditValueEqual(Object oldVal, Object newVal) {
|
|
|
+ if (Objects.equals(oldVal, newVal)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (newVal == null) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (isBlankAuditValue(oldVal) && isBlankAuditValue(newVal)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (oldVal instanceof Number || newVal instanceof Number || oldVal instanceof BigDecimal || newVal instanceof BigDecimal) {
|
|
|
+ try {
|
|
|
+ BigDecimal oldBd = oldVal == null ? null : new BigDecimal(String.valueOf(oldVal));
|
|
|
+ BigDecimal newBd = new BigDecimal(String.valueOf(newVal));
|
|
|
+ if (oldBd != null) {
|
|
|
+ return oldBd.compareTo(newBd) == 0;
|
|
|
+ }
|
|
|
+ } catch (Exception ignore) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isBlankAuditValue(Object val) {
|
|
|
+ return val == null || (val instanceof String && StringUtils.isBlank((String) val));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Set<String> resolvePlatformExemptAuditFields() {
|
|
|
+ Set<String> exempt = new HashSet<String>();
|
|
|
+ try {
|
|
|
+ JSONObject switchConfig = configUtil.generateConfigByKey("medicalMall.func.switch");
|
|
|
+ if (switchConfig == null) {
|
|
|
+ return exempt;
|
|
|
+ }
|
|
|
+ JSONArray columns = switchConfig.getJSONArray("platformProductColumns");
|
|
|
+ if (columns == null || columns.isEmpty()) {
|
|
|
+ return exempt;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < columns.size(); i++) {
|
|
|
+ String column = columns.getString(i);
|
|
|
+ if (com.fs.common.utils.StringUtils.isNotEmpty(column)) {
|
|
|
+ exempt.add(column.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("读取 platformProductColumns 失败", e);
|
|
|
+ }
|
|
|
+ return exempt;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R submitAudit(ProductAuditDTO auditDTO) {
|
|
|
+ if (!isPlatformProductAuditEnabled()) {
|
|
|
+ return R.error("未开启总商品库审核");
|
|
|
+ }
|
|
|
+ List<Long> productIds = auditDTO.getProductIds();
|
|
|
+ if (productIds == null || productIds.isEmpty()) {
|
|
|
+ return R.error("请选择商品!");
|
|
|
+ }
|
|
|
+ int submittable = fsPlatformProductScrmMapper.countSubmittable(productIds);
|
|
|
+ if (submittable != productIds.size()) {
|
|
|
+ return R.error("仅未提交或已退回的商品可以提交审核");
|
|
|
+ }
|
|
|
+ int rows = fsPlatformProductScrmMapper.submitAudit(productIds);
|
|
|
+ if (rows < 1) {
|
|
|
+ return R.error("提交审核失败");
|
|
|
+ }
|
|
|
+ storeAuditLogUtil.addBatchAuditList(productIds, "提交审核", null);
|
|
|
+ return R.ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R batchAudit(ProductAuditDTO auditDTO) {
|
|
|
+ if (!isPlatformProductAuditEnabled()) {
|
|
|
+ return R.error("未开启总商品库审核");
|
|
|
+ }
|
|
|
+ List<Long> productIds = auditDTO.getProductIds();
|
|
|
+ Integer isAudit = auditDTO.getIsAudit();
|
|
|
+ if (productIds == null || productIds.isEmpty()) {
|
|
|
+ return R.error("请选择商品!");
|
|
|
+ }
|
|
|
+ if (isAudit == null || (isAudit != 1 && isAudit != 2)) {
|
|
|
+ return R.error("通过或退回不能为空!");
|
|
|
+ }
|
|
|
+ if (isAudit == 2 && StringUtils.isBlank(auditDTO.getReason())) {
|
|
|
+ return R.error("退回必须填写理由");
|
|
|
+ }
|
|
|
+ int pendingCount = fsPlatformProductScrmMapper.countByProductIdsAndAudit(productIds, "0");
|
|
|
+ if (pendingCount != productIds.size()) {
|
|
|
+ return R.error("仅允许审核待审核商品,请重新选择");
|
|
|
+ }
|
|
|
+ int rows = fsPlatformProductScrmMapper.batchAudit(auditDTO);
|
|
|
+ if (rows < 1) {
|
|
|
+ return R.error("审核失败");
|
|
|
+ }
|
|
|
+ storeAuditLogUtil.addBatchAuditList(productIds, auditDTO.getReason(), auditDTO.getAttachImage());
|
|
|
+ return R.ok();
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|