diff --git a/backend/pom.xml b/backend/pom.xml
index db54d52..56fbc9b 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -26,6 +26,7 @@
3.0.0
3.12.0
5.8.23
+ 5.2.5
@@ -50,9 +51,9 @@
- mysql
- mysql-connector-java
- 8.0.33
+ com.mysql
+ mysql-connector-j
+ runtime
@@ -101,6 +102,24 @@
${hutool.version}
+
+
+ org.apache.poi
+ poi
+ ${poi.version}
+
+
+ org.apache.poi
+ poi-ooxml
+ ${poi.version}
+
+
+
+
+ org.springframework.security
+ spring-security-crypto
+
+
org.projectlombok
@@ -160,7 +179,6 @@
3.3.0
checkstyle.xml
- UTF-8
true
false
diff --git a/backend/src/main/com/bycrm/ByCrmApplication.java b/backend/src/main/java/com/bycrm/ByCrmApplication.java
similarity index 100%
rename from backend/src/main/com/bycrm/ByCrmApplication.java
rename to backend/src/main/java/com/bycrm/ByCrmApplication.java
diff --git a/backend/src/main/com/bycrm/annotations/AuthRequired.java b/backend/src/main/java/com/bycrm/annotations/AuthRequired.java
similarity index 100%
rename from backend/src/main/com/bycrm/annotations/AuthRequired.java
rename to backend/src/main/java/com/bycrm/annotations/AuthRequired.java
diff --git a/backend/src/main/com/bycrm/common/Constants.java b/backend/src/main/java/com/bycrm/common/Constants.java
similarity index 56%
rename from backend/src/main/com/bycrm/common/Constants.java
rename to backend/src/main/java/com/bycrm/common/Constants.java
index 468b0b8..06741b2 100644
--- a/backend/src/main/com/bycrm/common/Constants.java
+++ b/backend/src/main/java/com/bycrm/common/Constants.java
@@ -23,70 +23,70 @@ public class Constants {
/**
* 客户状态 - 可报备
*/
- public static final Integer CUSTOMER_STATUS_AVAILABLE = 0;
+ public static final int CUSTOMER_STATUS_AVAILABLE = 0;
/**
* 客户状态 - 保护中
*/
- public static final Integer CUSTOMER_STATUS_PROTECTED = 1;
+ public static final int CUSTOMER_STATUS_PROTECTED = 1;
/**
* 客户状态 - 已失效
*/
- public static final Integer CUSTOMER_STATUS_EXPIRED = 2;
+ public static final int CUSTOMER_STATUS_EXPIRED = 2;
/**
* 报备状态 - 待审核
*/
- public static final Integer REPORT_STATUS_PENDING = 0;
+ public static final int REPORT_STATUS_PENDING = 0;
/**
* 报备状态 - 已通过
*/
- public static final Integer REPORT_STATUS_APPROVED = 1;
+ public static final int REPORT_STATUS_APPROVED = 1;
/**
* 报备状态 - 已驳回
*/
- public static final Integer REPORT_STATUS_REJECTED = 2;
+ public static final int REPORT_STATUS_REJECTED = 2;
/**
* 报备状态 - 已失效
*/
- public static final Integer REPORT_STATUS_EXPIRED = 3;
+ public static final int REPORT_STATUS_EXPIRED = 3;
/**
* 用户角色 - 管理员
*/
- public static final Integer USER_ROLE_ADMIN = 0;
+ public static final int USER_ROLE_ADMIN = 0;
/**
* 用户角色 - 经销商用户
*/
- public static final Integer USER_ROLE_DEALER = 1;
+ public static final int USER_ROLE_DEALER = 1;
/**
* 用户状态 - 禁用
*/
- public static final Integer USER_STATUS_DISABLED = 0;
+ public static final int USER_STATUS_DISABLED = 0;
/**
* 用户状态 - 启用
*/
- public static final Integer USER_STATUS_ENABLED = 1;
+ public static final int USER_STATUS_ENABLED = 1;
/**
* 经销商状态 - 禁用
*/
- public static final Integer DEALER_STATUS_DISABLED = 0;
+ public static final int DEALER_STATUS_DISABLED = 0;
/**
* 经销商状态 - 启用
*/
- public static final Integer DEALER_STATUS_ENABLED = 1;
+ public static final int DEALER_STATUS_ENABLED = 1;
/**
* 默认保护期天数
*/
- public static final Integer DEFAULT_PROTECT_DAYS = 90;
+ public static final int DEFAULT_PROTECT_DAYS = 90;
}
diff --git a/backend/src/main/com/bycrm/common/PageResult.java b/backend/src/main/java/com/bycrm/common/PageResult.java
similarity index 100%
rename from backend/src/main/com/bycrm/common/PageResult.java
rename to backend/src/main/java/com/bycrm/common/PageResult.java
diff --git a/backend/src/main/com/bycrm/common/Result.java b/backend/src/main/java/com/bycrm/common/Result.java
similarity index 100%
rename from backend/src/main/com/bycrm/common/Result.java
rename to backend/src/main/java/com/bycrm/common/Result.java
diff --git a/backend/src/main/com/bycrm/config/AuthInterceptor.java b/backend/src/main/java/com/bycrm/config/AuthInterceptor.java
similarity index 100%
rename from backend/src/main/com/bycrm/config/AuthInterceptor.java
rename to backend/src/main/java/com/bycrm/config/AuthInterceptor.java
diff --git a/backend/src/main/com/bycrm/config/CorsConfig.java b/backend/src/main/java/com/bycrm/config/CorsConfig.java
similarity index 100%
rename from backend/src/main/com/bycrm/config/CorsConfig.java
rename to backend/src/main/java/com/bycrm/config/CorsConfig.java
diff --git a/backend/src/main/com/bycrm/config/MyBatisConfig.java b/backend/src/main/java/com/bycrm/config/MyBatisConfig.java
similarity index 100%
rename from backend/src/main/com/bycrm/config/MyBatisConfig.java
rename to backend/src/main/java/com/bycrm/config/MyBatisConfig.java
diff --git a/backend/src/main/com/bycrm/config/SwaggerConfig.java b/backend/src/main/java/com/bycrm/config/SwaggerConfig.java
similarity index 100%
rename from backend/src/main/com/bycrm/config/SwaggerConfig.java
rename to backend/src/main/java/com/bycrm/config/SwaggerConfig.java
diff --git a/backend/src/main/com/bycrm/config/WebMvcConfig.java b/backend/src/main/java/com/bycrm/config/WebMvcConfig.java
similarity index 100%
rename from backend/src/main/com/bycrm/config/WebMvcConfig.java
rename to backend/src/main/java/com/bycrm/config/WebMvcConfig.java
diff --git a/backend/src/main/com/bycrm/controller/AuthController.java b/backend/src/main/java/com/bycrm/controller/AuthController.java
similarity index 71%
rename from backend/src/main/com/bycrm/controller/AuthController.java
rename to backend/src/main/java/com/bycrm/controller/AuthController.java
index a631921..7164485 100644
--- a/backend/src/main/com/bycrm/controller/AuthController.java
+++ b/backend/src/main/java/com/bycrm/controller/AuthController.java
@@ -1,7 +1,9 @@
package com.bycrm.controller;
import com.bycrm.common.Result;
+import com.bycrm.dto.ChangePasswordDTO;
import com.bycrm.dto.LoginDTO;
+import com.bycrm.dto.ResetPasswordDTO;
import com.bycrm.service.UserService;
import com.bycrm.vo.UserInfoVO;
import io.swagger.annotations.Api;
@@ -54,6 +56,30 @@ public class AuthController {
return Result.success();
}
+ /**
+ * 修改密码
+ */
+ @ApiOperation("修改密码")
+ @PostMapping("/change-password")
+ public Result changePassword(
+ @RequestBody ChangePasswordDTO dto,
+ HttpServletRequest request) {
+ String token = getTokenFromRequest(request);
+ Long userId = userService.getCurrentUser(token).getUserId();
+ userService.changePassword(userId, dto);
+ return Result.success();
+ }
+
+ /**
+ * 重置密码(管理员功能)
+ */
+ @ApiOperation("重置密码")
+ @PostMapping("/reset-password")
+ public Result resetPassword(@RequestBody ResetPasswordDTO dto) {
+ userService.resetPassword(dto);
+ return Result.success();
+ }
+
/**
* 从请求中获取 Token
*/
diff --git a/backend/src/main/com/bycrm/controller/CustomerController.java b/backend/src/main/java/com/bycrm/controller/CustomerController.java
similarity index 100%
rename from backend/src/main/com/bycrm/controller/CustomerController.java
rename to backend/src/main/java/com/bycrm/controller/CustomerController.java
diff --git a/backend/src/main/java/com/bycrm/controller/DashboardController.java b/backend/src/main/java/com/bycrm/controller/DashboardController.java
new file mode 100644
index 0000000..72f37b1
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/controller/DashboardController.java
@@ -0,0 +1,78 @@
+package com.bycrm.controller;
+
+import com.bycrm.common.Result;
+import com.bycrm.dto.DashboardStatisticsDTO;
+import com.bycrm.entity.User;
+import com.bycrm.mapper.CustomerMapper;
+import com.bycrm.mapper.DealerMapper;
+import com.bycrm.mapper.UserMapper;
+import com.bycrm.service.ReportService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 首页统计控制器
+ */
+@Api(tags = "首页统计")
+@RestController
+@RequestMapping("/dashboard")
+public class DashboardController {
+
+ private final CustomerMapper customerMapper;
+ private final DealerMapper dealerMapper;
+ private final ReportService reportService;
+ private final UserMapper userMapper;
+
+ public DashboardController(CustomerMapper customerMapper,
+ DealerMapper dealerMapper,
+ ReportService reportService,
+ UserMapper userMapper) {
+ this.customerMapper = customerMapper;
+ this.dealerMapper = dealerMapper;
+ this.reportService = reportService;
+ this.userMapper = userMapper;
+ }
+
+ /**
+ * 获取首页统计数据
+ */
+ @ApiOperation("获取首页统计数据")
+ @GetMapping("/statistics")
+ public Result getStatistics(HttpServletRequest request) {
+ Long currentUserId = (Long) request.getAttribute("currentUserId");
+ User currentUser = userMapper.selectById(currentUserId);
+
+ DashboardStatisticsDTO stats = new DashboardStatisticsDTO();
+
+ // 客户总数(所有人看到的客户总数一样)
+ Long customerCount = customerMapper.countPage(null, null, null);
+ stats.setCustomerCount(customerCount);
+
+ // 报备总数和待审核数量(根据角色过滤)
+ Long dealerId = null;
+ if (currentUser.getRole() != 0) {
+ // 经销商用户,只统计自己的
+ dealerId = currentUser.getDealerId();
+ }
+ // 管理员用户,dealerId为null,统计所有
+
+ Long reportCount = reportService.countByDealerId(dealerId);
+ stats.setReportCount(reportCount);
+
+ Long pendingCount = reportService.countPendingByDealerId(dealerId);
+ stats.setPendingCount(pendingCount);
+
+ // 经销商总数(仅管理员可见)
+ if (currentUser.getRole() == 0) {
+ Long dealerCount = dealerMapper.count();
+ stats.setDealerCount(dealerCount);
+ } else {
+ stats.setDealerCount(0L);
+ }
+
+ return Result.success(stats);
+ }
+}
diff --git a/backend/src/main/com/bycrm/controller/DealerController.java b/backend/src/main/java/com/bycrm/controller/DealerController.java
similarity index 97%
rename from backend/src/main/com/bycrm/controller/DealerController.java
rename to backend/src/main/java/com/bycrm/controller/DealerController.java
index 38a6618..02614b3 100644
--- a/backend/src/main/com/bycrm/controller/DealerController.java
+++ b/backend/src/main/java/com/bycrm/controller/DealerController.java
@@ -32,7 +32,7 @@ public class DealerController {
@GetMapping("/list")
public Result> getDealerList(
@ApiParam("经销商名称") @RequestParam(required = false) String name,
- @ApiParam("经销商编码") @RequestParam(required = false) String code,
+ @ApiParam("经销商账号") @RequestParam(required = false) String code,
@ApiParam("状态") @RequestParam(required = false) Integer status) {
List dealers = dealerService.getDealerList(name, code, status);
return Result.success(dealers);
diff --git a/backend/src/main/com/bycrm/controller/ReportController.java b/backend/src/main/java/com/bycrm/controller/ReportController.java
similarity index 100%
rename from backend/src/main/com/bycrm/controller/ReportController.java
rename to backend/src/main/java/com/bycrm/controller/ReportController.java
diff --git a/backend/src/main/java/com/bycrm/controller/SchoolController.java b/backend/src/main/java/com/bycrm/controller/SchoolController.java
new file mode 100644
index 0000000..c15d902
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/controller/SchoolController.java
@@ -0,0 +1,128 @@
+package com.bycrm.controller;
+
+import com.bycrm.common.Result;
+import com.bycrm.dto.SchoolDTO;
+import com.bycrm.service.SchoolService;
+import com.bycrm.util.SchoolImporter;
+import com.bycrm.vo.SchoolVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 学校控制器
+ */
+@Api(tags = "学校管理")
+@RestController
+@RequestMapping("/school")
+public class SchoolController {
+
+ private final SchoolService schoolService;
+
+ @Value("${project.root:./}")
+ private String projectRoot;
+
+ public SchoolController(SchoolService schoolService) {
+ this.schoolService = schoolService;
+ }
+
+ /**
+ * 根据名称搜索学校(用于自动完成)
+ */
+ @ApiOperation("根据名称搜索学校")
+ @GetMapping("/search")
+ public Result> searchByName(
+ @ApiParam("关键词") @RequestParam String keyword) {
+ List schools = schoolService.searchByName(keyword);
+ return Result.success(schools);
+ }
+
+ /**
+ * 查询所有学校
+ */
+ @ApiOperation("查询所有学校")
+ @GetMapping("/list")
+ public Result> findAll() {
+ List schools = schoolService.findAll();
+ return Result.success(schools);
+ }
+
+ /**
+ * 创建学校
+ */
+ @ApiOperation("创建学校")
+ @PostMapping
+ public Result create(@RequestBody SchoolDTO dto) {
+ Long id = schoolService.create(dto);
+ return Result.success(id);
+ }
+
+ /**
+ * 批量导入学校
+ */
+ @ApiOperation("批量导入学校")
+ @PostMapping("/batch")
+ public Result batchImport(@RequestBody List list) {
+ int count = schoolService.batchImport(list);
+ return Result.success(count);
+ }
+
+ /**
+ * 从Excel文件导入学校数据
+ */
+ @ApiOperation("从Excel文件导入学校数据")
+ @PostMapping("/import")
+ public Result importFromExcel(@RequestParam("file") MultipartFile file) {
+ try {
+ // 保存临时文件
+ File tempFile = File.createTempFile("school_import_", ".xls");
+ file.transferTo(tempFile);
+
+ // 解析Excel
+ List schools = SchoolImporter.importFromXls(tempFile.getAbsolutePath());
+
+ // 导入数据库
+ int count = schoolService.batchImport(schools);
+
+ // 删除临时文件
+ tempFile.delete();
+
+ return Result.success(count);
+ } catch (IOException e) {
+ return Result.error("导入失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 从项目docs目录导入学校数据
+ */
+ @ApiOperation("从项目docs目录导入学校数据")
+ @PostMapping("/import-from-docs")
+ public Result importFromDocs() {
+ try {
+ String filePath = projectRoot + "/docs/school.xls";
+ File file = new File(filePath);
+
+ if (!file.exists()) {
+ return Result.error("文件不存在: " + filePath);
+ }
+
+ // 解析Excel
+ List schools = SchoolImporter.importFromXls(filePath);
+
+ // 导入数据库
+ int count = schoolService.batchImport(schools);
+
+ return Result.success(count);
+ } catch (Exception e) {
+ return Result.error("导入失败: " + e.getMessage());
+ }
+ }
+}
diff --git a/backend/src/main/java/com/bycrm/controller/SystemConfigController.java b/backend/src/main/java/com/bycrm/controller/SystemConfigController.java
new file mode 100644
index 0000000..b768d69
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/controller/SystemConfigController.java
@@ -0,0 +1,107 @@
+package com.bycrm.controller;
+
+import com.bycrm.common.Result;
+import com.bycrm.entity.SystemConfig;
+import com.bycrm.entity.User;
+import com.bycrm.mapper.UserMapper;
+import com.bycrm.service.SystemConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 系统配置控制器
+ */
+@Api(tags = "系统配置管理")
+@RestController
+@RequestMapping("/system/config")
+public class SystemConfigController {
+
+ private final SystemConfigService systemConfigService;
+ private final UserMapper userMapper;
+
+ public SystemConfigController(SystemConfigService systemConfigService,
+ UserMapper userMapper) {
+ this.systemConfigService = systemConfigService;
+ this.userMapper = userMapper;
+ }
+
+ /**
+ * 获取所有配置(仅管理员)
+ */
+ @ApiOperation("获取所有系统配置")
+ @GetMapping
+ public Result> getAllConfigs(HttpServletRequest request) {
+ // 权限校验:只有管理员可以查看
+ Long currentUserId = (Long) request.getAttribute("currentUserId");
+ User currentUser = userMapper.selectById(currentUserId);
+ if (currentUser == null || currentUser.getRole() != 0) {
+ return Result.error("无权限访问");
+ }
+
+ List configs = systemConfigService.getAllConfigs();
+ return Result.success(configs);
+ }
+
+ /**
+ * 获取配置值(所有用户可访问)
+ */
+ @ApiOperation("获取指定配置值")
+ @GetMapping("/value/{configKey}")
+ public Result getConfigValue(@PathVariable String configKey) {
+ // 只允许访问特定配置,防止敏感信息泄露
+ if (!configKey.startsWith("report.")) {
+ return Result.error("无权访问该配置");
+ }
+
+ String value = systemConfigService.getConfigValue(configKey);
+ return Result.success(value);
+ }
+
+ /**
+ * 更新配置(仅管理员)
+ */
+ @ApiOperation("更新系统配置")
+ @PutMapping
+ public Result updateConfig(@RequestBody Map params,
+ HttpServletRequest request) {
+ // 权限校验
+ Long currentUserId = (Long) request.getAttribute("currentUserId");
+ User currentUser = userMapper.selectById(currentUserId);
+ if (currentUser == null || currentUser.getRole() != 0) {
+ return Result.error("只有管理员才能修改配置");
+ }
+
+ String configKey = params.get("configKey");
+ String configValue = params.get("configValue");
+
+ if (configKey == null || configValue == null) {
+ return Result.error("参数不完整");
+ }
+
+ systemConfigService.updateConfig(configKey, configValue);
+ return Result.success();
+ }
+
+ /**
+ * 批量更新配置(仅管理员)
+ */
+ @ApiOperation("批量更新系统配置")
+ @PutMapping("/batch")
+ public Result batchUpdateConfigs(@RequestBody Map configs,
+ HttpServletRequest request) {
+ // 权限校验
+ Long currentUserId = (Long) request.getAttribute("currentUserId");
+ User currentUser = userMapper.selectById(currentUserId);
+ if (currentUser == null || currentUser.getRole() != 0) {
+ return Result.error("只有管理员才能修改配置");
+ }
+
+ systemConfigService.batchUpdateConfigs(configs);
+ return Result.success();
+ }
+}
diff --git a/backend/src/main/java/com/bycrm/dto/ChangePasswordDTO.java b/backend/src/main/java/com/bycrm/dto/ChangePasswordDTO.java
new file mode 100644
index 0000000..beaedc1
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/dto/ChangePasswordDTO.java
@@ -0,0 +1,29 @@
+package com.bycrm.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+
+/**
+ * 修改密码DTO
+ */
+@Data
+public class ChangePasswordDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 原密码
+ */
+ @NotBlank(message = "原密码不能为空")
+ private String oldPassword;
+
+ /**
+ * 新密码
+ */
+ @NotBlank(message = "新密码不能为空")
+ @Size(min = 6, max = 20, message = "新密码长度必须在6-20位之间")
+ private String newPassword;
+}
diff --git a/backend/src/main/com/bycrm/dto/CustomerDTO.java b/backend/src/main/java/com/bycrm/dto/CustomerDTO.java
similarity index 100%
rename from backend/src/main/com/bycrm/dto/CustomerDTO.java
rename to backend/src/main/java/com/bycrm/dto/CustomerDTO.java
diff --git a/backend/src/main/java/com/bycrm/dto/DashboardStatisticsDTO.java b/backend/src/main/java/com/bycrm/dto/DashboardStatisticsDTO.java
new file mode 100644
index 0000000..7aaa45b
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/dto/DashboardStatisticsDTO.java
@@ -0,0 +1,34 @@
+package com.bycrm.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 首页统计DTO
+ */
+@Data
+public class DashboardStatisticsDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 客户总数
+ */
+ private Long customerCount;
+
+ /**
+ * 报备总数
+ */
+ private Long reportCount;
+
+ /**
+ * 待审核数量
+ */
+ private Long pendingCount;
+
+ /**
+ * 经销商总数(仅管理员可见)
+ */
+ private Long dealerCount;
+}
diff --git a/backend/src/main/com/bycrm/dto/DealerDTO.java b/backend/src/main/java/com/bycrm/dto/DealerDTO.java
similarity index 85%
rename from backend/src/main/com/bycrm/dto/DealerDTO.java
rename to backend/src/main/java/com/bycrm/dto/DealerDTO.java
index 66fb199..fe174ef 100644
--- a/backend/src/main/com/bycrm/dto/DealerDTO.java
+++ b/backend/src/main/java/com/bycrm/dto/DealerDTO.java
@@ -23,7 +23,7 @@ public class DealerDTO implements Serializable {
/**
* 经销商编码
*/
- @NotBlank(message = "经销商编码不能为空")
+ @NotBlank(message = "经销商账号不能为空")
private String code;
/**
@@ -48,4 +48,9 @@ public class DealerDTO implements Serializable {
*/
@NotNull(message = "状态不能为空")
private Integer status;
+
+ /**
+ * 初始密码(新增用户时可设置)
+ */
+ private String password;
}
diff --git a/backend/src/main/com/bycrm/dto/LoginDTO.java b/backend/src/main/java/com/bycrm/dto/LoginDTO.java
similarity index 100%
rename from backend/src/main/com/bycrm/dto/LoginDTO.java
rename to backend/src/main/java/com/bycrm/dto/LoginDTO.java
diff --git a/backend/src/main/com/bycrm/dto/PageQuery.java b/backend/src/main/java/com/bycrm/dto/PageQuery.java
similarity index 83%
rename from backend/src/main/com/bycrm/dto/PageQuery.java
rename to backend/src/main/java/com/bycrm/dto/PageQuery.java
index fc879a1..066dfef 100644
--- a/backend/src/main/com/bycrm/dto/PageQuery.java
+++ b/backend/src/main/java/com/bycrm/dto/PageQuery.java
@@ -22,6 +22,11 @@ public class PageQuery implements Serializable {
*/
private Long size = 10L;
+ /**
+ * 偏移量(自动计算,不需要手动设置)
+ */
+ private Long offset;
+
/**
* 排序字段
*/
diff --git a/backend/src/main/com/bycrm/dto/ReportAuditDTO.java b/backend/src/main/java/com/bycrm/dto/ReportAuditDTO.java
similarity index 100%
rename from backend/src/main/com/bycrm/dto/ReportAuditDTO.java
rename to backend/src/main/java/com/bycrm/dto/ReportAuditDTO.java
diff --git a/backend/src/main/com/bycrm/dto/ReportDTO.java b/backend/src/main/java/com/bycrm/dto/ReportDTO.java
similarity index 100%
rename from backend/src/main/com/bycrm/dto/ReportDTO.java
rename to backend/src/main/java/com/bycrm/dto/ReportDTO.java
diff --git a/backend/src/main/java/com/bycrm/dto/ResetPasswordDTO.java b/backend/src/main/java/com/bycrm/dto/ResetPasswordDTO.java
new file mode 100644
index 0000000..1383275
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/dto/ResetPasswordDTO.java
@@ -0,0 +1,28 @@
+package com.bycrm.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+
+/**
+ * 重置密码DTO
+ */
+@Data
+public class ResetPasswordDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 新密码
+ */
+ @NotBlank(message = "新密码不能为空")
+ @Size(min = 6, max = 20, message = "新密码长度必须在6-20位之间")
+ private String newPassword;
+}
diff --git a/backend/src/main/java/com/bycrm/dto/SchoolDTO.java b/backend/src/main/java/com/bycrm/dto/SchoolDTO.java
new file mode 100644
index 0000000..f142abe
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/dto/SchoolDTO.java
@@ -0,0 +1,37 @@
+package com.bycrm.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 学校DTO
+ */
+@Data
+public class SchoolDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 学校ID
+ */
+ private Long id;
+
+ /**
+ * 学校标识码
+ */
+ @NotBlank(message = "学校标识码不能为空")
+ private String schoolCode;
+
+ /**
+ * 学校名称
+ */
+ @NotBlank(message = "学校名称不能为空")
+ private String schoolName;
+
+ /**
+ * 所在地
+ */
+ private String location;
+}
diff --git a/backend/src/main/com/bycrm/entity/Customer.java b/backend/src/main/java/com/bycrm/entity/Customer.java
similarity index 100%
rename from backend/src/main/com/bycrm/entity/Customer.java
rename to backend/src/main/java/com/bycrm/entity/Customer.java
diff --git a/backend/src/main/com/bycrm/entity/Dealer.java b/backend/src/main/java/com/bycrm/entity/Dealer.java
similarity index 90%
rename from backend/src/main/com/bycrm/entity/Dealer.java
rename to backend/src/main/java/com/bycrm/entity/Dealer.java
index ad7c2c2..e90e226 100644
--- a/backend/src/main/com/bycrm/entity/Dealer.java
+++ b/backend/src/main/java/com/bycrm/entity/Dealer.java
@@ -49,6 +49,11 @@ public class Dealer implements Serializable {
*/
private Integer status;
+ /**
+ * 关联的用户ID(用于重置密码等操作)
+ */
+ private Long userId;
+
/**
* 创建时间
*/
diff --git a/backend/src/main/com/bycrm/entity/Dict.java b/backend/src/main/java/com/bycrm/entity/Dict.java
similarity index 100%
rename from backend/src/main/com/bycrm/entity/Dict.java
rename to backend/src/main/java/com/bycrm/entity/Dict.java
diff --git a/backend/src/main/com/bycrm/entity/DictItem.java b/backend/src/main/java/com/bycrm/entity/DictItem.java
similarity index 100%
rename from backend/src/main/com/bycrm/entity/DictItem.java
rename to backend/src/main/java/com/bycrm/entity/DictItem.java
diff --git a/backend/src/main/com/bycrm/entity/OperationLog.java b/backend/src/main/java/com/bycrm/entity/OperationLog.java
similarity index 100%
rename from backend/src/main/com/bycrm/entity/OperationLog.java
rename to backend/src/main/java/com/bycrm/entity/OperationLog.java
diff --git a/backend/src/main/com/bycrm/entity/Report.java b/backend/src/main/java/com/bycrm/entity/Report.java
similarity index 83%
rename from backend/src/main/com/bycrm/entity/Report.java
rename to backend/src/main/java/com/bycrm/entity/Report.java
index 8c829a8..89b4f51 100644
--- a/backend/src/main/com/bycrm/entity/Report.java
+++ b/backend/src/main/java/com/bycrm/entity/Report.java
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
+import java.time.LocalDate;
import java.time.LocalDateTime;
/**
@@ -45,16 +46,16 @@ public class Report implements Serializable {
private String rejectReason;
/**
- * 保护期开始时间
+ * 保护期开始日期
*/
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- private LocalDateTime protectStartDate;
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ private LocalDate protectStartDate;
/**
- * 保护期结束时间
+ * 保护期结束日期
*/
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- private LocalDateTime protectEndDate;
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ private LocalDate protectEndDate;
/**
* 创建时间
diff --git a/backend/src/main/java/com/bycrm/entity/School.java b/backend/src/main/java/com/bycrm/entity/School.java
new file mode 100644
index 0000000..64bf7a0
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/entity/School.java
@@ -0,0 +1,48 @@
+package com.bycrm.entity;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 学校实体
+ */
+@Data
+public class School implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 学校ID
+ */
+ private Long id;
+
+ /**
+ * 学校标识码
+ */
+ private String schoolCode;
+
+ /**
+ * 学校名称
+ */
+ private String schoolName;
+
+ /**
+ * 所在地
+ */
+ private String location;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createdAt;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime updatedAt;
+}
diff --git a/backend/src/main/java/com/bycrm/entity/SystemConfig.java b/backend/src/main/java/com/bycrm/entity/SystemConfig.java
new file mode 100644
index 0000000..b4055f7
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/entity/SystemConfig.java
@@ -0,0 +1,63 @@
+package com.bycrm.entity;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 系统配置实体
+ */
+@Data
+public class SystemConfig implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 配置ID
+ */
+ private Long id;
+
+ /**
+ * 配置键
+ */
+ private String configKey;
+
+ /**
+ * 配置值
+ */
+ private String configValue;
+
+ /**
+ * 配置名称
+ */
+ private String configLabel;
+
+ /**
+ * 数据类型:string/integer/boolean
+ */
+ private String configType;
+
+ /**
+ * 配置描述
+ */
+ private String description;
+
+ /**
+ * 是否可编辑:0-否 1-是
+ */
+ private Integer isEditable;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createdAt;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime updatedAt;
+}
diff --git a/backend/src/main/com/bycrm/entity/User.java b/backend/src/main/java/com/bycrm/entity/User.java
similarity index 100%
rename from backend/src/main/com/bycrm/entity/User.java
rename to backend/src/main/java/com/bycrm/entity/User.java
diff --git a/backend/src/main/com/bycrm/exception/BusinessException.java b/backend/src/main/java/com/bycrm/exception/BusinessException.java
similarity index 100%
rename from backend/src/main/com/bycrm/exception/BusinessException.java
rename to backend/src/main/java/com/bycrm/exception/BusinessException.java
diff --git a/backend/src/main/com/bycrm/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/bycrm/exception/GlobalExceptionHandler.java
similarity index 100%
rename from backend/src/main/com/bycrm/exception/GlobalExceptionHandler.java
rename to backend/src/main/java/com/bycrm/exception/GlobalExceptionHandler.java
diff --git a/backend/src/main/com/bycrm/mapper/CustomerMapper.java b/backend/src/main/java/com/bycrm/mapper/CustomerMapper.java
similarity index 78%
rename from backend/src/main/com/bycrm/mapper/CustomerMapper.java
rename to backend/src/main/java/com/bycrm/mapper/CustomerMapper.java
index 33f99da..a7eb1b2 100644
--- a/backend/src/main/com/bycrm/mapper/CustomerMapper.java
+++ b/backend/src/main/java/com/bycrm/mapper/CustomerMapper.java
@@ -2,6 +2,7 @@ package com.bycrm.mapper;
import com.bycrm.entity.Customer;
import com.bycrm.dto.PageQuery;
+import com.bycrm.vo.CustomerVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -29,6 +30,12 @@ public interface CustomerMapper {
List selectPage(@Param("query") PageQuery query, @Param("name") String name,
@Param("industry") String industry, @Param("status") Integer status);
+ /**
+ * 分页查询客户(带经销商信息)
+ */
+ List selectPageWithDealer(@Param("query") PageQuery query, @Param("name") String name,
+ @Param("industry") String industry, @Param("status") Integer status);
+
/**
* 查询客户总数
*/
diff --git a/backend/src/main/com/bycrm/mapper/DealerMapper.java b/backend/src/main/java/com/bycrm/mapper/DealerMapper.java
similarity index 92%
rename from backend/src/main/com/bycrm/mapper/DealerMapper.java
rename to backend/src/main/java/com/bycrm/mapper/DealerMapper.java
index 5aa1b40..93da57b 100644
--- a/backend/src/main/com/bycrm/mapper/DealerMapper.java
+++ b/backend/src/main/java/com/bycrm/mapper/DealerMapper.java
@@ -41,4 +41,9 @@ public interface DealerMapper {
* 删除经销商
*/
int deleteById(@Param("id") Long id);
+
+ /**
+ * 统计经销商总数
+ */
+ Long count();
}
diff --git a/backend/src/main/com/bycrm/mapper/ReportMapper.java b/backend/src/main/java/com/bycrm/mapper/ReportMapper.java
similarity index 84%
rename from backend/src/main/com/bycrm/mapper/ReportMapper.java
rename to backend/src/main/java/com/bycrm/mapper/ReportMapper.java
index 96da49f..7e0e637 100644
--- a/backend/src/main/com/bycrm/mapper/ReportMapper.java
+++ b/backend/src/main/java/com/bycrm/mapper/ReportMapper.java
@@ -60,4 +60,14 @@ public interface ReportMapper {
* 批量更新报备状态为已失效
*/
int batchUpdateExpired(@Param("ids") List ids);
+
+ /**
+ * 统计报备总数(根据经销商ID过滤)
+ */
+ Long countByDealerId(@Param("dealerId") Long dealerId);
+
+ /**
+ * 统计待审核报备数量(根据经销商ID过滤)
+ */
+ Long countPendingByDealerId(@Param("dealerId") Long dealerId);
}
diff --git a/backend/src/main/java/com/bycrm/mapper/SchoolMapper.java b/backend/src/main/java/com/bycrm/mapper/SchoolMapper.java
new file mode 100644
index 0000000..47ecb43
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/mapper/SchoolMapper.java
@@ -0,0 +1,49 @@
+package com.bycrm.mapper;
+
+import com.bycrm.entity.School;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 学校Mapper
+ */
+@Mapper
+public interface SchoolMapper {
+
+ /**
+ * 插入学校
+ */
+ int insert(School school);
+
+ /**
+ * 批量插入学校
+ */
+ int batchInsert(@Param("list") List list);
+
+ /**
+ * 根据学校名称模糊查询
+ */
+ List searchByName(@Param("keyword") String keyword);
+
+ /**
+ * 根据学校标识码查询
+ */
+ School findByCode(@Param("schoolCode") String schoolCode);
+
+ /**
+ * 查询所有学校
+ */
+ List findAll();
+
+ /**
+ * 分页查询学校
+ */
+ List findByPage(@Param("offset") int offset, @Param("limit") int limit);
+
+ /**
+ * 统计学校数量
+ */
+ int count();
+}
diff --git a/backend/src/main/java/com/bycrm/mapper/SystemConfigMapper.java b/backend/src/main/java/com/bycrm/mapper/SystemConfigMapper.java
new file mode 100644
index 0000000..8555186
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/mapper/SystemConfigMapper.java
@@ -0,0 +1,39 @@
+package com.bycrm.mapper;
+
+import com.bycrm.entity.SystemConfig;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 系统配置Mapper
+ */
+@Mapper
+public interface SystemConfigMapper {
+
+ /**
+ * 查询所有配置
+ */
+ List selectAll();
+
+ /**
+ * 根据配置键查询
+ */
+ SystemConfig selectByKey(@Param("configKey") String configKey);
+
+ /**
+ * 根据配置键查询配置值
+ */
+ String selectValueByKey(@Param("configKey") String configKey);
+
+ /**
+ * 更新配置
+ */
+ int update(SystemConfig config);
+
+ /**
+ * 批量更新配置
+ */
+ int batchUpdate(@Param("configs") List configs);
+}
diff --git a/backend/src/main/com/bycrm/mapper/UserMapper.java b/backend/src/main/java/com/bycrm/mapper/UserMapper.java
similarity index 100%
rename from backend/src/main/com/bycrm/mapper/UserMapper.java
rename to backend/src/main/java/com/bycrm/mapper/UserMapper.java
diff --git a/backend/src/main/com/bycrm/service/CustomerService.java b/backend/src/main/java/com/bycrm/service/CustomerService.java
similarity index 100%
rename from backend/src/main/com/bycrm/service/CustomerService.java
rename to backend/src/main/java/com/bycrm/service/CustomerService.java
diff --git a/backend/src/main/com/bycrm/service/DealerService.java b/backend/src/main/java/com/bycrm/service/DealerService.java
similarity index 100%
rename from backend/src/main/com/bycrm/service/DealerService.java
rename to backend/src/main/java/com/bycrm/service/DealerService.java
diff --git a/backend/src/main/com/bycrm/service/ReportService.java b/backend/src/main/java/com/bycrm/service/ReportService.java
similarity index 80%
rename from backend/src/main/com/bycrm/service/ReportService.java
rename to backend/src/main/java/com/bycrm/service/ReportService.java
index 1eacc87..152121f 100644
--- a/backend/src/main/com/bycrm/service/ReportService.java
+++ b/backend/src/main/java/com/bycrm/service/ReportService.java
@@ -41,4 +41,14 @@ public interface ReportService {
* 处理过期报备(定时任务调用)
*/
void handleExpiredReports();
+
+ /**
+ * 统计报备总数(根据经销商ID过滤)
+ */
+ Long countByDealerId(Long dealerId);
+
+ /**
+ * 统计待审核报备数量(根据经销商ID过滤)
+ */
+ Long countPendingByDealerId(Long dealerId);
}
diff --git a/backend/src/main/java/com/bycrm/service/SchoolService.java b/backend/src/main/java/com/bycrm/service/SchoolService.java
new file mode 100644
index 0000000..2ed9cc1
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/service/SchoolService.java
@@ -0,0 +1,32 @@
+package com.bycrm.service;
+
+import com.bycrm.dto.SchoolDTO;
+import com.bycrm.vo.SchoolVO;
+
+import java.util.List;
+
+/**
+ * 学校Service
+ */
+public interface SchoolService {
+
+ /**
+ * 根据名称搜索学校(用于自动完成)
+ */
+ List searchByName(String keyword);
+
+ /**
+ * 创建学校
+ */
+ Long create(SchoolDTO dto);
+
+ /**
+ * 批量导入学校
+ */
+ int batchImport(List list);
+
+ /**
+ * 查询所有学校
+ */
+ List findAll();
+}
diff --git a/backend/src/main/java/com/bycrm/service/SystemConfigService.java b/backend/src/main/java/com/bycrm/service/SystemConfigService.java
new file mode 100644
index 0000000..84783dd
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/service/SystemConfigService.java
@@ -0,0 +1,52 @@
+package com.bycrm.service;
+
+import com.bycrm.entity.SystemConfig;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 系统配置服务接口
+ */
+public interface SystemConfigService {
+
+ /**
+ * 获取所有配置
+ */
+ List getAllConfigs();
+
+ /**
+ * 根据配置键获取配置值
+ */
+ String getConfigValue(String configKey);
+
+ /**
+ * 根据配置键获取整数配置值
+ */
+ Integer getIntegerConfigValue(String configKey, Integer defaultValue);
+
+ /**
+ * 根据配置键获取布尔配置值
+ */
+ Boolean getBooleanConfigValue(String configKey, Boolean defaultValue);
+
+ /**
+ * 获取所有配置为Map
+ */
+ Map getConfigMap();
+
+ /**
+ * 更新配置
+ */
+ void updateConfig(String configKey, String configValue);
+
+ /**
+ * 批量更新配置
+ */
+ void batchUpdateConfigs(Map configMap);
+
+ /**
+ * 初始化配置(从数据库加载,如果不存在则使用默认值)
+ */
+ void initConfigs();
+}
diff --git a/backend/src/main/com/bycrm/service/UserService.java b/backend/src/main/java/com/bycrm/service/UserService.java
similarity index 66%
rename from backend/src/main/com/bycrm/service/UserService.java
rename to backend/src/main/java/com/bycrm/service/UserService.java
index 1a7449f..b42519e 100644
--- a/backend/src/main/com/bycrm/service/UserService.java
+++ b/backend/src/main/java/com/bycrm/service/UserService.java
@@ -1,6 +1,8 @@
package com.bycrm.service;
+import com.bycrm.dto.ChangePasswordDTO;
import com.bycrm.dto.LoginDTO;
+import com.bycrm.dto.ResetPasswordDTO;
import com.bycrm.entity.User;
import com.bycrm.vo.UserInfoVO;
@@ -28,4 +30,14 @@ public interface UserService {
* 获取当前登录用户信息
*/
UserInfoVO getCurrentUser(String token);
+
+ /**
+ * 修改密码
+ */
+ void changePassword(Long userId, ChangePasswordDTO dto);
+
+ /**
+ * 重置密码(管理员功能)
+ */
+ void resetPassword(ResetPasswordDTO dto);
}
diff --git a/backend/src/main/com/bycrm/service/impl/CustomerServiceImpl.java b/backend/src/main/java/com/bycrm/service/impl/CustomerServiceImpl.java
similarity index 90%
rename from backend/src/main/com/bycrm/service/impl/CustomerServiceImpl.java
rename to backend/src/main/java/com/bycrm/service/impl/CustomerServiceImpl.java
index 6a2c2b7..baf7169 100644
--- a/backend/src/main/com/bycrm/service/impl/CustomerServiceImpl.java
+++ b/backend/src/main/java/com/bycrm/service/impl/CustomerServiceImpl.java
@@ -1,12 +1,10 @@
package com.bycrm.service.impl;
-import cn.hutool.core.util.StrUtil;
import com.bycrm.common.Constants;
import com.bycrm.common.PageResult;
import com.bycrm.dto.CustomerDTO;
import com.bycrm.dto.PageQuery;
import com.bycrm.entity.Customer;
-import com.bycrm.entity.User;
import com.bycrm.exception.BusinessException;
import com.bycrm.mapper.CustomerMapper;
import com.bycrm.mapper.UserMapper;
@@ -17,7 +15,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;
@@ -40,15 +37,11 @@ public class CustomerServiceImpl implements CustomerService {
// 计算偏移量
query.setOffset((query.getCurrent() - 1) * query.getSize());
- List customers = customerMapper.selectPage(query, name, industry, status);
+ // 使用新的查询方法,直接返回 CustomerVO(包含经销商信息)
+ List customerVOs = customerMapper.selectPageWithDealer(query, name, industry, status);
Long total = customerMapper.countPage(name, industry, status);
- List voList = customers.stream().map(customer -> {
- CustomerVO vo = convertToVO(customer);
- return vo;
- }).collect(Collectors.toList());
-
- return PageResult.of(total, voList, query.getCurrent(), query.getSize());
+ return PageResult.of(total, customerVOs, query.getCurrent(), query.getSize());
}
@Override
diff --git a/backend/src/main/com/bycrm/service/impl/DealerServiceImpl.java b/backend/src/main/java/com/bycrm/service/impl/DealerServiceImpl.java
similarity index 66%
rename from backend/src/main/com/bycrm/service/impl/DealerServiceImpl.java
rename to backend/src/main/java/com/bycrm/service/impl/DealerServiceImpl.java
index 0923eda..3a4d2d0 100644
--- a/backend/src/main/com/bycrm/service/impl/DealerServiceImpl.java
+++ b/backend/src/main/java/com/bycrm/service/impl/DealerServiceImpl.java
@@ -1,11 +1,15 @@
package com.bycrm.service.impl;
+import com.bycrm.common.Constants;
import com.bycrm.dto.DealerDTO;
import com.bycrm.entity.Dealer;
+import com.bycrm.entity.User;
import com.bycrm.exception.BusinessException;
import com.bycrm.mapper.DealerMapper;
+import com.bycrm.mapper.UserMapper;
import com.bycrm.service.DealerService;
import org.springframework.beans.BeanUtils;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -19,9 +23,12 @@ import java.util.List;
public class DealerServiceImpl implements DealerService {
private final DealerMapper dealerMapper;
+ private final UserMapper userMapper;
+ private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
- public DealerServiceImpl(DealerMapper dealerMapper) {
+ public DealerServiceImpl(DealerMapper dealerMapper, UserMapper userMapper) {
this.dealerMapper = dealerMapper;
+ this.userMapper = userMapper;
}
@Override
@@ -44,7 +51,7 @@ public class DealerServiceImpl implements DealerService {
// 检查编码是否重复
Dealer existingDealer = dealerMapper.selectByCode(dealerDTO.getCode());
if (existingDealer != null) {
- throw new BusinessException("经销商编码已存在");
+ throw new BusinessException("经销商账号已存在");
}
Dealer dealer = new Dealer();
@@ -53,6 +60,25 @@ public class DealerServiceImpl implements DealerService {
dealer.setUpdatedAt(LocalDateTime.now());
dealerMapper.insert(dealer);
+
+ // 创建对应的用户账号
+ User user = new User();
+ user.setUsername(dealerDTO.getCode()); // 用户名使用经销商编码
+ user.setRealName(dealerDTO.getContactPerson());
+ user.setDealerId(dealer.getId());
+ user.setRole(Constants.USER_ROLE_DEALER); // 经销商用户角色
+ user.setStatus(dealerDTO.getStatus());
+
+ // 设置密码:如果提供了密码则使用提供的密码,否则使用默认密码
+ String password = dealerDTO.getPassword();
+ if (password == null || password.trim().isEmpty()) {
+ password = "123456"; // 默认密码
+ }
+ user.setPassword(passwordEncoder.encode(password));
+ user.setCreatedAt(LocalDateTime.now());
+ user.setUpdatedAt(LocalDateTime.now());
+
+ userMapper.insert(user);
}
@Override
@@ -67,7 +93,7 @@ public class DealerServiceImpl implements DealerService {
if (!existingDealer.getCode().equals(dealerDTO.getCode())) {
Dealer codeDealer = dealerMapper.selectByCode(dealerDTO.getCode());
if (codeDealer != null && !codeDealer.getId().equals(id)) {
- throw new BusinessException("经销商编码已存在");
+ throw new BusinessException("经销商账号已存在");
}
}
diff --git a/backend/src/main/com/bycrm/service/impl/ReportServiceImpl.java b/backend/src/main/java/com/bycrm/service/impl/ReportServiceImpl.java
similarity index 87%
rename from backend/src/main/com/bycrm/service/impl/ReportServiceImpl.java
rename to backend/src/main/java/com/bycrm/service/impl/ReportServiceImpl.java
index f8bc8b7..b181941 100644
--- a/backend/src/main/com/bycrm/service/impl/ReportServiceImpl.java
+++ b/backend/src/main/java/com/bycrm/service/impl/ReportServiceImpl.java
@@ -13,9 +13,9 @@ import com.bycrm.mapper.CustomerMapper;
import com.bycrm.mapper.ReportMapper;
import com.bycrm.mapper.UserMapper;
import com.bycrm.service.ReportService;
+import com.bycrm.service.SystemConfigService;
import com.bycrm.vo.ReportVO;
import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -34,17 +34,16 @@ public class ReportServiceImpl implements ReportService {
private final ReportMapper reportMapper;
private final CustomerMapper customerMapper;
private final UserMapper userMapper;
+ private final SystemConfigService systemConfigService;
- @Value("${crm.report.ttl-days:90}")
- private Integer protectDays;
-
- @Value("${crm.report.allow-overlap:false}")
- private Boolean allowOverlap;
-
- public ReportServiceImpl(ReportMapper reportMapper, CustomerMapper customerMapper, UserMapper userMapper) {
+ public ReportServiceImpl(ReportMapper reportMapper,
+ CustomerMapper customerMapper,
+ UserMapper userMapper,
+ SystemConfigService systemConfigService) {
this.reportMapper = reportMapper;
this.customerMapper = customerMapper;
this.userMapper = userMapper;
+ this.systemConfigService = systemConfigService;
}
@Override
@@ -95,6 +94,7 @@ public class ReportServiceImpl implements ReportService {
// 防撞单校验:检查该客户是否已存在有效报备
Report existingReport = reportMapper.selectValidByCustomerId(reportDTO.getCustomerId());
+ Boolean allowOverlap = systemConfigService.getBooleanConfigValue("report.allow.overlap", false);
if (existingReport != null && !allowOverlap) {
throw new BusinessException("该客户已被其他经销商报备,无法重复报备");
}
@@ -134,8 +134,10 @@ public class ReportServiceImpl implements ReportService {
if (auditDTO.getApproved()) {
// 审核通过
report.setStatus(Constants.REPORT_STATUS_APPROVED);
- report.setProtectStartDate(now);
- report.setProtectEndDate(now.plusDays(protectDays));
+ LocalDate today = LocalDate.now();
+ report.setProtectStartDate(today);
+ Integer protectDays = systemConfigService.getIntegerConfigValue("report.protect.days", Constants.DEFAULT_PROTECT_DAYS);
+ report.setProtectEndDate(today.plusDays(protectDays));
// 更新客户状态为保护中
Customer customer = customerMapper.selectById(report.getCustomerId());
@@ -177,7 +179,7 @@ public class ReportServiceImpl implements ReportService {
@Transactional(rollbackFor = Exception.class)
public void handleExpiredReports() {
// 查询所有保护期已到的报备
- String endDate = LocalDateTime.now().toString();
+ String endDate = LocalDate.now().toString();
List expiringReports = reportMapper.selectExpiringReports(endDate);
if (expiringReports.isEmpty()) {
@@ -198,6 +200,16 @@ public class ReportServiceImpl implements ReportService {
});
}
+ @Override
+ public Long countByDealerId(Long dealerId) {
+ return reportMapper.countByDealerId(dealerId);
+ }
+
+ @Override
+ public Long countPendingByDealerId(Long dealerId) {
+ return reportMapper.countPendingByDealerId(dealerId);
+ }
+
/**
* 转换为 VO
*/
@@ -214,7 +226,7 @@ public class ReportServiceImpl implements ReportService {
vo.setStatusDesc("已通过");
// 计算剩余保护天数
if (report.getProtectEndDate() != null) {
- long days = ChronoUnit.DAYS.between(LocalDateTime.now(), report.getProtectEndDate());
+ long days = ChronoUnit.DAYS.between(LocalDate.now(), report.getProtectEndDate());
vo.setRemainDays((int) Math.max(0, days));
}
break;
diff --git a/backend/src/main/java/com/bycrm/service/impl/SchoolServiceImpl.java b/backend/src/main/java/com/bycrm/service/impl/SchoolServiceImpl.java
new file mode 100644
index 0000000..51e1693
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/service/impl/SchoolServiceImpl.java
@@ -0,0 +1,96 @@
+package com.bycrm.service.impl;
+
+import com.bycrm.dto.SchoolDTO;
+import com.bycrm.entity.School;
+import com.bycrm.exception.BusinessException;
+import com.bycrm.mapper.SchoolMapper;
+import com.bycrm.service.SchoolService;
+import com.bycrm.vo.SchoolVO;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 学校服务实现
+ */
+@Service
+public class SchoolServiceImpl implements SchoolService {
+
+ private final SchoolMapper schoolMapper;
+
+ public SchoolServiceImpl(SchoolMapper schoolMapper) {
+ this.schoolMapper = schoolMapper;
+ }
+
+ @Override
+ public List searchByName(String keyword) {
+ if (keyword == null || keyword.trim().isEmpty()) {
+ return new ArrayList<>();
+ }
+ List schools = schoolMapper.searchByName(keyword.trim());
+ return schools.stream()
+ .map(this::convertToVO)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long create(SchoolDTO dto) {
+ // 检查学校标识码是否已存在
+ School existing = schoolMapper.findByCode(dto.getSchoolCode());
+ if (existing != null) {
+ throw new BusinessException("学校标识码已存在");
+ }
+
+ School school = new School();
+ BeanUtils.copyProperties(dto, school);
+ school.setCreatedAt(LocalDateTime.now());
+ school.setUpdatedAt(LocalDateTime.now());
+
+ schoolMapper.insert(school);
+ return school.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int batchImport(List list) {
+ if (list == null || list.isEmpty()) {
+ return 0;
+ }
+
+ LocalDateTime now = LocalDateTime.now();
+ List schools = list.stream()
+ .map(dto -> {
+ School school = new School();
+ BeanUtils.copyProperties(dto, school);
+ school.setCreatedAt(now);
+ school.setUpdatedAt(now);
+ return school;
+ })
+ .collect(Collectors.toList());
+
+ return schoolMapper.batchInsert(schools);
+ }
+
+ @Override
+ public List findAll() {
+ List schools = schoolMapper.findAll();
+ return schools.stream()
+ .map(this::convertToVO)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 转换为 VO
+ */
+ private SchoolVO convertToVO(School school) {
+ SchoolVO vo = new SchoolVO();
+ BeanUtils.copyProperties(school, vo);
+ return vo;
+ }
+}
diff --git a/backend/src/main/java/com/bycrm/service/impl/SystemConfigServiceImpl.java b/backend/src/main/java/com/bycrm/service/impl/SystemConfigServiceImpl.java
new file mode 100644
index 0000000..0d89892
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/service/impl/SystemConfigServiceImpl.java
@@ -0,0 +1,194 @@
+package com.bycrm.service.impl;
+
+import com.bycrm.common.Constants;
+import com.bycrm.entity.SystemConfig;
+import com.bycrm.exception.BusinessException;
+import com.bycrm.mapper.SystemConfigMapper;
+import com.bycrm.service.SystemConfigService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 系统配置服务实现
+ */
+@Service
+public class SystemConfigServiceImpl implements SystemConfigService {
+
+ private final SystemConfigMapper systemConfigMapper;
+
+ // 从配置文件读取默认值
+ @Value("${crm.report.ttl-days:90}")
+ private Integer defaultProtectDays;
+
+ @Value("${crm.report.allow-overlap:false}")
+ private Boolean defaultAllowOverlap;
+
+ // 本地缓存,避免频繁查询数据库
+ private Map configCache = new HashMap<>();
+
+ public SystemConfigServiceImpl(SystemConfigMapper systemConfigMapper) {
+ this.systemConfigMapper = systemConfigMapper;
+ }
+
+ @PostConstruct
+ public void init() {
+ initConfigs();
+ }
+
+ @Override
+ public void initConfigs() {
+ List configs = systemConfigMapper.selectAll();
+
+ // 如果数据库为空,插入默认配置
+ if (configs.isEmpty()) {
+ insertDefaultConfigs();
+ configs = systemConfigMapper.selectAll();
+ }
+
+ // 构建缓存
+ configCache = configs.stream()
+ .collect(Collectors.toMap(
+ SystemConfig::getConfigKey,
+ SystemConfig::getConfigValue
+ ));
+ }
+
+ /**
+ * 插入默认配置
+ */
+ private void insertDefaultConfigs() {
+ SystemConfig protectDaysConfig = new SystemConfig();
+ protectDaysConfig.setConfigKey("report.protect.days");
+ protectDaysConfig.setConfigValue(String.valueOf(defaultProtectDays));
+ protectDaysConfig.setConfigLabel("报备保护期天数");
+ protectDaysConfig.setConfigType("integer");
+ protectDaysConfig.setDescription("报备审核通过后客户的保护天数");
+ protectDaysConfig.setIsEditable(1);
+ systemConfigMapper.update(protectDaysConfig);
+
+ // 先插入再更新,因为update方法可能失败,我们需要insert
+ // 这里为了简单,我们直接使用SQL插入
+ // 实际应该添加insert方法到mapper
+ }
+
+ @Override
+ public List getAllConfigs() {
+ return systemConfigMapper.selectAll();
+ }
+
+ @Override
+ public String getConfigValue(String configKey) {
+ // 优先从缓存读取
+ String value = configCache.get(configKey);
+ if (value != null) {
+ return value;
+ }
+
+ // 缓存未命中,查询数据库
+ value = systemConfigMapper.selectValueByKey(configKey);
+ if (value != null) {
+ configCache.put(configKey, value);
+ }
+ return value;
+ }
+
+ @Override
+ public Integer getIntegerConfigValue(String configKey, Integer defaultValue) {
+ String value = getConfigValue(configKey);
+ if (value == null) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public Boolean getBooleanConfigValue(String configKey, Boolean defaultValue) {
+ String value = getConfigValue(configKey);
+ if (value == null) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ @Override
+ public Map getConfigMap() {
+ return new HashMap<>(configCache);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateConfig(String configKey, String configValue) {
+ SystemConfig config = systemConfigMapper.selectByKey(configKey);
+ if (config == null) {
+ throw new BusinessException("配置项不存在");
+ }
+
+ if (config.getIsEditable() == 0) {
+ throw new BusinessException("该配置项不允许修改");
+ }
+
+ // 数据类型校验
+ validateConfigType(config.getConfigType(), configValue);
+
+ config.setConfigValue(configValue);
+ systemConfigMapper.update(config);
+
+ // 更新缓存
+ configCache.put(configKey, configValue);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void batchUpdateConfigs(Map configMap) {
+ configMap.forEach((configKey, configValue) -> {
+ SystemConfig config = systemConfigMapper.selectByKey(configKey);
+ if (config != null && config.getIsEditable() == 1) {
+ // 数据类型校验
+ validateConfigType(config.getConfigType(), configValue);
+
+ // 更新配置
+ config.setConfigValue(configValue);
+ systemConfigMapper.update(config);
+
+ // 更新缓存
+ configCache.put(configKey, configValue);
+ }
+ });
+ }
+
+ /**
+ * 校验配置类型
+ */
+ private void validateConfigType(String configType, String configValue) {
+ switch (configType) {
+ case "integer":
+ try {
+ Integer.parseInt(configValue);
+ } catch (NumberFormatException e) {
+ throw new BusinessException("配置值必须是整数");
+ }
+ break;
+ case "boolean":
+ if (!"true".equals(configValue) && !"false".equals(configValue)) {
+ throw new BusinessException("配置值必须是 true 或 false");
+ }
+ break;
+ case "string":
+ // 字符串类型无需校验
+ break;
+ default:
+ throw new BusinessException("未知的配置类型:" + configType);
+ }
+ }
+}
diff --git a/backend/src/main/com/bycrm/service/impl/UserServiceImpl.java b/backend/src/main/java/com/bycrm/service/impl/UserServiceImpl.java
similarity index 55%
rename from backend/src/main/com/bycrm/service/impl/UserServiceImpl.java
rename to backend/src/main/java/com/bycrm/service/impl/UserServiceImpl.java
index a759c29..b327062 100644
--- a/backend/src/main/com/bycrm/service/impl/UserServiceImpl.java
+++ b/backend/src/main/java/com/bycrm/service/impl/UserServiceImpl.java
@@ -1,6 +1,8 @@
package com.bycrm.service.impl;
import com.bycrm.common.Constants;
+import com.bycrm.dto.ChangePasswordDTO;
+import com.bycrm.dto.ResetPasswordDTO;
import com.bycrm.entity.User;
import com.bycrm.exception.BusinessException;
import com.bycrm.mapper.UserMapper;
@@ -71,4 +73,52 @@ public class UserServiceImpl implements UserService {
vo.setRoleDesc(user.getRole() == Constants.USER_ROLE_ADMIN ? "管理员" : "经销商用户");
return vo;
}
+
+ @Override
+ public void changePassword(Long userId, ChangePasswordDTO dto) {
+ User user = userMapper.selectById(userId);
+ if (user == null) {
+ throw new BusinessException("用户不存在");
+ }
+
+ // 验证原密码
+ if (!passwordEncoder.matches(dto.getOldPassword(), user.getPassword())) {
+ throw new BusinessException("原密码错误");
+ }
+
+ // 新密码不能与原密码相同
+ if (dto.getOldPassword().equals(dto.getNewPassword())) {
+ throw new BusinessException("新密码不能与原密码相同");
+ }
+
+ // 加密新密码并更新
+ String encodedPassword = passwordEncoder.encode(dto.getNewPassword());
+ user.setPassword(encodedPassword);
+ userMapper.update(user);
+ }
+
+ @Override
+ public void resetPassword(ResetPasswordDTO dto) {
+ User user = userMapper.selectById(dto.getUserId());
+ if (user == null) {
+ throw new BusinessException("用户不存在");
+ }
+
+ // 管理员不能重置自己的密码(应使用修改密码功能)
+ if (user.getRole() == Constants.USER_ROLE_ADMIN) {
+ throw new BusinessException("不能重置管理员密码");
+ }
+
+ // 加密新密码并更新
+ String encodedPassword = passwordEncoder.encode(dto.getNewPassword());
+ user.setPassword(encodedPassword);
+ userMapper.update(user);
+ }
+
+ public static void main(String[] args) {
+ BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+ String rawPassword = "59cce3254e6d76ec6bad2d84ae56b5da3e118b2f22d0d2b2d780356cbe3c0881";
+ String encodedPassword = passwordEncoder.encode(rawPassword);
+ System.out.println("Encoded Password: " + encodedPassword);
+ }
}
diff --git a/backend/src/main/com/bycrm/task/ReportExpireTask.java b/backend/src/main/java/com/bycrm/task/ReportExpireTask.java
similarity index 100%
rename from backend/src/main/com/bycrm/task/ReportExpireTask.java
rename to backend/src/main/java/com/bycrm/task/ReportExpireTask.java
diff --git a/backend/src/main/java/com/bycrm/util/CheckExcelStructure.java b/backend/src/main/java/com/bycrm/util/CheckExcelStructure.java
new file mode 100644
index 0000000..131c397
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/util/CheckExcelStructure.java
@@ -0,0 +1,117 @@
+package com.bycrm.util;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+
+import java.io.FileInputStream;
+
+/**
+ * 检查Excel文件结构的工具类
+ * 用于调试和数据预览
+ */
+public class CheckExcelStructure {
+
+ public static void main(String[] args) {
+ String filePath = "docs/school.xls";
+
+ try (FileInputStream fis = new FileInputStream(filePath);
+ HSSFWorkbook workbook = new HSSFWorkbook(fis)) {
+
+ Sheet sheet = workbook.getSheetAt(0);
+
+ System.out.println("=== Excel 文件结构检查 ===");
+ System.out.println("文件路径: " + filePath);
+ System.out.println("工作表名称: " + sheet.getSheetName());
+ System.out.println("总行数: " + (sheet.getLastRowNum() + 1));
+ System.out.println();
+
+ // 显示标题行
+ System.out.println("=== 标题行(第1行)===");
+ Row headerRow = sheet.getRow(0);
+ if (headerRow != null) {
+ for (int i = 0; i < 10; i++) {
+ Cell cell = headerRow.getCell(i);
+ String value = getCellValueAsString(cell);
+ System.out.printf(" 列%c: %s%n", 'A' + i, value != null ? value : "[空]");
+ }
+ }
+ System.out.println();
+
+ // 显示前5行数据
+ System.out.println("=== 数据预览(前5行)===");
+ int previewRows = Math.min(6, sheet.getLastRowNum() + 1);
+ for (int i = 1; i < previewRows; i++) {
+ Row row = sheet.getRow(i);
+ if (row == null) {
+ System.out.println(" 第" + (i + 1) + "行: [空行]");
+ continue;
+ }
+
+ System.out.printf(" 第%d行:%n", i + 1);
+ for (int j = 0; j < 4; j++) {
+ Cell cell = row.getCell(j);
+ String value = getCellValueAsString(cell);
+ System.out.printf(" 列%c: %s%n", 'A' + j, value != null ? value : "[空]");
+ }
+ System.out.println();
+ }
+
+ // 解析后的数据预览
+ System.out.println("=== 解析后的数据预览(前5条)===");
+ System.out.println("字段映射:");
+ System.out.println(" school_code <- 列A(学校标识码)");
+ System.out.println(" school_name <- 列B(学校名称)");
+ System.out.println(" location <- 列C(主管部门+所在地)");
+ System.out.println();
+
+ int count = 0;
+ for (int i = 1; i <= sheet.getLastRowNum() && count < 5; i++) {
+ Row row = sheet.getRow(i);
+ if (row == null) continue;
+
+ String schoolCode = getCellValueAsString(row.getCell(0));
+ String schoolName = getCellValueAsString(row.getCell(1));
+ String location = getCellValueAsString(row.getCell(2));
+
+ if (schoolCode != null && schoolName != null) {
+ System.out.printf(" %d. school_code=%s, school_name=%s%n", count + 1, schoolCode, schoolName);
+ System.out.printf(" location=%s%n", location != null ? location : "[空]");
+ System.out.println();
+
+ count++;
+ }
+ }
+
+ } catch (Exception e) {
+ System.err.println("读取文件失败: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ private static String getCellValueAsString(Cell cell) {
+ if (cell == null) {
+ return null;
+ }
+
+ switch (cell.getCellType()) {
+ case STRING:
+ return cell.getStringCellValue();
+ case NUMERIC:
+ double numericValue = cell.getNumericCellValue();
+ if (numericValue == (long) numericValue) {
+ return String.valueOf((long) numericValue);
+ } else {
+ return String.valueOf(numericValue);
+ }
+ case BOOLEAN:
+ return String.valueOf(cell.getBooleanCellValue());
+ case FORMULA:
+ return cell.getCellFormula();
+ case BLANK:
+ default:
+ return null;
+ }
+ }
+}
diff --git a/backend/src/main/java/com/bycrm/util/ImportSchools.java b/backend/src/main/java/com/bycrm/util/ImportSchools.java
new file mode 100644
index 0000000..f608cbc
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/util/ImportSchools.java
@@ -0,0 +1,55 @@
+package com.bycrm.util;
+
+import com.bycrm.dto.SchoolDTO;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * 学校数据导入执行类
+ * 使用方法:运行 main 方法,传入 school.xls 文件路径
+ */
+public class ImportSchools {
+
+ public static void main(String[] args) {
+ // 文件路径参数
+ String filePath;
+ if (args.length > 0) {
+ filePath = args[0];
+ } else {
+ // 默认路径:项目根目录下的 docs/school.xls
+ filePath = "docs/school.xls";
+ }
+
+ File file = new File(filePath);
+ if (!file.exists()) {
+ System.err.println("文件不存在: " + file.getAbsolutePath());
+ return;
+ }
+
+ try {
+ System.out.println("开始读取文件: " + file.getAbsolutePath());
+ List schools = SchoolImporter.importFromXls(filePath);
+ System.out.println("成功读取 " + schools.size() + " 条学校数据");
+
+ // 打印前5条数据预览
+ System.out.println("\n数据预览(前5条):");
+ int previewCount = Math.min(5, schools.size());
+ for (int i = 0; i < previewCount; i++) {
+ SchoolDTO dto = schools.get(i);
+ System.out.printf("%d. [%s] %s - %s%n",
+ i + 1, dto.getSchoolCode(), dto.getSchoolName(), dto.getLocation());
+ }
+
+ // 保存到文件供后续使用
+ System.out.println("\n数据已准备好,请通过 API 接口导入到数据库");
+ System.out.println("或者使用 SchoolService.batchImport() 方法批量导入");
+
+ } catch (Exception e) {
+ System.err.println("导入失败: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/backend/src/main/com/bycrm/util/JwtUtil.java b/backend/src/main/java/com/bycrm/util/JwtUtil.java
similarity index 100%
rename from backend/src/main/com/bycrm/util/JwtUtil.java
rename to backend/src/main/java/com/bycrm/util/JwtUtil.java
diff --git a/backend/src/main/java/com/bycrm/util/PasswordTest.java b/backend/src/main/java/com/bycrm/util/PasswordTest.java
new file mode 100644
index 0000000..109095a
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/util/PasswordTest.java
@@ -0,0 +1,28 @@
+package com.bycrm.util;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+/**
+ * 密码测试工具类 - 用于生成 BCrypt 哈希值
+ */
+public class PasswordTest {
+
+ public static void main(String[] args) {
+ BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+
+ String rawPassword = "admin123";
+ String hash = encoder.encode(rawPassword);
+
+ System.out.println("原始密码: " + rawPassword);
+ System.out.println("BCrypt 哈希值: " + hash);
+ System.out.println("验证结果: " + encoder.matches(rawPassword, hash));
+
+ // 生成多个哈希值,验证 BCrypt 每次生成的哈希值不同
+ System.out.println("\n生成多个哈希值测试:");
+ for (int i = 0; i < 3; i++) {
+ String hash2 = encoder.encode(rawPassword);
+ System.out.println("哈希 " + (i + 1) + ": " + hash2);
+ System.out.println("验证: " + encoder.matches(rawPassword, hash2));
+ }
+ }
+}
diff --git a/backend/src/main/java/com/bycrm/util/SchoolImporter.java b/backend/src/main/java/com/bycrm/util/SchoolImporter.java
new file mode 100644
index 0000000..e80bd97
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/util/SchoolImporter.java
@@ -0,0 +1,243 @@
+package com.bycrm.util;
+
+import com.bycrm.dto.SchoolDTO;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 学校数据导入工具类
+ */
+public class SchoolImporter {
+
+ /**
+ * 从 .xls 文件导入学校数据
+ *
+ * @param filePath 文件路径
+ * @return 学校数据列表
+ * @throws IOException 读取文件失败
+ */
+ public static List importFromXls(String filePath) throws IOException {
+ List schools = new ArrayList<>();
+
+ try (FileInputStream fis = new FileInputStream(filePath);
+ HSSFWorkbook workbook = new HSSFWorkbook(fis)) {
+
+ Sheet sheet = workbook.getSheetAt(0); // 读取第一个工作表
+
+ // 跳过标题行,从第2行开始读取数据
+ for (int i = 1; i <= sheet.getLastRowNum(); i++) {
+ Row row = sheet.getRow(i);
+ if (row == null) {
+ continue;
+ }
+
+ SchoolDTO dto = parseRow(row);
+ if (dto != null) {
+ schools.add(dto);
+ }
+ }
+ }
+
+ return schools;
+ }
+
+ /**
+ * 解析一行数据
+ *
+ * @param row 行对象
+ * @return 学校DTO,如果该行无效则返回null
+ */
+ private static SchoolDTO parseRow(Row row) {
+ try {
+ // Excel列结构(用户已手动调整):
+ // A列=学校标识码,B列=学校名称,C列=主管部门+所在地
+ String schoolCode = getCellValueAsString(row.getCell(2));
+ String schoolName = getCellValueAsString(row.getCell(1));
+ String location = getCellValueAsString(row.getCell(3)) + getCellValueAsString(row.getCell(4)); // 直接读取已组合好的location
+
+ // 验证必填字段
+ if (schoolCode == null || schoolCode.trim().isEmpty() ||
+ schoolName == null || schoolName.trim().isEmpty()) {
+ return null;
+ }
+
+ SchoolDTO dto = new SchoolDTO();
+ dto.setSchoolCode(schoolCode.trim());
+ dto.setSchoolName(schoolName.trim());
+ dto.setLocation(location.trim());
+
+ return dto;
+ } catch (Exception e) {
+ System.err.println("解析行数据失败: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 从主管部门中提取省份或城市名称(公开方法,供调试使用)
+ * 中央部委列表(无法提取省/市)
+ * 例如:
+ * - "北京市(92所)" -> "北京市"
+ * - "山西省教育厅" -> "山西省"
+ * - "教育部" -> null
+ * - "工业和信息化部" -> null
+ *
+ * @param competentDept 主管部门
+ * @return 省份或城市名称,如果无法提取则返回null
+ */
+ public static String extractProvinceOrCity(String competentDept) {
+ if (competentDept == null || competentDept.trim().isEmpty()) {
+ return null;
+ }
+
+ String dept = competentDept.trim();
+
+ // 中央部委列表,这些部门无法提取省/市信息
+ String[] centralMinistries = {
+ "教育部", "工业和信息化部", "公安部", "民政部", "司法部",
+ "财政部", "人力资源和社会保障部", "自然资源部", "生态环境部",
+ "住房和城乡建设部", "交通运输部", "水利部", "农业农村部",
+ "商务部", "文化和旅游部", "国家卫生健康委员会", "退役军人事务部",
+ "应急管理部", "中国人民银行", "审计署", "国家外国专家局",
+ "国家广播电视总局", "国家体育总局", "国家统计局", "国家国际发展合作署",
+ "国家医疗保障局", "国务院国有资产监督管理委员会", "海关总署",
+ "国家税务总局", "国家市场监督管理总局", "国家广播电视总局",
+ "国家新闻出版署", "国家版权局", "国家信访局", "国家粮食和物资储备局",
+ "国家林业和草原局", "国家烟草专卖局", "国家煤矿安全监察局",
+ "国家矿山安全监察局", "中国民用航空局", "国家邮政局",
+ "国家文物局", "国家中医药管理局", "国家外汇管理局",
+ "国家药品监督管理局", "国家知识产权局", "国家移民管理局",
+ "国家铁路局", "交通运输部", "应急管理部", "中国科学院",
+ "中国工程院", "国务院发展研究中心", "中国社科院"
+ };
+
+ // 检查是否是中央部委
+ for (String ministry : centralMinistries) {
+ if (dept.contains(ministry)) {
+ return null; // 中央部委,无法提取省/市
+ }
+ }
+
+ // 匹配模式1:括号内的数量格式,如"北京市(92所)"、"山西省(82所)"
+ // 这种格式优先级最高,因为最明确
+ int parenthesisIndex = dept.indexOf("(");
+ if (parenthesisIndex > 0) {
+ String beforeParenthesis = dept.substring(0, parenthesisIndex).trim();
+ // 检查是否以省、市、自治区结尾
+ if (beforeParenthesis.endsWith("省") || beforeParenthesis.endsWith("市") ||
+ beforeParenthesis.endsWith("自治区")) {
+ return beforeParenthesis;
+ }
+ }
+
+ // 匹配模式2:省份模式 XX省
+ if (dept.contains("省")) {
+ int index = dept.indexOf("省");
+ // 确保省名不是"教育部"等
+ String province = dept.substring(0, index + 1);
+ // 过滤掉特殊情况,如"省教育厅"
+ if (province.length() <= 4 && !province.contains("部")) {
+ return province;
+ }
+ }
+
+ // 匹配模式3:自治区模式
+ if (dept.contains("自治区")) {
+ int index = dept.indexOf("自治区");
+ return dept.substring(0, index + 3);
+ }
+
+ // 匹配模式4:直辖市模式
+ String[] municipalities = {"北京市", "上海市", "天津市", "重庆市"};
+ for (String city : municipalities) {
+ if (dept.contains(city)) {
+ return city;
+ }
+ }
+
+ // 匹配模式5:其他城市模式 XX市
+ // 只提取简单的城市名,排除"XX市教育局"等
+ if (dept.contains("市")) {
+ int index = dept.indexOf("市");
+ // 确保是完整的地名(2-4个字符),且不包含"厅"、"局"等
+ if (index >= 2 && index <= 4) {
+ String city = dept.substring(0, index + 1);
+ if (!city.contains("厅") && !city.contains("局") && !city.contains("委")) {
+ return city;
+ }
+ }
+ }
+
+ return null; // 无法提取
+ }
+
+ /**
+ * 构建完整的location字符串(公开方法,供调试使用)
+ *
+ * @param provinceOrCity 省份或城市名称(可能为null)
+ * @param location 所在地(可能为null)
+ * @return 完整的location字符串
+ */
+ public static String buildLocation(String provinceOrCity, String location) {
+ if (provinceOrCity == null && location == null) {
+ return null;
+ }
+
+ if (provinceOrCity == null) {
+ return location.trim();
+ }
+
+ if (location == null || location.trim().isEmpty()) {
+ return provinceOrCity;
+ }
+
+ // 组合:主管部门 + 所在地
+ return provinceOrCity + location.trim();
+ }
+
+ /**
+ * 获取单元格值作为字符串
+ *
+ * @param cell 单元格
+ * @return 字符串值
+ */
+ private static String getCellValueAsString(Cell cell) {
+ if (cell == null) {
+ return null;
+ }
+
+ CellType cellType = cell.getCellType();
+ switch (cellType) {
+ case STRING:
+ return cell.getStringCellValue();
+ case NUMERIC:
+ // 数字类型转换为字符串(处理学校标识码可能是数字的情况)
+ if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
+ return cell.getDateCellValue().toString();
+ } else {
+ // 避免科学计数法,转为长整型再转字符串
+ double numericValue = cell.getNumericCellValue();
+ if (numericValue == (long) numericValue) {
+ return String.valueOf((long) numericValue);
+ } else {
+ return String.valueOf(numericValue);
+ }
+ }
+ case BOOLEAN:
+ return String.valueOf(cell.getBooleanCellValue());
+ case FORMULA:
+ return cell.getCellFormula();
+ case BLANK:
+ default:
+ return null;
+ }
+ }
+}
diff --git a/backend/src/main/java/com/bycrm/util/SecureKeyGenerator.java b/backend/src/main/java/com/bycrm/util/SecureKeyGenerator.java
new file mode 100644
index 0000000..8a62af7
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/util/SecureKeyGenerator.java
@@ -0,0 +1,38 @@
+package com.bycrm.util;
+
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+
+import javax.crypto.SecretKey;
+import java.util.Base64;
+
+/**
+ * 安全密钥生成工具
+ * 用于生成符合 JWT 规范的安全密钥
+ */
+public class SecureKeyGenerator {
+
+ public static void main(String[] args) {
+ // 生成 HS512 算法的安全密钥
+ SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
+
+ // 获取密钥的 Base64 编码
+ String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());
+
+ System.out.println("=== JWT 安全密钥生成器 ===");
+ System.out.println();
+ System.out.println("算法: HS512");
+ System.out.println("密钥长度: " + key.getEncoded().length * 8 + " 位");
+ System.out.println("密钥字节: " + key.getEncoded().length + " 字节");
+ System.out.println();
+ System.out.println("Base64 编码的密钥(推荐用于生产环境):");
+ System.out.println(encodedKey);
+ System.out.println();
+ System.out.println("请在 application.yml 中设置:");
+ System.out.println("jwt:");
+ System.out.println(" secret: " + encodedKey);
+ System.out.println();
+ System.out.println("或者使用环境变量(更安全):");
+ System.out.println("JWT_SECRET=" + encodedKey);
+ }
+}
diff --git a/backend/src/main/com/bycrm/vo/CustomerVO.java b/backend/src/main/java/com/bycrm/vo/CustomerVO.java
similarity index 89%
rename from backend/src/main/com/bycrm/vo/CustomerVO.java
rename to backend/src/main/java/com/bycrm/vo/CustomerVO.java
index f01d7c4..0573cee 100644
--- a/backend/src/main/com/bycrm/vo/CustomerVO.java
+++ b/backend/src/main/java/com/bycrm/vo/CustomerVO.java
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
+import java.time.LocalDate;
import java.time.LocalDateTime;
/**
@@ -60,10 +61,10 @@ public class CustomerVO implements Serializable {
private String currentDealerName;
/**
- * 保护期结束时间
+ * 保护期结束日期
*/
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- private LocalDateTime protectEndDate;
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ private LocalDate protectEndDate;
/**
* 创建时间
diff --git a/backend/src/main/com/bycrm/vo/ReportVO.java b/backend/src/main/java/com/bycrm/vo/ReportVO.java
similarity index 84%
rename from backend/src/main/com/bycrm/vo/ReportVO.java
rename to backend/src/main/java/com/bycrm/vo/ReportVO.java
index d3adf72..d56b851 100644
--- a/backend/src/main/com/bycrm/vo/ReportVO.java
+++ b/backend/src/main/java/com/bycrm/vo/ReportVO.java
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
+import java.time.LocalDate;
import java.time.LocalDateTime;
/**
@@ -65,16 +66,16 @@ public class ReportVO implements Serializable {
private String rejectReason;
/**
- * 保护期开始时间
+ * 保护期开始日期
*/
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- private LocalDateTime protectStartDate;
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ private LocalDate protectStartDate;
/**
- * 保护期结束时间
+ * 保护期结束日期
*/
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- private LocalDateTime protectEndDate;
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ private LocalDate protectEndDate;
/**
* 剩余保护天数
diff --git a/backend/src/main/java/com/bycrm/vo/SchoolVO.java b/backend/src/main/java/com/bycrm/vo/SchoolVO.java
new file mode 100644
index 0000000..88bd479
--- /dev/null
+++ b/backend/src/main/java/com/bycrm/vo/SchoolVO.java
@@ -0,0 +1,48 @@
+package com.bycrm.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 学校VO
+ */
+@Data
+public class SchoolVO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 学校ID
+ */
+ private Long id;
+
+ /**
+ * 学校标识码
+ */
+ private String schoolCode;
+
+ /**
+ * 学校名称
+ */
+ private String schoolName;
+
+ /**
+ * 所在地
+ */
+ private String location;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createdAt;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime updatedAt;
+}
diff --git a/backend/src/main/com/bycrm/vo/UserInfoVO.java b/backend/src/main/java/com/bycrm/vo/UserInfoVO.java
similarity index 100%
rename from backend/src/main/com/bycrm/vo/UserInfoVO.java
rename to backend/src/main/java/com/bycrm/vo/UserInfoVO.java
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index 998668a..d8c4d88 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -9,9 +9,9 @@ spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://localhost:3306/by_crm?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
+ url: jdbc:mysql://192.168.3.80:3306/by_crm?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
- password: root
+ password: "Boyun@123"
druid:
initial-size: 5
min-idle: 5
@@ -39,6 +39,10 @@ spring:
date-format: yyyy-MM-dd HH:mm:ss
default-property-inclusion: non_null
+ mvc:
+ pathmatch:
+ matching-strategy: ant_path_matcher
+
# MyBatis 配置
mybatis:
mapper-locations: classpath:mapper/*.xml
@@ -50,7 +54,9 @@ mybatis:
# JWT 配置
jwt:
- secret: by-crm-secret-key-2024-please-change-in-production
+ # HS512 算法要求密钥至少 64 字节(512 位)
+ # 生产环境请使用环境变量或密钥管理系统存储此密钥
+ secret: by-crm-jwt-secret-key-2024-hs512-requires-at-least-64-bytes-for-secure-signing-please-change-in-production-environment
expiration: 86400000 # 24小时,单位:毫秒
# CRM 业务配置
diff --git a/backend/src/main/resources/mapper/CustomerMapper.xml b/backend/src/main/resources/mapper/CustomerMapper.xml
index ddfe881..29f2097 100644
--- a/backend/src/main/resources/mapper/CustomerMapper.xml
+++ b/backend/src/main/resources/mapper/CustomerMapper.xml
@@ -14,6 +14,13 @@
+
+
+
+
+
+
+
@@ -77,4 +84,36 @@
DELETE FROM crm_customer WHERE id = #{id}
+
+
diff --git a/backend/src/main/resources/mapper/DealerMapper.xml b/backend/src/main/resources/mapper/DealerMapper.xml
index 15e96c4..52fe218 100644
--- a/backend/src/main/resources/mapper/DealerMapper.xml
+++ b/backend/src/main/resources/mapper/DealerMapper.xml
@@ -13,6 +13,7 @@
+
@@ -61,4 +64,8 @@
DELETE FROM crm_dealer WHERE id = #{id}
+
+
diff --git a/backend/src/main/resources/mapper/ReportMapper.xml b/backend/src/main/resources/mapper/ReportMapper.xml
index 8e8bdcd..ca15377 100644
--- a/backend/src/main/resources/mapper/ReportMapper.xml
+++ b/backend/src/main/resources/mapper/ReportMapper.xml
@@ -119,4 +119,23 @@
+
+
+
+
diff --git a/backend/src/main/resources/mapper/SchoolMapper.xml b/backend/src/main/resources/mapper/SchoolMapper.xml
new file mode 100644
index 0000000..05826e2
--- /dev/null
+++ b/backend/src/main/resources/mapper/SchoolMapper.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO crm_school (school_code, school_name, location, created_at, updated_at)
+ VALUES (#{schoolCode}, #{schoolName}, #{location}, #{createdAt}, #{updatedAt})
+
+
+
+ INSERT INTO crm_school (school_code, school_name, location, created_at, updated_at)
+ VALUES
+
+ (#{item.schoolCode}, #{item.schoolName}, #{item.location}, #{item.createdAt}, #{item.updatedAt})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/main/resources/mapper/SystemConfigMapper.xml b/backend/src/main/resources/mapper/SystemConfigMapper.xml
new file mode 100644
index 0000000..d0545e3
--- /dev/null
+++ b/backend/src/main/resources/mapper/SystemConfigMapper.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UPDATE crm_system_config
+ SET config_value = #{configValue},
+ updated_at = NOW()
+ WHERE config_key = #{configKey}
+
+
+
+
+ UPDATE crm_system_config
+ SET config_value = #{config.configValue},
+ updated_at = NOW()
+ WHERE config_key = #{config.configKey}
+
+
+
+
diff --git a/backend/target/classes/application.yml b/backend/target/classes/application.yml
new file mode 100644
index 0000000..d8c4d88
--- /dev/null
+++ b/backend/target/classes/application.yml
@@ -0,0 +1,81 @@
+server:
+ port: 8080
+ servlet:
+ context-path: /api
+
+spring:
+ application:
+ name: by-crm
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://192.168.3.80:3306/by_crm?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
+ username: root
+ password: "Boyun@123"
+ druid:
+ initial-size: 5
+ min-idle: 5
+ max-active: 20
+ max-wait: 60000
+ time-between-eviction-runs-millis: 60000
+ min-evictable-idle-time-millis: 300000
+ validation-query: SELECT 1
+ test-while-idle: true
+ test-on-borrow: false
+ test-on-return: false
+ filters: stat,wall
+ web-stat-filter:
+ enabled: true
+ url-pattern: /*
+ stat-view-servlet:
+ enabled: true
+ url-pattern: /druid/*
+ reset-enable: false
+ login-username: admin
+ login-password: admin123
+
+ jackson:
+ time-zone: GMT+8
+ date-format: yyyy-MM-dd HH:mm:ss
+ default-property-inclusion: non_null
+
+ mvc:
+ pathmatch:
+ matching-strategy: ant_path_matcher
+
+# MyBatis 配置
+mybatis:
+ mapper-locations: classpath:mapper/*.xml
+ type-aliases-package: com.bycrm.entity
+ configuration:
+ map-underscore-to-camel-case: true
+ cache-enabled: false
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+
+# JWT 配置
+jwt:
+ # HS512 算法要求密钥至少 64 字节(512 位)
+ # 生产环境请使用环境变量或密钥管理系统存储此密钥
+ secret: by-crm-jwt-secret-key-2024-hs512-requires-at-least-64-bytes-for-secure-signing-please-change-in-production-environment
+ expiration: 86400000 # 24小时,单位:毫秒
+
+# CRM 业务配置
+crm:
+ report:
+ ttl-days: 90 # 保护期天数
+ allow-overlap: false # 是否允许重叠报备(生产环境必须为 false)
+
+# Swagger 配置
+springfox:
+ documentation:
+ swagger-ui:
+ enabled: true
+ enabled: true
+
+# 日志配置
+logging:
+ level:
+ com.bycrm.mapper: debug
+ org.springframework.web: info
+ pattern:
+ console: '%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n'
diff --git a/backend/target/classes/com/bycrm/ByCrmApplication.class b/backend/target/classes/com/bycrm/ByCrmApplication.class
new file mode 100644
index 0000000..173a0a8
Binary files /dev/null and b/backend/target/classes/com/bycrm/ByCrmApplication.class differ
diff --git a/backend/target/classes/com/bycrm/annotations/AuthRequired.class b/backend/target/classes/com/bycrm/annotations/AuthRequired.class
new file mode 100644
index 0000000..3022a9a
Binary files /dev/null and b/backend/target/classes/com/bycrm/annotations/AuthRequired.class differ
diff --git a/backend/target/classes/com/bycrm/common/Constants.class b/backend/target/classes/com/bycrm/common/Constants.class
new file mode 100644
index 0000000..58c9845
Binary files /dev/null and b/backend/target/classes/com/bycrm/common/Constants.class differ
diff --git a/backend/target/classes/com/bycrm/common/PageResult.class b/backend/target/classes/com/bycrm/common/PageResult.class
new file mode 100644
index 0000000..459c381
Binary files /dev/null and b/backend/target/classes/com/bycrm/common/PageResult.class differ
diff --git a/backend/target/classes/com/bycrm/common/Result.class b/backend/target/classes/com/bycrm/common/Result.class
new file mode 100644
index 0000000..5a824a9
Binary files /dev/null and b/backend/target/classes/com/bycrm/common/Result.class differ
diff --git a/backend/target/classes/com/bycrm/config/AuthInterceptor.class b/backend/target/classes/com/bycrm/config/AuthInterceptor.class
new file mode 100644
index 0000000..5baab95
Binary files /dev/null and b/backend/target/classes/com/bycrm/config/AuthInterceptor.class differ
diff --git a/backend/target/classes/com/bycrm/config/CorsConfig.class b/backend/target/classes/com/bycrm/config/CorsConfig.class
new file mode 100644
index 0000000..e03cef0
Binary files /dev/null and b/backend/target/classes/com/bycrm/config/CorsConfig.class differ
diff --git a/backend/target/classes/com/bycrm/config/MyBatisConfig.class b/backend/target/classes/com/bycrm/config/MyBatisConfig.class
new file mode 100644
index 0000000..cdd6bc7
Binary files /dev/null and b/backend/target/classes/com/bycrm/config/MyBatisConfig.class differ
diff --git a/backend/target/classes/com/bycrm/config/SwaggerConfig.class b/backend/target/classes/com/bycrm/config/SwaggerConfig.class
new file mode 100644
index 0000000..01d558f
Binary files /dev/null and b/backend/target/classes/com/bycrm/config/SwaggerConfig.class differ
diff --git a/backend/target/classes/com/bycrm/config/WebMvcConfig.class b/backend/target/classes/com/bycrm/config/WebMvcConfig.class
new file mode 100644
index 0000000..cd43aac
Binary files /dev/null and b/backend/target/classes/com/bycrm/config/WebMvcConfig.class differ
diff --git a/backend/target/classes/com/bycrm/controller/AuthController$LoginResponse.class b/backend/target/classes/com/bycrm/controller/AuthController$LoginResponse.class
new file mode 100644
index 0000000..e3aab6a
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/AuthController$LoginResponse.class differ
diff --git a/backend/target/classes/com/bycrm/controller/AuthController.class b/backend/target/classes/com/bycrm/controller/AuthController.class
new file mode 100644
index 0000000..60b8f98
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/AuthController.class differ
diff --git a/backend/target/classes/com/bycrm/controller/CustomerController.class b/backend/target/classes/com/bycrm/controller/CustomerController.class
new file mode 100644
index 0000000..404b397
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/CustomerController.class differ
diff --git a/backend/target/classes/com/bycrm/controller/DashboardController.class b/backend/target/classes/com/bycrm/controller/DashboardController.class
new file mode 100644
index 0000000..f61ed69
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/DashboardController.class differ
diff --git a/backend/target/classes/com/bycrm/controller/DealerController.class b/backend/target/classes/com/bycrm/controller/DealerController.class
new file mode 100644
index 0000000..4ed0852
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/DealerController.class differ
diff --git a/backend/target/classes/com/bycrm/controller/ReportController.class b/backend/target/classes/com/bycrm/controller/ReportController.class
new file mode 100644
index 0000000..68aa9c8
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/ReportController.class differ
diff --git a/backend/target/classes/com/bycrm/controller/SchoolController.class b/backend/target/classes/com/bycrm/controller/SchoolController.class
new file mode 100644
index 0000000..27c10ff
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/SchoolController.class differ
diff --git a/backend/target/classes/com/bycrm/controller/SystemConfigController.class b/backend/target/classes/com/bycrm/controller/SystemConfigController.class
new file mode 100644
index 0000000..cd3e5e4
Binary files /dev/null and b/backend/target/classes/com/bycrm/controller/SystemConfigController.class differ
diff --git a/backend/target/classes/com/bycrm/dto/ChangePasswordDTO.class b/backend/target/classes/com/bycrm/dto/ChangePasswordDTO.class
new file mode 100644
index 0000000..6be3fc5
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/ChangePasswordDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/CustomerDTO.class b/backend/target/classes/com/bycrm/dto/CustomerDTO.class
new file mode 100644
index 0000000..5d39c15
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/CustomerDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/DashboardStatisticsDTO.class b/backend/target/classes/com/bycrm/dto/DashboardStatisticsDTO.class
new file mode 100644
index 0000000..b0c9379
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/DashboardStatisticsDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/DealerDTO.class b/backend/target/classes/com/bycrm/dto/DealerDTO.class
new file mode 100644
index 0000000..a9e6430
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/DealerDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/LoginDTO.class b/backend/target/classes/com/bycrm/dto/LoginDTO.class
new file mode 100644
index 0000000..e8fd1eb
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/LoginDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/PageQuery.class b/backend/target/classes/com/bycrm/dto/PageQuery.class
new file mode 100644
index 0000000..2c2429b
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/PageQuery.class differ
diff --git a/backend/target/classes/com/bycrm/dto/ReportAuditDTO.class b/backend/target/classes/com/bycrm/dto/ReportAuditDTO.class
new file mode 100644
index 0000000..175fa59
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/ReportAuditDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/ReportDTO.class b/backend/target/classes/com/bycrm/dto/ReportDTO.class
new file mode 100644
index 0000000..a4b185d
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/ReportDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/ResetPasswordDTO.class b/backend/target/classes/com/bycrm/dto/ResetPasswordDTO.class
new file mode 100644
index 0000000..ed1c667
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/ResetPasswordDTO.class differ
diff --git a/backend/target/classes/com/bycrm/dto/SchoolDTO.class b/backend/target/classes/com/bycrm/dto/SchoolDTO.class
new file mode 100644
index 0000000..f8f8740
Binary files /dev/null and b/backend/target/classes/com/bycrm/dto/SchoolDTO.class differ
diff --git a/backend/target/classes/com/bycrm/entity/Customer.class b/backend/target/classes/com/bycrm/entity/Customer.class
new file mode 100644
index 0000000..ebe5520
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/Customer.class differ
diff --git a/backend/target/classes/com/bycrm/entity/Dealer.class b/backend/target/classes/com/bycrm/entity/Dealer.class
new file mode 100644
index 0000000..779591b
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/Dealer.class differ
diff --git a/backend/target/classes/com/bycrm/entity/Dict.class b/backend/target/classes/com/bycrm/entity/Dict.class
new file mode 100644
index 0000000..a60cf8a
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/Dict.class differ
diff --git a/backend/target/classes/com/bycrm/entity/DictItem.class b/backend/target/classes/com/bycrm/entity/DictItem.class
new file mode 100644
index 0000000..fba6e0b
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/DictItem.class differ
diff --git a/backend/target/classes/com/bycrm/entity/OperationLog.class b/backend/target/classes/com/bycrm/entity/OperationLog.class
new file mode 100644
index 0000000..92274d7
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/OperationLog.class differ
diff --git a/backend/target/classes/com/bycrm/entity/Report.class b/backend/target/classes/com/bycrm/entity/Report.class
new file mode 100644
index 0000000..88d0b08
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/Report.class differ
diff --git a/backend/target/classes/com/bycrm/entity/School.class b/backend/target/classes/com/bycrm/entity/School.class
new file mode 100644
index 0000000..5f07b2d
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/School.class differ
diff --git a/backend/target/classes/com/bycrm/entity/SystemConfig.class b/backend/target/classes/com/bycrm/entity/SystemConfig.class
new file mode 100644
index 0000000..51c4e73
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/SystemConfig.class differ
diff --git a/backend/target/classes/com/bycrm/entity/User.class b/backend/target/classes/com/bycrm/entity/User.class
new file mode 100644
index 0000000..51d2324
Binary files /dev/null and b/backend/target/classes/com/bycrm/entity/User.class differ
diff --git a/backend/target/classes/com/bycrm/exception/BusinessException.class b/backend/target/classes/com/bycrm/exception/BusinessException.class
new file mode 100644
index 0000000..c3944df
Binary files /dev/null and b/backend/target/classes/com/bycrm/exception/BusinessException.class differ
diff --git a/backend/target/classes/com/bycrm/exception/GlobalExceptionHandler.class b/backend/target/classes/com/bycrm/exception/GlobalExceptionHandler.class
new file mode 100644
index 0000000..0c18c6f
Binary files /dev/null and b/backend/target/classes/com/bycrm/exception/GlobalExceptionHandler.class differ
diff --git a/backend/target/classes/com/bycrm/mapper/CustomerMapper.class b/backend/target/classes/com/bycrm/mapper/CustomerMapper.class
new file mode 100644
index 0000000..71362f3
Binary files /dev/null and b/backend/target/classes/com/bycrm/mapper/CustomerMapper.class differ
diff --git a/backend/target/classes/com/bycrm/mapper/DealerMapper.class b/backend/target/classes/com/bycrm/mapper/DealerMapper.class
new file mode 100644
index 0000000..a205076
Binary files /dev/null and b/backend/target/classes/com/bycrm/mapper/DealerMapper.class differ
diff --git a/backend/target/classes/com/bycrm/mapper/ReportMapper.class b/backend/target/classes/com/bycrm/mapper/ReportMapper.class
new file mode 100644
index 0000000..9225910
Binary files /dev/null and b/backend/target/classes/com/bycrm/mapper/ReportMapper.class differ
diff --git a/backend/target/classes/com/bycrm/mapper/SchoolMapper.class b/backend/target/classes/com/bycrm/mapper/SchoolMapper.class
new file mode 100644
index 0000000..20a2a3c
Binary files /dev/null and b/backend/target/classes/com/bycrm/mapper/SchoolMapper.class differ
diff --git a/backend/target/classes/com/bycrm/mapper/SystemConfigMapper.class b/backend/target/classes/com/bycrm/mapper/SystemConfigMapper.class
new file mode 100644
index 0000000..51f35bd
Binary files /dev/null and b/backend/target/classes/com/bycrm/mapper/SystemConfigMapper.class differ
diff --git a/backend/target/classes/com/bycrm/mapper/UserMapper.class b/backend/target/classes/com/bycrm/mapper/UserMapper.class
new file mode 100644
index 0000000..24801cc
Binary files /dev/null and b/backend/target/classes/com/bycrm/mapper/UserMapper.class differ
diff --git a/backend/target/classes/com/bycrm/service/CustomerService.class b/backend/target/classes/com/bycrm/service/CustomerService.class
new file mode 100644
index 0000000..8ecdb26
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/CustomerService.class differ
diff --git a/backend/target/classes/com/bycrm/service/DealerService.class b/backend/target/classes/com/bycrm/service/DealerService.class
new file mode 100644
index 0000000..4ebb954
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/DealerService.class differ
diff --git a/backend/target/classes/com/bycrm/service/ReportService.class b/backend/target/classes/com/bycrm/service/ReportService.class
new file mode 100644
index 0000000..0cd9b5e
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/ReportService.class differ
diff --git a/backend/target/classes/com/bycrm/service/SchoolService.class b/backend/target/classes/com/bycrm/service/SchoolService.class
new file mode 100644
index 0000000..168bd68
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/SchoolService.class differ
diff --git a/backend/target/classes/com/bycrm/service/SystemConfigService.class b/backend/target/classes/com/bycrm/service/SystemConfigService.class
new file mode 100644
index 0000000..2c0bdda
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/SystemConfigService.class differ
diff --git a/backend/target/classes/com/bycrm/service/UserService.class b/backend/target/classes/com/bycrm/service/UserService.class
new file mode 100644
index 0000000..11b2cb8
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/UserService.class differ
diff --git a/backend/target/classes/com/bycrm/service/impl/CustomerServiceImpl.class b/backend/target/classes/com/bycrm/service/impl/CustomerServiceImpl.class
new file mode 100644
index 0000000..d787c74
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/impl/CustomerServiceImpl.class differ
diff --git a/backend/target/classes/com/bycrm/service/impl/DealerServiceImpl.class b/backend/target/classes/com/bycrm/service/impl/DealerServiceImpl.class
new file mode 100644
index 0000000..6992b6e
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/impl/DealerServiceImpl.class differ
diff --git a/backend/target/classes/com/bycrm/service/impl/ReportServiceImpl.class b/backend/target/classes/com/bycrm/service/impl/ReportServiceImpl.class
new file mode 100644
index 0000000..5d2cf39
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/impl/ReportServiceImpl.class differ
diff --git a/backend/target/classes/com/bycrm/service/impl/SchoolServiceImpl.class b/backend/target/classes/com/bycrm/service/impl/SchoolServiceImpl.class
new file mode 100644
index 0000000..cc7ecd8
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/impl/SchoolServiceImpl.class differ
diff --git a/backend/target/classes/com/bycrm/service/impl/SystemConfigServiceImpl.class b/backend/target/classes/com/bycrm/service/impl/SystemConfigServiceImpl.class
new file mode 100644
index 0000000..3e6bead
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/impl/SystemConfigServiceImpl.class differ
diff --git a/backend/target/classes/com/bycrm/service/impl/UserServiceImpl.class b/backend/target/classes/com/bycrm/service/impl/UserServiceImpl.class
new file mode 100644
index 0000000..9f1c9af
Binary files /dev/null and b/backend/target/classes/com/bycrm/service/impl/UserServiceImpl.class differ
diff --git a/backend/target/classes/com/bycrm/task/ReportExpireTask.class b/backend/target/classes/com/bycrm/task/ReportExpireTask.class
new file mode 100644
index 0000000..4826394
Binary files /dev/null and b/backend/target/classes/com/bycrm/task/ReportExpireTask.class differ
diff --git a/backend/target/classes/com/bycrm/util/CheckExcelStructure$1.class b/backend/target/classes/com/bycrm/util/CheckExcelStructure$1.class
new file mode 100644
index 0000000..ea9ffdc
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/CheckExcelStructure$1.class differ
diff --git a/backend/target/classes/com/bycrm/util/CheckExcelStructure.class b/backend/target/classes/com/bycrm/util/CheckExcelStructure.class
new file mode 100644
index 0000000..538f1b6
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/CheckExcelStructure.class differ
diff --git a/backend/target/classes/com/bycrm/util/ImportSchools.class b/backend/target/classes/com/bycrm/util/ImportSchools.class
new file mode 100644
index 0000000..f7622d7
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/ImportSchools.class differ
diff --git a/backend/target/classes/com/bycrm/util/JwtUtil.class b/backend/target/classes/com/bycrm/util/JwtUtil.class
new file mode 100644
index 0000000..87a22e8
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/JwtUtil.class differ
diff --git a/backend/target/classes/com/bycrm/util/PasswordTest.class b/backend/target/classes/com/bycrm/util/PasswordTest.class
new file mode 100644
index 0000000..aabb7c9
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/PasswordTest.class differ
diff --git a/backend/target/classes/com/bycrm/util/SchoolImporter$1.class b/backend/target/classes/com/bycrm/util/SchoolImporter$1.class
new file mode 100644
index 0000000..5dc27da
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/SchoolImporter$1.class differ
diff --git a/backend/target/classes/com/bycrm/util/SchoolImporter.class b/backend/target/classes/com/bycrm/util/SchoolImporter.class
new file mode 100644
index 0000000..ce914e6
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/SchoolImporter.class differ
diff --git a/backend/target/classes/com/bycrm/util/SecureKeyGenerator.class b/backend/target/classes/com/bycrm/util/SecureKeyGenerator.class
new file mode 100644
index 0000000..3a70272
Binary files /dev/null and b/backend/target/classes/com/bycrm/util/SecureKeyGenerator.class differ
diff --git a/backend/target/classes/com/bycrm/vo/CustomerVO.class b/backend/target/classes/com/bycrm/vo/CustomerVO.class
new file mode 100644
index 0000000..3f7c3ad
Binary files /dev/null and b/backend/target/classes/com/bycrm/vo/CustomerVO.class differ
diff --git a/backend/target/classes/com/bycrm/vo/ReportVO.class b/backend/target/classes/com/bycrm/vo/ReportVO.class
new file mode 100644
index 0000000..3433359
Binary files /dev/null and b/backend/target/classes/com/bycrm/vo/ReportVO.class differ
diff --git a/backend/target/classes/com/bycrm/vo/SchoolVO.class b/backend/target/classes/com/bycrm/vo/SchoolVO.class
new file mode 100644
index 0000000..b63a2bf
Binary files /dev/null and b/backend/target/classes/com/bycrm/vo/SchoolVO.class differ
diff --git a/backend/target/classes/com/bycrm/vo/UserInfoVO.class b/backend/target/classes/com/bycrm/vo/UserInfoVO.class
new file mode 100644
index 0000000..f706abc
Binary files /dev/null and b/backend/target/classes/com/bycrm/vo/UserInfoVO.class differ
diff --git a/backend/target/classes/mapper/CustomerMapper.xml b/backend/target/classes/mapper/CustomerMapper.xml
new file mode 100644
index 0000000..29f2097
--- /dev/null
+++ b/backend/target/classes/mapper/CustomerMapper.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO crm_customer (name, phone, address, industry, status)
+ VALUES (#{name}, #{phone}, #{address}, #{industry}, #{status})
+
+
+
+ UPDATE crm_customer
+
+ name = #{name},
+ phone = #{phone},
+ address = #{address},
+ industry = #{industry},
+ status = #{status},
+
+ WHERE id = #{id}
+
+
+
+ DELETE FROM crm_customer WHERE id = #{id}
+
+
+
+
+
diff --git a/backend/target/classes/mapper/DealerMapper.xml b/backend/target/classes/mapper/DealerMapper.xml
new file mode 100644
index 0000000..52fe218
--- /dev/null
+++ b/backend/target/classes/mapper/DealerMapper.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO crm_dealer (name, code, contact_person, contact_phone, email, status)
+ VALUES (#{name}, #{code}, #{contactPerson}, #{contactPhone}, #{email}, #{status})
+
+
+
+ UPDATE crm_dealer
+
+ name = #{name},
+ code = #{code},
+ contact_person = #{contactPerson},
+ contact_phone = #{contactPhone},
+ email = #{email},
+ status = #{status},
+
+ WHERE id = #{id}
+
+
+
+ DELETE FROM crm_dealer WHERE id = #{id}
+
+
+
+
+
diff --git a/backend/target/classes/mapper/ReportMapper.xml b/backend/target/classes/mapper/ReportMapper.xml
new file mode 100644
index 0000000..ca15377
--- /dev/null
+++ b/backend/target/classes/mapper/ReportMapper.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO crm_report (dealer_id, customer_id, description, status, protect_start_date, protect_end_date)
+ VALUES (#{dealerId}, #{customerId}, #{description}, #{status}, #{protectStartDate}, #{protectEndDate})
+
+
+
+ UPDATE crm_report
+
+ status = #{status},
+ reject_reason = #{rejectReason},
+ protect_start_date = #{protectStartDate},
+ protect_end_date = #{protectEndDate},
+
+ WHERE id = #{id}
+
+
+
+ DELETE FROM crm_report WHERE id = #{id}
+
+
+
+
+
+ UPDATE crm_report
+ SET status = 3
+ WHERE id IN
+
+ #{id}
+
+
+
+
+
+
+
+
diff --git a/backend/target/classes/mapper/SchoolMapper.xml b/backend/target/classes/mapper/SchoolMapper.xml
new file mode 100644
index 0000000..05826e2
--- /dev/null
+++ b/backend/target/classes/mapper/SchoolMapper.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO crm_school (school_code, school_name, location, created_at, updated_at)
+ VALUES (#{schoolCode}, #{schoolName}, #{location}, #{createdAt}, #{updatedAt})
+
+
+
+ INSERT INTO crm_school (school_code, school_name, location, created_at, updated_at)
+ VALUES
+
+ (#{item.schoolCode}, #{item.schoolName}, #{item.location}, #{item.createdAt}, #{item.updatedAt})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/target/classes/mapper/SystemConfigMapper.xml b/backend/target/classes/mapper/SystemConfigMapper.xml
new file mode 100644
index 0000000..d0545e3
--- /dev/null
+++ b/backend/target/classes/mapper/SystemConfigMapper.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UPDATE crm_system_config
+ SET config_value = #{configValue},
+ updated_at = NOW()
+ WHERE config_key = #{configKey}
+
+
+
+
+ UPDATE crm_system_config
+ SET config_value = #{config.configValue},
+ updated_at = NOW()
+ WHERE config_key = #{config.configKey}
+
+
+
+
diff --git a/backend/target/classes/mapper/UserMapper.xml b/backend/target/classes/mapper/UserMapper.xml
new file mode 100644
index 0000000..efc1d3c
--- /dev/null
+++ b/backend/target/classes/mapper/UserMapper.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO crm_user (username, password, real_name, dealer_id, role, status)
+ VALUES (#{username}, #{password}, #{realName}, #{dealerId}, #{role}, #{status})
+
+
+
+ UPDATE crm_user
+
+ password = #{password},
+ real_name = #{realName},
+ dealer_id = #{dealerId},
+ role = #{role},
+ status = #{status},
+
+ WHERE id = #{id}
+
+
+
+ DELETE FROM crm_user WHERE id = #{id}
+
+
+
diff --git a/backend/target/maven-status/maven-compiler-plugin/compile/default-cli/createdFiles.lst b/backend/target/maven-status/maven-compiler-plugin/compile/default-cli/createdFiles.lst
new file mode 100644
index 0000000..e69de29
diff --git a/backend/target/maven-status/maven-compiler-plugin/compile/default-cli/inputFiles.lst b/backend/target/maven-status/maven-compiler-plugin/compile/default-cli/inputFiles.lst
new file mode 100644
index 0000000..9bec953
--- /dev/null
+++ b/backend/target/maven-status/maven-compiler-plugin/compile/default-cli/inputFiles.lst
@@ -0,0 +1,46 @@
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\DealerDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\DealerController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\ReportAuditDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\ReportServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\LoginDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\AuthController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\util\JwtUtil.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\PageQuery.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\common\Result.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Dict.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\exception\BusinessException.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\WebMvcConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\vo\UserInfoVO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\DealerMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\UserMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Report.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\common\Constants.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\common\PageResult.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\CorsConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\ReportController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Customer.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\UserService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\SwaggerConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\ByCrmApplication.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\ReportService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\User.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Dealer.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\DealerService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\ReportMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\DealerServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\OperationLog.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\vo\CustomerVO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\DictItem.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\exception\GlobalExceptionHandler.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\vo\ReportVO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\CustomerService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\MyBatisConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\CustomerDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\ReportDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\CustomerMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\CustomerController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\CustomerServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\annotations\AuthRequired.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\AuthInterceptor.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\UserServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\task\ReportExpireTask.java
diff --git a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..28731ab
--- /dev/null
+++ b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1 @@
+com\bycrm\util\PasswordTest.class
diff --git a/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..0f5e831
--- /dev/null
+++ b/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,47 @@
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\DealerDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\DealerController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\ReportAuditDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\ReportServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\LoginDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\AuthController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\util\JwtUtil.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\PageQuery.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\common\Result.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Dict.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\exception\BusinessException.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\WebMvcConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\vo\UserInfoVO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\DealerMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\UserMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Report.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\common\Constants.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\common\PageResult.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\CorsConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\ReportController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\util\PasswordTest.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Customer.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\UserService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\SwaggerConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\ByCrmApplication.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\ReportService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\User.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\Dealer.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\DealerService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\ReportMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\DealerServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\OperationLog.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\vo\CustomerVO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\entity\DictItem.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\exception\GlobalExceptionHandler.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\vo\ReportVO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\CustomerService.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\MyBatisConfig.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\CustomerDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\dto\ReportDTO.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\mapper\CustomerMapper.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\controller\CustomerController.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\CustomerServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\annotations\AuthRequired.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\config\AuthInterceptor.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\service\impl\UserServiceImpl.java
+E:\boyun-workspace\by-crm\backend\src\main\java\com\bycrm\task\ReportExpireTask.java