diff --git a/.image/Java监控.jpg b/.image/Java监控.jpg new file mode 100644 index 0000000..6ad522a Binary files /dev/null and b/.image/Java监控.jpg differ diff --git a/.image/MySQL.jpg b/.image/MySQL.jpg new file mode 100644 index 0000000..64a1940 Binary files /dev/null and b/.image/MySQL.jpg differ diff --git a/.image/OA请假-列表.jpg b/.image/OA请假-列表.jpg new file mode 100644 index 0000000..787bb73 Binary files /dev/null and b/.image/OA请假-列表.jpg differ diff --git a/.image/OA请假-发起.jpg b/.image/OA请假-发起.jpg new file mode 100644 index 0000000..1a7342d Binary files /dev/null and b/.image/OA请假-发起.jpg differ diff --git a/.image/OA请假-详情.jpg b/.image/OA请假-详情.jpg new file mode 100644 index 0000000..a83e7c1 Binary files /dev/null and b/.image/OA请假-详情.jpg differ diff --git a/.image/Redis.jpg b/.image/Redis.jpg new file mode 100644 index 0000000..9569352 Binary files /dev/null and b/.image/Redis.jpg differ diff --git a/.image/admin-uniapp/01.png b/.image/admin-uniapp/01.png new file mode 100644 index 0000000..0f65d99 Binary files /dev/null and b/.image/admin-uniapp/01.png differ diff --git a/.image/admin-uniapp/02.png b/.image/admin-uniapp/02.png new file mode 100644 index 0000000..05ec781 Binary files /dev/null and b/.image/admin-uniapp/02.png differ diff --git a/.image/admin-uniapp/03.png b/.image/admin-uniapp/03.png new file mode 100644 index 0000000..f400c68 Binary files /dev/null and b/.image/admin-uniapp/03.png differ diff --git a/.image/admin-uniapp/04.png b/.image/admin-uniapp/04.png new file mode 100644 index 0000000..d5d5ea0 Binary files /dev/null and b/.image/admin-uniapp/04.png differ diff --git a/.image/admin-uniapp/05.png b/.image/admin-uniapp/05.png new file mode 100644 index 0000000..1de6d8a Binary files /dev/null and b/.image/admin-uniapp/05.png differ diff --git a/.image/admin-uniapp/06.png b/.image/admin-uniapp/06.png new file mode 100644 index 0000000..400ae90 Binary files /dev/null and b/.image/admin-uniapp/06.png differ diff --git a/.image/admin-uniapp/07.png b/.image/admin-uniapp/07.png new file mode 100644 index 0000000..2ed8c0f Binary files /dev/null and b/.image/admin-uniapp/07.png differ diff --git a/.image/admin-uniapp/08.png b/.image/admin-uniapp/08.png new file mode 100644 index 0000000..090e64a Binary files /dev/null and b/.image/admin-uniapp/08.png differ diff --git a/.image/admin-uniapp/09.png b/.image/admin-uniapp/09.png new file mode 100644 index 0000000..f2032c8 Binary files /dev/null and b/.image/admin-uniapp/09.png differ diff --git a/.image/common/ai-feature.png b/.image/common/ai-feature.png new file mode 100644 index 0000000..b4a55f5 Binary files /dev/null and b/.image/common/ai-feature.png differ diff --git a/.image/common/ai-preview.gif b/.image/common/ai-preview.gif new file mode 100644 index 0000000..5f13ac4 Binary files /dev/null and b/.image/common/ai-preview.gif differ diff --git a/.image/common/bpm-feature.png b/.image/common/bpm-feature.png new file mode 100644 index 0000000..23787fb Binary files /dev/null and b/.image/common/bpm-feature.png differ diff --git a/.image/common/crm-feature.png b/.image/common/crm-feature.png new file mode 100644 index 0000000..e1c9670 Binary files /dev/null and b/.image/common/crm-feature.png differ diff --git a/.image/common/erp-feature.png b/.image/common/erp-feature.png new file mode 100644 index 0000000..d30b30e Binary files /dev/null and b/.image/common/erp-feature.png differ diff --git a/.image/common/infra-feature.png b/.image/common/infra-feature.png new file mode 100644 index 0000000..f5cef50 Binary files /dev/null and b/.image/common/infra-feature.png differ diff --git a/.image/common/mall-feature.png b/.image/common/mall-feature.png new file mode 100644 index 0000000..cca05c0 Binary files /dev/null and b/.image/common/mall-feature.png differ diff --git a/.image/common/mall-preview.png b/.image/common/mall-preview.png new file mode 100644 index 0000000..e164cd2 Binary files /dev/null and b/.image/common/mall-preview.png differ diff --git a/.image/common/project-vs.png b/.image/common/project-vs.png new file mode 100644 index 0000000..561e092 Binary files /dev/null and b/.image/common/project-vs.png differ diff --git a/.image/common/ruoyi-vue-pro-architecture.png b/.image/common/ruoyi-vue-pro-architecture.png new file mode 100644 index 0000000..7bd7d59 Binary files /dev/null and b/.image/common/ruoyi-vue-pro-architecture.png differ diff --git a/.image/common/ruoyi-vue-pro-biz.png b/.image/common/ruoyi-vue-pro-biz.png new file mode 100644 index 0000000..24a385a Binary files /dev/null and b/.image/common/ruoyi-vue-pro-biz.png differ diff --git a/.image/common/system-feature.png b/.image/common/system-feature.png new file mode 100644 index 0000000..366087c Binary files /dev/null and b/.image/common/system-feature.png differ diff --git a/.image/common/yudao-cloud-architecture.png b/.image/common/yudao-cloud-architecture.png new file mode 100644 index 0000000..59416d8 Binary files /dev/null and b/.image/common/yudao-cloud-architecture.png differ diff --git a/.image/common/yudao-roadmap.png b/.image/common/yudao-roadmap.png new file mode 100644 index 0000000..f4becc9 Binary files /dev/null and b/.image/common/yudao-roadmap.png differ diff --git a/.image/个人中心.jpg b/.image/个人中心.jpg new file mode 100644 index 0000000..ce57f6e Binary files /dev/null and b/.image/个人中心.jpg differ diff --git a/.image/代码生成.jpg b/.image/代码生成.jpg new file mode 100644 index 0000000..751603e Binary files /dev/null and b/.image/代码生成.jpg differ diff --git a/.image/令牌管理.jpg b/.image/令牌管理.jpg new file mode 100644 index 0000000..04abf4d Binary files /dev/null and b/.image/令牌管理.jpg differ diff --git a/.image/任务列表-审批.jpg b/.image/任务列表-审批.jpg new file mode 100644 index 0000000..cba312a Binary files /dev/null and b/.image/任务列表-审批.jpg differ diff --git a/.image/任务列表-已办.jpg b/.image/任务列表-已办.jpg new file mode 100644 index 0000000..7a8d0fb Binary files /dev/null and b/.image/任务列表-已办.jpg differ diff --git a/.image/任务列表-待办.jpg b/.image/任务列表-待办.jpg new file mode 100644 index 0000000..a90323f Binary files /dev/null and b/.image/任务列表-待办.jpg differ diff --git a/.image/任务日志.jpg b/.image/任务日志.jpg new file mode 100644 index 0000000..599e50a Binary files /dev/null and b/.image/任务日志.jpg differ diff --git a/.image/商户信息.jpg b/.image/商户信息.jpg new file mode 100644 index 0000000..483eace Binary files /dev/null and b/.image/商户信息.jpg differ diff --git a/.image/在线用户.jpg b/.image/在线用户.jpg new file mode 100644 index 0000000..b183009 Binary files /dev/null and b/.image/在线用户.jpg differ diff --git a/.image/大屏设计器-列表.jpg b/.image/大屏设计器-列表.jpg new file mode 100644 index 0000000..9a45c3b Binary files /dev/null and b/.image/大屏设计器-列表.jpg differ diff --git a/.image/大屏设计器-编辑.jpg b/.image/大屏设计器-编辑.jpg new file mode 100644 index 0000000..63298a0 Binary files /dev/null and b/.image/大屏设计器-编辑.jpg differ diff --git a/.image/大屏设计器-预览.jpg b/.image/大屏设计器-预览.jpg new file mode 100644 index 0000000..501d9ea Binary files /dev/null and b/.image/大屏设计器-预览.jpg differ diff --git a/.image/字典数据.jpg b/.image/字典数据.jpg new file mode 100644 index 0000000..8298c89 Binary files /dev/null and b/.image/字典数据.jpg differ diff --git a/.image/字典类型.jpg b/.image/字典类型.jpg new file mode 100644 index 0000000..6613392 Binary files /dev/null and b/.image/字典类型.jpg differ diff --git a/.image/定时任务.jpg b/.image/定时任务.jpg new file mode 100644 index 0000000..d5bbd85 Binary files /dev/null and b/.image/定时任务.jpg differ diff --git a/.image/岗位管理.jpg b/.image/岗位管理.jpg new file mode 100644 index 0000000..42b64d2 Binary files /dev/null and b/.image/岗位管理.jpg differ diff --git a/.image/应用信息-列表.jpg b/.image/应用信息-列表.jpg new file mode 100644 index 0000000..da419a2 Binary files /dev/null and b/.image/应用信息-列表.jpg differ diff --git a/.image/应用信息-编辑.jpg b/.image/应用信息-编辑.jpg new file mode 100644 index 0000000..913cfbc Binary files /dev/null and b/.image/应用信息-编辑.jpg differ diff --git a/.image/应用管理.jpg b/.image/应用管理.jpg new file mode 100644 index 0000000..6e7789f Binary files /dev/null and b/.image/应用管理.jpg differ diff --git a/.image/我的流程-列表.jpg b/.image/我的流程-列表.jpg new file mode 100644 index 0000000..223d17a Binary files /dev/null and b/.image/我的流程-列表.jpg differ diff --git a/.image/我的流程-发起.jpg b/.image/我的流程-发起.jpg new file mode 100644 index 0000000..7a83306 Binary files /dev/null and b/.image/我的流程-发起.jpg differ diff --git a/.image/我的流程-详情.jpg b/.image/我的流程-详情.jpg new file mode 100644 index 0000000..6a01541 Binary files /dev/null and b/.image/我的流程-详情.jpg differ diff --git a/.image/报表设计器-图形报表.jpg b/.image/报表设计器-图形报表.jpg new file mode 100644 index 0000000..681b318 Binary files /dev/null and b/.image/报表设计器-图形报表.jpg differ diff --git a/.image/报表设计器-打印设计.jpg b/.image/报表设计器-打印设计.jpg new file mode 100644 index 0000000..bb86da6 Binary files /dev/null and b/.image/报表设计器-打印设计.jpg differ diff --git a/.image/报表设计器-数据报表.jpg b/.image/报表设计器-数据报表.jpg new file mode 100644 index 0000000..9ca5b9b Binary files /dev/null and b/.image/报表设计器-数据报表.jpg differ diff --git a/.image/操作日志.jpg b/.image/操作日志.jpg new file mode 100644 index 0000000..4a0611a Binary files /dev/null and b/.image/操作日志.jpg differ diff --git a/.image/支付订单.jpg b/.image/支付订单.jpg new file mode 100644 index 0000000..0a56dd7 Binary files /dev/null and b/.image/支付订单.jpg differ diff --git a/.image/敏感词.jpg b/.image/敏感词.jpg new file mode 100644 index 0000000..92a5397 Binary files /dev/null and b/.image/敏感词.jpg differ diff --git a/.image/数据库文档.jpg b/.image/数据库文档.jpg new file mode 100644 index 0000000..a4339d9 Binary files /dev/null and b/.image/数据库文档.jpg differ diff --git a/.image/文件管理.jpg b/.image/文件管理.jpg new file mode 100644 index 0000000..054b19f Binary files /dev/null and b/.image/文件管理.jpg differ diff --git a/.image/文件管理2.jpg b/.image/文件管理2.jpg new file mode 100644 index 0000000..b12e5c3 Binary files /dev/null and b/.image/文件管理2.jpg differ diff --git a/.image/文件配置.jpg b/.image/文件配置.jpg new file mode 100644 index 0000000..e618049 Binary files /dev/null and b/.image/文件配置.jpg differ diff --git a/.image/日志中心.jpg b/.image/日志中心.jpg new file mode 100644 index 0000000..27c1c6c Binary files /dev/null and b/.image/日志中心.jpg differ diff --git a/.image/流程模型-列表.jpg b/.image/流程模型-列表.jpg new file mode 100644 index 0000000..ffdc584 Binary files /dev/null and b/.image/流程模型-列表.jpg differ diff --git a/.image/流程模型-定义.jpg b/.image/流程模型-定义.jpg new file mode 100644 index 0000000..18b316c Binary files /dev/null and b/.image/流程模型-定义.jpg differ diff --git a/.image/流程模型-设计.jpg b/.image/流程模型-设计.jpg new file mode 100644 index 0000000..9614969 Binary files /dev/null and b/.image/流程模型-设计.jpg differ diff --git a/.image/流程表单.jpg b/.image/流程表单.jpg new file mode 100644 index 0000000..60669c1 Binary files /dev/null and b/.image/流程表单.jpg differ diff --git a/.image/生成效果.jpg b/.image/生成效果.jpg new file mode 100644 index 0000000..98ff2cc Binary files /dev/null and b/.image/生成效果.jpg differ diff --git a/.image/用户分组.jpg b/.image/用户分组.jpg new file mode 100644 index 0000000..39af1cd Binary files /dev/null and b/.image/用户分组.jpg differ diff --git a/.image/用户管理.jpg b/.image/用户管理.jpg new file mode 100644 index 0000000..844604a Binary files /dev/null and b/.image/用户管理.jpg differ diff --git a/.image/登录.jpg b/.image/登录.jpg new file mode 100644 index 0000000..b782b98 Binary files /dev/null and b/.image/登录.jpg differ diff --git a/.image/登录日志.jpg b/.image/登录日志.jpg new file mode 100644 index 0000000..25662d9 Binary files /dev/null and b/.image/登录日志.jpg differ diff --git a/.image/短信日志.jpg b/.image/短信日志.jpg new file mode 100644 index 0000000..ada8e56 Binary files /dev/null and b/.image/短信日志.jpg differ diff --git a/.image/短信模板.jpg b/.image/短信模板.jpg new file mode 100644 index 0000000..09381cc Binary files /dev/null and b/.image/短信模板.jpg differ diff --git a/.image/短信渠道.jpg b/.image/短信渠道.jpg new file mode 100644 index 0000000..df3a5c3 Binary files /dev/null and b/.image/短信渠道.jpg differ diff --git a/.image/租户套餐.png b/.image/租户套餐.png new file mode 100644 index 0000000..9663167 Binary files /dev/null and b/.image/租户套餐.png differ diff --git a/.image/租户管理.jpg b/.image/租户管理.jpg new file mode 100644 index 0000000..647416a Binary files /dev/null and b/.image/租户管理.jpg differ diff --git a/.image/系统接口.jpg b/.image/系统接口.jpg new file mode 100644 index 0000000..6d39d42 Binary files /dev/null and b/.image/系统接口.jpg differ diff --git a/.image/菜单管理.jpg b/.image/菜单管理.jpg new file mode 100644 index 0000000..ad3b797 Binary files /dev/null and b/.image/菜单管理.jpg differ diff --git a/.image/表单构建.jpg b/.image/表单构建.jpg new file mode 100644 index 0000000..81f0374 Binary files /dev/null and b/.image/表单构建.jpg differ diff --git a/.image/角色管理.jpg b/.image/角色管理.jpg new file mode 100644 index 0000000..eed776e Binary files /dev/null and b/.image/角色管理.jpg differ diff --git a/.image/访问日志.jpg b/.image/访问日志.jpg new file mode 100644 index 0000000..ef301aa Binary files /dev/null and b/.image/访问日志.jpg differ diff --git a/.image/退款订单.jpg b/.image/退款订单.jpg new file mode 100644 index 0000000..2c6c6c9 Binary files /dev/null and b/.image/退款订单.jpg differ diff --git a/.image/通知公告.jpg b/.image/通知公告.jpg new file mode 100644 index 0000000..97bb42f Binary files /dev/null and b/.image/通知公告.jpg differ diff --git a/.image/部门管理.jpg b/.image/部门管理.jpg new file mode 100644 index 0000000..6eab233 Binary files /dev/null and b/.image/部门管理.jpg differ diff --git a/.image/配置管理.jpg b/.image/配置管理.jpg new file mode 100644 index 0000000..0abaec9 Binary files /dev/null and b/.image/配置管理.jpg differ diff --git a/.image/链路追踪.jpg b/.image/链路追踪.jpg new file mode 100644 index 0000000..12f7aa8 Binary files /dev/null and b/.image/链路追踪.jpg differ diff --git a/.image/错误日志.jpg b/.image/错误日志.jpg new file mode 100644 index 0000000..eb615ea Binary files /dev/null and b/.image/错误日志.jpg differ diff --git a/.image/错误码管理.jpg b/.image/错误码管理.jpg new file mode 100644 index 0000000..ea91dde Binary files /dev/null and b/.image/错误码管理.jpg differ diff --git a/.image/首页.jpg b/.image/首页.jpg new file mode 100644 index 0000000..10a7fde Binary files /dev/null and b/.image/首页.jpg differ diff --git a/yudao-module-car/.DS_Store b/yudao-module-car/.DS_Store index 8747342..41fe92a 100644 Binary files a/yudao-module-car/.DS_Store and b/yudao-module-car/.DS_Store differ diff --git a/yudao-module-car/yudao-module-car-biz 2.zip b/yudao-module-car/yudao-module-car-biz 2.zip new file mode 100644 index 0000000..54c7d09 Binary files /dev/null and b/yudao-module-car/yudao-module-car-biz 2.zip differ diff --git a/yudao-module-car/yudao-module-car-biz.zip b/yudao-module-car/yudao-module-car-biz.zip new file mode 100644 index 0000000..c4ca67b Binary files /dev/null and b/yudao-module-car/yudao-module-car-biz.zip differ diff --git a/yudao-module-car/yudao-module-car-biz/.DS_Store b/yudao-module-car/yudao-module-car-biz/.DS_Store new file mode 100644 index 0000000..f73b6d3 Binary files /dev/null and b/yudao-module-car/yudao-module-car-biz/.DS_Store differ diff --git a/yudao-module-car/yudao-module-car-biz/pom.xml b/yudao-module-car/yudao-module-car-biz/pom.xml index a1ae3f5..dc9305e 100644 --- a/yudao-module-car/yudao-module-car-biz/pom.xml +++ b/yudao-module-car/yudao-module-car-biz/pom.xml @@ -68,5 +68,30 @@ 2.2.0-jdk8-snapshot compile + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + com.openhtmltopdf + openhtmltopdf-pdfbox + 1.0.10 + + + com.openhtmltopdf + openhtmltopdf-svg-support + 1.0.10 + + + + + cn.iocoder.boot + yudao-module-infra-biz + ${revision} + diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/login/vo/TireLoginRespVO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/login/vo/TireLoginRespVO.java index 34e8542..2c6aecb 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/login/vo/TireLoginRespVO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/login/vo/TireLoginRespVO.java @@ -30,7 +30,7 @@ public class TireLoginRespVO{ @Schema(description = "门店ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11176") @ExcelProperty("门店ID") - private Long storeId; + private String storeId; @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11178") @ExcelProperty("仓库编号") diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/RenewalOrderController.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/RenewalOrderController.java index 83cc87b..a29686f 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/RenewalOrderController.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/RenewalOrderController.java @@ -1,9 +1,15 @@ package cn.iocoder.yudao.module.car.controller.admin.renewalorder; +import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import com.google.common.collect.Lists; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; @@ -18,6 +24,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import static cn.iocoder.yudao.framework.common.enums.UserTypeEnum.ADMIN; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; @@ -53,6 +61,22 @@ public class RenewalOrderController { return success(true); } + @PostMapping("/generate-contract") + @Operation(summary = "生成在线合同 PDF 并上传(回写 contractUrl)") + @Parameter(name = "id", description = "订单编号", required = true) + @PreAuthorize("@ss.hasPermission('car:renewal-order:update')") + public CommonResult generateContract(@RequestParam("id") Long id) { + return success(renewalOrderService.generateContract(id)); + } + + @GetMapping("/generate-contract-html") + @Operation(summary = "生成合同 HTML(用于预览)") + @Parameter(name = "id", description = "订单编号", required = true) + @PreAuthorize("@ss.hasPermission('car:renewal-order:query')") + public CommonResult generateContractHtml(@RequestParam("id") Long id) { + return success(renewalOrderService.generateContractHtml(id)); + } + @DeleteMapping("/delete") @Operation(summary = "删除车辆续保订单") @Parameter(name = "id", description = "编号", required = true) @@ -70,15 +94,28 @@ public class RenewalOrderController { RenewalOrderDO renewalOrder = renewalOrderService.getRenewalOrder(id); return success(BeanUtils.toBean(renewalOrder, RenewalOrderRespVO.class)); } + @Resource + private PermissionService permissionService; @GetMapping("/page") @Operation(summary = "获得车辆续保订单分页") @PreAuthorize("@ss.hasPermission('car:renewal-order:query')") - public CommonResult> getRenewalOrderPage(@Valid RenewalOrderPageReqVO pageReqVO) { + public CommonResult> getRenewalOrderPage( + @Valid RenewalOrderPageReqVO pageReqVO) { + + Long userId = SecurityFrameworkUtils.getLoginUserId(); + + boolean isTenantAdmin = permissionService.hasAnyRoles(userId, "tenant_admin"); + + if (!isTenantAdmin) { + pageReqVO.setCreator(String.valueOf(userId)); + } + PageResult pageResult = renewalOrderService.getRenewalOrderPage(pageReqVO); return success(BeanUtils.toBean(pageResult, RenewalOrderRespVO.class)); } + @GetMapping("/export-excel") @Operation(summary = "导出车辆续保订单 Excel") @PreAuthorize("@ss.hasPermission('car:renewal-order:export')") diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderPageReqVO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderPageReqVO.java index f5cd3ba..cfe2741 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderPageReqVO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderPageReqVO.java @@ -96,15 +96,48 @@ public class RenewalOrderPageReqVO extends PageParam { @Schema(description = "合同备注", example = "你说的对") private String contractRemark; + private String contractUrl; @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; + private String creator; + private Long storeId; private String storeName; private String invoiceUrl; + private String productType; + @Schema(description = "客户签名") + private String customerSignatureUrl; + + @Schema(description = "身份证正面") + private String idCardFrontUrl; + + @Schema(description = "身份证反面") + private String idCardBackUrl; + + @Schema(description = "行驶证") + private String drivingLicenseUrl; + + @Schema(description = "购车发票(多张,JSON字符串)") + private List carInvoiceUrls; + + @Schema(description = "购置税发票(多张,JSON字符串)") + private List purchaseTaxInvoiceUrls; + + @Schema(description = "商业险保单(多张,JSON字符串)") + private List businessInsurancePolicyUrls; + + @Schema(description = "合格证") + private String certificateOfConformityUrl; + + @Schema(description = "里程表照片") + private String odometerPhotoUrl; + + @Schema(description = "车名牌照片") + private String nameplatePhotoUrl; } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderRespVO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderRespVO.java index 582b0f6..ef2c70f 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderRespVO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderRespVO.java @@ -122,6 +122,7 @@ public class RenewalOrderRespVO { @Schema(description = "合同备注", example = "你说的对") @ExcelProperty("合同备注") private String contractRemark; + private String contractUrl; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") @@ -132,5 +133,45 @@ public class RenewalOrderRespVO { private String storeName; private String invoiceUrl; + private String productType; + @Schema(description = "客户签名") + @ExcelProperty("客户签名") + private String customerSignatureUrl; + + @Schema(description = "身份证正面") + @ExcelProperty("身份证正面") + private String idCardFrontUrl; + + @Schema(description = "身份证反面") + @ExcelProperty("身份证反面") + private String idCardBackUrl; + + @Schema(description = "行驶证") + @ExcelProperty("行驶证") + private String drivingLicenseUrl; + + @Schema(description = "购车发票(多张)") + @ExcelProperty("购车发票") + private List carInvoiceUrls; + + @Schema(description = "购置税发票(多张)") + @ExcelProperty("购置税发票") + private List purchaseTaxInvoiceUrls; + + @Schema(description = "商业险保单(多张)") + @ExcelProperty("商业险保单") + private List businessInsurancePolicyUrls; + + @Schema(description = "合格证") + @ExcelProperty("合格证") + private String certificateOfConformityUrl; + + @Schema(description = "里程表照片") + @ExcelProperty("里程表照片") + private String odometerPhotoUrl; + + @Schema(description = "车名牌照片") + @ExcelProperty("车名牌照片") + private String nameplatePhotoUrl; } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderSaveReqVO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderSaveReqVO.java index 6ede6aa..af48cb1 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderSaveReqVO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalorder/vo/RenewalOrderSaveReqVO.java @@ -94,6 +94,7 @@ public class RenewalOrderSaveReqVO { @Schema(description = "合同备注", example = "你说的对") private String contractRemark; + private String contractUrl; private Long storeId; @@ -101,4 +102,35 @@ public class RenewalOrderSaveReqVO { private String invoiceUrl; + private String productType; + @Schema(description = "客户签名") + private String customerSignatureUrl; + + @Schema(description = "身份证正面") + private String idCardFrontUrl; + + @Schema(description = "身份证反面") + private String idCardBackUrl; + + @Schema(description = "行驶证") + private String drivingLicenseUrl; + + @Schema(description = "购车发票(多张)") + private List carInvoiceUrls; + + @Schema(description = "购置税发票(多张)") + private List purchaseTaxInvoiceUrls; + + @Schema(description = "商业险保单(多张)") + private List businessInsurancePolicyUrls; + + @Schema(description = "合格证") + private String certificateOfConformityUrl; + + @Schema(description = "里程表照片") + private String odometerPhotoUrl; + + @Schema(description = "车名牌照片") + private String nameplatePhotoUrl; + } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalproduct/RenewalProductController.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalproduct/RenewalProductController.java index 38fe34f..4e6ac14 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalproduct/RenewalProductController.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/renewalproduct/RenewalProductController.java @@ -79,6 +79,17 @@ public class RenewalProductController { return success(BeanUtils.toBean(pageResult, RenewalProductRespVO.class)); } + @GetMapping("/findByProductType") + @Operation(summary = "获得车辆续保产品信息") + @PreAuthorize("@ss.hasPermission('car:renewal-product:query')") + public CommonResult> findByProductType() { + // 调用服务层的方法,查询所有符合条件的记录 + List list = renewalProductService.findByProductType(); + + // 返回成功结果,转换为 VO 类型 + return success(BeanUtils.toBean(list, RenewalProductRespVO.class)); + } + @GetMapping("/export-excel") @Operation(summary = "导出车辆续保产品信息 Excel") @PreAuthorize("@ss.hasPermission('car:renewal-product:export')") diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/store/StoreController.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/store/StoreController.java index 10f466e..f27be79 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/store/StoreController.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/store/StoreController.java @@ -6,14 +6,17 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.car.controller.admin.renewalproduct.vo.RenewalProductRespVO; import cn.iocoder.yudao.module.car.controller.admin.store.vo.StorePageReqVO; import cn.iocoder.yudao.module.car.controller.admin.store.vo.StoreRespVO; import cn.iocoder.yudao.module.car.controller.admin.store.vo.StoreSaveReqVO; +import cn.iocoder.yudao.module.car.dal.dataobject.renewalproduct.RenewalProductDO; import cn.iocoder.yudao.module.car.dal.dataobject.store.StoreDO; import cn.iocoder.yudao.module.car.service.store.StoreService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -71,6 +74,16 @@ public class StoreController { return success(BeanUtils.toBean(pageResult, StoreRespVO.class)); } + @GetMapping("/findByProductType") + @Operation(summary = "获得车辆续保产品信息") + public CommonResult> findByProductType() { + // 调用服务层的方法,查询所有符合条件的记录 + List list = storeService.findByProductType(); + + // 返回成功结果,转换为 VO 类型 + return success(BeanUtils.toBean(list, StoreRespVO.class)); + } + @GetMapping("/pageAll") @Operation(summary = "获得门店管理分页-全部") public CommonResult> getStorePageAll(@Valid StorePageReqVO pageReqVO) { diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserPageReqVO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserPageReqVO.java index 0968a41..c0187d1 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserPageReqVO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserPageReqVO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.car.controller.admin.tireuser.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,7 +18,7 @@ public class TireUserPageReqVO extends PageParam { private String queryType; @Schema(description = "门店ID,关联门店表", example = "27998") - private Long storeId; + private String storeId; @Schema(description = "仓库ID,关联仓库表", example = "27998") private Long warehouseId; @@ -25,6 +26,12 @@ public class TireUserPageReqVO extends PageParam { @Schema(description = "用户ID,关联用户表", example = "27998") private Long userId; + @Schema(description = "产品类别(car_renewal_product_type:00 无忧,01 延保)", example = "00") + private String productType; + @Schema(description = "用户表") private UserPageReqVO user; + + @Schema(description = "门店名称", example = "门店名称") + private String storeName; } diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserRespVO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserRespVO.java index 7d023d2..3275098 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserRespVO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserRespVO.java @@ -17,7 +17,7 @@ public class TireUserRespVO { @Schema(description = "门店ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11176") @ExcelProperty("门店ID") - private Long storeId; + private String storeId; @Schema(description = "仓库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11176") @ExcelProperty("仓库编号") @@ -28,7 +28,15 @@ public class TireUserRespVO { @ExcelProperty("用户关联用户表") private Long userId; + @Schema(description = "产品类别(car_renewal_product_type:00 无忧,01 延保)", example = "00") + @ExcelProperty("产品类别(car_renewal_product_type:00 无忧,01 延保)") + private String productType; + @Schema(description = "用户表") @ExcelProperty("用户关联用户表") private UserRespVO user; + + @Schema(description = "门店名称", example = "门店名称") + @ExcelProperty("门店名称") + private String storeName; } diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserSaveReqVO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserSaveReqVO.java index 7e903d8..e8f3243 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserSaveReqVO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/controller/admin/tireuser/vo/TireUserSaveReqVO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.car.controller.admin.tireuser.vo; import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -12,7 +13,7 @@ public class TireUserSaveReqVO { private Integer id; @Schema(description = "门店编号", example = "我是一个用户") - private Long storeId; + private String storeId; @Schema(description = "仓库编号", example = "我是一个用户") private Long warehouseId; @@ -20,6 +21,12 @@ public class TireUserSaveReqVO { @Schema(description = "用户编号", example = "我是一个用户") private Long userId; + @Schema(description = "产品类别(car_renewal_product_type:00 无忧,01 延保)", example = "00") + private String productType; + @Schema(description = "用户表") private UserSaveReqVO user; + + @Schema(description = "门店名称", example = "门店名称") + private String storeName; } diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/renewalorder/RenewalOrderDO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/renewalorder/RenewalOrderDO.java index 392d3bc..8b189a3 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/renewalorder/RenewalOrderDO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/renewalorder/RenewalOrderDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.car.dal.dataobject.renewalorder; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.*; import java.time.LocalDate; @@ -135,6 +136,7 @@ public class RenewalOrderDO extends BaseDO { * 合同备注 */ private String contractRemark; + private String contractUrl; private Long storeId; @@ -144,4 +146,58 @@ public class RenewalOrderDO extends BaseDO { private String invoiceUrl; + private String productType; + /** + * 客户签名 + */ + private String customerSignatureUrl; + + /** + * 身份证正面 + */ + private String idCardFrontUrl; + + /** + * 身份证反面 + */ + private String idCardBackUrl; + + /** + * 行驶证 + */ + private String drivingLicenseUrl; + + /** + * 购车发票(多张,JSON字符串) + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List carInvoiceUrls; + + /** + * 购置税发票(多张,JSON字符串) + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List purchaseTaxInvoiceUrls; + + /** + * 商业险保单(多张,JSON字符串) + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List businessInsurancePolicyUrls; + + /** + * 合格证 + */ + private String certificateOfConformityUrl; + + /** + * 里程表照片 + */ + private String odometerPhotoUrl; + + /** + * 车名牌照片 + */ + private String nameplatePhotoUrl; + } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/tireuser/TireUserDO.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/tireuser/TireUserDO.java index 9f668b8..3d152a1 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/tireuser/TireUserDO.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/dataobject/tireuser/TireUserDO.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; /** @@ -32,7 +33,7 @@ public class TireUserDO extends BaseDO{ /** * 门店ID */ - private Long storeId; + private String storeId; /** * 仓库ID @@ -44,6 +45,10 @@ public class TireUserDO extends BaseDO{ */ private Long userId; + /** + * 产品类别(car_renewal_product_type:00 无忧,01 延保) + */ + private String productType; /** * 用户信息(非数据库字段) @@ -51,4 +56,12 @@ public class TireUserDO extends BaseDO{ @TableField(exist = false) private AdminUserDO user; + + /** + * 门店名称 + */ + @TableField(exist = false) + private String storeName; + + } diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalorder/RenewalOrderMapper.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalorder/RenewalOrderMapper.java index f432c00..8fa4a65 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalorder/RenewalOrderMapper.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalorder/RenewalOrderMapper.java @@ -45,8 +45,8 @@ public interface RenewalOrderMapper extends BaseMapperX { .eq(reqVO.getServiceBuyer() != null, RenewalOrderDO::getServiceBuyer, reqVO.getServiceBuyer()) .eq(reqVO.getCarBuyer() != null, RenewalOrderDO::getCarBuyer, reqVO.getCarBuyer()) .eq(reqVO.getCertType() != null, RenewalOrderDO::getCertType, reqVO.getCertType()) - .eq(reqVO.getMobile() != null, RenewalOrderDO::getMobile, reqVO.getMobile()) - .eq(reqVO.getCertNo() != null, RenewalOrderDO::getCertNo, reqVO.getCertNo()) + .like(reqVO.getMobile() != null, RenewalOrderDO::getMobile, reqVO.getMobile()) + .like(reqVO.getCertNo() != null, RenewalOrderDO::getCertNo, reqVO.getCertNo()) .eq(reqVO.getContactAddress() != null, RenewalOrderDO::getContactAddress, reqVO.getContactAddress()) .eq(reqVO.getMemberEmail() != null, RenewalOrderDO::getMemberEmail, reqVO.getMemberEmail()) .eq(reqVO.getProductId() != null, RenewalOrderDO::getProductId, reqVO.getProductId()) @@ -57,8 +57,10 @@ public interface RenewalOrderMapper extends BaseMapperX { .eq(reqVO.getProductFee() != null, RenewalOrderDO::getProductFee, reqVO.getProductFee()) .eq(reqVO.getSettlementMethod() != null, RenewalOrderDO::getSettlementMethod, reqVO.getSettlementMethod()) .eq(reqVO.getRemark() != null, RenewalOrderDO::getRemark, reqVO.getRemark()) - .eq(reqVO.getInputUser() != null, RenewalOrderDO::getInputUser, reqVO.getInputUser()) + .like(reqVO.getInputUser() != null, RenewalOrderDO::getInputUser, reqVO.getInputUser()) .eq(reqVO.getContractRemark() != null, RenewalOrderDO::getContractRemark, reqVO.getContractRemark()) + .eq(reqVO.getStoreId() != null, RenewalOrderDO::getStoreId, reqVO.getStoreId()) + .eq(reqVO.getCreator() != null, RenewalOrderDO::getCreator, reqVO.getCreator()) .orderByDesc(RenewalOrderDO::getId); // 3. createTime 范围处理 diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalproduct/RenewalProductMapper.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalproduct/RenewalProductMapper.java index b995892..912ed12 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalproduct/RenewalProductMapper.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/renewalproduct/RenewalProductMapper.java @@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.car.controller.admin.renewalproduct.vo.RenewalProductPageReqVO; import cn.iocoder.yudao.module.car.dal.dataobject.renewalproduct.RenewalProductDO; +import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * 车辆续保产品信息 Mapper * @@ -25,4 +28,32 @@ public interface RenewalProductMapper extends BaseMapperX { .orderByDesc(RenewalProductDO::getId)); } + default List findByProductType(String productType) { + if (StringUtils.isNotBlank(productType)) { + // 将 productType 按照逗号分割成数组 + String[] types = productType.split(","); + + StringBuilder sqlBuilder = new StringBuilder(); + // 避免多余的 AND 拼接 + for (int i = 0; i < types.length; i++) { + if (i > 0) { + sqlBuilder.append(" OR "); // 连接多个条件 + } + // 使用 FIND_IN_SET 来检查每个 type 是否存在于 product_type 字段中 + sqlBuilder.append("FIND_IN_SET('").append(types[i]).append("', product_type) > 0"); + } + + // 构造带有动态条件的查询 + return selectList(new LambdaQueryWrapperX() + .apply("(" + sqlBuilder.toString() + ")") // 不要再加额外的 AND + .orderByDesc(RenewalProductDO::getId)); // 按照 id 排序,视情况调整 + } + + // 如果 productType 为空或 null,查询所有数据 + return selectList(new LambdaQueryWrapperX() + .orderByDesc(RenewalProductDO::getId)); // 按照 id 排序,返回所有数据 + } + + + } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/store/StoreMapper.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/store/StoreMapper.java index 771c35e..c2f1e24 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/store/StoreMapper.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/dal/mysql/store/StoreMapper.java @@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.car.controller.admin.store.vo.StorePageReqVO; import cn.iocoder.yudao.module.car.dal.dataobject.store.StoreDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; /** * 门店管理 Mapper @@ -23,7 +26,24 @@ public interface StoreMapper extends BaseMapperX { .eqIfPresent(StoreDO::getLatitude, reqVO.getLatitude()) .eqIfPresent(StoreDO::getLongitude, reqVO.getLongitude()) .eqIfPresent(StoreDO::getPostalCode, reqVO.getPostalCode()) - .eqIfPresent(StoreDO::getPhoneNumber, reqVO.getPhoneNumber()) + .likeIfPresent(StoreDO::getPhoneNumber, reqVO.getPhoneNumber()) + .eqIfPresent(StoreDO::getEmail, reqVO.getEmail()) + .likeIfPresent(StoreDO::getManagerName, reqVO.getManagerName()) + .betweenIfPresent(StoreDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(StoreDO::getStatus, reqVO.getStatus()) + .orderByDesc(StoreDO::getId)); + } + + default PageResult selectPageByIds(StorePageReqVO reqVO, List storeIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .inIfPresent(StoreDO::getId, storeIds) + .likeIfPresent(StoreDO::getStoreName, reqVO.getStoreName()) + .eqIfPresent(StoreDO::getAreaId, reqVO.getAreaId()) + .eqIfPresent(StoreDO::getDetailedLocation, reqVO.getDetailedLocation()) + .eqIfPresent(StoreDO::getLatitude, reqVO.getLatitude()) + .eqIfPresent(StoreDO::getLongitude, reqVO.getLongitude()) + .eqIfPresent(StoreDO::getPostalCode, reqVO.getPostalCode()) + .likeIfPresent(StoreDO::getPhoneNumber, reqVO.getPhoneNumber()) .eqIfPresent(StoreDO::getEmail, reqVO.getEmail()) .likeIfPresent(StoreDO::getManagerName, reqVO.getManagerName()) .betweenIfPresent(StoreDO::getCreateTime, reqVO.getCreateTime()) diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/contract/ContractService.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/contract/ContractService.java new file mode 100644 index 0000000..df16352 --- /dev/null +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/contract/ContractService.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.car.service.contract; + +import cn.iocoder.yudao.module.car.dal.dataobject.renewalorder.RenewalOrderDO; + +import java.io.OutputStream; + +/** + * 合同生成服务接口 + * + * @author 芋道源码 + */ +public interface ContractService { + + /** + * 生成合同 PDF + * + * @param renewalOrder 续保订单 + * @param outputStream 输出流 + * @return PDF 文件的 URL(上传后的路径) + */ + String generateContractPdf(RenewalOrderDO renewalOrder, OutputStream outputStream); + + /** + * 生成合同 PDF 并上传 + * + * @param renewalOrder 续保订单 + * @return PDF 文件的 URL(上传后的路径) + */ + String generateAndUploadContract(RenewalOrderDO renewalOrder); + + /** + * 生成合同 HTML(用于预览) + * + * @param renewalOrder 续保订单 + * @return 渲染后的 HTML 字符串 + */ + String generateContractHtml(RenewalOrderDO renewalOrder); + + /** + * 计算合同 SHA256 Hash(用于防篡改) + * + * @param pdfBytes PDF 文件字节数组 + * @return SHA256 Hash 值 + */ + String calculateContractHash(byte[] pdfBytes); +} diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/contract/ContractServiceImpl.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/contract/ContractServiceImpl.java new file mode 100644 index 0000000..651222d --- /dev/null +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/contract/ContractServiceImpl.java @@ -0,0 +1,551 @@ +package cn.iocoder.yudao.module.car.service.contract; + +import cn.iocoder.yudao.module.car.dal.dataobject.renewalorder.RenewalOrderDO; +import cn.iocoder.yudao.module.infra.api.file.FileApi; +import cn.iocoder.yudao.module.infra.service.file.FileService; +import cn.hutool.core.util.StrUtil; +import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.context.Context; + +import javax.annotation.Resource; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.MessageDigest; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.regex.Pattern; + +/** + * 合同生成服务实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class ContractServiceImpl implements ContractService { + + @Resource + private TemplateEngine templateEngine; + + @Resource + private FileApi fileApi; + + @Resource + private FileService fileService; + + private static final String CONTRACT_TEMPLATE = "contract/renewal-contract"; + private static final String SEAL_IMAGE_PATH = "static/seal.png"; + private static final String CONTRACT_PDF_DIR = "contractpdf"; + + private static final Pattern FILE_NAME_ILLEGAL_CHARS = Pattern.compile("[\\\\/:*?\"<>|\\n\\r\\t]"); + + // 缓存字体文件路径,避免重复查找 + private static volatile String cachedFontPath = null; + + // 中文字体路径(使用系统字体或资源文件) + private static final String[] FONT_PATHS = { + "/System/Library/Fonts/Hiragino Sans GB.ttc", // macOS 冬青黑体简体中文(推荐) + "/System/Library/Fonts/STHeiti Light.ttc", // macOS 黑体-细体 + "/System/Library/Fonts/STHeiti Medium.ttc", // macOS 黑体-中等 + "/System/Library/Fonts/PingFang.ttc", // macOS 苹方(如果存在) + "C:/Windows/Fonts/simsun.ttc", // Windows 宋体 + "C:/Windows/Fonts/simhei.ttf", // Windows 黑体 + "C:/Windows/Fonts/msyh.ttc", // Windows 微软雅黑 + "C:/Windows/Fonts/msyhbd.ttc", // Windows 微软雅黑 Bold + "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", // Linux 文泉驿微米黑 + "/usr/share/fonts/truetype/arphic/uming.ttc", // Linux 文鼎字体 + "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf" // Linux Droid + }; + + @Override + public String generateContractPdf(RenewalOrderDO renewalOrder, OutputStream outputStream) { + try { + // 1. 准备模板数据 + Context context = prepareTemplateContext(renewalOrder); + + // 2. 渲染 HTML 模板 + String html = templateEngine.process(CONTRACT_TEMPLATE, context); + log.debug("HTML 模板渲染成功,长度: {}", html.length()); + + // 3. 将 HTML 转换为 PDF + PdfRendererBuilder builder = new PdfRendererBuilder(); + builder.withHtmlContent(html, null); + builder.toStream(outputStream); + + // 配置中文字体支持(必须在 withHtmlContent 之后,run() 之前) + configureChineseFonts(builder); + + log.info("开始生成 PDF..."); + builder.run(); + log.info("PDF 生成成功"); + + return null; // 直接输出到流,不返回 URL + } catch (Exception e) { + log.error("生成合同 PDF 失败", e); + // 不输出完整的堆栈信息中的敏感内容,只输出错误消息 + String errorMsg = e.getMessage(); + if (errorMsg != null && errorMsg.length() > 500) { + errorMsg = errorMsg.substring(0, 500) + "..."; + } + throw new RuntimeException("生成合同 PDF 失败: " + errorMsg, e); + } + } + + /** + * 生成合同 HTML(用于预览) + */ + @Override + public String generateContractHtml(RenewalOrderDO renewalOrder) { + Context context = prepareTemplateContext(renewalOrder); + return templateEngine.process(CONTRACT_TEMPLATE, context); + } + + @Override + public String generateAndUploadContract(RenewalOrderDO renewalOrder) { + try { + // 1. 生成 PDF 到字节数组 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + generateContractPdf(renewalOrder, baos); + byte[] pdfBytes = baos.toByteArray(); + + // 2. 计算 SHA256 Hash(用于防篡改) + String hash = calculateContractHash(pdfBytes); + log.info("合同 PDF Hash: {}", hash); + + // 3. 上传文件 + String displayName = generateDisplayName(renewalOrder); + String storagePath = generateStoragePath(renewalOrder); + // FileApi 签名:createFile(name, path, content) + String url = fileApi.createFile(displayName, storagePath, pdfBytes); + + return url; + } catch (Exception e) { + log.error("生成并上传合同失败", e); + throw new RuntimeException("生成并上传合同失败: " + e.getMessage(), e); + } + } + + @Override + public String calculateContractHash(byte[] pdfBytes) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hashBytes = digest.digest(pdfBytes); + StringBuilder sb = new StringBuilder(); + for (byte b : hashBytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (Exception e) { + log.error("计算合同 Hash 失败", e); + throw new RuntimeException("计算合同 Hash 失败: " + e.getMessage(), e); + } + } + + /** + * 准备模板上下文数据 + */ + private Context prepareTemplateContext(RenewalOrderDO renewalOrder) { + Context context = new Context(); + + // 基础信息 + context.setVariable("order", renewalOrder); + + // 凭证编号:TABLA + 日期 + 序号(使用订单ID) + String certificateNo = generateCertificateNo(renewalOrder); + context.setVariable("certificateNo", certificateNo); + + // 服务期限 + LocalDate startDate = LocalDate.now(); + LocalDate endDate = startDate.plusYears(3); + context.setVariable("serviceStartDate", startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + context.setVariable("serviceEndDate", endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + + // 服务期限(用于权益说明) + context.setVariable("serviceStartDateText", startDate.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"))); + context.setVariable("serviceEndDateText", endDate.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"))); + + // 签章图片路径(转换为 base64) + // 注意:openhtmltopdf 可能不支持 WebP 格式,如果出错会跳过签章显示 + try { + String sealBase64 = loadSealImageAsBase64(); + context.setVariable("sealImageBase64", sealBase64 != null ? sealBase64 : ""); + if (sealBase64 == null || sealBase64.isEmpty()) { + log.warn("签章图片未加载,PDF 中将不显示签章"); + } + } catch (Exception e) { + log.warn("加载签章图片时出错,将不显示签章: {}", e.getMessage()); + context.setVariable("sealImageBase64", ""); + } + + // 客户签名图片(转换为 base64) + // 如果订单中有客户签名URL,需要转换为base64用于PDF生成 + log.info("========== 开始处理客户签名 =========="); + log.info("订单ID: {}", renewalOrder.getId()); + log.info("客户签名URL: {}", renewalOrder.getCustomerSignatureUrl()); + + String customerSignatureBase64 = ""; + if (renewalOrder.getCustomerSignatureUrl() != null && !renewalOrder.getCustomerSignatureUrl().isEmpty()) { + try { + log.info("开始加载客户签名图片: {}", renewalOrder.getCustomerSignatureUrl()); + customerSignatureBase64 = loadCustomerSignatureAsBase64(renewalOrder.getCustomerSignatureUrl()); + if (customerSignatureBase64 == null || customerSignatureBase64.isEmpty()) { + log.warn("客户签名图片未加载,PDF 中将不显示客户签名"); + } else { + log.info("客户签名图片加载成功,base64长度: {}", customerSignatureBase64.length()); + log.info("base64前缀: {}", customerSignatureBase64.length() > 50 ? customerSignatureBase64.substring(0, 50) + "..." : customerSignatureBase64); + } + } catch (Exception e) { + log.error("加载客户签名图片时出错,将不显示客户签名: {}", e.getMessage(), e); + } + } else { + log.warn("========== 订单中没有客户签名URL,PDF中将不显示客户签名 =========="); + } + context.setVariable("customerSignatureBase64", customerSignatureBase64); + log.info("设置模板变量 customerSignatureBase64: {}", customerSignatureBase64 != null && !customerSignatureBase64.isEmpty() ? "已设置(长度: " + customerSignatureBase64.length() + ")" : "为空"); + log.info("========== 客户签名处理完成 =========="); + + // 防篡改 Hash(模板展示用,最终 Hash 以生成后 PDF 计算为准;此处先放占位/可不展示) + context.setVariable("hash", ""); + + return context; + } + + /** + * 生成凭证编号 + */ + private String generateCertificateNo(RenewalOrderDO renewalOrder) { + String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + String idStr = String.format("%03d", renewalOrder.getId() % 1000); + return "TABLA" + dateStr + idStr; + } + + /** + * 生成文件名 + */ + private String generateStoragePath(RenewalOrderDO renewalOrder) { + // MinIO 存储路径必须增加 /contractpdf 前缀 + // path 不要以 / 开头,避免不同存储适配器对绝对路径处理不一致 + return CONTRACT_PDF_DIR + "/" + generateDisplayName(renewalOrder); + } + + /** 生成展示名称(用于文件记录展示) */ + private String generateDisplayName(RenewalOrderDO renewalOrder) { + // 客户名称:优先使用车主/客户名称(carBuyer);为空则兜底“客户” + String customerName = renewalOrder.getCarBuyer(); + if (customerName == null || customerName.trim().isEmpty()) { + customerName = "客户"; + } + customerName = sanitizeFileName(customerName.trim()); + // 时间戳:毫秒 + long ts = System.currentTimeMillis(); + return customerName + "-途安出行保障服务凭证(" + ts + ").pdf"; + } + + private String sanitizeFileName(String name) { + // 替换非法字符,避免 MinIO/浏览器下载时文件名异常 + String s = FILE_NAME_ILLEGAL_CHARS.matcher(name).replaceAll("_"); + // 控制长度(避免过长导致部分浏览器/存储不兼容) + if (s.length() > 60) { + s = s.substring(0, 60); + } + return s; + } + + /** + * 配置中文字体支持 + */ + private void configureChineseFonts(PdfRendererBuilder builder) { + try { + // 查找可用的中文字体文件 + log.info("开始查找中文字体文件..."); + InputStream fontStream = findChineseFontStream(); + if (fontStream == null) { + log.error("❌ 未找到中文字体文件!"); + log.error("请确认:"); + log.error("1. 字体文件已放到 src/main/resources/fonts/ 目录下"); + log.error("2. 项目已重新编译(mvn clean compile)"); + log.error("3. 字体文件已打包到 jar 中"); + log.error("支持的字体文件名:simsun.ttc, simsun.ttf, simhei.ttf, msyh.ttc, PingFang.ttc"); + + // 列出所有尝试的路径用于诊断 + log.error("诊断信息 - 尝试过的资源路径:"); + String[] testPaths = {"fonts/chinese.ttf", "fonts/simsun.ttc", "fonts/simsun.ttf", "fonts/Hiragino Sans GB.ttc"}; + for (String path : testPaths) { + try { + ClassPathResource testRes = new ClassPathResource(path); + boolean exists = testRes.exists(); + log.error(" {} -> {}", path, exists ? "✓ 找到" : "✗ 未找到"); + } catch (Exception e) { + log.error(" {} -> 检查失败: {}", path, e.getMessage()); + } + } + + throw new RuntimeException("未找到中文字体文件,无法生成包含中文的 PDF"); + } + + // 测试读取字体文件 + try { + byte[] testBytes = new byte[10]; + fontStream.read(testBytes); + fontStream.close(); + log.info("字体文件验证成功,文件大小: {} bytes", testBytes.length); + } catch (Exception e) { + log.error("字体文件读取失败", e); + throw new RuntimeException("字体文件读取失败: " + e.getMessage()); + } + + // useFont 方法需要 Supplier,每次调用都会重新打开流 + // 这里需要确保每次返回的流都是有效的 + // 关键:只注册一个字体族(SimSun),避免 openhtmltopdf 重复尝试加载不存在的字体族导致 fallback 为 null + builder.useFont(() -> { + try { + InputStream stream = findChineseFontStream(); + if (stream == null) { + log.error("字体流为 null"); + } + return stream; + } catch (Exception e) { + log.error("加载字体流失败", e); + return null; + } + }, "SimSun"); + + log.info("成功配置中文字体"); + } catch (Exception e) { + log.error("配置中文字体失败", e); + throw new RuntimeException("配置中文字体失败: " + e.getMessage(), e); + } + } + + /** + * 查找中文字体文件并返回 InputStream + */ + private InputStream findChineseFontStream() { + // 如果已缓存字体路径,直接使用 + if (cachedFontPath != null) { + try { + java.io.File fontFile = new java.io.File(cachedFontPath); + if (fontFile.exists() && fontFile.canRead()) { + return new java.io.FileInputStream(fontFile); + } else { + cachedFontPath = null; // 缓存失效,重新查找 + } + } catch (Exception e) { + log.warn("使用缓存的字体路径失败,重新查找: {}", cachedFontPath, e); + cachedFontPath = null; + } + } + + // 方法1:尝试从资源文件加载字体 + String[] resourceFontNames = { + // 优先:从 TTC 提取出来的单字体(避免 openhtmltopdf/pdfbox 对 .ttc 兼容性问题) + "fonts/chinese.ttf", + // 兼容:旧的字体路径 + "fonts/simsun.ttf", + "fonts/simhei.ttf", + "fonts/msyh.ttc", + "fonts/PingFang.ttc", + "fonts/simsun.ttc", + "fonts/Hiragino Sans GB.ttc" + }; + + for (String fontName : resourceFontNames) { + try { + ClassPathResource fontResource = new ClassPathResource(fontName); + if (fontResource.exists()) { + try { + // 尝试获取文件的绝对路径 + java.io.File file = fontResource.getFile(); + if (file != null && file.exists()) { + cachedFontPath = file.getAbsolutePath(); + log.info("✓ 从资源文件找到字体(绝对路径): {}", cachedFontPath); + return new java.io.FileInputStream(file); + } + } catch (Exception e) { + // 如果不是文件系统资源(可能是 jar 包内),使用流 + try { + InputStream stream = fontResource.getInputStream(); + if (stream != null) { + log.info("✓ 从资源文件加载字体流: {}", fontName); + // 对于 jar 内的资源,无法缓存路径,直接返回流 + return stream; + } + } catch (Exception e2) { + log.debug("读取字体资源文件失败: {}", fontName, e2); + } + } + } + } catch (Exception e) { + log.debug("检查资源字体文件失败: {}", fontName, e); + } + } + + // 方法2:尝试加载系统字体 + log.info("资源文件中未找到字体,尝试从系统路径加载..."); + for (String fontPath : FONT_PATHS) { + try { + java.io.File fontFile = new java.io.File(fontPath); + if (fontFile.exists() && fontFile.isFile() && fontFile.canRead()) { + cachedFontPath = fontPath; + log.info("✓ 从系统路径加载字体成功: {}", fontPath); + return new java.io.FileInputStream(fontFile); + } + } catch (Exception e) { + log.debug("加载系统字体失败: {}", fontPath, e); + } + } + + log.error("❌ 未找到任何中文字体文件!"); + log.error("请确认:1. 字体文件在 src/main/resources/fonts/ 目录下"); + log.error(" 2. 项目已重新编译"); + log.error(" 3. 字体文件已打包到 jar 中"); + return null; + } + + /** + * 加载签章图片并转换为 Base64(PNG) + */ + private String loadSealImageAsBase64() { + try { + ClassPathResource resource = new ClassPathResource(SEAL_IMAGE_PATH); + if (!resource.exists()) { + log.warn("签章图片文件不存在: {}", SEAL_IMAGE_PATH); + return ""; + } + try (InputStream inputStream = resource.getInputStream(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { + byte[] data = new byte[8192]; + int nRead; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + byte[] imageBytes = buffer.toByteArray(); + if (imageBytes.length == 0) { + log.warn("签章图片文件为空: {}", SEAL_IMAGE_PATH); + return ""; + } + String base64 = java.util.Base64.getEncoder().encodeToString(imageBytes); + return "data:image/png;base64," + base64; + } + } catch (Exception e) { + log.warn("加载签章图片失败,将不显示签章: {}", e.getMessage()); + return ""; + } + } + + /** + * 检测图片类型 + */ + private String detectImageType(byte[] imageBytes) { + if (imageBytes.length < 4) { + return "image/png"; // 默认 + } + + // 检查文件头 + // PNG: 89 50 4E 47 + if (imageBytes[0] == (byte)0x89 && imageBytes[1] == 0x50 && + imageBytes[2] == 0x4E && imageBytes[3] == 0x47) { + return "image/png"; + } + // JPEG: FF D8 FF + if (imageBytes[0] == (byte)0xFF && imageBytes[1] == (byte)0xD8 && imageBytes[2] == (byte)0xFF) { + return "image/jpeg"; + } + // GIF: 47 49 46 38 + if (imageBytes[0] == 0x47 && imageBytes[1] == 0x49 && + imageBytes[2] == 0x46 && imageBytes[3] == 0x38) { + return "image/gif"; + } + // WebP: RIFF...WEBP + if (imageBytes.length >= 12 && + imageBytes[0] == 0x52 && imageBytes[1] == 0x49 && + imageBytes[2] == 0x46 && imageBytes[3] == 0x46 && + imageBytes[8] == 0x57 && imageBytes[9] == 0x45 && + imageBytes[10] == 0x42 && imageBytes[11] == 0x50) { + return "image/webp"; + } + + // 默认返回 PNG + return "image/png"; + } + + /** + * 加载客户签名图片并转换为 Base64 + * + * @param signatureUrl 客户签名图片的 URL + * @return Base64 编码的图片字符串(data:image/png;base64,... 格式) + */ + private String loadCustomerSignatureAsBase64(String signatureUrl) { + if (signatureUrl == null || signatureUrl.isEmpty()) { + return ""; + } + + try { + byte[] imageBytes = null; + + // 方法1:尝试从 URL 中提取 configId 和 path,使用 FileService 获取 + // URL 格式:{domain}/admin-api/infra/file/{configId}/get/{path} + if (signatureUrl.contains("/admin-api/infra/file/") && signatureUrl.contains("/get/")) { + try { + // 提取 configId 和 path + String[] parts = signatureUrl.split("/admin-api/infra/file/"); + if (parts.length == 2) { + String afterPrefix = parts[1]; + String[] configAndPath = afterPrefix.split("/get/", 2); + if (configAndPath.length == 2) { + Long configId = Long.parseLong(configAndPath[0]); + String path = configAndPath[1]; + // URL 解码路径 + path = java.net.URLDecoder.decode(path, "UTF-8"); + + // 使用 FileService 获取文件内容 + imageBytes = fileService.getFileContent(configId, path); + log.info("通过 FileService 成功加载客户签名: configId={}, path={}", configId, path); + } + } + } catch (Exception e) { + log.debug("从 URL 提取路径失败,尝试直接下载: {}", e.getMessage()); + } + } + + // 方法2:如果方法1失败,直接通过 HTTP 下载 + if (imageBytes == null || imageBytes.length == 0) { + try { + java.net.URL url = new java.net.URL(signatureUrl); + try (InputStream inputStream = url.openStream(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { + byte[] data = new byte[8192]; + int nRead; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + imageBytes = buffer.toByteArray(); + log.info("通过 HTTP 直接下载成功加载客户签名: {}", signatureUrl); + } + } catch (Exception e) { + log.warn("通过 HTTP 下载客户签名失败: {}", e.getMessage()); + } + } + + if (imageBytes == null || imageBytes.length == 0) { + log.warn("客户签名图片文件为空: {}", signatureUrl); + return ""; + } + + // 检测图片类型 + String imageType = detectImageType(imageBytes); + String base64 = java.util.Base64.getEncoder().encodeToString(imageBytes); + return "data:" + imageType + ";base64," + base64; + + } catch (Exception e) { + log.error("加载客户签名图片失败,将不显示客户签名: {} - {}", signatureUrl, e.getMessage(), e); + return ""; + } + } +} diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderService.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderService.java index 2d4fbbb..14e9bc6 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderService.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderService.java @@ -52,4 +52,20 @@ public interface RenewalOrderService { */ PageResult getRenewalOrderPage(RenewalOrderPageReqVO pageReqVO); + /** + * 生成合同 PDF(在线)并上传,回写订单的 contractUrl + * + * @param id 订单编号 + * @return 合同 URL + */ + String generateContract(Long id); + + /** + * 生成合同 HTML(用于预览) + * + * @param id 订单编号 + * @return 合同 HTML 字符串 + */ + String generateContractHtml(Long id); + } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImpl.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImpl.java index ac48696..43648c9 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImpl.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.car.service.renewalorder; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; @@ -13,6 +14,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.car.dal.mysql.renewalorder.RenewalOrderMapper; +import cn.iocoder.yudao.module.car.service.contract.ContractService; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.car.enums.ErrorCodeConstants.*; @@ -22,6 +24,7 @@ import static cn.iocoder.yudao.module.car.enums.ErrorCodeConstants.*; * * @author 芋道源码 */ +@Slf4j @Service @Validated public class RenewalOrderServiceImpl implements RenewalOrderService { @@ -29,10 +32,17 @@ public class RenewalOrderServiceImpl implements RenewalOrderService { @Resource private RenewalOrderMapper renewalOrderMapper; + @Resource + private ContractService contractService; + @Override public Long createRenewalOrder(RenewalOrderSaveReqVO createReqVO) { // 插入 RenewalOrderDO renewalOrder = BeanUtils.toBean(createReqVO, RenewalOrderDO.class); + // 数据库字段 contract_url 为 NOT NULL 且无默认值,新增时必须给一个兜底值 + if (renewalOrder.getContractUrl() == null) { + renewalOrder.setContractUrl(""); + } renewalOrderMapper.insert(renewalOrder); // 返回 return renewalOrder.getId(); @@ -71,4 +81,30 @@ public class RenewalOrderServiceImpl implements RenewalOrderService { return renewalOrderMapper.selectPage(pageReqVO); } + @Override + @Transactional(rollbackFor = Exception.class) + public String generateContract(Long id) { + RenewalOrderDO order = renewalOrderMapper.selectById(id); + if (order == null) { + throw exception(RENEWAL_ORDER_NOT_EXISTS); + } + // 添加日志,确认订单数据是否正确加载 + log.info("生成合同 - 订单ID: {}, 客户签名URL: {}", id, order.getCustomerSignatureUrl()); + String url = contractService.generateAndUploadContract(order); + // 回写合同 URL + RenewalOrderDO update = new RenewalOrderDO(); + update.setId(id); + update.setContractUrl(url); + renewalOrderMapper.updateById(update); + return url; + } + + @Override + public String generateContractHtml(Long id) { + RenewalOrderDO order = renewalOrderMapper.selectById(id); + if (order == null) { + throw exception(RENEWAL_ORDER_NOT_EXISTS); + } + return contractService.generateContractHtml(order); + } } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductService.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductService.java index 904965d..50ba5c0 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductService.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductService.java @@ -52,4 +52,5 @@ public interface RenewalProductService { */ PageResult getRenewalProductPage(RenewalProductPageReqVO pageReqVO); + List findByProductType(); } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductServiceImpl.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductServiceImpl.java index d46fcd9..1eee03f 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductServiceImpl.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/renewalproduct/RenewalProductServiceImpl.java @@ -1,11 +1,19 @@ package cn.iocoder.yudao.module.car.service.renewalproduct; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.car.dal.dataobject.tireuser.TireUserDO; +import cn.iocoder.yudao.module.car.service.tireuser.TireUserService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.util.stream.Collectors; + import cn.iocoder.yudao.module.car.controller.admin.renewalproduct.vo.*; import cn.iocoder.yudao.module.car.dal.dataobject.renewalproduct.RenewalProductDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -28,30 +36,30 @@ public class RenewalProductServiceImpl implements RenewalProductService { @Resource private RenewalProductMapper renewalProductMapper; + @Resource + private TireUserService tireUserService; + @Resource + private PermissionService permissionService; + + // ================== CRUD ================== @Override public Long createRenewalProduct(RenewalProductSaveReqVO createReqVO) { - // 插入 RenewalProductDO renewalProduct = BeanUtils.toBean(createReqVO, RenewalProductDO.class); renewalProductMapper.insert(renewalProduct); - // 返回 return renewalProduct.getId(); } @Override public void updateRenewalProduct(RenewalProductSaveReqVO updateReqVO) { - // 校验存在 validateRenewalProductExists(updateReqVO.getId()); - // 更新 RenewalProductDO updateObj = BeanUtils.toBean(updateReqVO, RenewalProductDO.class); renewalProductMapper.updateById(updateObj); } @Override public void deleteRenewalProduct(Long id) { - // 校验存在 validateRenewalProductExists(id); - // 删除 renewalProductMapper.deleteById(id); } @@ -71,4 +79,23 @@ public class RenewalProductServiceImpl implements RenewalProductService { return renewalProductMapper.selectPage(pageReqVO); } -} \ No newline at end of file + // ================== 业务查询 ================== + + @Override + public List findByProductType() { + Long userId = SecurityFrameworkUtils.getLoginUserId(); + + // 租户管理员:查全部 + if (permissionService.hasAnyRoles(userId, "tenant_admin")) { + return renewalProductMapper.findByProductType(null); + } + + // 普通用户:按用户配置的产品类型查 + TireUserDO tireUsers = tireUserService.findByUserId(userId); + if (StringUtils.isBlank(tireUsers.getProductType())) { + return Collections.emptyList(); + } + + return renewalProductMapper.findByProductType(tireUsers.getProductType()); + } +} diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreService.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreService.java index b1ee470..7747a8b 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreService.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreService.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.car.controller.admin.store.vo.StoreSaveReqVO; import cn.iocoder.yudao.module.car.dal.dataobject.store.StoreDO; import javax.validation.Valid; +import java.util.List; /** * 门店管理 Service 接口 @@ -52,4 +53,5 @@ public interface StoreService { */ PageResult getStorePage(StorePageReqVO pageReqVO); + List findByProductType(); } \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreServiceImpl.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreServiceImpl.java index 69d2dd6..06839a5 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreServiceImpl.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/store/StoreServiceImpl.java @@ -2,14 +2,21 @@ package cn.iocoder.yudao.module.car.service.store; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.car.controller.admin.store.vo.StorePageReqVO; import cn.iocoder.yudao.module.car.controller.admin.store.vo.StoreSaveReqVO; import cn.iocoder.yudao.module.car.dal.dataobject.store.StoreDO; import cn.iocoder.yudao.module.car.dal.mysql.store.StoreMapper; +import cn.iocoder.yudao.module.car.service.tireuser.TireUserService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.car.enums.ErrorCodeConstants.STORE_NOT_EXISTS; @@ -25,47 +32,97 @@ public class StoreServiceImpl implements StoreService { @Resource private StoreMapper storeMapper; + @Resource + private PermissionService permissionService; + @Resource + private TireUserService tireUserService; + + // ==================== CRUD ==================== @Override public Integer createStore(StoreSaveReqVO createReqVO) { - // 插入 StoreDO store = BeanUtils.toBean(createReqVO, StoreDO.class); storeMapper.insert(store); - // 返回 return store.getId(); } @Override public void updateStore(StoreSaveReqVO updateReqVO) { - // 校验存在 validateStoreExists(updateReqVO.getId()); - // 更新 StoreDO updateObj = BeanUtils.toBean(updateReqVO, StoreDO.class); storeMapper.updateById(updateObj); } @Override public void deleteStore(Integer id) { - // 校验存在 validateStoreExists(id); - // 删除 storeMapper.deleteById(id); } + @Override + public StoreDO getStore(Integer id) { + return storeMapper.selectById(id); + } + private void validateStoreExists(Integer id) { if (storeMapper.selectById(id) == null) { throw exception(STORE_NOT_EXISTS); } } - @Override - public StoreDO getStore(Integer id) { - return storeMapper.selectById(id); - } + // ==================== Query ==================== @Override public PageResult getStorePage(StorePageReqVO pageReqVO) { - return storeMapper.selectPage(pageReqVO); + if (isTenantAdmin()) { + return storeMapper.selectPage(pageReqVO); + } + + List storeIds = getCurrentUserStoreIds(); + if (storeIds.isEmpty()) { + return PageResult.empty(); + } + + return storeMapper.selectPageByIds(pageReqVO, storeIds); } -} \ No newline at end of file + /** + * 当前用户可访问的门店列表(用于下拉框等场景) + */ + @Override + public List findByProductType() { + if (isTenantAdmin()) { + return storeMapper.selectList(); + } + + List storeIds = getCurrentUserStoreIds(); + if (storeIds.isEmpty()) { + return Collections.emptyList(); + } + + return storeMapper.selectBatchIds(storeIds); + } + + // ==================== Private ==================== + + private boolean isTenantAdmin() { + Long userId = SecurityFrameworkUtils.getLoginUserId(); + return permissionService.hasAnyRoles(userId, "tenant_admin"); + } + + private List getCurrentUserStoreIds() { + Long userId = SecurityFrameworkUtils.getLoginUserId(); + String storeIds = tireUserService.findByUserId(userId).getStoreId(); + + if (storeIds == null || storeIds.trim().isEmpty()) { + return Collections.emptyList(); + } + + return Arrays.stream(storeIds.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(Long::valueOf) + .collect(Collectors.toList()); + } + +} diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserService.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserService.java index 65bfa25..f20857e 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserService.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserService.java @@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.car.controller.admin.tireuser.vo.TireUserPageReqV import cn.iocoder.yudao.module.car.controller.admin.tireuser.vo.TireUserSaveReqVO; import cn.iocoder.yudao.module.car.dal.dataobject.tireuser.TireUserDO; +import java.util.List; + /** * 门店用户管理 Service 接口 * diff --git a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserServiceImpl.java b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserServiceImpl.java index a322039..518d7d1 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserServiceImpl.java +++ b/yudao-module-car/yudao-module-car-biz/src/main/java/cn/iocoder/yudao/module/car/service/tireuser/TireUserServiceImpl.java @@ -45,12 +45,7 @@ public class TireUserServiceImpl implements TireUserService { @Transactional public Integer createTireUser(TireUserSaveReqVO createReqVO) { Long userId = adminUserService.createUser(createReqVO.getUser()); - Long storeId = createReqVO.getStoreId(); - Long warehouseId = createReqVO.getWarehouseId(); - - TireUserDO entity = new TireUserDO(); - entity.setStoreId(storeId); - entity.setWarehouseId(warehouseId); + TireUserDO entity = BeanUtils.toBean(createReqVO, TireUserDO.class); entity.setUserId(userId); return tireUserMapper.insert(entity); } diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/.gitkeep b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/.gitkeep new file mode 100644 index 0000000..64266a9 --- /dev/null +++ b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/.gitkeep @@ -0,0 +1,2 @@ +# 此文件用于确保 fonts 目录被 Git 追踪 +# 字体文件 simsun.ttc 应该放在此目录下 diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/README.md b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/README.md new file mode 100644 index 0000000..30893b7 --- /dev/null +++ b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/README.md @@ -0,0 +1,51 @@ +# 中文字体文件说明 + +## 字体文件要求 + +为了正确显示中文,需要在此目录下放置中文字体文件。 + +## 推荐字体 + +1. **SimSun (宋体)** - `simsun.ttc` 或 `simsun.ttf` + - Windows 系统自带,路径:`C:/Windows/Fonts/simsun.ttc` + - 适合正式文档 + +2. **SimHei (黑体)** - `simhei.ttf` + - Windows 系统自带,路径:`C:/Windows/Fonts/simhei.ttf` + - 适合标题 + +3. **Microsoft YaHei (微软雅黑)** - `msyh.ttc` + - Windows 系统自带,路径:`C:/Windows/Fonts/msyh.ttc` + - 现代美观 + +4. **PingFang SC (苹方)** - `PingFang.ttc` + - macOS 系统自带,路径:`/System/Library/Fonts/PingFang.ttc` + +## 如何获取字体文件 + +### Windows 系统 +1. 打开 `C:/Windows/Fonts/` 目录 +2. 找到 `simsun.ttc`(宋体)或 `msyh.ttc`(微软雅黑) +3. 复制到本目录,重命名为 `simsun.ttc` + +### macOS 系统 +1. 打开 `/System/Library/Fonts/` 目录 +2. 找到 `PingFang.ttc`(苹方) +3. 复制到本目录,重命名为 `simsun.ttc` + +### Linux 系统 +```bash +# 安装文泉驿字体 +sudo apt-get install fonts-wqy-microhei +# 或 +sudo yum install wqy-microhei-fonts + +# 然后从系统字体目录复制 +cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ./simsun.ttc +``` + +## 注意事项 + +- 字体文件有版权限制,请确保您有使用权限 +- 建议使用系统自带的免费字体 +- 文件命名:建议使用 `simsun.ttc` 或 `simsun.ttf`(代码中优先查找此文件名) diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/chinese.ttf b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/chinese.ttf new file mode 100644 index 0000000..590ecf6 Binary files /dev/null and b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/chinese.ttf differ diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/simsun.ttc b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/simsun.ttc new file mode 100644 index 0000000..048d843 Binary files /dev/null and b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/simsun.ttc differ diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/检查字体.sh b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/检查字体.sh new file mode 100755 index 0000000..e66089a --- /dev/null +++ b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/检查字体.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# 检查字体文件的脚本 + +echo "==========================================" +echo "检查字体文件" +echo "==========================================" +echo "" + +FONT_DIR="./" +FONT_FILES=("simsun.ttc" "simsun.ttf" "simhei.ttf" "msyh.ttc" "PingFang.ttc") + +found=false + +for font in "${FONT_FILES[@]}"; do + if [ -f "$FONT_DIR/$font" ]; then + size=$(ls -lh "$FONT_DIR/$font" | awk '{print $5}') + echo "✓ 找到字体文件: $font (大小: $size)" + found=true + else + echo "✗ 未找到: $font" + fi +done + +echo "" +if [ "$found" = false ]; then + echo "==========================================" + echo "❌ 错误:未找到任何字体文件!" + echo "==========================================" + echo "" + echo "请按照以下步骤操作:" + echo "" + echo "【Windows 系统】" + echo "1. 打开 C:\\Windows\\Fonts 目录" + echo "2. 复制 simsun.ttc 文件" + echo "3. 粘贴到本目录(fonts 目录)" + echo "" + echo "【macOS 系统】" + echo "执行命令:" + echo " cp /System/Library/Fonts/PingFang.ttc ./simsun.ttc" + echo "" + echo "【Linux 系统】" + echo "执行命令:" + echo " sudo cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ./simsun.ttc" + echo "" +else + echo "==========================================" + echo "✓ 字体文件已就绪" + echo "==========================================" +fi diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/获取字体说明.txt b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/获取字体说明.txt new file mode 100644 index 0000000..7eb72ad --- /dev/null +++ b/yudao-module-car/yudao-module-car-biz/src/main/resources/fonts/获取字体说明.txt @@ -0,0 +1,35 @@ +========================================== +如何获取中文字体文件 +========================================== + +由于字体文件有版权限制,我无法直接下载。请按以下步骤操作: + +【Windows 系统】 +1. 打开文件资源管理器 +2. 进入 C:\Windows\Fonts 目录 +3. 找到以下字体文件之一: + - simsun.ttc (宋体) - 推荐 + - msyh.ttc (微软雅黑) + - simhei.ttf (黑体) +4. 复制该字体文件 +5. 粘贴到本目录(fonts 目录) +6. 重命名为 simsun.ttc(如果原文件名不同) + +【macOS 系统】 +1. 打开 Finder +2. 按 Command+Shift+G 打开"前往文件夹" +3. 输入:/System/Library/Fonts +4. 找到 PingFang.ttc 或 STSong.ttc +5. 复制到本目录,重命名为 simsun.ttc + +【Linux 系统】 +在终端执行: +sudo cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ./simsun.ttc + +或者安装字体后复制: +sudo apt-get install fonts-wqy-microhei +sudo cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ./simsun.ttc + +========================================== +放置字体文件后,重新编译项目即可生效 +========================================== diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/store/StoreMapper.xml b/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/store/StoreMapper.xml index 90e1efe..997fbfd 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/store/StoreMapper.xml +++ b/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/store/StoreMapper.xml @@ -8,5 +8,4 @@ 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ --> - \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/tireuser/TireUserMapper.xml b/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/tireuser/TireUserMapper.xml index e50b9af..67ba273 100644 --- a/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/tireuser/TireUserMapper.xml +++ b/yudao-module-car/yudao-module-car-biz/src/main/resources/mapper/tireuser/TireUserMapper.xml @@ -3,25 +3,41 @@ + - + \ No newline at end of file diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/static/seal.png b/yudao-module-car/yudao-module-car-biz/src/main/resources/static/seal.png new file mode 100644 index 0000000..53c14eb Binary files /dev/null and b/yudao-module-car/yudao-module-car-biz/src/main/resources/static/seal.png differ diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/static/seal.webp b/yudao-module-car/yudao-module-car-biz/src/main/resources/static/seal.webp new file mode 100644 index 0000000..1e7b479 Binary files /dev/null and b/yudao-module-car/yudao-module-car-biz/src/main/resources/static/seal.webp differ diff --git a/yudao-module-car/yudao-module-car-biz/src/main/resources/templates/contract/renewal-contract.html b/yudao-module-car/yudao-module-car-biz/src/main/resources/templates/contract/renewal-contract.html new file mode 100644 index 0000000..b1fa548 --- /dev/null +++ b/yudao-module-car/yudao-module-car-biz/src/main/resources/templates/contract/renewal-contract.html @@ -0,0 +1,312 @@ + + + + + 途安出行+保障服务凭证 + + + + +
+
途安伴旅
+
+
+

途安出行+保障服务凭证

+ +
+
凭证编号:TABLA20260101001
+
+ +
+
合同条款:
+

+ 途安出行+保障服务产品为龙岩途安伴旅汽车服务有限公司(以下简称“本公司”)和保险公司合作的一款保障类服务产品,途安出行+保障服务产品已向保险公司投递相关保险。 + 服务合同中双方约定的服务期限、承保范围、保障责任、除外责任、申请资料等内容如下: +

+
+ +
+
车主信息卡及服务声明(摘录):
+ + + + + + + + + + + + + + + + + + + + + + + + +
行驶证车主证件号码
车辆品牌联系电话
车架号(VIN)发动机号
购车发票价车牌号码
车主联系地址
服务经销商服务费
+
+ +
+
一、服务期限:
+

3年,自 2026-01-16 零时起至 2029-01-16 二十四时止。

+
+ +
+
二、服务范围:
+

+ 中华人民共和国境内(港、澳、台除外),7座及以下乘用车(非营运车)。燃油车车龄3个月内,新能源车龄6个月内的新车,不承保次新车、二手车,车龄以行驶证初登日期计算。 + a. 超出车龄约定不予承保;b. 车辆购买时不存在重大事故;车辆的性能、动力没有被改装过;车辆非水泡、火烧车; + c. 车辆出险时已经全额购买机动车损失保险;d.车辆必须按照厂家车辆使用手册、厂家说明书等相关规定进行使用; + e. 车辆使用性质为非公共服务用途车辆、非比赛竞赛用车。 +

+
+ +
+
三、保障责任:
+

+ 在服务有效期内,当标的车辆遭遇机动车商业保险所保障的各类车损(仅限双方事故),包括车辆驾驶人在此次交通事故责任认定中属于全责、主要责任、同等责任、次要责任或无责任(需触发对方商业车险三者险责任且非零结案), + 如标的车辆的车损在本次事故中由商业车险公司核定最终支付的定损金额达到标的车发票金额10%-30%,回原店维修可申请维修款补贴; + 超过30%回原店维修则可通过原购买服务的门店申请维修款补贴或车辆置换服务(仅可选择一种赔偿方式)。 +

+
+ +
+
四、补贴标准:
+

+ 权益一:标的车发票金额×10%≤定损金额<标的车发票金额×30%的,补贴金额=定损金额×10%的维修款补贴。服务期限:2026年01月16日至2029年01月16日。
+ 权益二:定损金额≥标的车发票金额×30%,且非全损及推定全损时,如车主选择换新车,车主需签署《机动车置换服务合同》让渡原车及商业车险所有权并配合完成标的车辆过户工作,补贴金额=标的车发票金额+车辆购置税+车辆登记费。服务期限:2026年01月16日至2026年01月16日。
+ 权益三:标的车辆因意外事故导致全损的,补偿金额=置换新车产生的差价费用+车辆登记费。标的车辆因意外事故导致推定全损,补偿金额=车辆折旧金额+车辆登记费。具体补偿金额按以下补贴标准执行。服务期限:2026年01月04日至2029年01月16日。
+ 说明:a. 服务期内标的车发票金额×10%≤定损金额<标的车发票金额×30%的,每年最多享受2次赔偿且限额5000元/次。(当触发定损金额≥标的车发票金额×30%及全损,补贴责任结束后服务终止);b. 置换新车产生的差价费用=标的车发票金额-车损险赔付金额;车辆折旧金额=标的车发票金额×车辆自购买本服务产品之日起计算的已使用月数×0.82%(月折旧率),不足一个月不纳入计算;c. 车辆登记费包括新车注册登记过程中发生的关税、车船税、验车费、上牌费(1000元为限)等政府部门收费。购置税以车辆发票显示的购置税金额或标的车发票金额×购置税率为准;车辆购置税金额及额外费用=车辆购置发票金额×8.55%(上限),触发全损及推定全损情况下上牌费、购置税不超过购车发票价(二手车车辆商业险车损定价)的7%;d. 标的车发票金额/购置税金额与出险时重新购置同款车型新车发票价金额/购置税金额不一致的,以两者取低者为准进行赔付(因同款车型停售或车主主动要求置换其他车型时,同样适用本原则);e. 本服务凭证内所有权益触发赔付时,每次赔付的绝对免赔额为300元/次; + 备注:权益一、权益二、权益三仅限购买服务的服务期限内有效。针对同一次事故,或者多次事故同一次进场,都仅可选择一种保障服务且仅限一次赔付,当触发权益二或权益三换新车服务完成后,此项产品内的所有赔偿服务条款均已结束,所有定损金额以保险公司车损险维修定损金额为准。 +

+
+ +
+
五、除外责任:
+

+ a.当车辆全损或推定全损时,未能提供被保险车辆购买商业车险的承保公司,所提供全损或者推定全损的盖章协议或证明; + b.当车辆遭受损害事故仅导致部分损失时,定损金额未达到标的车发票金额×10%的; + c.当车辆遭受损害事故仅导致部分损失时,未能提供相关车辆购买商业车险的承保公司出具的赔偿协议或证明的; + d.车辆的认证信息、车辆行驶证或实际使用性质与服务凭证中适用条件要求不一致的; + e.车辆所有人与服务凭证车主不一致的; + f.车辆遭受的损害事故发生在服务期限之外,或车主未在服务凭证规定的期限内提出新车置换或补贴要求; + g.任何形式的人身伤害、财产损失,及除本合同所列车辆置换费用以外其他任何费用支出; + h.车辆全损及购置新车的过程中所产生的任何间接损失、赔偿责任; + i.标的车辆驾驶员或车主的欺诈、不诚实、酒驾、毒驾等违法犯罪行为; + j.战争、敌对行为、军事行动、武装冲突、恐怖主义活动、罢工、暴动、骚乱; + k.盗抢、划痕(划痕险)、自燃水淹、自然灾害、核爆炸、核裂变、核聚变; + l.放射性污染及其他各种环境污染; + m.行政行为、司法行为。 +

+
+ +
+
六、申请资料:
+

+ 发生保险责任事故时,车主应配合提供相应索赔材料:(1)索赔申请书;(2)原购车发票原件;(3)《车辆增值服务合同》原件;(4)被保车辆行驶证正副页、车主身份证正反面;(5)商业险保单;(6)事故证明或交通事故陈述书或保险公司查勘报告,如无则协商增加10%绝对免赔率;(7)全损或推定全损协议及赔款水单截图;(8)置换后新购车发票;(9)新购车合格证;(10)新购车购置税发票;(11)上牌服务费发票;(12)车架号照片;(13)前后45度角整车照片;(14)车辆损失照片;(15)领款人信息(姓名、账号、开户行);(16)非被保险人收款需提供收款信息并签署权益转让协议;(17)反洗钱资料:单案赔款超1万元需提供相关身份资料。未达到全损或推定全损且未置换新车的,无需提供第7—11项材料。 +

+
+ +
+
七、特别约定:
+

+ 未经补贴接收方书面同意,本凭证及对应保单不得退保、批改(不影响补贴接收方权益的批改除外) ;如发生保障服务赔偿责任时,按补贴接收方的权益转让书支付赔款。 以上服务权益补贴服务必须到我司授权合作门店(优先原购买本服务门店)进行维修方可办理。如本车辆未购买商业车损险或商业险拒赔的则不能享受理赔服务。 服务期间内如发生车辆转让、过户、变更车辆使用性质,保障服务责任终止。 +

+
+ +
+
八、特别声明:
+

+ 特别声明:车主应当在24小时内向商业险保险公司及交管部门报案,同时致电龙岩途安伴旅汽车服务有限公司全国统一客服热线:18039881137 进行备案,并通过龙岩途安伴旅汽车服务有限公司向保险公司索赔。服务保障正式生效日期:在付款后即T+12日(T为此项服务产品的付款购买日)为车辆使用观察期,此期限内发生的事故将不产生赔付责任,后续为此项服务保障权益的正式生效日期,付款行为以途安伴旅书面确认收到款项之日为准。如系分期客户,若其未按合同/微信通知以及其他告知的约定,按时支付分期款项,经催告后在合理期限内仍未支付的,合同终止,且本公司在款项未结清期限内有权利拒绝赔偿和退还已支付款项。关于退费:凭证生成日起,3天(含)扣除300元工本费无息退还剩余保费,3天以上不支持退款;已发生补贴责任的不可退费。 +

+
+ +
+ + + + + +
+ + + + + +
客户签名: + 客户签名 +
+
+
+
服务商:
+
签章:seal
+
+
+
+ +
+ + + diff --git a/yudao-module-car/yudao-module-car-biz/src/test/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImplTest.java b/yudao-module-car/yudao-module-car-biz/src/test/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImplTest.java index 0a7d9e7..2dfa8ad 100644 --- a/yudao-module-car/yudao-module-car-biz/src/test/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImplTest.java +++ b/yudao-module-car/yudao-module-car-biz/src/test/java/cn/iocoder/yudao/module/car/service/renewalorder/RenewalOrderServiceImplTest.java @@ -136,6 +136,7 @@ public class RenewalOrderServiceImplTest extends BaseDbUnitTest { o.setRemark(null); o.setInputUser(null); o.setContractRemark(null); + o.setContractUrl(null); o.setCreateTime(null); }); renewalOrderMapper.insert(dbRenewalOrder); @@ -191,6 +192,7 @@ public class RenewalOrderServiceImplTest extends BaseDbUnitTest { renewalOrderMapper.insert(cloneIgnoreId(dbRenewalOrder, o -> o.setInputUser(null))); // 测试 contractRemark 不匹配 renewalOrderMapper.insert(cloneIgnoreId(dbRenewalOrder, o -> o.setContractRemark(null))); + renewalOrderMapper.insert(cloneIgnoreId(dbRenewalOrder, o -> o.setContractUrl(null))); // 测试 createTime 不匹配 renewalOrderMapper.insert(cloneIgnoreId(dbRenewalOrder, o -> o.setCreateTime(null))); // 准备参数 @@ -221,6 +223,7 @@ public class RenewalOrderServiceImplTest extends BaseDbUnitTest { reqVO.setRemark(null); reqVO.setInputUser(null); reqVO.setContractRemark(null); + reqVO.setContractUrl(null); reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); // 调用 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java index b2f95d6..d176ffe 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java @@ -4,6 +4,7 @@ import cn.hutool.core.lang.Assert; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; import cn.iocoder.yudao.framework.ip.core.utils.IPUtils; import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO; @@ -34,6 +35,23 @@ public class AreaController { return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class)); } + @GetMapping("/getCityTree") + @Operation(summary = "获得市树") + public CommonResult> getCityTree() { + Area area = AreaUtils.getArea(Area.ID_CHINA); + Assert.notNull(area, "获取不到中国"); + filterCityTree(area); + return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class)); + } + + private void filterCityTree(Area area){ + if(area.getType() == 3){ + area.setChildren(null); + }else { + area.getChildren().forEach(this::filterCityTree); + } + } + @GetMapping("/get-by-ip") @Operation(summary = "获得 IP 对应的地区名") @Parameter(name = "ip", description = "IP", required = true) diff --git a/yudao-server/.DS_Store b/yudao-server/.DS_Store index 87936ff..a16c783 100644 Binary files a/yudao-server/.DS_Store and b/yudao-server/.DS_Store differ