diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/customer/customerinfo/BmsCustomerController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/customer/customerinfo/BmsCustomerController.java index 472d54a3..58c31a03 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/customer/customerinfo/BmsCustomerController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/customer/customerinfo/BmsCustomerController.java @@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.bms.common.annotation.Log; import com.bms.common.core.controller.BaseController; @@ -73,6 +74,16 @@ public class BmsCustomerController extends BaseController return success(bmsCustomerService.selectBmsCustomerByCustomerId(customerId)); } + /** + * 查询客户业务操作日志(新建/修改/删除及字段级变更明细) + */ + @PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:query')") + @GetMapping("/log/list") + public AjaxResult logList(@RequestParam("customerId") Long customerId) + { + return success(bmsCustomerService.selectCustomerLogList(customerId)); + } + /** * 新增客户信息 */ diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillDetail.java b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillDetail.java index 16b85456..f16c5606 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillDetail.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/domain/ArBillDetail.java @@ -34,6 +34,10 @@ public class ArBillDetail implements Serializable { private BigDecimal actualWeight; private BigDecimal volumeWeight; private BigDecimal chargeWeight; + /** 订单计费时命中的运费单价快照,对应 prc_order_bill.freight_rate。 */ + private BigDecimal freightRate; + /** 订单计费时的标准处理费快照,对应 prc_order_bill.registration_fee。 */ + private BigDecimal registrationFee; private BigDecimal pkgLength; private BigDecimal pkgWidth; private BigDecimal pkgHeight; diff --git a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java index 63fe9b5a..889ce3bd 100644 --- a/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/ar/service/impl/ArBillServiceImpl.java @@ -1142,6 +1142,8 @@ public class ArBillServiceImpl implements IArBillService { detail.setActualWeight(source.getActualWeight()); detail.setVolumeWeight(source.getVolumeWeight()); detail.setChargeWeight(source.getChargeWeight() == null ? source.getWeight() : source.getChargeWeight()); + detail.setFreightRate(source.getFreightRate()); + detail.setRegistrationFee(source.getRegistrationFee()); detail.setPkgLength(source.getPkgLength()); detail.setPkgWidth(source.getPkgWidth()); detail.setPkgHeight(source.getPkgHeight()); @@ -1614,42 +1616,11 @@ public class ArBillServiceImpl implements IArBillService { } private BigDecimal exportUnitPrice(ArBillDetail detail) { - BigDecimal direct = firstFeeUnitPrice(detail.getFees()); - if (direct != null) { - return direct; - } - BigDecimal freight = feeAmount(detail, "BASIC_FREIGHT"); - if (freight == null || detail.getChargeWeight() == null || detail.getChargeWeight().compareTo(BigDecimal.ZERO) <= 0) { - return null; - } - return freight.divide(detail.getChargeWeight(), 4, java.math.RoundingMode.HALF_UP); - } - - private BigDecimal firstFeeUnitPrice(List fees) { - if (fees == null) { - return null; - } - for (ArBillDetailFee fee : fees) { - if (fee.getUnitPrice() != null) { - return fee.getUnitPrice(); - } - } - return null; + return detail == null ? null : detail.getFreightRate(); } private BigDecimal exportProcessingFee(ArBillDetail detail) { - BigDecimal total = BigDecimal.ZERO; - boolean found = false; - if (detail.getFees() != null) { - for (ArBillDetailFee fee : detail.getFees()) { - String name = fee.getFeeName() == null ? "" : fee.getFeeName(); - if ("REGISTRATION_FEE".equalsIgnoreCase(fee.getFeeType()) || name.contains("处理费")) { - total = total.add(nvl(fee.getAmount())); - found = true; - } - } - } - return found ? total : null; + return detail == null ? null : detail.getRegistrationFee(); } private String exportFormula(ArBillDetail detail) { diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/domain/BmsCustomerLog.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/domain/BmsCustomerLog.java new file mode 100644 index 00000000..a1d80c0a --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/domain/BmsCustomerLog.java @@ -0,0 +1,80 @@ +package com.bms.customer.customerinfo.domain; + +import java.util.List; + +import lombok.Data; +import com.bms.common.core.domain.BaseEntity; + +/** + * 客户业务操作日志对象 ctr_customer_log + * + * @author bms + * @date 2026-09-30 + */ +@Data +public class BmsCustomerLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 操作类型:新建 */ + public static final String OP_INSERT = "1"; + /** 操作类型:修改 */ + public static final String OP_UPDATE = "2"; + /** 操作类型:删除 */ + public static final String OP_DELETE = "3"; + + /** 日志ID */ + private Long logId; + + /** 客户ID */ + private Long customerId; + + /** 客户编码(操作时快照) */ + private String customerCode; + + /** 客户名称(操作时快照) */ + private String customerName; + + /** 操作类型(1新建 2修改 3删除) */ + private String operationType; + + /** 变更明细JSON(落库内容):[{field,fieldLabel,oldValue,newValue}] */ + private String changeContent; + + /** 操作人姓名 */ + private String createByName; + + /** 变更明细(由 changeContent 解析,供前端直接展示) */ + private List changes; + + /** + * 单个字段的变更明细 + */ + @Data + public static class FieldChange + { + /** 字段名 */ + private String field; + + /** 字段中文名 */ + private String fieldLabel; + + /** 修改前的值 */ + private String oldValue; + + /** 修改后的值 */ + private String newValue; + + public FieldChange() + { + } + + public FieldChange(String field, String fieldLabel, String oldValue, String newValue) + { + this.field = field; + this.fieldLabel = fieldLabel; + this.oldValue = oldValue; + this.newValue = newValue; + } + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/mapper/BmsCustomerLogMapper.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/mapper/BmsCustomerLogMapper.java new file mode 100644 index 00000000..74caffcd --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/mapper/BmsCustomerLogMapper.java @@ -0,0 +1,30 @@ +package com.bms.customer.customerinfo.mapper; + +import java.util.List; + +import com.bms.customer.customerinfo.domain.BmsCustomerLog; + +/** + * 客户业务操作日志Mapper接口 + * + * @author bms + * @date 2026-09-30 + */ +public interface BmsCustomerLogMapper +{ + /** + * 新增客户操作日志 + * + * @param customerLog 日志 + * @return 结果 + */ + public int insertBmsCustomerLog(BmsCustomerLog customerLog); + + /** + * 按客户查询全部业务日志(时间倒序) + * + * @param customerId 客户ID + * @return 日志列表 + */ + public List selectBmsCustomerLogList(Long customerId); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/IBmsCustomerLogService.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/IBmsCustomerLogService.java new file mode 100644 index 00000000..ac985d48 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/IBmsCustomerLogService.java @@ -0,0 +1,35 @@ +package com.bms.customer.customerinfo.service; + +import java.util.List; + +import com.bms.customer.customerinfo.domain.BmsCustomer; +import com.bms.customer.customerinfo.domain.BmsCustomerLog; + +/** + * 客户业务操作日志Service接口 + * + * @author bms + * @date 2026-09-30 + */ +public interface IBmsCustomerLogService +{ + /** + * 记录新建客户日志(含建档时的字段初始值) + */ + void recordInsert(BmsCustomer customer); + + /** + * 记录修改客户日志(字段级 修改前→修改后,无实际变更时不记录) + */ + void recordUpdate(BmsCustomer oldCustomer, BmsCustomer newCustomer); + + /** + * 记录删除客户日志 + */ + void recordDelete(BmsCustomer customer); + + /** + * 查询某客户的全部业务日志(时间倒序,含已解析的变更明细) + */ + List selectByCustomerId(Long customerId); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/IBmsCustomerService.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/IBmsCustomerService.java index 0c0fc716..b6476ae3 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/IBmsCustomerService.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/IBmsCustomerService.java @@ -5,6 +5,7 @@ import com.bms.customer.customerinfo.domain.BmsCustomer; import com.bms.customer.customerinfo.domain.BmsCustomerAttachment; import com.bms.customer.customerinfo.domain.BmsCustomerCompany; import com.bms.customer.customerinfo.domain.BmsCustomerContact; +import com.bms.customer.customerinfo.domain.BmsCustomerLog; /** * 客户主Service接口 @@ -22,6 +23,14 @@ public interface IBmsCustomerService */ public BmsCustomer selectBmsCustomerByCustomerId(Long customerId); + /** + * 查询某客户的全部业务操作日志(时间倒序) + * + * @param customerId 客户ID + * @return 业务日志列表 + */ + public List selectCustomerLogList(Long customerId); + /** * 查询客户主列表 * diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerLogServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerLogServiceImpl.java new file mode 100644 index 00000000..65799ca5 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerLogServiceImpl.java @@ -0,0 +1,263 @@ +package com.bms.customer.customerinfo.service.impl; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +import com.alibaba.fastjson2.JSON; +import com.bms.common.core.domain.entity.SysUser; +import com.bms.common.utils.DateUtils; +import com.bms.common.utils.SecurityUtils; +import com.bms.customer.customerinfo.domain.BmsCustomer; +import com.bms.customer.customerinfo.domain.BmsCustomerLog; +import com.bms.customer.customerinfo.mapper.BmsCustomerLogMapper; +import com.bms.customer.customerinfo.service.IBmsCustomerLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 客户业务操作日志Service实现 + * + * @author bms + * @date 2026-09-30 + */ +@Service +public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService +{ + @Autowired + private BmsCustomerLogMapper bmsCustomerLogMapper; + + /** 业务类型字典 */ + private static final Map CUSTOMER_TYPE_MAP = Map.of( + "0", "直客", "1", "同行", "2", "集运"); + + /** 启用状态字典 */ + private static final Map ENABLE_MAP = Map.of("0", "正常", "1", "停用"); + + /** 结算方式字典 */ + private static final Map SETTLEMENT_TYPE_MAP = Map.of( + "0", "月结", "1", "周结", "2", "日结"); + + /** + * 需要记录日志的主表字段(顺序即展示顺序)。 + * requireNonBlank 为 true 的字段对应 update SQL 的 <if test="x!=null and x!=''">, + * 其余动态字段仅在新值非 null 时落库;业务员/结算员/客服为无条件写入。 + */ + private static final List FIELD_DEFS = List.of( + new FieldDef("customerCode", "客户编码", BmsCustomer::getCustomerCode, true, Function.identity()), + new FieldDef("customerName", "客户名称", BmsCustomer::getCustomerName, true, Function.identity()), + new FieldDef("customerCodeDw", "对外代码", BmsCustomer::getCustomerCodeDw, false, Function.identity()), + new FieldDef("creditCode", "统一社会信用代码", BmsCustomer::getCreditCode, false, Function.identity()), + new FieldDef("customerType", "业务类型", BmsCustomer::getCustomerType, false, CUSTOMER_TYPE_MAP::get), + new FieldDef("isEnable", "启用状态", BmsCustomer::getIsEnable, false, ENABLE_MAP::get), + new FieldDef("salesUserId", "业务员", c -> userDisplay(c.getSalesUserId(), c.getSalesUser()), false, Function.identity()), + new FieldDef("ed", "资金额度(元)", BmsCustomer::getEd, false, BmsCustomerLogServiceImpl::numText), + new FieldDef("zq", "回款账期(天)", BmsCustomer::getZq, false, BmsCustomerLogServiceImpl::numText), + new FieldDef("settlementUserId", "结算员", c -> userDisplay(c.getSettlementUserId(), c.getSettlementUser()), false, Function.identity()), + new FieldDef("serviceUserId", "客服", c -> userDisplay(c.getServiceUserId(), c.getServiceUser()), false, Function.identity()), + new FieldDef("settlementType", "结算方式", BmsCustomer::getSettlementType, false, SETTLEMENT_TYPE_MAP::get), + new FieldDef("accountBalance", "账户余额(元)", BmsCustomer::getAccountBalance, false, BmsCustomerLogServiceImpl::numText), + new FieldDef("foreignCurrencyBalance", "外币余额(元)", BmsCustomer::getForeignCurrencyBalance, false, BmsCustomerLogServiceImpl::numText), + new FieldDef("weightThreshold", "重量阈值(kg)", BmsCustomer::getWeightThreshold, false, BmsCustomerLogServiceImpl::numText), + new FieldDef("throwRatio", "抛比(%)", BmsCustomer::getThrowRatio, false, BmsCustomerLogServiceImpl::numText), + new FieldDef("productCodes", "绑定产品", BmsCustomer::getProductCodes, false, Function.identity()), + new FieldDef("remark", "备注", BmsCustomer::getRemark, false, Function.identity())); + + @Override + public void recordInsert(BmsCustomer customer) + { + if (customer == null || customer.getCustomerId() == null) + { + return; + } + List changes = new ArrayList<>(); + for (FieldDef def : FIELD_DEFS) + { + String value = def.display(customer); + if (value != null && !value.isEmpty()) + { + changes.add(new BmsCustomerLog.FieldChange(def.field, def.label, "", value)); + } + } + saveLog(customer, BmsCustomerLog.OP_INSERT, changes); + } + + @Override + public void recordUpdate(BmsCustomer oldCustomer, BmsCustomer newCustomer) + { + if (oldCustomer == null || newCustomer == null || newCustomer.getCustomerId() == null) + { + return; + } + List changes = new ArrayList<>(); + for (FieldDef def : FIELD_DEFS) + { + // 与 update SQL 的动态条件保持一致:未提交(null/空串)的字段本次不落库,不参与比对 + Object raw = def.rawGetter.apply(newCustomer); + if (raw == null) + { + continue; + } + if (def.requireNonBlank && raw.toString().trim().isEmpty()) + { + continue; + } + String oldValue = def.display(oldCustomer); + String newValue = def.display(newCustomer); + if (!sameValue(def.rawGetter.apply(oldCustomer), raw)) + { + changes.add(new BmsCustomerLog.FieldChange(def.field, def.label, + oldValue == null ? "" : oldValue, newValue == null ? "" : newValue)); + } + } + if (changes.isEmpty()) + { + return; + } + // 日志快照以修改后的客户编码/名称为准 + saveLog(newCustomer, BmsCustomerLog.OP_UPDATE, changes); + } + + @Override + public void recordDelete(BmsCustomer customer) + { + if (customer == null || customer.getCustomerId() == null) + { + return; + } + saveLog(customer, BmsCustomerLog.OP_DELETE, null); + } + + @Override + public List selectByCustomerId(Long customerId) + { + List list = bmsCustomerLogMapper.selectBmsCustomerLogList(customerId); + for (BmsCustomerLog log : list) + { + if (log.getChangeContent() != null && !log.getChangeContent().isEmpty()) + { + log.setChanges(JSON.parseArray(log.getChangeContent(), BmsCustomerLog.FieldChange.class)); + } + } + return list; + } + + private void saveLog(BmsCustomer customer, String operationType, List changes) + { + BmsCustomerLog log = new BmsCustomerLog(); + log.setCustomerId(customer.getCustomerId()); + log.setCustomerCode(customer.getCustomerCode()); + log.setCustomerName(customer.getCustomerName()); + log.setOperationType(operationType); + if (changes != null) + { + log.setChangeContent(JSON.toJSONString(changes)); + } + try + { + SysUser user = SecurityUtils.getLoginUser().getUser(); + log.setCreateBy(user.getUserName()); + log.setCreateByName(user.getNickName()); + } + catch (Exception ignored) + { + // 无登录上下文(如系统任务)时不阻断业务 + } + log.setCreateTime(DateUtils.getNowDate()); + bmsCustomerLogMapper.insertBmsCustomerLog(log); + } + + /** 人员字段展示:姓名(工号),缺姓名时只显示工号 */ + private static String userDisplay(String code, String name) + { + String c = trimToNull(code); + String n = trimToNull(name); + if (c == null && n == null) + { + return ""; + } + if (c == null) + { + return n; + } + if (n == null || n.equals(c)) + { + return c; + } + return n + "(" + c + ")"; + } + + private static String numText(Object num) + { + return num == null ? "" : num.toString(); + } + + private static String trimToNull(String s) + { + if (s == null) + { + return null; + } + String t = s.trim(); + return t.isEmpty() ? null : t; + } + + /** 数值按大小比较(规避 BigDecimal 精度差异),其余按字符串比较 */ + private static boolean sameValue(Object a, Object b) + { + if (a instanceof BigDecimal && b instanceof BigDecimal) + { + return ((BigDecimal) a).compareTo((BigDecimal) b) == 0; + } + return Objects.equals(a == null ? null : a.toString(), b == null ? null : b.toString()); + } + + /** + * 字段定义 + */ + private static final class FieldDef + { + private final String field; + private final String label; + /** 取原始值(用于变更判断) */ + private final Function rawGetter; + /** 动态写入时是否要求非空串(对应 update SQL 的 != '' 判断) */ + private final boolean requireNonBlank; + /** 原始值 -> 字典翻译(查不到字典时回退原值) */ + private final Function dictTranslator; + + private FieldDef(String field, String label, Function rawGetter, + boolean requireNonBlank, Function dictTranslator) + { + this.field = field; + this.label = label; + this.rawGetter = rawGetter::apply; + this.requireNonBlank = requireNonBlank; + this.dictTranslator = dictTranslator; + } + + /** 取日志展示值:人员/数字类直接是文本;字典类翻译失败回退原值 */ + private String display(BmsCustomer c) + { + Object raw = rawGetter.apply(c); + if (raw == null) + { + return ""; + } + String text = raw.toString(); + if (text.isEmpty()) + { + return ""; + } + if (dictTranslator == null) + { + return text; + } + String translated = dictTranslator.apply(text); + return translated != null ? translated : text; + } + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerServiceImpl.java index b04ff8a8..159d64c7 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerServiceImpl.java @@ -1,5 +1,6 @@ package com.bms.customer.customerinfo.service.impl; +import java.util.ArrayList; import java.util.List; import com.bms.common.annotation.DataScope; @@ -14,6 +15,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.bms.customer.customerinfo.mapper.BmsCustomerMapper; import com.bms.customer.customerinfo.domain.BmsCustomer; +import com.bms.customer.customerinfo.domain.BmsCustomerLog; +import com.bms.customer.customerinfo.service.IBmsCustomerLogService; import com.bms.customer.customerinfo.service.IBmsCustomerService; import com.bms.customer.customermoney.service.ICustomerMoneyService; @@ -32,6 +35,9 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService @Autowired private ICustomerMoneyService customerMoneyService; + @Autowired + private IBmsCustomerLogService customerLogService; + /** * 查询客户主 * @@ -58,9 +64,18 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService return bmsCustomer; } + /** + * 查询某客户的全部业务操作日志(时间倒序) + */ + @Override + public List selectCustomerLogList(Long customerId) + { + return customerLogService.selectByCustomerId(customerId); + } + /** * 查询客户主列表 - * + * * @param bmsCustomer 客户主 * @return 客户主 */ @@ -235,6 +250,8 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService saveBmsCustomerContacts(bmsCustomer.getCustomerId(), bmsCustomer.getContactList()); // 新建客户即初始化默认CNY账户:余额0、总额度1000 CNY,写INIT流水 customerMoneyService.initDefaultCnyAccount(bmsCustomer.getCustomerId()); + // 业务日志:新建(含建档初始值) + customerLogService.recordInsert(bmsCustomer); } return rows; } @@ -252,6 +269,8 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService SysUser user = SecurityUtils.getLoginUser().getUser(); bmsCustomer.setUpdateTime(DateUtils.getNowDate()); bmsCustomer.setUpdateBy(user.getUserName()); + // 修改前先取旧值快照,用于业务日志记录字段级变更 + BmsCustomer oldCustomer = bmsCustomerMapper.selectBmsCustomerByCustomerId(bmsCustomer.getCustomerId()); // 仅重建客户档案模块的附件,收款水单等其他业务模块的附件保留不动 bmsCustomerMapper.deleteBmsCustomerProfileAttachmentByCustomerId(bmsCustomer.getCustomerId()); if (bmsCustomer.getAttachmentList() != null) @@ -266,7 +285,13 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService saveBmsCustomerCompanies(bmsCustomer.getCustomerId(), bmsCustomer.getCompanyList()); // 联系人采用增量 upsert saveBmsCustomerContacts(bmsCustomer.getCustomerId(), bmsCustomer.getContactList()); - return bmsCustomerMapper.updateBmsCustomer(bmsCustomer); + int rows = bmsCustomerMapper.updateBmsCustomer(bmsCustomer); + // 业务日志:修改(字段级 修改前→修改后;无实际变更时不记录) + if (rows > 0) + { + customerLogService.recordUpdate(oldCustomer, bmsCustomer); + } + return rows; } /** @@ -279,13 +304,31 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService @Override public int deleteBmsCustomerByCustomerIds(Long[] customerIds) { + // 删除前取客户快照,用于业务日志 + List snapshotList = new ArrayList<>(); + for (Long customerId : customerIds) + { + BmsCustomer snapshot = bmsCustomerMapper.selectBmsCustomerByCustomerId(customerId); + if (snapshot != null) + { + snapshotList.add(snapshot); + } + } for (Long customerId : customerIds) { bmsCustomerMapper.deleteBmsCustomerAttachmentByCustomerId(customerId); bmsCustomerMapper.deleteBmsCustomerCompanyByCustomerId(customerId); bmsCustomerMapper.deleteBmsCustomerContactByCustomerId(customerId); } - return bmsCustomerMapper.deleteBmsCustomerByCustomerIds(customerIds); + int rows = bmsCustomerMapper.deleteBmsCustomerByCustomerIds(customerIds); + if (rows > 0) + { + for (BmsCustomer snapshot : snapshotList) + { + customerLogService.recordDelete(snapshot); + } + } + return rows; } /** @@ -298,10 +341,16 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService @Override public int deleteBmsCustomerByCustomerId(Long customerId) { + BmsCustomer snapshot = bmsCustomerMapper.selectBmsCustomerByCustomerId(customerId); bmsCustomerMapper.deleteBmsCustomerAttachmentByCustomerId(customerId); bmsCustomerMapper.deleteBmsCustomerCompanyByCustomerId(customerId); bmsCustomerMapper.deleteBmsCustomerContactByCustomerId(customerId); - return bmsCustomerMapper.deleteBmsCustomerByCustomerId(customerId); + int rows = bmsCustomerMapper.deleteBmsCustomerByCustomerId(customerId); + if (rows > 0 && snapshot != null) + { + customerLogService.recordDelete(snapshot); + } + return rows; } /** diff --git a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillDetailMapper.xml b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillDetailMapper.xml index bb27940a..7aae0fd5 100644 --- a/bms-back/bms-system/src/main/resources/mapper/ar/ArBillDetailMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/ar/ArBillDetailMapper.xml @@ -22,6 +22,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + @@ -35,7 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -43,7 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select d.detail_id, d.ar_bill_id, d.prc_order_bill_id, d.detail_no, d.order_no, d.tracking_no, d.transfer_no, d.receive_time, d.product_code, d.product_name, d.goods_description, d.country_code, d.country_name, d.dest_postcode, - d.actual_weight, d.volume_weight, d.charge_weight, d.pkg_length, d.pkg_width, d.pkg_height, + d.actual_weight, d.volume_weight, d.charge_weight, d.freight_rate, d.registration_fee, d.pkg_length, d.pkg_width, d.pkg_height, d.currency, d.total_amount, d.remark, d.create_by, d.create_time, f.detail_fee_id as fee_detail_id, f.detail_id as fee_parent_detail_id, f.prc_income_bill_fee_id as fee_prc_income_bill_fee_id, @@ -97,7 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + where customer_id = #{customerId} + order by create_time desc, log_id desc + + + diff --git a/bms-back/sql/20260930_add_ar_bill_detail_freight_rate.sql b/bms-back/sql/20260930_add_ar_bill_detail_freight_rate.sql new file mode 100644 index 00000000..e58f8c24 --- /dev/null +++ b/bms-back/sql/20260930_add_ar_bill_detail_freight_rate.sql @@ -0,0 +1,17 @@ +-- 应收账单订单明细保留订单计费时实际命中的运费单价快照。 +ALTER TABLE `ar_bill_detail` + ADD COLUMN `freight_rate` decimal(10,4) DEFAULT NULL COMMENT '运费单价快照' AFTER `charge_weight`; + +ALTER TABLE `ar_bill_detail` + ADD COLUMN `registration_fee` decimal(10,2) DEFAULT NULL COMMENT '处理费快照' AFTER `freight_rate`; + +-- 回填已生成的历史应收账单,避免导出单价为空。 +UPDATE `ar_bill_detail` d +INNER JOIN `prc_order_bill` p ON p.`bill_id` = d.`prc_order_bill_id` +SET d.`freight_rate` = p.`freight_rate` +WHERE d.`freight_rate` IS NULL; + +UPDATE `ar_bill_detail` d +INNER JOIN `prc_order_bill` p ON p.`bill_id` = d.`prc_order_bill_id` +SET d.`registration_fee` = p.`registration_fee` +WHERE d.`registration_fee` IS NULL; diff --git a/bms-back/sql/bms_init.sql b/bms-back/sql/bms_init.sql index b7e88d4a..1f050eb7 100644 --- a/bms-back/sql/bms_init.sql +++ b/bms-back/sql/bms_init.sql @@ -118,6 +118,8 @@ CREATE TABLE `ar_bill_detail` ( `actual_weight` decimal(12,3) DEFAULT NULL COMMENT '实际重量KG', `volume_weight` decimal(12,3) DEFAULT NULL COMMENT '材积重量KG', `charge_weight` decimal(12,3) DEFAULT NULL COMMENT '计费重量KG', + `freight_rate` decimal(10,4) DEFAULT NULL COMMENT '运费单价快照', + `registration_fee` decimal(10,2) DEFAULT NULL COMMENT '处理费快照', `pkg_length` decimal(10,2) DEFAULT NULL COMMENT '长度CM', `pkg_width` decimal(10,2) DEFAULT NULL COMMENT '宽度CM', `pkg_height` decimal(10,2) DEFAULT NULL COMMENT '高度CM', diff --git a/bms-front/src/api/customersuppliermanagement/customerinfo/customer.js b/bms-front/src/api/customersuppliermanagement/customerinfo/customer.js index 7aec7caf..60444d95 100644 --- a/bms-front/src/api/customersuppliermanagement/customerinfo/customer.js +++ b/bms-front/src/api/customersuppliermanagement/customerinfo/customer.js @@ -17,6 +17,15 @@ export function getCustomer(customerId) { }) } +// 查询客户业务操作日志 +export function listCustomerLog(customerId) { + return request({ + url: '/customersupplier/customerinfo/log/list', + method: 'get', + params: { customerId } + }) +} + // 新增客户主 export function addCustomer(data) { return request({ diff --git a/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue b/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue index 39938790..1fc649c2 100644 --- a/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue +++ b/bms-front/src/views/customersuppliermanagement/customerinfo/index.vue @@ -85,6 +85,16 @@ >客户余额 + + 日志 + - + @@ -184,11 +195,62 @@ v-model:limit="queryParams.pageSize" @pagination="getList" /> + + + +
+ 客户编码:{{ logCustomer.customerCode || '-' }} + 共 {{ logList.length }} 条日志(按操作时间倒序) +
+ + + + + + + + + + + + + + + + + + +