diff --git a/yudao-module-visit/pom.xml b/yudao-module-visit/pom.xml
index 6fc0c27114fbbab689bd9972b892c327a7b62032..d4f34c9e016103f29339dde0c53a62ca64ed725d 100644
--- a/yudao-module-visit/pom.xml
+++ b/yudao-module-visit/pom.xml
@@ -3,18 +3,18 @@
cn.iocoder.boot
yudao
- ${revision}
+ ${revision}
4.0.0
yudao-module-visit
- jar
+ jar
- ${project.artifactId}
-
- visit 模块,主要实现 拜访系统主要业务逻辑。
+ ${project.artifactId}
+
+ visit 模块,主要实现 拜访 等功能。
-
+
cn.iocoder.boot
@@ -37,6 +37,10 @@
cn.iocoder.boot
yudao-spring-boot-starter-test
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-excel
+
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/DemoTestController.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/DemoTestController.java
deleted file mode 100644
index 97e8f8299a73a590e20d24ddca8bf1345d0a1edc..0000000000000000000000000000000000000000
--- a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/DemoTestController.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.visit.controller.admin;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Operation;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-
-@Tag(name = "管理后台 - Test")
-@RestController
-@RequestMapping("/demo/test")
-@Validated
-public class DemoTestController {
-
- // 这个构造方法,只是方便大家,验证 Controller 有生效
- public DemoTestController() {
- System.out.println(getClass() + "生效啦!!!");
- }
-
- @GetMapping("/get")
- @Operation(summary = "获取 test 信息")
- public CommonResult get() {
- return success("true");
- }
-
-}
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java
new file mode 100644
index 0000000000000000000000000000000000000000..e99aab72f1e6edbb5bfb21d4f34595b2fec5285e
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.visit.controller.admin.customerinfo;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+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.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
+import cn.iocoder.yudao.module.visit.service.customerinfo.CustomerInfoService;
+
+@Tag(name = "管理后台 - 客户信息")
+@RestController
+@RequestMapping("/visit/customer-info")
+@Validated
+public class CustomerInfoController {
+
+ @Resource
+ private CustomerInfoService customerInfoService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建客户信息")
+ @PreAuthorize("@ss.hasPermission('visit:customer-info:create')")
+ public CommonResult createCustomerInfo(@Valid @RequestBody CustomerInfoSaveReqVO createReqVO) {
+ return success(customerInfoService.createCustomerInfo(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新客户信息")
+ @PreAuthorize("@ss.hasPermission('visit:customer-info:update')")
+ public CommonResult updateCustomerInfo(@Valid @RequestBody CustomerInfoSaveReqVO updateReqVO) {
+ customerInfoService.updateCustomerInfo(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除客户信息")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('visit:customer-info:delete')")
+ public CommonResult deleteCustomerInfo(@RequestParam("id") Long id) {
+ customerInfoService.deleteCustomerInfo(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得客户信息")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('visit:customer-info:query')")
+ public CommonResult getCustomerInfo(@RequestParam("id") Long id) {
+ CustomerInfoDO customerInfo = customerInfoService.getCustomerInfo(id);
+ return success(BeanUtils.toBean(customerInfo, CustomerInfoRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得客户信息分页")
+ @PreAuthorize("@ss.hasPermission('visit:customer-info:query')")
+ public CommonResult> getCustomerInfoPage(@Valid CustomerInfoPageReqVO pageReqVO) {
+ PageResult pageResult = customerInfoService.getCustomerInfoPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, CustomerInfoRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出客户信息 Excel")
+ @PreAuthorize("@ss.hasPermission('visit:customer-info:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportCustomerInfoExcel(@Valid CustomerInfoPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = customerInfoService.getCustomerInfoPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "客户信息.xls", "数据", CustomerInfoRespVO.class,
+ BeanUtils.toBean(list, CustomerInfoRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoPageReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoPageReqVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..5688d0c2b2e60df6846650a5b4ce3ce8aa841911
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoPageReqVO.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import java.math.BigDecimal;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 客户信息分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CustomerInfoPageReqVO extends PageParam {
+
+ @Schema(description = "客户姓名")
+ private String customerName;
+
+ @Schema(description = "联系方式")
+ private String contact;
+
+ @Schema(description = "公司名称")
+ private String companyName;
+
+ @Schema(description = "性质等级")
+ private Integer customerType;
+
+ @Schema(description = "市级行政区编码")
+ private String cityCode;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoRespVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoRespVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a9e296e8b2fca43182e1549ef549a855100df8d
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoRespVO.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.math.BigDecimal;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+@Schema(description = "管理后台 - 客户信息 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class CustomerInfoRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("客户姓名")
+ private String customerName;
+
+ @Schema(description = "联系方式", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("联系方式")
+ private String contact;
+
+ @Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("公司名称")
+ private String companyName;
+
+ @Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty(value = "性质等级", converter = DictConvert.class)
+ @DictFormat("customer_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer customerType;
+
+ @Schema(description = "所在地区", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("所在地区")
+ private String regionFullName;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..104ec3320ad00db57d2fcac08bbfcca24b495ec8
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java
@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 客户信息新增/修改 Request VO")
+@Data
+public class CustomerInfoSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "客户姓名不能为空")
+ private String customerName;
+
+ @Schema(description = "联系方式", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "联系方式不能为空")
+ private String contact;
+
+ @Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "公司名称不能为空")
+ private String companyName;
+
+ @Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "性质等级不能为空")
+ private Integer customerType;
+
+ @Schema(description = "省名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "省名称不能为空")
+ private String provinceName;
+
+ @Schema(description = "市名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "市名称不能为空")
+ private String cityName;
+
+ @Schema(description = "区名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "区名称不能为空")
+ private String areaName;
+
+ @Schema(description = "所在地区", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "所在地区不能为空")
+ private String regionFullName;
+
+ @Schema(description = "详细地址", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "详细地址不能为空")
+ private String locationText;
+
+ @Schema(description = "经度", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "经度不能为空")
+ private BigDecimal longitude;
+
+ @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "纬度不能为空")
+ private BigDecimal latitude;
+
+ @Schema(description = "定位静态图 URL", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "定位静态图 URL不能为空")
+ private String locationImage;
+
+ @Schema(description = "产品信息")
+ private String productIds;
+
+ @Schema(description = "客户部门")
+ private String department;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fbdb29f403cf37f581c30c9f2057b67d4a08441
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.visit.controller.admin.info;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+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.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
+import cn.iocoder.yudao.module.visit.service.info.InfoService;
+
+@Tag(name = "管理后台 - 客户拜访记录")
+@RestController
+@RequestMapping("/visit/info")
+@Validated
+public class InfoController {
+
+ @Resource
+ private InfoService infoService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建客户拜访记录")
+ @PreAuthorize("@ss.hasPermission('visit:info:create')")
+ public CommonResult createInfo(@Valid @RequestBody InfoSaveReqVO createReqVO) {
+ return success(infoService.createInfo(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新客户拜访记录")
+ @PreAuthorize("@ss.hasPermission('visit:info:update')")
+ public CommonResult updateInfo(@Valid @RequestBody InfoSaveReqVO updateReqVO) {
+ infoService.updateInfo(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除客户拜访记录")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('visit:info:delete')")
+ public CommonResult deleteInfo(@RequestParam("id") Long id) {
+ infoService.deleteInfo(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得客户拜访记录")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('visit:info:query')")
+ public CommonResult getInfo(@RequestParam("id") Long id) {
+ InfoDO info = infoService.getInfo(id);
+ return success(BeanUtils.toBean(info, InfoRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得客户拜访记录分页")
+ @PreAuthorize("@ss.hasPermission('visit:info:query')")
+ public CommonResult> getInfoPage(@Valid InfoPageReqVO pageReqVO) {
+ PageResult pageResult = infoService.getInfoPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, InfoRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出客户拜访记录 Excel")
+ @PreAuthorize("@ss.hasPermission('visit:info:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportInfoExcel(@Valid InfoPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = infoService.getInfoPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "客户拜访记录.xls", "数据", InfoRespVO.class,
+ BeanUtils.toBean(list, InfoRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPageReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPageReqVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..0db3e48686f83d513f1657be53dcdf45904c3db7
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPageReqVO.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.visit.controller.admin.info.vo;
+
+import lombok.*;
+
+import java.time.LocalDate;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 客户拜访记录分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfoPageReqVO extends PageParam {
+
+ @Schema(description = "客户姓名")
+ private String customerName;
+
+ @Schema(description = "联系方式(客户手机号)")
+ private String contact;
+
+ @Schema(description = "客户公司名称")
+ private String companyName;
+
+ @Schema(description = "拜访日期")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDate[] visitDate;
+
+ @Schema(description = "性质等级", example = "1")
+ private Integer customerStatus;
+
+ @Schema(description = "拜访品种(多选,逗号分隔)")
+ private String visitProductIds;
+
+ @Schema(description = "拜访方式(字典)")
+ private Integer visitMethod;
+
+ @Schema(description = "拜访类型(字典)", example = "2")
+ private Integer visitType;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoRespVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoRespVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..abfce77b73c9d25282dfe827cb09143029d18b5d
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoRespVO.java
@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.visit.controller.admin.info.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.time.LocalDate;
+import java.util.*;
+import java.math.BigDecimal;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+@Schema(description = "管理后台 - 客户拜访记录 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class InfoRespVO {
+
+ @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty("主键ID")
+ private Long id;
+
+ @Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("客户姓名")
+ private String customerName;
+
+ @Schema(description = "联系方式(客户手机号)", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("联系方式(客户手机号)")
+ private String contact;
+
+ @Schema(description = "客户公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("客户公司名称")
+ private String companyName;
+
+ @Schema(description = "拜访日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("拜访日期")
+ private LocalDate visitDate;
+
+ @Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty("性质等级")
+ private Integer customerStatus;
+
+ @Schema(description = "拜访品种(多选,逗号分隔)")
+ @ExcelProperty("拜访品种(多选,逗号分隔)")
+ private String visitProductIds;
+
+ @Schema(description = "拜访方式(字典)")
+ @ExcelProperty(value = "拜访方式(字典)", converter = DictConvert.class)
+ @DictFormat("visit_method") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer visitMethod;
+
+ @Schema(description = "拜访类型(字典)", example = "2")
+ @ExcelProperty(value = "拜访类型(字典)", converter = DictConvert.class)
+ @DictFormat("visit_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer visitType;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bfc036af91052be3d27fa9c61b6ca842fa05f6f
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java
@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.module.visit.controller.admin.info.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.time.LocalDate;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 客户拜访记录新增/修改 Request VO")
+@Data
+public class InfoSaveReqVO {
+
+ @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "客户姓名", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "客户姓名不能为空")
+ private String customerName;
+
+ @Schema(description = "联系方式(客户手机号)", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "联系方式(客户手机号)不能为空")
+ private String contact;
+
+ @Schema(description = "客户公司名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "客户公司名称不能为空")
+ private String companyName;
+
+ @Schema(description = "省名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "省名称不能为空")
+ private String provinceName;
+
+ @Schema(description = "市名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "市名称不能为空")
+ private String cityName;
+
+ @Schema(description = "区名称", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "区名称不能为空")
+ private String areaName;
+
+ @Schema(description = "定位地址文字描述", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "定位地址文字描述不能为空")
+ private String locationText;
+
+ @Schema(description = "经度", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "经度不能为空")
+ private BigDecimal longitude;
+
+ @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "纬度不能为空")
+ private BigDecimal latitude;
+
+ @Schema(description = "定位静态图URL", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "定位静态图URL不能为空")
+ private String locationImage;
+
+ @Schema(description = "拜访日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "拜访日期不能为空")
+ private LocalDate visitDate;
+
+ @Schema(description = "性质等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "性质等级不能为空")
+ private Integer customerStatus;
+
+ @Schema(description = "拜访品种(多选,逗号分隔)")
+ private String visitProductIds;
+
+ @Schema(description = "拜访方式(字典)")
+ private Integer visitMethod;
+
+ @Schema(description = "拜访类型(字典)", example = "2")
+ private Integer visitType;
+
+ @Schema(description = "服务内容")
+ private String serviceContent;
+
+ @Schema(description = "客户反馈")
+ private String customerFeedback;
+
+ @Schema(description = "服务记录图片URL列表(JSON数组)")
+ private String serviceImages;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/ProductController.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/ProductController.java
new file mode 100644
index 0000000000000000000000000000000000000000..15fff174f4e1a54905fbc19379b9ccbb425b4b8f
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/ProductController.java
@@ -0,0 +1,99 @@
+package cn.iocoder.yudao.module.visit.controller.admin.product;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+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.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
+import cn.iocoder.yudao.module.visit.service.product.ProductService;
+
+@Tag(name = "管理后台 - 产品")
+@RestController
+@RequestMapping("/visit/product")
+@Validated
+public class ProductController {
+
+ @Resource
+ private ProductService productService;
+
+ public ProductController(){
+ System.out.println("生效");
+ }
+
+ @PostMapping("/create")
+ @Operation(summary = "创建产品")
+ @PreAuthorize("@ss.hasPermission('visit:product:create')")
+ public CommonResult createProduct(@Valid @RequestBody ProductSaveReqVO createReqVO) {
+ return success(productService.createProduct(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新产品")
+ @PreAuthorize("@ss.hasPermission('visit:product:update')")
+ public CommonResult updateProduct(@Valid @RequestBody ProductSaveReqVO updateReqVO) {
+ productService.updateProduct(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除产品")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('visit:product:delete')")
+ public CommonResult deleteProduct(@RequestParam("id") Long id) {
+ productService.deleteProduct(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得产品")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('visit:product:query')")
+ public CommonResult getProduct(@RequestParam("id") Long id) {
+ ProductDO product = productService.getProduct(id);
+ return success(BeanUtils.toBean(product, ProductRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得产品分页")
+ @PreAuthorize("@ss.hasPermission('visit:product:query')")
+ public CommonResult> getProductPage(@Valid ProductPageReqVO pageReqVO) {
+ PageResult pageResult = productService.getProductPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, ProductRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出产品 Excel")
+ @PreAuthorize("@ss.hasPermission('visit:product:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportProductExcel(@Valid ProductPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = productService.getProductPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "产品.xls", "数据", ProductRespVO.class,
+ BeanUtils.toBean(list, ProductRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductPageReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductPageReqVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..5edcd10630ffba2e5fb324231c2f6159af36faa8
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductPageReqVO.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.visit.controller.admin.product.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 产品分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ProductPageReqVO extends PageParam {
+
+ @Schema(description = "品种名称", example = "阿莫西林胶囊")
+ private String productName;
+
+ @Schema(description = "包装信息", example = "盒装")
+ private String packageName;
+
+ @Schema(description = "规格", example = "10x20mg")
+ private String specification;
+
+ @Schema(description = "状态(1:正常,0:禁用)", example = "1")
+ private Integer status;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductRespVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductRespVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..893721fa1117de065b5d7bd4eb215b880ec093ed
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductRespVO.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.visit.controller.admin.product.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+@Schema(description = "管理后台 - 产品 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ProductRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "品种名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "阿莫西林胶囊")
+ @ExcelProperty("品种名称")
+ private String productName;
+
+ @Schema(description = "包装信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒装")
+ @ExcelProperty("包装信息")
+ private String packageName;
+
+ @Schema(description = "规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "10x20mg")
+ @ExcelProperty("规格")
+ private String specification;
+
+ @Schema(description = "状态(1:正常,0:禁用)", example = "1")
+ @ExcelProperty(value = "状态(1:正常,0:禁用)", converter = DictConvert.class)
+ @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer status;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductSaveReqVO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductSaveReqVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..135f50affce93fb7cc3a6810b83c908d0d9b5e79
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/vo/ProductSaveReqVO.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.visit.controller.admin.product.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 产品新增/修改 Request VO")
+@Data
+public class ProductSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "品种名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "阿莫西林胶囊")
+ @NotEmpty(message = "品种名称不能为空")
+ private String productName;
+
+ @Schema(description = "包装信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒装")
+ @NotEmpty(message = "包装信息不能为空")
+ private String packageName;
+
+ @Schema(description = "规格", requiredMode = Schema.RequiredMode.REQUIRED, example = "10x20mg")
+ @NotEmpty(message = "规格不能为空")
+ private String specification;
+
+ @Schema(description = "状态(1:正常,0:禁用)", example = "1")
+ private Integer status;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/customerinfo/CustomerInfoDO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/customerinfo/CustomerInfoDO.java
new file mode 100644
index 0000000000000000000000000000000000000000..23e73cb8ab06deaeede357e6f38fff510ddda4c0
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/customerinfo/CustomerInfoDO.java
@@ -0,0 +1,105 @@
+package cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo;
+
+import lombok.*;
+import java.util.*;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 客户信息 DO
+ *
+ * @author 超级管理员
+ */
+@TableName("visit_customer_info")
+@KeySequence("visit_customer_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CustomerInfoDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 客户姓名
+ */
+ private String customerName;
+ /**
+ * 联系方式
+ */
+ private String contact;
+ /**
+ * 公司名称
+ */
+ private String companyName;
+ /**
+ * 性质等级
+ *
+ * 枚举 {@link TODO customer_type 对应的类}
+ */
+ private Integer customerType;
+ /**
+ * 省级行政区编码
+ */
+ private String provinceCode;
+ /**
+ * 省名称
+ */
+ private String provinceName;
+ /**
+ * 市级行政区编码
+ */
+ private String cityCode;
+ /**
+ * 市名称
+ */
+ private String cityName;
+ /**
+ * 区行政区编码
+ */
+ private String areaCode;
+ /**
+ * 区名称
+ */
+ private String areaName;
+ /**
+ * 所在地区
+ */
+ private String regionFullName;
+ /**
+ * 详细地址
+ */
+ private String locationText;
+ /**
+ * 经度
+ */
+ private BigDecimal longitude;
+ /**
+ * 纬度
+ */
+ private BigDecimal latitude;
+ /**
+ * 定位静态图 URL
+ */
+ private String locationImage;
+ /**
+ * 产品信息
+ */
+ private String productIds;
+ /**
+ * 客户部门
+ *
+ * 枚举 {@link TODO customer_dept 对应的类}
+ */
+ private String department;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/info/InfoDO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/info/InfoDO.java
new file mode 100644
index 0000000000000000000000000000000000000000..45578461ceadd97ae5aefbd41b13dda2e9040e1d
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/info/InfoDO.java
@@ -0,0 +1,123 @@
+package cn.iocoder.yudao.module.visit.dal.dataobject.info;
+
+import lombok.*;
+
+import java.time.LocalDate;
+import java.util.*;
+import java.math.BigDecimal;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 客户拜访记录 DO
+ *
+ * @author 超级管理员
+ */
+@TableName("visit_info")
+@KeySequence("visit_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfoDO extends BaseDO {
+
+ /**
+ * 主键ID
+ */
+ @TableId
+ private Long id;
+ /**
+ * 客户姓名
+ */
+ private String customerName;
+ /**
+ * 联系方式(客户手机号)
+ */
+ private String contact;
+ /**
+ * 客户公司名称
+ */
+ private String companyName;
+ /**
+ * 省编码
+ */
+ private String provinceCode;
+ /**
+ * 省名称
+ */
+ private String provinceName;
+ /**
+ * 市编码
+ */
+ private String cityCode;
+ /**
+ * 市名称
+ */
+ private String cityName;
+ /**
+ * 区编码
+ */
+ private String areaCode;
+ /**
+ * 区名称
+ */
+ private String areaName;
+ /**
+ * 定位地址文字描述
+ */
+ private String locationText;
+ /**
+ * 经度
+ */
+ private BigDecimal longitude;
+ /**
+ * 纬度
+ */
+ private BigDecimal latitude;
+ /**
+ * 定位静态图URL
+ */
+ private String locationImage;
+ /**
+ * 拜访日期
+ */
+ private LocalDate visitDate;
+ /**
+ * 性质等级
+ */
+ private Integer customerStatus;
+ /**
+ * 拜访品种(多选,逗号分隔)
+ */
+ private String visitProductIds;
+ /**
+ * 拜访方式(字典)
+ *
+ * 枚举 {@link TODO visit_method 对应的类}
+ */
+ private Integer visitMethod;
+ /**
+ * 拜访类型(字典)
+ *
+ * 枚举 {@link TODO visit_type 对应的类}
+ */
+ private Integer visitType;
+ /**
+ * 服务内容
+ */
+ private String serviceContent;
+ /**
+ * 客户反馈
+ */
+ private String customerFeedback;
+ /**
+ * 服务记录图片URL列表(JSON数组)
+ */
+ private String serviceImages;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/product/ProductDO.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/product/ProductDO.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e1926de40ddab6e3cc0952d2bf4b468fff629c7
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/dataobject/product/ProductDO.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.visit.dal.dataobject.product;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 产品 DO
+ *
+ * @author 超级管理员
+ */
+@TableName("visit_product")
+@KeySequence("visit_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ProductDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 品种名称
+ */
+ private String productName;
+ /**
+ * 包装信息
+ */
+ private String packageName;
+ /**
+ * 规格
+ */
+ private String specification;
+ /**
+ * 状态(1:正常,0:禁用)
+ *
+ * 枚举
+ */
+ private Integer status;
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/customerinfo/CustomerInfoMapper.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/customerinfo/CustomerInfoMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..353339e9102f0cdbddcc5dffa100e5bd6d23c2f3
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/customerinfo/CustomerInfoMapper.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.visit.dal.mysql.customerinfo;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
+
+/**
+ * 客户信息 Mapper
+ *
+ * @author 超级管理员
+ */
+@Mapper
+public interface CustomerInfoMapper extends BaseMapperX {
+
+ default PageResult selectPage(CustomerInfoPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(CustomerInfoDO::getCustomerName, reqVO.getCustomerName())
+ .eqIfPresent(CustomerInfoDO::getContact, reqVO.getContact())
+ .likeIfPresent(CustomerInfoDO::getCompanyName, reqVO.getCompanyName())
+ .eqIfPresent(CustomerInfoDO::getCustomerType, reqVO.getCustomerType())
+ .eqIfPresent(CustomerInfoDO::getCityCode, reqVO.getCityCode())
+ .betweenIfPresent(CustomerInfoDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(CustomerInfoDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/info/InfoMapper.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/info/InfoMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c8df46c66dc925db4e242a0efa5ffc6a4430f4b
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/info/InfoMapper.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.visit.dal.mysql.info;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
+
+/**
+ * 客户拜访记录 Mapper
+ *
+ * @author 超级管理员
+ */
+@Mapper
+public interface InfoMapper extends BaseMapperX {
+
+ default PageResult selectPage(InfoPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(InfoDO::getCustomerName, reqVO.getCustomerName())
+ .eqIfPresent(InfoDO::getContact, reqVO.getContact())
+ .likeIfPresent(InfoDO::getCompanyName, reqVO.getCompanyName())
+ .betweenIfPresent(InfoDO::getVisitDate, reqVO.getVisitDate())
+ .eqIfPresent(InfoDO::getCustomerStatus, reqVO.getCustomerStatus())
+ .eqIfPresent(InfoDO::getVisitProductIds, reqVO.getVisitProductIds())
+ .eqIfPresent(InfoDO::getVisitMethod, reqVO.getVisitMethod())
+ .eqIfPresent(InfoDO::getVisitType, reqVO.getVisitType())
+ .betweenIfPresent(InfoDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(InfoDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/product/ProductMapper.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/product/ProductMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..403a09f5f1020899ebe8e812d8cc77266336458b
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/dal/mysql/product/ProductMapper.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.visit.dal.mysql.product;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
+
+/**
+ * 产品 Mapper
+ *
+ * @author 超级管理员
+ */
+@Mapper
+public interface ProductMapper extends BaseMapperX {
+
+ default PageResult selectPage(ProductPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(ProductDO::getProductName, reqVO.getProductName())
+ .eqIfPresent(ProductDO::getPackageName, reqVO.getPackageName())
+ .eqIfPresent(ProductDO::getSpecification, reqVO.getSpecification())
+ .eqIfPresent(ProductDO::getStatus, reqVO.getStatus())
+ .betweenIfPresent(ProductDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(ProductDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c4906075fa7c5df042ebf0a18aedc7e0552c27e
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.visit.enums;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/*
+ * System 错误码枚举类
+ *
+ * system 系统,使用 1-002-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+ // ========== 产品 ==========
+ ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1001000001, "产品不存在");
+
+ // ========== 客户信息 ==========
+ ErrorCode CUSTOMER_INFO_NOT_EXISTS = new ErrorCode(1002000001, "客户信息不存在");
+
+ // ========== 客户拜访记录 ==========
+ ErrorCode INFO_NOT_EXISTS = new ErrorCode(1003000001, "客户拜访记录不存在");
+}
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoService.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..4fdec38f31293b88f222c48acc23d957d225e491
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.visit.service.customerinfo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 客户信息 Service 接口
+ *
+ * @author 超级管理员
+ */
+public interface CustomerInfoService {
+
+ /**
+ * 创建客户信息
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createCustomerInfo(@Valid CustomerInfoSaveReqVO createReqVO);
+
+ /**
+ * 更新客户信息
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateCustomerInfo(@Valid CustomerInfoSaveReqVO updateReqVO);
+
+ /**
+ * 删除客户信息
+ *
+ * @param id 编号
+ */
+ void deleteCustomerInfo(Long id);
+
+ /**
+ * 获得客户信息
+ *
+ * @param id 编号
+ * @return 客户信息
+ */
+ CustomerInfoDO getCustomerInfo(Long id);
+
+ /**
+ * 获得客户信息分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 客户信息分页
+ */
+ PageResult getCustomerInfoPage(CustomerInfoPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..fccd2583b79cd4a9ca466e16c198082f0cb0cf85
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.visit.service.customerinfo;
+
+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 cn.iocoder.yudao.module.visit.controller.admin.customerinfo.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.customerinfo.CustomerInfoDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.visit.dal.mysql.customerinfo.CustomerInfoMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.visit.enums.ErrorCodeConstants.*;
+
+/**
+ * 客户信息 Service 实现类
+ *
+ * @author 超级管理员
+ */
+@Service
+@Validated
+public class CustomerInfoServiceImpl implements CustomerInfoService {
+
+ @Resource
+ private CustomerInfoMapper customerInfoMapper;
+
+ @Override
+ public Long createCustomerInfo(CustomerInfoSaveReqVO createReqVO) {
+ // 插入
+ CustomerInfoDO customerInfo = BeanUtils.toBean(createReqVO, CustomerInfoDO.class);
+ customerInfoMapper.insert(customerInfo);
+ // 返回
+ return customerInfo.getId();
+ }
+
+ @Override
+ public void updateCustomerInfo(CustomerInfoSaveReqVO updateReqVO) {
+ // 校验存在
+ validateCustomerInfoExists(updateReqVO.getId());
+ // 更新
+ CustomerInfoDO updateObj = BeanUtils.toBean(updateReqVO, CustomerInfoDO.class);
+ customerInfoMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteCustomerInfo(Long id) {
+ // 校验存在
+ validateCustomerInfoExists(id);
+ // 删除
+ customerInfoMapper.deleteById(id);
+ }
+
+ private void validateCustomerInfoExists(Long id) {
+ if (customerInfoMapper.selectById(id) == null) {
+ throw exception(CUSTOMER_INFO_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public CustomerInfoDO getCustomerInfo(Long id) {
+ return customerInfoMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getCustomerInfoPage(CustomerInfoPageReqVO pageReqVO) {
+ return customerInfoMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..523e5cd49ece65abb1af9e78a47a40383475580c
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.visit.service.info;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 客户拜访记录 Service 接口
+ *
+ * @author 超级管理员
+ */
+public interface InfoService {
+
+ /**
+ * 创建客户拜访记录
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createInfo(@Valid InfoSaveReqVO createReqVO);
+
+ /**
+ * 更新客户拜访记录
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateInfo(@Valid InfoSaveReqVO updateReqVO);
+
+ /**
+ * 删除客户拜访记录
+ *
+ * @param id 编号
+ */
+ void deleteInfo(Long id);
+
+ /**
+ * 获得客户拜访记录
+ *
+ * @param id 编号
+ * @return 客户拜访记录
+ */
+ InfoDO getInfo(Long id);
+
+ /**
+ * 获得客户拜访记录分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 客户拜访记录分页
+ */
+ PageResult getInfoPage(InfoPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdb2ee1497a3d8c3f4ecd1781aea8fac562a1ac2
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.visit.service.info;
+
+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 cn.iocoder.yudao.module.visit.controller.admin.info.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.info.InfoDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.visit.dal.mysql.info.InfoMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.visit.enums.ErrorCodeConstants.*;
+
+/**
+ * 客户拜访记录 Service 实现类
+ *
+ * @author 超级管理员
+ */
+@Service
+@Validated
+public class InfoServiceImpl implements InfoService {
+
+ @Resource
+ private InfoMapper infoMapper;
+
+ @Override
+ public Long createInfo(InfoSaveReqVO createReqVO) {
+ // 插入
+ InfoDO info = BeanUtils.toBean(createReqVO, InfoDO.class);
+ infoMapper.insert(info);
+ // 返回
+ return info.getId();
+ }
+
+ @Override
+ public void updateInfo(InfoSaveReqVO updateReqVO) {
+ // 校验存在
+ validateInfoExists(updateReqVO.getId());
+ // 更新
+ InfoDO updateObj = BeanUtils.toBean(updateReqVO, InfoDO.class);
+ infoMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteInfo(Long id) {
+ // 校验存在
+ validateInfoExists(id);
+ // 删除
+ infoMapper.deleteById(id);
+ }
+
+ private void validateInfoExists(Long id) {
+ if (infoMapper.selectById(id) == null) {
+ throw exception(INFO_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public InfoDO getInfo(Long id) {
+ return infoMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getInfoPage(InfoPageReqVO pageReqVO) {
+ return infoMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductService.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductService.java
new file mode 100644
index 0000000000000000000000000000000000000000..38657eae5c5f2c51c8fdd89e11ed06a9ffcab3fd
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.visit.service.product;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 产品 Service 接口
+ *
+ * @author 超级管理员
+ */
+public interface ProductService {
+
+ /**
+ * 创建产品
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createProduct(@Valid ProductSaveReqVO createReqVO);
+
+ /**
+ * 更新产品
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateProduct(@Valid ProductSaveReqVO updateReqVO);
+
+ /**
+ * 删除产品
+ *
+ * @param id 编号
+ */
+ void deleteProduct(Long id);
+
+ /**
+ * 获得产品
+ *
+ * @param id 编号
+ * @return 产品
+ */
+ ProductDO getProduct(Long id);
+
+ /**
+ * 获得产品分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 产品分页
+ */
+ PageResult getProductPage(ProductPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductServiceImpl.java b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..a34c84427908379c44ebb11502972dee6653ec96
--- /dev/null
+++ b/yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.visit.service.product;
+
+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 cn.iocoder.yudao.module.visit.controller.admin.product.vo.*;
+import cn.iocoder.yudao.module.visit.dal.dataobject.product.ProductDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.visit.dal.mysql.product.ProductMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.visit.enums.ErrorCodeConstants.*;
+
+/**
+ * 产品 Service 实现类
+ *
+ * @author 超级管理员
+ */
+@Service
+@Validated
+public class ProductServiceImpl implements ProductService {
+
+ @Resource
+ private ProductMapper productMapper;
+
+ @Override
+ public Long createProduct(ProductSaveReqVO createReqVO) {
+ // 插入
+ ProductDO product = BeanUtils.toBean(createReqVO, ProductDO.class);
+ productMapper.insert(product);
+ // 返回
+ return product.getId();
+ }
+
+ @Override
+ public void updateProduct(ProductSaveReqVO updateReqVO) {
+ // 校验存在
+ validateProductExists(updateReqVO.getId());
+ // 更新
+ ProductDO updateObj = BeanUtils.toBean(updateReqVO, ProductDO.class);
+ productMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteProduct(Long id) {
+ // 校验存在
+ validateProductExists(id);
+ // 删除
+ productMapper.deleteById(id);
+ }
+
+ private void validateProductExists(Long id) {
+ if (productMapper.selectById(id) == null) {
+ throw exception(PRODUCT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public ProductDO getProduct(Long id) {
+ return productMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getProductPage(ProductPageReqVO pageReqVO) {
+ return productMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/resources/mapper/customerinfo/CustomerInfoMapper.xml b/yudao-module-visit/src/main/resources/mapper/customerinfo/CustomerInfoMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f6722ff7b5b424976efdf090875eeebeb00532fa
--- /dev/null
+++ b/yudao-module-visit/src/main/resources/mapper/customerinfo/CustomerInfoMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/resources/mapper/info/InfoMapper.xml b/yudao-module-visit/src/main/resources/mapper/info/InfoMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f2c3dad2ba0dfab07b24e2d70255f586bee9e3a2
--- /dev/null
+++ b/yudao-module-visit/src/main/resources/mapper/info/InfoMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-visit/src/main/resources/mapper/product/ProductMapper.xml b/yudao-module-visit/src/main/resources/mapper/product/ProductMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b71003de02a764ca8d93dcc4afb949a9c78b17c8
--- /dev/null
+++ b/yudao-module-visit/src/main/resources/mapper/product/ProductMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index bdb30b49f0182756a5d5afe04911cc01545a2d2d..a8bddbf971df8a8cd7e384f8ce35b983cf75605c 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -271,7 +271,7 @@ yudao:
front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类
unit-test-enable: false # 是否生成单元测试
tenant: # 多租户相关配置项
- enable: true
+ enable: false
ignore-urls:
- /jmreport/* # 积木报表,无法携带租户编号
ignore-visit-urls:
diff --git a/yudao-ui/yudao-ui-admin-vue3/.env b/yudao-ui/yudao-ui-admin-vue3/.env
index a6a17702b53db7958342d03a720a07151c44b0f1..917478e83bf20740755bc5e2165ec9014484f0bf 100644
--- a/yudao-ui/yudao-ui-admin-vue3/.env
+++ b/yudao-ui/yudao-ui-admin-vue3/.env
@@ -8,7 +8,7 @@ VITE_PORT=80
VITE_OPEN=false
# 租户开关
-VITE_APP_TENANT_ENABLE=true
+VITE_APP_TENANT_ENABLE=false
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true
diff --git a/yudao-ui/yudao-ui-admin-vue3/public/logo.png b/yudao-ui/yudao-ui-admin-vue3/public/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..ebf3b3bd9b578ab3341b42fb019fd04a065e73cd
Binary files /dev/null and b/yudao-ui/yudao-ui-admin-vue3/public/logo.png differ
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/api/visit/customerinfo/index.ts b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/customerinfo/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..00d80ddccfa1759caee12ae91a59dc4cfbaa256c
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/customerinfo/index.ts
@@ -0,0 +1,53 @@
+import request from '@/config/axios'
+
+// 客户信息 VO
+export interface CustomerInfoVO {
+ id: number // 编号
+ customerName: string // 客户姓名
+ contact: string // 联系方式
+ companyName: string // 公司名称
+ customerType: number // 性质等级
+ provinceName: string // 省名称
+ cityName: string // 市名称
+ areaName: string // 区名称
+ regionFullName: string // 所在地区
+ locationText: string // 详细地址
+ longitude: number // 经度
+ latitude: number // 纬度
+ locationImage: string // 定位静态图 URL
+ productIds: string // 产品信息
+ department: string // 客户部门
+}
+
+// 客户信息 API
+export const CustomerInfoApi = {
+ // 查询客户信息分页
+ getCustomerInfoPage: async (params: any) => {
+ return await request.get({ url: `/visit/customer-info/page`, params })
+ },
+
+ // 查询客户信息详情
+ getCustomerInfo: async (id: number) => {
+ return await request.get({ url: `/visit/customer-info/get?id=` + id })
+ },
+
+ // 新增客户信息
+ createCustomerInfo: async (data: CustomerInfoVO) => {
+ return await request.post({ url: `/visit/customer-info/create`, data })
+ },
+
+ // 修改客户信息
+ updateCustomerInfo: async (data: CustomerInfoVO) => {
+ return await request.put({ url: `/visit/customer-info/update`, data })
+ },
+
+ // 删除客户信息
+ deleteCustomerInfo: async (id: number) => {
+ return await request.delete({ url: `/visit/customer-info/delete?id=` + id })
+ },
+
+ // 导出客户信息 Excel
+ exportCustomerInfo: async (params) => {
+ return await request.download({ url: `/visit/customer-info/export-excel`, params })
+ },
+}
\ No newline at end of file
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cb575a5292e0da845e5f1e247e6e1a0b5ee51536
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts
@@ -0,0 +1,57 @@
+import request from '@/config/axios'
+
+// 客户拜访记录 VO
+export interface InfoVO {
+ id: number // 主键ID
+ customerName: string // 客户姓名
+ contact: string // 联系方式(客户手机号)
+ companyName: string // 客户公司名称
+ provinceName: string // 省名称
+ cityName: string // 市名称
+ areaName: string // 区名称
+ locationText: string // 定位地址文字描述
+ longitude: number // 经度
+ latitude: number // 纬度
+ locationImage: string // 定位静态图URL
+ visitDate: Date // 拜访日期
+ customerStatus: number // 性质等级
+ visitProductIds: string // 拜访品种(多选,逗号分隔)
+ visitMethod: number // 拜访方式(字典)
+ visitType: number // 拜访类型(字典)
+ serviceContent: string // 服务内容
+ customerFeedback: string // 客户反馈
+ serviceImages: string // 服务记录图片URL列表(JSON数组)
+}
+
+// 客户拜访记录 API
+export const InfoApi = {
+ // 查询客户拜访记录分页
+ getInfoPage: async (params: any) => {
+ return await request.get({ url: `/visit/info/page`, params })
+ },
+
+ // 查询客户拜访记录详情
+ getInfo: async (id: number) => {
+ return await request.get({ url: `/visit/info/get?id=` + id })
+ },
+
+ // 新增客户拜访记录
+ createInfo: async (data: InfoVO) => {
+ return await request.post({ url: `/visit/info/create`, data })
+ },
+
+ // 修改客户拜访记录
+ updateInfo: async (data: InfoVO) => {
+ return await request.put({ url: `/visit/info/update`, data })
+ },
+
+ // 删除客户拜访记录
+ deleteInfo: async (id: number) => {
+ return await request.delete({ url: `/visit/info/delete?id=` + id })
+ },
+
+ // 导出客户拜访记录 Excel
+ exportInfo: async (params) => {
+ return await request.download({ url: `/visit/info/export-excel`, params })
+ },
+}
\ No newline at end of file
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/api/visit/product/index.ts b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/product/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..51dea7117a03f3ab7905e3c6f281ce5679625146
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/api/visit/product/index.ts
@@ -0,0 +1,43 @@
+import request from '@/config/axios'
+
+// 产品 VO
+export interface ProductVO {
+ id: number // 编号
+ productName: string // 品种名称
+ packageName: string // 包装信息
+ specification: string // 规格
+ status: number // 状态(1:正常,0:禁用)
+}
+
+// 产品 API
+export const ProductApi = {
+ // 查询产品分页
+ getProductPage: async (params: any) => {
+ return await request.get({ url: `/visit/product/page`, params })
+ },
+
+ // 查询产品详情
+ getProduct: async (id: number) => {
+ return await request.get({ url: `/visit/product/get?id=` + id })
+ },
+
+ // 新增产品
+ createProduct: async (data: ProductVO) => {
+ return await request.post({ url: `/visit/product/create`, data })
+ },
+
+ // 修改产品
+ updateProduct: async (data: ProductVO) => {
+ return await request.put({ url: `/visit/product/update`, data })
+ },
+
+ // 删除产品
+ deleteProduct: async (id: number) => {
+ return await request.delete({ url: `/visit/product/delete?id=` + id })
+ },
+
+ // 导出产品 Excel
+ exportProduct: async (params) => {
+ return await request.download({ url: `/visit/product/export-excel`, params })
+ },
+}
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/utils/dict.ts b/yudao-ui/yudao-ui-admin-vue3/src/utils/dict.ts
index 947f9f31a23497b1da6ee108dcd980084ab9c8e5..aa4f8a33d3fad737320d72a6eb54f02dd1a72e95 100644
--- a/yudao-ui/yudao-ui-admin-vue3/src/utils/dict.ts
+++ b/yudao-ui/yudao-ui-admin-vue3/src/utils/dict.ts
@@ -113,6 +113,12 @@ export enum DICT_TYPE {
TERMINAL = 'terminal', // 终端
DATE_INTERVAL = 'date_interval', // 数据间隔
+ // ========== VISIT 模块 ==========
+ CUSTOMER_TYPE = 'customer_type',
+ CUSTOMER_DEPT = 'customer_dept',
+ VISIT_METHOD = 'visit_method',
+ VISIT_TYPE = 'visit_type',
+
// ========== SYSTEM 模块 ==========
SYSTEM_USER_SEX = 'system_user_sex',
SYSTEM_MENU_TYPE = 'system_menu_type',
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/CustomerInfoForm.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/CustomerInfoForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..78d4987c91a40b6c4a4d81bdad64aa23dc70624e
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/CustomerInfoForm.vue
@@ -0,0 +1,183 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/index.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..57b2ceb2e163bf04d3f5c09475c17cfade58ce46
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/index.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/InfoForm.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/InfoForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..78a0f9e931750c27425ffb404882bc91528e7232
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/InfoForm.vue
@@ -0,0 +1,210 @@
+
+
+
+
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/index.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7ce97c8cc78f03e29eee3e8ff01e4e167d20d61e
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/index.vue
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/ProductForm.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/ProductForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7f331c3f7d96e359bcc5830f78c60a54581f447d
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/ProductForm.vue
@@ -0,0 +1,117 @@
+
+
+
+
diff --git a/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b25762df73ea3df06bdd6a9f9521ce5a1086f7ab
--- /dev/null
+++ b/yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+