diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java
index 434c164..0fc9777 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java
@@ -61,6 +61,10 @@ public class CustomerArchive extends TenantEntity {
@Schema(description = "客商性质")
private String customerNature;
+ @Schema(description = "是否广西百强:0否,1是")
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private Integer guangxiTop100;
+
@Schema(description = "统一社会信用代码")
private String unifiedCreditCode;
@@ -101,6 +105,10 @@ public class CustomerArchive extends TenantEntity {
@Schema(description = "经营范围")
private String businessScope;
+ @Schema(description = "网络货运平台:0否,1是")
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private Integer networkFreightPlatform;
+
@Schema(description = "营业期限类型")
private String businessTermType;
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerInvoiceContact.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerInvoiceContact.java
new file mode 100644
index 0000000..c27d5e5
--- /dev/null
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerInvoiceContact.java
@@ -0,0 +1,71 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+import java.io.Serial;
+
+/**
+ * 客商发票联系信息实体类
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("blade_customer_invoice_contact")
+@Schema(description = "客商发票联系信息")
+public class CustomerInvoiceContact extends TenantEntity {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "发票信息ID")
+ private Long invoiceId;
+
+ @Schema(description = "联系人")
+ private String contactName;
+
+ @Schema(description = "联系电话")
+ private String contactPhone;
+
+ @Schema(description = "邮箱地址")
+ private String email;
+
+ @Schema(description = "所属部门ID集合")
+ private String deptIds;
+
+ @Schema(description = "所属部门")
+ private String deptNames;
+
+ @Schema(description = "备注")
+ private String remark;
+
+}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerInvoiceInfo.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerInvoiceInfo.java
index 896a10c..e24a93a 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerInvoiceInfo.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerInvoiceInfo.java
@@ -50,21 +50,15 @@ public class CustomerInvoiceInfo extends TenantEntity {
@Schema(description = "客商ID")
private Long customerId;
- @Schema(description = "受票方名称")
+ @Schema(description = "企业全称")
private String invoiceTitle;
- @Schema(description = "发票类型")
- private String invoiceType;
-
@Schema(description = "纳税人识别号")
private String taxNo;
@Schema(description = "开户行名称")
private String bankName;
- @Schema(description = "注册电话")
- private String registeredPhone;
-
@Schema(description = "银行账号")
private String bankAccount;
@@ -77,24 +71,6 @@ public class CustomerInvoiceInfo extends TenantEntity {
@Schema(description = "注册地址详细地址")
private String registeredDetailAddress;
- @Schema(description = "邮箱")
- private String email;
-
- @Schema(description = "收件人姓名")
- private String receiverName;
-
- @Schema(description = "收件人电话")
- private String receiverPhone;
-
- @Schema(description = "收件人地址")
- private String receiverAddress;
-
- @Schema(description = "收件人地址行政区划")
- private String receiverRegionName;
-
- @Schema(description = "收件人详细地址")
- private String receiverDetailAddress;
-
@Schema(description = "是否默认")
private Integer isDefault;
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InsuranceOcrTemplate.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InsuranceOcrTemplate.java
index 6fe108d..a67969d 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InsuranceOcrTemplate.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InsuranceOcrTemplate.java
@@ -52,6 +52,11 @@ public class InsuranceOcrTemplate extends TenantEntity {
@Schema(description = "模板名称")
private String name;
+ /** 车船类型:车辆/船舶。 */
+ @TableField("vehicle_type")
+ @Schema(description = "车船类型:车辆/船舶")
+ private String vehicleType;
+
/** 字段映射配置JSON。 */
@TableField("mapping_config")
@Schema(description = "字段映射配置JSON")
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/AnnualInspectionRecordExpiryStatVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/AnnualInspectionRecordExpiryStatVO.java
new file mode 100644
index 0000000..645035e
--- /dev/null
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/AnnualInspectionRecordExpiryStatVO.java
@@ -0,0 +1,55 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.pojo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 年检记录有效期统计
+ *
+ * @author Chill
+ */
+@Data
+@Schema(description = "年检记录有效期统计")
+public class AnnualInspectionRecordExpiryStatVO implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "全部")
+ private Long total;
+
+ @Schema(description = "30天内")
+ private Long within30;
+
+ @Schema(description = "已过期")
+ private Long expired;
+
+}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/AnnualInspectionRecordVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/AnnualInspectionRecordVO.java
index d1d3c96..b76896a 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/AnnualInspectionRecordVO.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/AnnualInspectionRecordVO.java
@@ -72,4 +72,18 @@ public class AnnualInspectionRecordVO extends AnnualInspectionRecord {
@Schema(description = "更新人姓名")
private String updateUserName;
+ @TableField(exist = false)
+ @Schema(description = "有效期状态:within30-30天内,expired-已过期")
+ private String expireStatus;
+
+ @TableField(exist = false)
+ @Schema(description = "当天日期")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate today;
+
+ @TableField(exist = false)
+ @Schema(description = "预警截止日期")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate warningDate;
+
}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java
index a413eeb..1a2f0eb 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerArchiveVO.java
@@ -77,4 +77,12 @@ public class CustomerArchiveVO extends CustomerArchive {
@Schema(description = "变更记录")
private List changeRecords = new ArrayList<>();
+ @TableField(exist = false)
+ @Schema(description = "是否保存变更记录(仅提交时为 true,保存时不记录)")
+ private Boolean recordChange;
+
+ @TableField(exist = false)
+ @Schema(description = "客商类型:external外部客商,internal内部组织")
+ private String customerKind;
+
}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerInvoiceContactVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerInvoiceContactVO.java
new file mode 100644
index 0000000..73a4c77
--- /dev/null
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerInvoiceContactVO.java
@@ -0,0 +1,45 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.pojo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.transport.pojo.entity.CustomerInvoiceContact;
+
+import java.io.Serial;
+
+/**
+ * 客商发票联系信息视图实体类
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "客商发票联系信息")
+public class CustomerInvoiceContactVO extends CustomerInvoiceContact {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerInvoiceInfoVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerInvoiceInfoVO.java
index 0d88697..a8d539d 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerInvoiceInfoVO.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CustomerInvoiceInfoVO.java
@@ -22,12 +22,15 @@
*/
package org.springblade.transport.pojo.vo;
+import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.transport.pojo.entity.CustomerInvoiceInfo;
import java.io.Serial;
+import java.util.ArrayList;
+import java.util.List;
/**
* 客商发票信息视图实体类
@@ -42,4 +45,8 @@ public class CustomerInvoiceInfoVO extends CustomerInvoiceInfo {
@Serial
private static final long serialVersionUID = 1L;
+ @TableField(exist = false)
+ @Schema(description = "联系信息")
+ private List contacts = new ArrayList<>();
+
}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictBizServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictBizServiceImpl.java
index dc23249..39398f0 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictBizServiceImpl.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictBizServiceImpl.java
@@ -136,7 +136,10 @@ public class DictBizServiceImpl extends ServiceImpl impl
@Override
public IPage parentList(Map dict, Query query) {
- IPage page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, DictBiz.class).lambda().eq(DictBiz::getParentId, CommonConstant.TOP_PARENT_ID).orderByAsc(DictBiz::getSort));
+ IPage page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, DictBiz.class).lambda()
+ .eq(DictBiz::getParentId, CommonConstant.TOP_PARENT_ID)
+ .orderByAsc(DictBiz::getSort)
+ .orderByDesc(DictBiz::getId));
return DictBizWrapper.build().pageVO(page);
}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictServiceImpl.java
index bdd5b15..3b0bf98 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictServiceImpl.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DictServiceImpl.java
@@ -123,7 +123,10 @@ public class DictServiceImpl extends ServiceImpl implements ID
@Override
public IPage parentList(Map dict, Query query) {
- IPage page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, Dict.class).lambda().eq(Dict::getParentId, CommonConstant.TOP_PARENT_ID).orderByAsc(Dict::getSort));
+ IPage page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, Dict.class).lambda()
+ .eq(Dict::getParentId, CommonConstant.TOP_PARENT_ID)
+ .orderByAsc(Dict::getSort)
+ .orderByDesc(Dict::getId));
return DictWrapper.build().pageVO(page);
}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java
index 988f72c..e97fa2d 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java
@@ -47,6 +47,7 @@ import org.springblade.transport.excel.AnnualInspectionRecordExcel;
import org.springblade.transport.excel.AnnualInspectionRecordExportExcel;
import org.springblade.transport.excel.AnnualInspectionRecordImporter;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
+import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import org.springblade.transport.service.IAnnualInspectionRecordService;
import org.springblade.transport.wrapper.AnnualInspectionRecordWrapper;
@@ -58,6 +59,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
+import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@@ -91,6 +93,7 @@ public class AnnualInspectionRecordController extends BladeController {
@ApiOperationSupport(order = 2)
@Operation(summary = "分页", description = "传入annualInspectionRecord")
public R> list(AnnualInspectionRecordVO annualInspectionRecord, Query query) {
+ fillExpiryDate(annualInspectionRecord);
IPage pages = annualInspectionRecordService.selectAnnualInspectionRecordPage(Condition.getPage(normalizeQuery(query)), annualInspectionRecord);
return R.data(pages);
}
@@ -109,8 +112,17 @@ public class AnnualInspectionRecordController extends BladeController {
return R.status(annualInspectionRecordService.deleteLogic(Func.toLongList(ids)));
}
- @PostMapping("/import-annual-inspection-record")
+ @GetMapping("/expiry-stat")
@ApiOperationSupport(order = 5)
+ @Operation(summary = "有效期统计", description = "传入annualInspectionRecord")
+ public R expiryStat(AnnualInspectionRecordVO annualInspectionRecord) {
+ fillExpiryDate(annualInspectionRecord);
+ annualInspectionRecord.setExpireStatus(null);
+ return R.data(annualInspectionRecordService.expiryStat(annualInspectionRecord));
+ }
+
+ @PostMapping("/import-annual-inspection-record")
+ @ApiOperationSupport(order = 6)
@Operation(summary = "导入年检记录", description = "传入excel")
public R importAnnualInspectionRecord(MultipartFile file, HttpServletResponse response) {
List failureList = annualInspectionRecordService.importAnnualInspectionRecord(ExcelUtil.read(file, AnnualInspectionRecordExcel.class));
@@ -122,17 +134,18 @@ public class AnnualInspectionRecordController extends BladeController {
}
@GetMapping("/export-annual-inspection-record")
- @ApiOperationSupport(order = 6)
+ @ApiOperationSupport(order = 7)
@Operation(summary = "导出年检记录")
public void exportAnnualInspectionRecord(AnnualInspectionRecordVO annualInspectionRecord,
@RequestParam(required = false) String ids,
HttpServletResponse response) {
+ fillExpiryDate(annualInspectionRecord);
List list = annualInspectionRecordService.exportAnnualInspectionRecord(buildExportQuery(annualInspectionRecord, ids));
ExcelUtil.export(response, "年检记录" + DateUtil.time(), "年检记录表", list, AnnualInspectionRecordExportExcel.class);
}
@GetMapping("/export-template")
- @ApiOperationSupport(order = 7)
+ @ApiOperationSupport(order = 8)
@Operation(summary = "导出模板")
public void exportTemplate(HttpServletResponse response) {
List list = new ArrayList<>();
@@ -155,6 +168,15 @@ public class AnnualInspectionRecordController extends BladeController {
return query;
}
+ private void fillExpiryDate(AnnualInspectionRecordVO annualInspectionRecord) {
+ if (annualInspectionRecord.getToday() == null) {
+ annualInspectionRecord.setToday(LocalDate.now());
+ }
+ if (annualInspectionRecord.getWarningDate() == null) {
+ annualInspectionRecord.setWarningDate(annualInspectionRecord.getToday().plusDays(30));
+ }
+ }
+
private LambdaQueryWrapper buildExportQuery(AnnualInspectionRecordVO annualInspectionRecord, String ids) {
LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery()
.eq(AnnualInspectionRecord::getIsDeleted, 0)
@@ -183,6 +205,15 @@ public class AnnualInspectionRecordController extends BladeController {
if (Func.isNotEmpty(annualInspectionRecord.getCreateTimeEnd())) {
queryWrapper.le(AnnualInspectionRecord::getCreateTime, annualInspectionRecord.getCreateTimeEnd());
}
+ if ("within30".equals(annualInspectionRecord.getExpireStatus())) {
+ queryWrapper.isNotNull(AnnualInspectionRecord::getValidUntilDate)
+ .ge(AnnualInspectionRecord::getValidUntilDate, annualInspectionRecord.getToday())
+ .le(AnnualInspectionRecord::getValidUntilDate, annualInspectionRecord.getWarningDate());
+ }
+ if ("expired".equals(annualInspectionRecord.getExpireStatus())) {
+ queryWrapper.isNotNull(AnnualInspectionRecord::getValidUntilDate)
+ .lt(AnnualInspectionRecord::getValidUntilDate, annualInspectionRecord.getToday());
+ }
return queryWrapper;
}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.java
index 477a993..887d05d 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.java
@@ -27,7 +27,9 @@ package org.springblade.transport.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Param;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
+import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import java.util.List;
@@ -46,6 +48,14 @@ public interface AnnualInspectionRecordMapper extends BaseMapper selectAnnualInspectionRecordPage(IPage page, AnnualInspectionRecordVO annualInspectionRecord);
+ List selectAnnualInspectionRecordPage(IPage page, @Param("annualInspectionRecord") AnnualInspectionRecordVO annualInspectionRecord);
+
+ /**
+ * 有效期统计
+ *
+ * @param annualInspectionRecord 查询参数
+ * @return 统计结果
+ */
+ AnnualInspectionRecordExpiryStatVO selectExpiryStat(@Param("annualInspectionRecord") AnnualInspectionRecordVO annualInspectionRecord);
}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.xml
index 53f87e9..30fa5f3 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.xml
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/AnnualInspectionRecordMapper.xml
@@ -26,6 +26,49 @@
+
+ valid_until_date IS NOT NULL
+ AND valid_until_date >= #{annualInspectionRecord.today}
+ AND valid_until_date <= #{annualInspectionRecord.warningDate}
+
+
+
+ valid_until_date IS NOT NULL
+ AND valid_until_date < #{annualInspectionRecord.today}
+
+
+
+ is_deleted = 0
+
+ AND create_dept = #{annualInspectionRecord.createDept}
+
+
+ AND vehicle_type = #{annualInspectionRecord.vehicleType}
+
+
+
+ AND vehicle_no LIKE #{vehicleNoLike}
+
+
+ AND inspection_assessment_date >= #{annualInspectionRecord.inspectionAssessmentDateStart}
+
+
+ AND inspection_assessment_date <= #{annualInspectionRecord.inspectionAssessmentDateEnd}
+
+
+ AND create_time >= #{annualInspectionRecord.createTimeStart}
+
+
+ AND create_time <= #{annualInspectionRecord.createTimeEnd}
+
+
+ AND
+
+
+ AND
+
+
+
SELECT
id,
@@ -52,30 +95,19 @@
FROM
blade_annual_inspection_record
WHERE
- is_deleted = 0
-
- AND create_dept = #{annualInspectionRecord.createDept}
-
-
- AND vehicle_type = #{annualInspectionRecord.vehicleType}
-
-
-
- AND vehicle_no LIKE #{vehicleNoLike}
-
-
- AND inspection_assessment_date >= #{annualInspectionRecord.inspectionAssessmentDateStart}
-
-
- AND inspection_assessment_date <= #{annualInspectionRecord.inspectionAssessmentDateEnd}
-
-
- AND create_time >= #{annualInspectionRecord.createTimeStart}
-
-
- AND create_time <= #{annualInspectionRecord.createTimeEnd}
-
+
ORDER BY create_time DESC
+
+ SELECT
+ COUNT(1) AS total,
+ SUM(CASE WHEN THEN 1 ELSE 0 END) AS within30,
+ SUM(CASE WHEN THEN 1 ELSE 0 END) AS expired
+ FROM
+ blade_annual_inspection_record
+ WHERE
+
+
+
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml
index a9f80c3..d9df135 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml
@@ -16,6 +16,7 @@
+
@@ -29,6 +30,7 @@
+
@@ -61,6 +63,7 @@
short_name,
full_name,
customer_nature,
+ guangxi_top100,
unified_credit_code,
customer_type,
project_name,
@@ -74,6 +77,7 @@
dept_name,
invoice_tax_rate,
business_scope,
+ network_freight_platform,
business_term_type,
business_end_date,
registered_capital,
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerInvoiceContactMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerInvoiceContactMapper.java
new file mode 100644
index 0000000..fe40da6
--- /dev/null
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerInvoiceContactMapper.java
@@ -0,0 +1,21 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.transport.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.transport.pojo.entity.CustomerInvoiceContact;
+
+/**
+ * 客商发票联系信息 Mapper 接口
+ *
+ * @author Chill
+ */
+public interface CustomerInvoiceContactMapper extends BaseMapper {
+}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IAnnualInspectionRecordService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IAnnualInspectionRecordService.java
index b114a0f..61889b3 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IAnnualInspectionRecordService.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IAnnualInspectionRecordService.java
@@ -31,6 +31,7 @@ import org.springblade.core.mp.base.BaseService;
import org.springblade.transport.excel.AnnualInspectionRecordExcel;
import org.springblade.transport.excel.AnnualInspectionRecordExportExcel;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
+import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import java.util.List;
@@ -44,6 +45,8 @@ public interface IAnnualInspectionRecordService extends BaseService selectAnnualInspectionRecordPage(IPage page, AnnualInspectionRecordVO annualInspectionRecord);
+ AnnualInspectionRecordExpiryStatVO expiryStat(AnnualInspectionRecordVO annualInspectionRecord);
+
boolean submit(AnnualInspectionRecord annualInspectionRecord);
List importAnnualInspectionRecord(List data);
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ICustomerArchiveService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ICustomerArchiveService.java
index 64b88d0..73ddd1a 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ICustomerArchiveService.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/ICustomerArchiveService.java
@@ -68,6 +68,7 @@ public interface ICustomerArchiveService extends BaseService {
/**
* 新增或修改客商档案
+ * 仅当 {@code customer.recordChange = true}(前端点「提交」)时写入变更记录;「保存」不落变更记录。
*
* @param customer 客商档案
* @return 是否成功
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/AnnualInspectionRecordServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/AnnualInspectionRecordServiceImpl.java
index d760205..563555b 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/AnnualInspectionRecordServiceImpl.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/AnnualInspectionRecordServiceImpl.java
@@ -36,13 +36,13 @@ import org.springblade.transport.excel.AnnualInspectionRecordExcel;
import org.springblade.transport.excel.AnnualInspectionRecordExportExcel;
import org.springblade.transport.mapper.AnnualInspectionRecordMapper;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
+import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import org.springblade.transport.service.IAnnualInspectionRecordService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
-import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -74,6 +74,24 @@ public class AnnualInspectionRecordServiceImpl extends BaseServiceImpl> records = parseChangeRecords(contractManage.getChangeRecordJson());
+ for (int index = records.size() - 1; index >= 0; index--) {
+ String changeReason = TransportBusinessSupport.trimToNull(
+ records.get(index).get("changeReason") == null ? null : String.valueOf(records.get(index).get("changeReason")));
+ if (Func.isNotEmpty(changeReason)) {
+ return changeReason;
+ }
+ }
+ return "重新提交变更";
+ }
+
private boolean canResubmitTemporary(ContractManage contractManage) {
String stage = contractManage.getContractStage();
String status = contractManage.getApprovalStatus();
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CustomerArchiveServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CustomerArchiveServiceImpl.java
index 4231efa..1a783a6 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CustomerArchiveServiceImpl.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CustomerArchiveServiceImpl.java
@@ -46,6 +46,7 @@ import org.springblade.transport.mapper.CustomerChangeRecordMapper;
import org.springblade.transport.mapper.CustomerContactMapper;
import org.springblade.transport.mapper.CustomerCreditScoreDetailMapper;
import org.springblade.transport.mapper.CustomerCreditScoreMapper;
+import org.springblade.transport.mapper.CustomerInvoiceContactMapper;
import org.springblade.transport.mapper.CustomerInvoiceInfoMapper;
import org.springblade.transport.mapper.CustomerReceiptAccountMapper;
import org.springblade.transport.mapper.UserCustomerScopeMapper;
@@ -59,6 +60,7 @@ import org.springblade.transport.pojo.entity.CustomerChangeRecord;
import org.springblade.transport.pojo.entity.CustomerContact;
import org.springblade.transport.pojo.entity.CustomerCreditScore;
import org.springblade.transport.pojo.entity.CustomerCreditScoreDetail;
+import org.springblade.transport.pojo.entity.CustomerInvoiceContact;
import org.springblade.transport.pojo.entity.CustomerInvoiceInfo;
import org.springblade.transport.pojo.entity.CustomerReceiptAccount;
import org.springblade.transport.pojo.vo.CustomerArchiveVO;
@@ -67,6 +69,7 @@ import org.springblade.transport.pojo.vo.CustomerContactVO;
import org.springblade.transport.pojo.vo.CustomerCreditScoreDetailVO;
import org.springblade.transport.pojo.vo.CustomerCreditScoreVO;
import org.springblade.transport.pojo.vo.CreditRatingStandardVO;
+import org.springblade.transport.pojo.vo.CustomerInvoiceContactVO;
import org.springblade.transport.pojo.vo.CustomerInvoiceInfoVO;
import org.springblade.transport.pojo.vo.CustomerReceiptAccountVO;
import org.springblade.transport.service.ICustomerArchiveService;
@@ -108,6 +111,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl invoices) {
+ private void insertInvoices(Long customerId, CustomerArchiveVO customer) {
+ List invoices = customer.getInvoices();
+ if (Func.isEmpty(invoices)) {
+ return;
+ }
+ boolean internal = isInternalCustomer(customer);
for (CustomerInvoiceInfoVO invoiceVO : invoices) {
if (Func.isEmpty(invoiceVO.getInvoiceTitle()) && Func.isEmpty(invoiceVO.getTaxNo())) {
continue;
}
- if (Func.isEmpty(invoiceVO.getInvoiceType())) {
- throw new ServiceException("发票类型不能为空");
+ if (Func.isEmpty(invoiceVO.getInvoiceTitle())) {
+ throw new ServiceException("企业全称不能为空");
}
- if (!List.of("增值税专用发票", "普通发票").contains(invoiceVO.getInvoiceType())) {
- throw new ServiceException("发票类型不合法");
+ if (Func.isEmpty(invoiceVO.getTaxNo())) {
+ throw new ServiceException("纳税人识别号不能为空");
+ }
+ if (Func.isEmpty(invoiceVO.getBankName())) {
+ throw new ServiceException("开户行名称不能为空");
+ }
+ if (Func.isEmpty(invoiceVO.getBankAccount())) {
+ throw new ServiceException("银行账号不能为空");
+ }
+ if (Func.isEmpty(invoiceVO.getRegisteredRegionName()) && Func.isEmpty(invoiceVO.getRegisteredAddress())) {
+ throw new ServiceException("注册地址不能为空");
+ }
+ if (Func.isEmpty(invoiceVO.getRegisteredDetailAddress())) {
+ throw new ServiceException("详细地址不能为空");
}
invoiceVO.setRegisteredAddress(buildAddress(invoiceVO.getRegisteredRegionName(), invoiceVO.getRegisteredDetailAddress(), invoiceVO.getRegisteredAddress()));
- invoiceVO.setReceiverAddress(buildAddress(invoiceVO.getReceiverRegionName(), invoiceVO.getReceiverDetailAddress(), invoiceVO.getReceiverAddress()));
CustomerInvoiceInfo invoice = Objects.requireNonNull(BeanUtil.copyProperties(invoiceVO, CustomerInvoiceInfo.class));
invoice.setId(IdWorker.getId());
invoice.setCustomerId(customerId);
invoice.setStatus(STATUS_ENABLED);
invoiceInfoMapper.insert(invoice);
+ insertInvoiceContacts(invoice.getId(), invoiceVO.getContacts(), internal, customer);
}
}
+ private void insertInvoiceContacts(Long invoiceId, List contacts, boolean internal,
+ CustomerArchiveVO customer) {
+ if (Func.isEmpty(contacts)) {
+ return;
+ }
+ for (CustomerInvoiceContactVO contactVO : contacts) {
+ if (isBlankInvoiceContact(contactVO)) {
+ continue;
+ }
+ if (Func.isEmpty(contactVO.getContactName())) {
+ throw new ServiceException("发票联系人不能为空");
+ }
+ if (Func.isEmpty(contactVO.getContactPhone())) {
+ throw new ServiceException("发票联系电话不能为空");
+ }
+ validateInvoiceContactEmail(contactVO.getEmail());
+ if (Func.isNotEmpty(contactVO.getRemark()) && contactVO.getRemark().length() > 200) {
+ throw new ServiceException("发票联系信息备注最多200个字");
+ }
+ if (internal && Func.isEmpty(contactVO.getDeptIds()) && Func.isEmpty(contactVO.getDeptNames())) {
+ throw new ServiceException("内部组织客商必须选择发票联系信息所属部门");
+ }
+ if (!internal) {
+ if (Func.isEmpty(contactVO.getDeptIds())) {
+ contactVO.setDeptIds(customer.getDeptIds());
+ }
+ if (Func.isEmpty(contactVO.getDeptNames())) {
+ contactVO.setDeptNames(Func.isNotEmpty(customer.getFullName()) ? customer.getFullName() : customer.getDeptName());
+ }
+ }
+ CustomerInvoiceContact contact = Objects.requireNonNull(BeanUtil.copyProperties(contactVO, CustomerInvoiceContact.class));
+ contact.setId(IdWorker.getId());
+ contact.setInvoiceId(invoiceId);
+ contact.setStatus(STATUS_ENABLED);
+ invoiceContactMapper.insert(contact);
+ }
+ }
+
+ private boolean isBlankInvoiceContact(CustomerInvoiceContactVO contactVO) {
+ return Func.isEmpty(contactVO.getContactName())
+ && Func.isEmpty(contactVO.getContactPhone())
+ && Func.isEmpty(contactVO.getEmail())
+ && Func.isEmpty(contactVO.getDeptIds())
+ && Func.isEmpty(contactVO.getDeptNames())
+ && Func.isEmpty(contactVO.getRemark());
+ }
+
+ private void validateInvoiceContactEmail(String email) {
+ if (Func.isEmpty(email)) {
+ return;
+ }
+ if (email.length() > 100) {
+ throw new ServiceException("发票联系邮箱不能超过100个字");
+ }
+ if (!email.matches("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")) {
+ throw new ServiceException("发票联系邮箱格式不正确");
+ }
+ }
+
+ private boolean isInternalCustomer(CustomerArchiveVO customer) {
+ String customerKind = customer.getCustomerKind();
+ if (Func.isNotEmpty(customerKind)) {
+ return "internal".equals(customerKind) || "内部组织".equals(customerKind);
+ }
+ return "internal".equals(customer.getCustomerNature()) || "内部组织".equals(customer.getCustomerNature());
+ }
+
private void insertScores(Long customerId, List scores) {
for (CustomerCreditScoreVO scoreVO : scores) {
if (Func.isEmpty(scoreVO.getQuantificationId()) && Func.isEmpty(scoreVO.getScoreDate())) {
@@ -529,6 +619,15 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl customerIds, boolean deleteScores) {
contactMapper.update(null, Wrappers.lambdaUpdate().in(CustomerContact::getCustomerId, customerIds).set(CustomerContact::getIsDeleted, 1));
receiptAccountMapper.update(null, Wrappers.lambdaUpdate().in(CustomerReceiptAccount::getCustomerId, customerIds).set(CustomerReceiptAccount::getIsDeleted, 1));
+ List invoices = invoiceInfoMapper.selectList(Wrappers.lambdaQuery()
+ .in(CustomerInvoiceInfo::getCustomerId, customerIds)
+ .eq(CustomerInvoiceInfo::getIsDeleted, 0));
+ if (Func.isNotEmpty(invoices)) {
+ List invoiceIds = invoices.stream().map(CustomerInvoiceInfo::getId).toList();
+ invoiceContactMapper.update(null, Wrappers.lambdaUpdate()
+ .in(CustomerInvoiceContact::getInvoiceId, invoiceIds)
+ .set(CustomerInvoiceContact::getIsDeleted, 1));
+ }
invoiceInfoMapper.update(null, Wrappers.lambdaUpdate().in(CustomerInvoiceInfo::getCustomerId, customerIds).set(CustomerInvoiceInfo::getIsDeleted, 1));
if (deleteScores) {
deleteScores(customerIds);
@@ -565,12 +664,27 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl loadInvoices(Long customerId) {
- return invoiceInfoMapper.selectList(Wrappers.lambdaQuery()
- .eq(CustomerInvoiceInfo::getCustomerId, customerId)
- .eq(CustomerInvoiceInfo::getIsDeleted, 0)
- .orderByDesc(CustomerInvoiceInfo::getIsDefault)
- .orderByAsc(CustomerInvoiceInfo::getCreateTime))
- .stream().map(invoice -> Objects.requireNonNull(BeanUtil.copyProperties(invoice, CustomerInvoiceInfoVO.class))).toList();
+ List invoices = invoiceInfoMapper.selectList(Wrappers.lambdaQuery()
+ .eq(CustomerInvoiceInfo::getCustomerId, customerId)
+ .eq(CustomerInvoiceInfo::getIsDeleted, 0)
+ .orderByDesc(CustomerInvoiceInfo::getIsDefault)
+ .orderByAsc(CustomerInvoiceInfo::getCreateTime));
+ if (Func.isEmpty(invoices)) {
+ return new ArrayList<>();
+ }
+ List invoiceIds = invoices.stream().map(CustomerInvoiceInfo::getId).toList();
+ Map> contactMap = invoiceContactMapper.selectList(Wrappers.lambdaQuery()
+ .in(CustomerInvoiceContact::getInvoiceId, invoiceIds)
+ .eq(CustomerInvoiceContact::getIsDeleted, 0)
+ .orderByAsc(CustomerInvoiceContact::getCreateTime))
+ .stream()
+ .map(contact -> Objects.requireNonNull(BeanUtil.copyProperties(contact, CustomerInvoiceContactVO.class)))
+ .collect(Collectors.groupingBy(CustomerInvoiceContactVO::getInvoiceId));
+ return invoices.stream().map(invoice -> {
+ CustomerInvoiceInfoVO invoiceVO = Objects.requireNonNull(BeanUtil.copyProperties(invoice, CustomerInvoiceInfoVO.class));
+ invoiceVO.setContacts(contactMap.getOrDefault(invoice.getId(), new ArrayList<>()));
+ return invoiceVO;
+ }).toList();
}
private List loadScores(Long customerId) {
@@ -621,6 +735,16 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl();
@@ -928,6 +1052,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl VEHICLE_TYPES = List.of("车辆", "船舶");
private static final List INSURANCE_FIELD_KEYS = List.of(
"保险类型", "保单号", "开始日期", "结束日期", "保额", "保费", "发票号", "开票日期", "备注"
);
@@ -64,6 +65,7 @@ public class InsuranceOcrTemplateServiceImpl extends BaseServiceImpl queryWrapper = Wrappers.lambdaQuery()
.eq(InsuranceOcrTemplate::getIsDeleted, 0)
.like(StringUtil.isNotBlank(insuranceOcrTemplate.getName()), InsuranceOcrTemplate::getName, insuranceOcrTemplate.getName())
+ .eq(StringUtil.isNotBlank(insuranceOcrTemplate.getVehicleType()), InsuranceOcrTemplate::getVehicleType, insuranceOcrTemplate.getVehicleType())
.orderByDesc(InsuranceOcrTemplate::getCreateTime);
return InsuranceOcrTemplateWrapper.build().pageVO(page(page, queryWrapper));
}
@@ -79,6 +81,7 @@ public class InsuranceOcrTemplateServiceImpl extends BaseServiceImpl NAME_MAX_LENGTH) {
throw new ServiceException("模板名称不能超过100个字符");
}
+ if (StringUtil.isBlank(insuranceOcrTemplate.getVehicleType())) {
+ throw new ServiceException("请选择车船类型");
+ }
+ if (!VEHICLE_TYPES.contains(insuranceOcrTemplate.getVehicleType())) {
+ throw new ServiceException("车船类型仅支持车辆或船舶");
+ }
Long nameCount = count(Wrappers.lambdaQuery()
.eq(InsuranceOcrTemplate::getIsDeleted, 0)
.eq(InsuranceOcrTemplate::getName, insuranceOcrTemplate.getName())
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InsuranceRecordServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InsuranceRecordServiceImpl.java
index 53136b5..3c0ed29 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InsuranceRecordServiceImpl.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InsuranceRecordServiceImpl.java
@@ -85,6 +85,7 @@ public class InsuranceRecordServiceImpl extends BaseServiceImpl INSURANCE_TYPES = Set.of("交强险", "商业险", "承运人责任险", "货运险", "船舶险");
private static final Set SUPPORT_FILE_TYPES = Set.of("jpg", "jpeg", "png", "bmp");
private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4})[-/.年](\\d{1,2})[-/.月](\\d{1,2})日?");
+ private static final Pattern POLICY_NO_PATTERN = Pattern.compile("^[a-zA-Z0-9]+$");
private final IBaiduOcrService baiduOcrService;
private final IInsuranceOcrTemplateService insuranceOcrTemplateService;
@@ -157,6 +158,11 @@ public class InsuranceRecordServiceImpl extends BaseServiceImpl invoiceInfos = customer == null ? List.of() : activeInvoiceInfos(customer.getId());
+ List invoiceInfos = customer == null ? List.of() : activeInvoiceInfoVOs(customer.getId());
Map result = new LinkedHashMap<>();
result.put("issuerName", first.getPayeeName());
result.put("receiverName", first.getPayerName());
@@ -321,8 +327,8 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl invoiceInfos = activeInvoiceInfos(customer.getId());
- CustomerInvoiceInfo invoiceInfo = invoiceInfos.stream()
+ List invoiceInfos = activeInvoiceInfoVOs(customer.getId());
+ CustomerInvoiceInfoVO invoiceInfo = invoiceInfos.stream()
.filter(item -> Objects.equals(item.getId(), request.getReceiverInvoiceInfoId()))
.findFirst().orElseThrow(() -> new ServiceException("请选择受票方有效的开票信息"));
String departmentEmails = normalizeDepartmentEmails(request.getDepartmentEmails(), invoiceInfos);
@@ -655,8 +661,32 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl invoiceInfoEmails(List invoiceInfos) {
- return invoiceInfos.stream().map(CustomerInvoiceInfo::getEmail).filter(Func::isNotEmpty)
+ private List activeInvoiceInfoVOs(Long customerId) {
+ List invoiceInfos = activeInvoiceInfos(customerId);
+ if (Func.isEmpty(invoiceInfos)) {
+ return List.of();
+ }
+ List invoiceIds = invoiceInfos.stream().map(CustomerInvoiceInfo::getId).toList();
+ Map> contactMap = customerInvoiceContactMapper.selectList(
+ Wrappers.lambdaQuery()
+ .in(CustomerInvoiceContact::getInvoiceId, invoiceIds)
+ .eq(CustomerInvoiceContact::getIsDeleted, 0)
+ .orderByAsc(CustomerInvoiceContact::getCreateTime))
+ .stream()
+ .map(contact -> Objects.requireNonNull(BeanUtil.copyProperties(contact, CustomerInvoiceContactVO.class)))
+ .collect(Collectors.groupingBy(CustomerInvoiceContactVO::getInvoiceId));
+ return invoiceInfos.stream().map(invoice -> {
+ CustomerInvoiceInfoVO invoiceVO = Objects.requireNonNull(BeanUtil.copyProperties(invoice, CustomerInvoiceInfoVO.class));
+ invoiceVO.setContacts(contactMap.getOrDefault(invoice.getId(), new ArrayList<>()));
+ return invoiceVO;
+ }).toList();
+ }
+
+ private List invoiceInfoEmails(List invoiceInfos) {
+ return invoiceInfos.stream()
+ .flatMap(invoice -> (invoice.getContacts() == null ? List.of() : invoice.getContacts()).stream())
+ .map(CustomerInvoiceContactVO::getEmail)
+ .filter(Func::isNotEmpty)
.flatMap(value -> splitEmails(value).stream())
.collect(Collectors.toMap(email -> email.toLowerCase(Locale.ROOT), Function.identity(),
(first, duplicate) -> first, LinkedHashMap::new)).values().stream().toList();
@@ -708,7 +738,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl invoiceInfos) {
+ private String normalizeDepartmentEmails(String value, List invoiceInfos) {
Map configuredEmails = invoiceInfoEmails(invoiceInfos).stream()
.collect(Collectors.toMap(email -> email.toLowerCase(Locale.ROOT), Function.identity(),
(first, duplicate) -> first, LinkedHashMap::new));
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/OtherExpenseRecordServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/OtherExpenseRecordServiceImpl.java
index 9d5de88..e854595 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/OtherExpenseRecordServiceImpl.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/OtherExpenseRecordServiceImpl.java
@@ -12,7 +12,9 @@ import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
+import org.springblade.system.cache.DictBizCache;
import org.springblade.system.cache.UserCache;
+import org.springblade.system.pojo.entity.DictBiz;
import org.springblade.transport.excel.OtherExpenseRecordExcel;
import org.springblade.transport.excel.OtherExpenseRecordExportExcel;
import org.springblade.transport.mapper.OtherExpenseRecordMapper;
@@ -28,6 +30,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* 其他费用记录 服务实现类
@@ -43,7 +46,7 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl EXPENSE_TYPES = Set.of("过路费", "停车费", "维修费", "保险费", "年检费", "装卸费", "其他");
+ private static final String EXPENSE_TYPE_DICT_CODE = "other_fee_category";
@Override
public IPage selectOtherExpenseRecordPage(IPage page, OtherExpenseRecordVO otherExpenseRecord) {
@@ -103,7 +106,7 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl validationErrors = new ArrayList<>();
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getExpenseDate()), "费用日期不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getExpenseType()), "费用类型不能为空");
- org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(otherExpenseRecord.getExpenseType()) && !EXPENSE_TYPES.contains(otherExpenseRecord.getExpenseType()), "费用类型不正确");
+ org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(otherExpenseRecord.getExpenseType()) && !loadExpenseTypeValues().contains(otherExpenseRecord.getExpenseType()), "费用类型不正确");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getVehicleType()), "车船类型不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(otherExpenseRecord.getVehicleType()) && !VEHICLE.equals(otherExpenseRecord.getVehicleType()) && !SHIP.equals(otherExpenseRecord.getVehicleType()), "车船类型不正确");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getVehicleNo()), "车牌号/船号不能为空");
@@ -152,7 +155,7 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl loadExpenseTypeValues() {
+ List expenseTypes = DictBizCache.getList(EXPENSE_TYPE_DICT_CODE);
+ if (Func.isEmpty(expenseTypes)) {
+ return Set.of();
+ }
+ return expenseTypes.stream()
+ .map(DictBiz::getDictValue)
+ .filter(Objects::nonNull)
+ .map(String::trim)
+ .filter(Func::isNotEmpty)
+ .collect(Collectors.toSet());
+ }
+
}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceivablePayableDetailServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceivablePayableDetailServiceImpl.java
index d5f0187..50749f1 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceivablePayableDetailServiceImpl.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ReceivablePayableDetailServiceImpl.java
@@ -1565,11 +1565,11 @@ public class ReceivablePayableDetailServiceImpl
private BigDecimal calculateRule(Map rule, Waybill waybill) {
String element = stringValue(rule, "billingElement", ""); String type = stringValue(rule, "billingType", "");
- BigDecimal base = measure(rule, waybill); BigDecimal unit = decimal(rule.get("unitPrice"));
+ BigDecimal measuredBase = measure(rule, waybill); BigDecimal unit = decimal(rule.get("unitPrice"));
List> ranges = ranges(rule);
boolean intervalUnitPrice = "区间单价".equals(type);
boolean intervalFlatPrice = type.contains("区间") && type.contains("一口价");
- if (!intervalUnitPrice && !intervalFlatPrice) base = applyMinimum(base, rule, waybill);
+ BigDecimal base = (!intervalUnitPrice && !intervalFlatPrice) ? applyMinimum(measuredBase, rule, waybill) : measuredBase;
if ("固定一口价".equals(type)) return unit.setScale(2, RoundingMode.HALF_UP);
if (ranges.isEmpty() || "固定单价".equals(type)) return base.multiply(unit).setScale(2, RoundingMode.HALF_UP);
if (intervalFlatPrice) return range(ranges, base).map(r -> decimal(r.get("unitPrice")).signum() == 0 ? unit : decimal(r.get("unitPrice"))).orElse(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
diff --git a/doc/sql/transport/blade_customer_archive.sql b/doc/sql/transport/blade_customer_archive.sql
index 680e9fc..640af81 100644
--- a/doc/sql/transport/blade_customer_archive.sql
+++ b/doc/sql/transport/blade_customer_archive.sql
@@ -9,6 +9,7 @@ CREATE TABLE `blade_customer_archive` (
`short_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客商简称',
`full_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '客商全称',
`customer_nature` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '客商性质',
+ `guangxi_top100` tinyint NULL DEFAULT NULL COMMENT '是否广西百强:0否,1是',
`unified_credit_code` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '统一社会信用代码',
`customer_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '客商类型',
`project_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属项目',
@@ -22,6 +23,7 @@ CREATE TABLE `blade_customer_archive` (
`dept_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '所属组织',
`invoice_tax_rate` decimal(6,2) NULL DEFAULT NULL COMMENT '开票税点',
`business_scope` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '经营范围',
+ `network_freight_platform` tinyint NULL DEFAULT NULL COMMENT '网络货运平台:0否,1是',
`business_term_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '营业期限类型',
`business_end_date` date NULL DEFAULT NULL COMMENT '营业期限截止日',
`registered_capital` decimal(18,2) NULL DEFAULT NULL COMMENT '注册资金(万元)',
@@ -116,21 +118,13 @@ CREATE TABLE `blade_customer_invoice_info` (
`id` bigint NOT NULL COMMENT '主键',
`tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',
`customer_id` bigint NOT NULL COMMENT '客商ID',
- `invoice_title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '受票方名称',
- `invoice_type` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '发票类型',
+ `invoice_title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '企业全称',
`tax_no` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '纳税人识别号',
`bank_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '开户行名称',
- `registered_phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '注册电话',
`bank_account` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '银行账号',
`registered_address` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '注册地址',
`registered_region_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '注册地址行政区划',
`registered_detail_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '注册地址详细地址',
- `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
- `receiver_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收件人姓名',
- `receiver_phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收件人电话',
- `receiver_address` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收件人地址',
- `receiver_region_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收件人地址行政区划',
- `receiver_detail_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收件人详细地址',
`is_default` int NULL DEFAULT 0 COMMENT '是否默认',
`create_user` bigint NULL DEFAULT NULL COMMENT '创建人',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
@@ -143,6 +137,31 @@ CREATE TABLE `blade_customer_invoice_info` (
KEY `idx_customer_invoice_customer` (`tenant_id`, `customer_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '客商发票信息';
+-- ----------------------------
+-- Table structure for blade_customer_invoice_contact
+-- ----------------------------
+DROP TABLE IF EXISTS `blade_customer_invoice_contact`;
+CREATE TABLE `blade_customer_invoice_contact` (
+ `id` bigint NOT NULL COMMENT '主键',
+ `tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',
+ `invoice_id` bigint NOT NULL COMMENT '发票信息ID',
+ `contact_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系人',
+ `contact_phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系电话',
+ `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱地址',
+ `dept_ids` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属部门ID集合',
+ `dept_names` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属部门',
+ `remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
+ `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',
+ `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
+ `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
+ `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',
+ `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
+ `status` int NULL DEFAULT 1 COMMENT '状态',
+ `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_customer_invoice_contact_invoice` (`tenant_id`, `invoice_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '客商发票联系信息';
+
-- ----------------------------
-- Table structure for blade_customer_credit_score
-- ----------------------------
diff --git a/doc/sql/transport/blade_customer_archive_guangxi_top100_20260914.sql b/doc/sql/transport/blade_customer_archive_guangxi_top100_20260914.sql
new file mode 100644
index 0000000..8f56222
--- /dev/null
+++ b/doc/sql/transport/blade_customer_archive_guangxi_top100_20260914.sql
@@ -0,0 +1,4 @@
+-- 客商档案:新增「是否广西百强」字段(是否,无默认值,未选择时保持 NULL)
+
+ALTER TABLE `blade_customer_archive`
+ ADD COLUMN `guangxi_top100` tinyint NULL DEFAULT NULL COMMENT '是否广西百强:0否,1是' AFTER `customer_nature`;
diff --git a/doc/sql/transport/blade_customer_archive_network_freight_platform_20260914.sql b/doc/sql/transport/blade_customer_archive_network_freight_platform_20260914.sql
new file mode 100644
index 0000000..d28f143
--- /dev/null
+++ b/doc/sql/transport/blade_customer_archive_network_freight_platform_20260914.sql
@@ -0,0 +1,4 @@
+-- 客商档案:新增「网络货运平台」字段(是否,无默认值)
+
+ALTER TABLE `blade_customer_archive`
+ ADD COLUMN `network_freight_platform` tinyint NULL DEFAULT NULL COMMENT '网络货运平台:0否,1是' AFTER `business_scope`;
diff --git a/doc/sql/transport/blade_customer_invoice_contact_20260914.sql b/doc/sql/transport/blade_customer_invoice_contact_20260914.sql
new file mode 100644
index 0000000..80d4cce
--- /dev/null
+++ b/doc/sql/transport/blade_customer_invoice_contact_20260914.sql
@@ -0,0 +1,63 @@
+-- 客商发票信息:去掉邮寄信息/发票类型/注册电话,改为维护多个联系信息
+
+CREATE TABLE IF NOT EXISTS `blade_customer_invoice_contact` (
+ `id` bigint NOT NULL COMMENT '主键',
+ `tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',
+ `invoice_id` bigint NOT NULL COMMENT '发票信息ID',
+ `contact_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系人',
+ `contact_phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系电话',
+ `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱地址',
+ `dept_ids` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属部门ID集合',
+ `dept_names` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属部门',
+ `remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
+ `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',
+ `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
+ `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
+ `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',
+ `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
+ `status` int NULL DEFAULT 1 COMMENT '状态',
+ `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_customer_invoice_contact_invoice` (`tenant_id`, `invoice_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '客商发票联系信息';
+
+INSERT INTO `blade_customer_invoice_contact` (
+ `id`, `tenant_id`, `invoice_id`, `contact_name`, `contact_phone`, `email`,
+ `dept_ids`, `dept_names`, `remark`, `create_user`, `create_dept`, `create_time`,
+ `update_user`, `update_time`, `status`, `is_deleted`
+)
+SELECT
+ invoice.`id`,
+ invoice.`tenant_id`,
+ invoice.`id`,
+ invoice.`receiver_name`,
+ invoice.`receiver_phone`,
+ invoice.`email`,
+ NULL,
+ NULL,
+ NULL,
+ invoice.`create_user`,
+ invoice.`create_dept`,
+ invoice.`create_time`,
+ invoice.`update_user`,
+ invoice.`update_time`,
+ IFNULL(invoice.`status`, 1),
+ IFNULL(invoice.`is_deleted`, 0)
+FROM `blade_customer_invoice_info` invoice
+WHERE (IFNULL(invoice.`receiver_name`, '') <> ''
+ OR IFNULL(invoice.`receiver_phone`, '') <> ''
+ OR IFNULL(invoice.`email`, '') <> '')
+ AND NOT EXISTS (
+ SELECT 1 FROM `blade_customer_invoice_contact` contact WHERE contact.`id` = invoice.`id`
+ );
+
+ALTER TABLE `blade_customer_invoice_info`
+ MODIFY COLUMN `invoice_title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '企业全称',
+ DROP COLUMN `invoice_type`,
+ DROP COLUMN `registered_phone`,
+ DROP COLUMN `email`,
+ DROP COLUMN `receiver_name`,
+ DROP COLUMN `receiver_phone`,
+ DROP COLUMN `receiver_address`,
+ DROP COLUMN `receiver_region_name`,
+ DROP COLUMN `receiver_detail_address`;
diff --git a/doc/sql/transport/blade_insurance_ocr_template.sql b/doc/sql/transport/blade_insurance_ocr_template.sql
index 8bd1f7f..7423078 100644
--- a/doc/sql/transport/blade_insurance_ocr_template.sql
+++ b/doc/sql/transport/blade_insurance_ocr_template.sql
@@ -6,6 +6,7 @@ CREATE TABLE `blade_insurance_ocr_template` (
`id` bigint(20) NOT NULL COMMENT '主键',
`tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '000000' COMMENT '租户ID',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '模板名称',
+ `vehicle_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '车辆' COMMENT '车船类型:车辆/船舶',
`mapping_config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '字段映射配置JSON',
`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
@@ -16,6 +17,7 @@ CREATE TABLE `blade_insurance_ocr_template` (
`is_deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_insurance_ocr_template_name` (`tenant_id`, `name`) USING BTREE,
+ KEY `idx_insurance_ocr_template_vehicle_type` (`tenant_id`, `vehicle_type`) USING BTREE,
KEY `idx_insurance_ocr_template_status` (`status`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='保险OCR识别模板';
diff --git a/doc/sql/transport/blade_insurance_ocr_template_vehicle_type_20260914.sql b/doc/sql/transport/blade_insurance_ocr_template_vehicle_type_20260914.sql
new file mode 100644
index 0000000..141d93e
--- /dev/null
+++ b/doc/sql/transport/blade_insurance_ocr_template_vehicle_type_20260914.sql
@@ -0,0 +1,5 @@
+-- 保险OCR识别模板:新增车船类型字段
+
+ALTER TABLE `blade_insurance_ocr_template`
+ ADD COLUMN `vehicle_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '车辆' COMMENT '车船类型:车辆/船舶' AFTER `name`,
+ ADD KEY `idx_insurance_ocr_template_vehicle_type` (`tenant_id`, `vehicle_type`) USING BTREE;
diff --git a/hs_err_pid22822.log b/hs_err_pid22822.log
new file mode 100644
index 0000000..9cc29df
--- /dev/null
+++ b/hs_err_pid22822.log
@@ -0,0 +1,157 @@
+#
+# A fatal error has been detected by the Java Runtime Environment:
+#
+# SIGBUS (0xa) at pc=0x0000000104d364c0, pid=22822, tid=5379
+#
+# JRE version: OpenJDK Runtime Environment Zulu17.44+15-CA (17.0.8+7) (build 17.0.8+7-LTS)
+# Java VM: OpenJDK 64-Bit Server VM Zulu17.44+15-CA (17.0.8+7-LTS, mixed mode, emulated-client, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)
+# Problematic frame:
+# C [libzip.dylib+0x64c0] newEntry+0x68
+#
+# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
+#
+# If you would like to submit a bug report, please visit:
+# http://www.azul.com/support/
+# The crash happened outside the Java Virtual Machine in native code.
+# See problematic frame for where to report the bug.
+#
+
+--------------- S U M M A R Y ------------
+
+Command Line: -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:54448,suspend=y,server=n -javaagent:/Users/liangxin/Library/Caches/JetBrains/IntelliJIdea2026.1/captureAgent/debugger-agent.jar=file:///var/folders/pk/drq93rk10q733kk02fgp89xh0000gn/T/capture14073385024433055703.props -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dmanagement.endpoints.jmx.exposure.include=* -Dkotlinx.coroutines.debug.enable.creation.stack.trace=false -Ddebugger.agent.enable.coroutines=true -Dkotlinx.coroutines.debug.enable.flows.stack.trace=true -Dkotlinx.coroutines.debug.enable.mutable.state.flows.stack.trace=true -Ddebugger.async.stack.trace.for.all.threads=true -Dfile.encoding=UTF-8 org.springblade.admin.AdminApplication
+
+Host: "Mac15,6" arm64, 12 cores, 36G, Darwin 25.6.0, macOS 26.6.2 (25G83)
+Time: Mon Sep 14 22:15:03 2026 CST elapsed time: 5.909458 seconds (0d 0h 0m 5s)
+
+--------------- T H R E A D ---------------
+
+Current thread (0x000000013601ac00): JavaThread "main" [_thread_in_native, id=5379, stack(0x000000016b250000,0x000000016b453000)]
+
+Stack: [0x000000016b250000,0x000000016b453000], sp=0x000000016b450260, free space=2048k
+Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
+C [libzip.dylib+0x64c0] newEntry+0x68
+C [libzip.dylib+0x6390] ZIP_GetEntry2+0x14c
+C [libzip.dylib+0x6d78] ZIP_FindEntry+0x3c
+V [libjvm.dylib+0x25a408] ClassPathZipEntry::open_entry(JavaThread*, char const*, int*, bool)+0xb4
+V [libjvm.dylib+0x25a53c] ClassPathZipEntry::open_stream(JavaThread*, char const*)+0x20
+V [libjvm.dylib+0x25d918] ClassLoader::load_class(Symbol*, bool, JavaThread*)+0x150
+V [libjvm.dylib+0x981d60] SystemDictionary::load_instance_class_impl(Symbol*, Handle, JavaThread*)+0x2d0
+V [libjvm.dylib+0x98063c] SystemDictionary::load_instance_class(unsigned int, Symbol*, Handle, JavaThread*)+0x30
+V [libjvm.dylib+0x97fd48] SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle, Handle, JavaThread*)+0x4dc
+V [libjvm.dylib+0x97f334] SystemDictionary::resolve_or_fail(Symbol*, Handle, Handle, bool, JavaThread*)+0x80
+V [libjvm.dylib+0x2beb54] ConstantPool::klass_at_impl(constantPoolHandle const&, int, JavaThread*)+0x1e0
+V [libjvm.dylib+0x46d6f0] InterpreterRuntime::_new(JavaThread*, ConstantPool*, int)+0x94
+j com.intellij.rt.debugger.agent.CaptureStorage.createCapturedStack(Ljava/lang/Throwable;Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;)Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;+14
+j com.intellij.rt.debugger.agent.CaptureStorage.access$500(Ljava/lang/Throwable;Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;)Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;+2
+j com.intellij.rt.debugger.agent.CaptureStorage$3.run()V+75
+j com.intellij.rt.debugger.agent.OverheadDetector$PerThread.runIfNoOverhead(Ljava/lang/Runnable;)Z+62
+j com.intellij.rt.debugger.agent.CaptureStorage.runWithOverheadTrackingAndWithoutThrowableCapture(Lcom/intellij/rt/debugger/agent/CaptureStorage$ThreadLocalContext;Ljava/lang/Runnable;)Z+15
+j com.intellij.rt.debugger.agent.CaptureStorage.capture(Ljava/lang/Object;)V+26
+j java.util.concurrent.FutureTask.(Ljava/util/concurrent/Callable;)V+5 java.base@17.0.8
+j org.springframework.cglib.core.internal.LoadingCache.createEntry(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+27
+j org.springframework.cglib.core.internal.LoadingCache.get(Ljava/lang/Object;)Ljava/lang/Object;+39
+j org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(Lorg/springframework/cglib/core/AbstractClassGenerator;Z)Ljava/lang/Object;+11
+j org.springframework.cglib.core.AbstractClassGenerator.create(Ljava/lang/Object;)Ljava/lang/Object;+115
+j org.springframework.cglib.reflect.FastClass$Generator.create()Lorg/springframework/cglib/reflect/FastClass;+19
+j org.springframework.cglib.proxy.MethodProxy.helper(Lorg/springframework/cglib/proxy/MethodProxy$CreateInfo;Ljava/lang/Class;)Lorg/springframework/cglib/reflect/FastClass;+54
+j org.springframework.cglib.proxy.MethodProxy.init()V+40
+j org.springframework.cglib.proxy.MethodProxy.create(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/springframework/cglib/proxy/MethodProxy;+80
+j org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration$BlockingLoadBalancerRetryConfig$$SpringCGLIB$$0.CGLIB$STATICHOOK1()V+112
+j org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration$BlockingLoadBalancerRetryConfig$$SpringCGLIB$$0.()V+3
+v ~StubRoutines::call_stub
+V [libjvm.dylib+0x4781f8] JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, JavaThread*)+0x394
+V [libjvm.dylib+0x457c84] InstanceKlass::call_class_initializer(JavaThread*)+0x1e8
+V [libjvm.dylib+0x456f48] InstanceKlass::initialize_impl(JavaThread*)+0x65c
+V [libjvm.dylib+0x51e020] JVM_FindClassFromCaller+0x340
+C [libjava.dylib+0x3a6c] Java_java_lang_Class_forName0+0x138
+J 1317 java.lang.Class.forName0(Ljava/lang/String;ZLjava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class; java.base@17.0.8 (0 bytes) @ 0x000000010ed5306c [0x000000010ed52fc0+0x00000000000000ac]
+V [libjvm.dylib+0x4715a0] InterpreterRuntime::resolve_from_cache(JavaThread*, Bytecodes::Code)+0x98
+
+Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
+j com.intellij.rt.debugger.agent.CaptureStorage.createCapturedStack(Ljava/lang/Throwable;Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;)Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;+14
+j com.intellij.rt.debugger.agent.CaptureStorage.access$500(Ljava/lang/Throwable;Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;)Lcom/intellij/rt/debugger/agent/CaptureStorage$CapturedStack;+2
+j com.intellij.rt.debugger.agent.CaptureStorage$3.run()V+75
+j com.intellij.rt.debugger.agent.OverheadDetector$PerThread.runIfNoOverhead(Ljava/lang/Runnable;)Z+62
+j com.intellij.rt.debugger.agent.CaptureStorage.runWithOverheadTrackingAndWithoutThrowableCapture(Lcom/intellij/rt/debugger/agent/CaptureStorage$ThreadLocalContext;Ljava/lang/Runnable;)Z+15
+j com.intellij.rt.debugger.agent.CaptureStorage.capture(Ljava/lang/Object;)V+26
+j java.util.concurrent.FutureTask.(Ljava/util/concurrent/Callable;)V+5 java.base@17.0.8
+j org.springframework.cglib.core.internal.LoadingCache.createEntry(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+27
+j org.springframework.cglib.core.internal.LoadingCache.get(Ljava/lang/Object;)Ljava/lang/Object;+39
+j org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(Lorg/springframework/cglib/core/AbstractClassGenerator;Z)Ljava/lang/Object;+11
+j org.springframework.cglib.core.AbstractClassGenerator.create(Ljava/lang/Object;)Ljava/lang/Object;+115
+j org.springframework.cglib.reflect.FastClass$Generator.create()Lorg/springframework/cglib/reflect/FastClass;+19
+j org.springframework.cglib.proxy.MethodProxy.helper(Lorg/springframework/cglib/proxy/MethodProxy$CreateInfo;Ljava/lang/Class;)Lorg/springframework/cglib/reflect/FastClass;+54
+j org.springframework.cglib.proxy.MethodProxy.init()V+40
+j org.springframework.cglib.proxy.MethodProxy.create(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/springframework/cglib/proxy/MethodProxy;+80
+j org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration$BlockingLoadBalancerRetryConfig$$SpringCGLIB$$0.CGLIB$STATICHOOK1()V+112
+j org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration$BlockingLoadBalancerRetryConfig$$SpringCGLIB$$0.()V+3
+v ~StubRoutines::call_stub
+J 1317 java.lang.Class.forName0(Ljava/lang/String;ZLjava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class; java.base@17.0.8 (0 bytes) @ 0x000000010ed5306c [0x000000010ed52fc0+0x00000000000000ac]
+J 1432 c1 java.lang.Class.forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class; java.base@17.0.8 (47 bytes) @ 0x000000010ed807c8 [0x000000010ed806c0+0x0000000000000108]
+j org.springframework.cglib.core.ReflectUtils.defineClass(Ljava/lang/String;[BLjava/lang/ClassLoader;Ljava/security/ProtectionDomain;Ljava/lang/Class;)Ljava/lang/Class;+460
+j org.springframework.cglib.core.AbstractClassGenerator.generate(Lorg/springframework/cglib/core/AbstractClassGenerator$ClassLoaderData;)Ljava/lang/Class;+209
+j org.springframework.cglib.proxy.Enhancer.generate(Lorg/springframework/cglib/core/AbstractClassGenerator$ClassLoaderData;)Ljava/lang/Class;+53
+j org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(Lorg/springframework/cglib/core/AbstractClassGenerator;)Ljava/lang/Object;+2
+j org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$$Lambda$857+0x00000070016c9418.apply(Ljava/lang/Object;)Ljava/lang/Object;+8
+j org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(Ljava/lang/Object;)Ljava/lang/Object;+5
+j org.springframework.cglib.core.internal.LoadingCache$$Lambda$859+0x00000070016c9a98.call()Ljava/lang/Object;+8
+j java.util.concurrent.FutureTask.run$$$capture()V+39 java.base@17.0.8
+j java.util.concurrent.FutureTask.run()V+5 java.base@17.0.8
+j org.springframework.cglib.core.internal.LoadingCache.createEntry(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+56
+j org.springframework.cglib.core.internal.LoadingCache.get(Ljava/lang/Object;)Ljava/lang/Object;+39
+j org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(Lorg/springframework/cglib/core/AbstractClassGenerator;Z)Ljava/lang/Object;+11
+j org.springframework.cglib.core.AbstractClassGenerator.create(Ljava/lang/Object;)Ljava/lang/Object;+115
+j org.springframework.cglib.proxy.Enhancer.createHelper()Ljava/lang/Object;+102
+j org.springframework.cglib.proxy.Enhancer.createClass()Ljava/lang/Class;+6
+j org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(Lorg/springframework/cglib/proxy/Enhancer;Z)Ljava/lang/Class;+1
+j org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/lang/Class;+134
+j org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;)V+418
+j org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;)V+78
+j org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(Ljava/util/Collection;Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;)V+61
+j org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;Ljava/util/List;)V+521
+j org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;)V+5
+j org.springframework.context.support.AbstractApplicationContext.refresh()V+62
+j org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh()V+1
+j org.springframework.boot.SpringApplication.refresh(Lorg/springframework/context/ConfigurableApplicationContext;)V+1
+j org.springframework.boot.SpringApplication.refreshContext(Lorg/springframework/context/ConfigurableApplicationContext;)V+19
+j org.springframework.boot.SpringApplication.run([Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;+113
+j org.springframework.boot.builder.SpringApplicationBuilder.run([Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;+38
+j org.springblade.core.launch.BladeApplication.run(Ljava/lang/String;Ljava/lang/Class;[Ljava/lang/String;)Lorg/springframework/context/ConfigurableApplicationContext;+9
+j org.springblade.admin.AdminApplication.main([Ljava/lang/String;)V+5
+v ~StubRoutines::call_stub
+
+siginfo: si_signo: 10 (SIGBUS), si_code: 1 (BUS_ADRALN), si_addr: 0x0000000104cfe958
+
+Register to memory mapping:
+
+ x0=0x0000600001ebc820 points into unknown readable memory: 0x0000000000000000 | 00 00 00 00 00 00 00 00
+ x1=0x0 is NULL
+ x2=0xfffffffffffffff0 is an unknown value
+ x3=0x0000600001ebc830 points into unknown readable memory: 0x0000000000000000 | 00 00 00 00 00 00 00 00
+ x4=0x0000600001ebc880 points into unknown readable memory: 0x0000000000000000 | 00 00 00 00 00 00 00 00
+ x5=0x000000009b858ffb is an unknown value
+ x6=0x000000001ba00000 is an unknown value
+ x7=0x00000005c0183f80 is an oop: com.intellij.rt.debugger.agent.CaptureStorage$ConcurrentIdentityWeakHashMap$WeakKey
+{0x00000005c0183f80} - klass: 'com/intellij/rt/debugger/agent/CaptureStorage$ConcurrentIdentityWeakHashMap$WeakKey'
+ - ---- fields (total size 4 words):
+ - private 'referent' 'Ljava/lang/Object;' @12 a 'java/lang/Thread'{0x00000005c0203dc0} (b80407b8)
+ - volatile 'queue' 'Ljava/lang/ref/ReferenceQueue;' @16 a 'java/lang/ref/ReferenceQueue'{0x00000005c0183ea0} (b80307d4)
+ - volatile 'next' 'Ljava/lang/ref/Reference;' @20 NULL (0)
+ - private transient 'discovered' 'Ljava/lang/ref/Reference;' @24 NULL (0)
+ - private final 'myHash' 'I' @28 897913732 (35851384)
+ x8=0x0000000104e2693c: getProcessHandle.procHandle+0x971c in /Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home/lib/libjava.dylib at 0x0000000104e00000
+ x9=0x0000000000128000 is an unknown value
+x10=0x0000600001ebc000 points into unknown readable memory: 0x4c2800498af20001 | 01 00 f2 8a 49 00 28 4c
+x11=0x0000000000000820 is an unknown value
+x12=0x0000000000000050 is an unknown value
+x13=0x0000000000000001 is an unknown value
+x14=0x00000000ffffff4e is an unknown value
+x15=0x00000000000007fb is an unknown value
+x16=0x00000001829fd030: __bzero+0 in /usr/lib/system/libsystem_platform.dylib at 0x00000001829fa000
+x17=0x00000001f0a754a8 points into unknown readable memory: 0x00000001829fd030 | 30 d0 9f 82 01 00 00 00
+x18=0x0 is NULL
+x19=0x0000600001ebc820 points into unknown readable memory: 0x0000000000000000 | 00 00 00 00 00 00 00 00
+x20=0x0 is NULL
+x21=0x0000600000bf4240 points into unknown readable memory: 0x0000600001af00c0 | c0 00 af 01 00 60 00 00
+x22=0x0000000104cfe93c points into unknown readable memory: 50 4b 01 02
+x23=
\ No newline at end of file