Browse Source

Update project structure and files

master
Trae AI 2 months ago
parent
commit
63a1dbd405
  1. 81
      CREATE_OPTIMIZATION_INDEXES.sql
  2. 224
      DEPLOYMENT_GUIDE.md
  3. BIN
      DL.war
  4. 377
      INFORMATION_TRACKING_SCHEME.md
  5. 184
      LATEST_DEPLOYMENT_GUIDE.md
  6. 146
      MAPPER_SERVICE_UPDATE_GUIDE.txt
  7. 163
      MAPPER_XML_DEPLOYMENT_GUIDE.txt
  8. 280
      PERFORMANCE_OPTIMIZATION_GUIDE.md
  9. 118
      TOMCAT_DEPLOYMENT_STEPS.txt
  10. 123
      UPDATE_DEPLOYMENT_GUIDE.txt
  11. 47
      deploy_optimization_changes.bat
  12. 59
      deploy_to_tomcat.bat
  13. 56
      deploy_to_tomcat.sh
  14. 69
      deploy_with_optimization.bat
  15. 295
      mvnw
  16. 189
      mvnw.cmd
  17. 5
      pom.xml
  18. 2
      src/main/java/com/example/web/WebApplication.java
  19. 25
      src/main/java/com/example/web/config/CharacterEncodingFilterConfig.java
  20. 6
      src/main/java/com/example/web/config/MyBatisConfig.java
  21. 38
      src/main/java/com/example/web/config/WebSocketConfig.java
  22. 1031
      src/main/java/com/example/web/controller/CustomerController.java
  23. 263
      src/main/java/com/example/web/controller/CustomerRestController.java
  24. 67
      src/main/java/com/example/web/controller/CustomerWebSocketController.java
  25. 1170
      src/main/java/com/example/web/controller/EnterpriseController.java
  26. 219
      src/main/java/com/example/web/controller/FollowupController.java
  27. 183
      src/main/java/com/example/web/controller/InformationTraController.java
  28. 70
      src/main/java/com/example/web/controller/LoginController.java
  29. 28
      src/main/java/com/example/web/controller/PersonnelController.java
  30. 72
      src/main/java/com/example/web/controller/PoolCustomerController.java
  31. 996
      src/main/java/com/example/web/controller/SupplyCustomerController.java
  32. 63
      src/main/java/com/example/web/controller/SupplyCustomerRecycleController.java
  33. 1107
      src/main/java/com/example/web/controller/SupplyEnterpriseController.java
  34. 73
      src/main/java/com/example/web/controller/SupplyPoolCustomerController.java
  35. 22
      src/main/java/com/example/web/dao/LoginMapper.java
  36. 20
      src/main/java/com/example/web/dao/PersonnelMapper.java
  37. 53
      src/main/java/com/example/web/dto/EnterpriseInfoDTO.java
  38. 80
      src/main/java/com/example/web/dto/ManagerAuthInfo.java
  39. 55
      src/main/java/com/example/web/dto/NotificationDTO.java
  40. 331
      src/main/java/com/example/web/dto/UnifiedCustomerDTO.java
  41. 432
      src/main/java/com/example/web/dto/UserProductCartDTO.java
  42. 12
      src/main/java/com/example/web/entity/Contacts.java
  43. 66
      src/main/java/com/example/web/entity/CustomerData.java
  44. 52
      src/main/java/com/example/web/entity/Favorites.java
  45. 27
      src/main/java/com/example/web/entity/InformationTra.java
  46. 36
      src/main/java/com/example/web/entity/Login.java
  47. 23
      src/main/java/com/example/web/entity/Notification.java
  48. 28
      src/main/java/com/example/web/entity/Personnel.java
  49. 12
      src/main/java/com/example/web/entity/Users.java
  50. 24
      src/main/java/com/example/web/entity/UsersContacts.java
  51. 5
      src/main/java/com/example/web/mapper/ContactsMapper.java
  52. 136
      src/main/java/com/example/web/mapper/CustomerMapper.java
  53. 24
      src/main/java/com/example/web/mapper/EnterpriseMapper.java
  54. 18
      src/main/java/com/example/web/mapper/InformationTraMapper.java
  55. 22
      src/main/java/com/example/web/mapper/LoginMapper.java
  56. 21
      src/main/java/com/example/web/mapper/PersonnelMapper.java
  57. 10
      src/main/java/com/example/web/mapper/SupplyCart_itemsMapper.java
  58. 24
      src/main/java/com/example/web/mapper/SupplyContactsMapper.java
  59. 24
      src/main/java/com/example/web/mapper/SupplyEnterpriseMapper.java
  60. 28
      src/main/java/com/example/web/mapper/SupplyManagersMapper.java
  61. 12
      src/main/java/com/example/web/mapper/SupplyProductsMapper.java
  62. 64
      src/main/java/com/example/web/mapper/SupplyUsersManagementsMapper.java
  63. 270
      src/main/java/com/example/web/mapper/SupplyUsersMapper.java
  64. 63
      src/main/java/com/example/web/mapper/UsersManagementsMapper.java
  65. 276
      src/main/java/com/example/web/mapper/UsersMapper.java
  66. 145
      src/main/java/com/example/web/mapper/WechatCustomerMapper.java
  67. 203
      src/main/java/com/example/web/scheduler/CustomerStatusScheduler.java
  68. 1364
      src/main/java/com/example/web/service/CustomerService.java
  69. 501
      src/main/java/com/example/web/service/EnterpriseService.java
  70. 65
      src/main/java/com/example/web/service/FollowUpService.java
  71. 105
      src/main/java/com/example/web/service/InformationTraService.java
  72. 265
      src/main/java/com/example/web/service/LoginService.java
  73. 48
      src/main/java/com/example/web/service/NotificationService.java
  74. 15
      src/main/java/com/example/web/service/PersonnelService.java
  75. 818
      src/main/java/com/example/web/service/PoolCustomerService.java
  76. 10
      src/main/java/com/example/web/service/RootService.java
  77. 204
      src/main/java/com/example/web/service/SupplyCustomerRecycleService.java
  78. 1330
      src/main/java/com/example/web/service/SupplyCustomerService.java
  79. 515
      src/main/java/com/example/web/service/SupplyEnterpriseService.java
  80. 816
      src/main/java/com/example/web/service/SupplyPoolCustomerService.java
  81. 8
      src/main/java/com/example/web/service/SupplyUserService.java
  82. 13
      src/main/java/com/example/web/service/UserService.java
  83. 1708
      src/main/java/com/example/web/service/impl/CustomerServiceImpl.java
  84. 63
      src/main/java/com/example/web/service/impl/LoginServiceImpl.java
  85. 27
      src/main/java/com/example/web/service/impl/PersonnelServiceImpl.java
  86. 138
      src/main/java/com/example/web/task/CustomerStatusTask.java
  87. 121
      src/main/java/com/example/web/utils/CustomerTraceUtil.java
  88. 70
      src/main/resources/application.yaml
  89. 21
      src/main/resources/mapper/Cart_itemsMapper.xml
  90. 160
      src/main/resources/mapper/CustomerMapper.xml
  91. 51
      src/main/resources/mapper/InformationTraMapper.xml
  92. 38
      src/main/resources/mapper/LoginMapper.xml
  93. 27
      src/main/resources/mapper/PersonnelMapper.xml
  94. 26
      src/main/resources/mapper/ProductsMapper.xml
  95. 42
      src/main/resources/mapper/RootdbMapper.xml
  96. 21
      src/main/resources/mapper/SupplyCart_itemsMapper.xml
  97. 63
      src/main/resources/mapper/SupplyContactsMapper.xml
  98. 79
      src/main/resources/mapper/SupplyEnterpriseMapper.xml
  99. 79
      src/main/resources/mapper/SupplyManagersMapper.xml
  100. 26
      src/main/resources/mapper/SupplyProductsMapper.xml

81
CREATE_OPTIMIZATION_INDEXES.sql

@ -1,81 +0,0 @@
-- 数据库索引创建脚本
-- 此脚本用于优化应用性能,为常用查询字段创建索引
-- ============================================
-- 用户相关表索引
-- ============================================
-- users表索引
CREATE INDEX IF NOT EXISTS idx_users_userId ON users(userId);
CREATE INDEX IF NOT EXISTS idx_users_phoneNumber ON users(phoneNumber);
CREATE INDEX IF NOT EXISTS idx_users_type ON users(type);
CREATE INDEX IF NOT EXISTS idx_users_level ON users(level);
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at);
-- ============================================
-- 管理员相关表索引
-- ============================================
-- managers表索引
CREATE INDEX IF NOT EXISTS idx_managers_id ON managers(id);
CREATE INDEX IF NOT EXISTS idx_managers_userName ON managers(userName);
CREATE INDEX IF NOT EXISTS idx_managers_managerId ON managers(managerId);
CREATE INDEX IF NOT EXISTS idx_managers_organization ON managers(organization);
CREATE INDEX IF NOT EXISTS idx_managers_managerdepartment ON managers(managerdepartment);
-- ============================================
-- 用户管理相关表索引
-- ============================================
-- usermanagements表索引
CREATE INDEX IF NOT EXISTS idx_usermanagements_userId ON usermanagements(userId);
CREATE INDEX IF NOT EXISTS idx_usermanagements_managerId ON usermanagements(managerId);
CREATE INDEX IF NOT EXISTS idx_usermanagements_userName ON usermanagements(userName);
CREATE INDEX IF NOT EXISTS idx_usermanagements_organization ON usermanagements(organization);
CREATE INDEX IF NOT EXISTS idx_usermanagements_managerdepartment ON usermanagements(managerdepartment);
-- ============================================
-- 产品相关表索引
-- ============================================
-- products表索引
CREATE INDEX IF NOT EXISTS idx_products_sellerId ON products(sellerId);
CREATE INDEX IF NOT EXISTS idx_products_created_at ON products(created_at);
-- ============================================
-- 购物车相关表索引
-- ============================================
-- cart_items表索引
CREATE INDEX IF NOT EXISTS idx_cart_items_userId ON cart_items(userId);
CREATE INDEX IF NOT EXISTS idx_cart_items_productId ON cart_items(productId);
-- ============================================
-- 企业相关表索引
-- ============================================
-- enterprise表索引 (假设存在)
CREATE INDEX IF NOT EXISTS idx_enterprise_id ON enterprise(id);
CREATE INDEX IF NOT EXISTS idx_enterprise_name ON enterprise(name);
-- ============================================
-- 联系方式相关表索引
-- ============================================
-- contacts表索引
CREATE INDEX IF NOT EXISTS idx_contacts_userId ON contacts(userId);
-- ============================================
-- 注意事项
-- ============================================
-- 1. 此脚本使用IF NOT EXISTS语法,可重复执行而不会报错
-- 2. 索引创建会占用额外的磁盘空间,提高写操作开销,但显著提升查询性能
-- 3. 建议在低峰期执行此脚本
-- 4. 执行后建议监控应用性能,确认优化效果
-- 5. 对于MySQL数据库,索引创建命令可能略有不同
-- ============================================
-- MySQL版本的部分索引创建语法示例(仅供参考)
-- ALTER TABLE users ADD INDEX idx_users_userId (userId);
-- ALTER TABLE users ADD INDEX idx_users_phoneNumber (phoneNumber);
-- 以此类推...

224
DEPLOYMENT_GUIDE.md

@ -1,224 +0,0 @@
# Spring Boot应用部署到Tomcat 10.1.48指南(含性能优化)
## 准备工作
1. **确认构建产物**
- 已生成WAR文件:`web-0.0.1-SNAPSHOT.war`,位于`target`目录下
- 确认包含最新性能优化:SQL查询优化、分页功能、二级缓存配置
2. **Tomcat环境要求**
- Tomcat版本:10.1.48(Jakarta EE 10兼容)
- JDK版本:17或更高(与项目`pom.xml`中配置的Java版本一致)
- 数据库:需创建优化索引(详见数据库索引优化部分)
## 部署步骤
### 1. 准备部署文件
```bash
# 将WAR文件重命名为DL.war(与context-path一致)
# 注意:在Windows命令提示符中使用
copy target\web-0.0.1-SNAPSHOT.war target\DL.war
# 或者在PowerShell中使用
# Copy-Item -Path "target\web-0.0.1-SNAPSHOT.war" -Destination "target\DL.war"
# 在Linux/Mac终端中使用(注意转义括号)
# cp d:\java\project\web\(8)\web\target\web-0.0.1-SNAPSHOT.war d:\java\project\web\(8)\web\target\DL.war
# 或者使用相对路径避免路径转义问题
# cd d:\java\project\web(8)\web && cp target\web-0.0.1-SNAPSHOT.war target\DL.war
```
### 2. 上传WAR文件到服务器
使用SFTP或SCP工具将`DL.war`文件上传到服务器的Tomcat目录:
```bash
# 示例:使用scp上传(注意处理路径中的括号)
# 方法1:转义括号
scp d:\java\project\web\(8)\web\target\DL.war user@your-server:/opt/tomcat/webapps/
# 方法2:使用相对路径(推荐)
cd d:\java\project\web(8)\web
scp target\DL.war user@your-server:/opt/tomcat/webapps/
# 方法3:使用引号包裹路径(在某些终端中有效)
scp "d:\java\project\web(8)\web\target\DL.war" user@your-server:/opt/tomcat/webapps/
```
### 3. 确保Tomcat目录权限正确
```bash
# 登录服务器后执行
cd /opt/tomcat
# 确保tomcat用户对webapps目录有写权限
chown -R tomcat:tomcat webapps/
chmod -R 755 webapps/
```
### 4. 配置Tomcat(可选但推荐)
#### 4.1 配置context.xml(解决可能的内存泄漏问题)
编辑`/opt/tomcat/conf/context.xml`文件,添加以下配置:
```xml
<Context antiResourceLocking="true" antiJARLocking="true">
<!-- 现有配置保持不变 -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
</Context>
```
#### 4.2 调整Tomcat内存配置
编辑`/opt/tomcat/bin/setenv.sh`(如果不存在则创建):
```bash
#!/bin/bash
# 为Tomcat设置适当的内存
JAVA_OPTS="-Xms512m -Xmx1024m -XX:MaxPermSize=256m"
# 添加Tomcat 10兼容性参数
JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.lang=ALL-UNNAMED"
```
给脚本添加执行权限:
```bash
chmod +x /opt/tomcat/bin/setenv.sh
```
### 5. 启动或重启Tomcat
```bash
# 切换到Tomcat的bin目录
cd /opt/tomcat/bin
# 停止Tomcat(如果正在运行)
./shutdown.sh
# 等待Tomcat完全停止(约30秒)
# 启动Tomcat
./startup.sh
```
### 6. 验证部署
1. **检查Tomcat日志**
```bash
tail -f /opt/tomcat/logs/catalina.out
```
2. **访问应用**
- 应用应该可以通过以下URL访问:`http://your-server-ip:8080/DL`
- 登录页面:`http://your-server-ip:8080/DL/loginmm.html`
## 常见问题排查
### 1. 端口冲突
如果Tomcat的8080端口已被占用,修改`/opt/tomcat/conf/server.xml`中的端口配置:
```xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
```
### 2. 数据库连接问题
确保数据库服务器允许来自Tomcat服务器IP的连接。检查应用配置中的数据库连接URL是否正确。
### 3. 类加载问题(Tomcat 10特有)
Tomcat 10使用Jakarta EE,所有`javax.*`包已改为`jakarta.*`。如果出现类找不到的错误:
- 检查是否有冲突的JAR包在WEB-INF/lib中
- 确保使用的是支持Jakarta EE的依赖版本
### 4. 内存溢出
如果出现内存溢出错误,增加Tomcat的内存分配,修改`setenv.sh`文件:
```bash
JAVA_OPTS="-Xms1024m -Xmx2048m -XX:MaxPermSize=512m"
```
## 数据库索引优化(重要)
在部署新版本前,请在数据库服务器上执行以下索引创建脚本,以提升查询性能:
```sql
-- 执行CREATE_OPTIMIZATION_INDEXES.sql文件中的脚本
-- 在服务器上执行:
source /path/to/CREATE_OPTIMIZATION_INDEXES.sql
-- 或直接复制脚本内容执行
-- 1. 为managers表创建索引
CREATE INDEX idx_managers_enterprise_id ON managers(enterprise_id);
CREATE INDEX idx_managers_user_name ON managers(user_name);
-- 2. 为users表创建索引
CREATE INDEX idx_users_user_id ON users(user_id);
CREATE INDEX idx_users_user_name ON users(user_name);
CREATE INDEX idx_users_status ON users(status);
-- 3. 为usermanagements表创建索引
CREATE INDEX idx_usermanagements_user_id ON usermanagements(user_id);
CREATE INDEX idx_usermanagements_role_id ON usermanagements(role_id);
CREATE INDEX idx_usermanagements_permission_level ON usermanagements(permission_level);
```
## 应用更新流程
1. **执行数据库索引优化**:按照上方数据库索引优化部分执行索引创建脚本
2. **停止Tomcat**:`./shutdown.sh`
3. **备份旧数据**:`cp -r /opt/tomcat/webapps/DL /path/to/backup/`
4. **删除旧的WAR文件和解压目录**:`rm -rf /opt/tomcat/webapps/DL*`
5. **上传新的WAR文件**
6. **启动Tomcat**:`./startup.sh`
7. **监控日志确认部署成功**:`tail -f /opt/tomcat/logs/catalina.out`
## 性能优化验证
部署完成后,请执行以下验证步骤确认性能优化效果:
1. **验证分页功能**
- 访问负责人管理页面,确认列表显示已启用分页
- 验证翻页功能正常,数据加载速度提升
2. **验证SQL查询性能**
- 执行常用查询操作,确认响应时间明显改善
- 监控数据库慢查询日志,检查是否有新的慢查询
3. **验证缓存效果**
- 连续访问相同数据页面,确认第二次访问速度更快
## 回滚方案
如遇部署问题,请按以下步骤回滚:
1. 停止Tomcat:`./shutdown.sh`
2. 删除新部署文件:`rm -rf /opt/tomcat/webapps/DL*`
3. 恢复备份:`cp -r /path/to/backup/DL /opt/tomcat/webapps/`
4. 恢复数据库索引(如需要)
5. 启动Tomcat:`./startup.sh`
## 注意事项
1. **备份**:部署前请备份现有应用数据和配置
2. **维护窗口**:选择低流量时段进行部署
3. **监控**:部署后密切监控应用性能和日志
4. **权限**:确保Tomcat用户对所有必要目录有正确权限
5. **性能监控**:部署后持续监控系统性能指标,必要时进行进一步优化
6. **索引维护**:定期检查数据库索引使用情况和碎片
---
部署时间:2024
版本:2.0(包含性能优化)
---
> 详细性能优化说明请参考:PERFORMANCE_OPTIMIZATION_GUIDE.md

BIN
DL.war

Binary file not shown.

377
INFORMATION_TRACKING_SCHEME.md

@ -1,377 +0,0 @@
# 客户信息跟踪系统解决方案
## 1. 需求分析
### 1.1 功能需求
- 前端记录业务员操作事件(查看详情、编辑、跟进)
- 获取当前账号信息(公司、部门、组织、角色、姓名)
- 根据电话号码在两个数据源中查询客户
- 将操作记录写入`informationtra`表
### 1.2 数据源要求
- `wechat`数据源:`users`表和`informationtra`表
- `primary`数据源:`contacts`表
### 1.3 数据流程
1. 前端传递操作事件、电话号码、事件类型
2. 后端获取当前用户认证信息
3. 根据电话号码查询两个数据源
4. 将查询结果与认证信息结合写入`informationtra`表
## 2. 数据库设计
### 2.1 现有表结构
```sql
CREATE TABLE `informationtra` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tracompany` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改者公司',
`tradepartment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改者部门',
`traorganization` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改者组织',
`trarole` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改者角色',
`trauserName` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改者名字',
`traassistant` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改协助人',
`userId` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户ID',
`operationEvent` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '操作事件',
`operationTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_userId` (`userId`),
KEY `idx_operationTime` (`operationTime`),
KEY `idx_trauserName` (`trauserName`),
CONSTRAINT `fk_informationtra_userId` FOREIGN KEY (`userId`) REFERENCES `users` (`userId`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='信息跟踪表';
```
## 3. 代码设计
### 3.1 实体类设计
#### InformationTra.java
```java
package com.example.web.entity;
import java.time.LocalDateTime;
public class InformationTra {
private Integer id;
private String tracompany;
private String tradepartment;
private String traorganization;
private String trarole;
private String trauserName;
private String traassistant;
private String userId;
private String operationEvent;
private LocalDateTime operationTime;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// Getters and Setters
// ...
}
```
### 3.2 Mapper设计
#### InformationTraMapper.java
```java
package com.example.web.mapper;
import com.example.web.entity.InformationTra;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface InformationTraMapper {
/**
* 插入操作记录
*/
int insertInformationTra(InformationTra informationTra);
/**
* 根据userId查询操作记录
*/
InformationTra selectByUserId(@Param("userId") String userId);
}
```
#### InformationTraMapper.xml
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.InformationTraMapper">
<resultMap id="informationTraMap" type="com.example.web.entity.InformationTra">
<id property="id" column="id"/>
<result property="tracompany" column="tracompany"/>
<result property="tradepartment" column="tradepartment"/>
<result property="traorganization" column="traorganization"/>
<result property="trarole" column="trarole"/>
<result property="trauserName" column="trauserName"/>
<result property="traassistant" column="traassistant"/>
<result property="userId" column="userId"/>
<result property="operationEvent" column="operationEvent"/>
<result property="operationTime" column="operationTime"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
</resultMap>
<insert id="insertInformationTra" parameterType="com.example.web.entity.InformationTra">
INSERT INTO informationtra (
tracompany, tradepartment, traorganization, trarole,
trauserName, traassistant, userId, operationEvent,
operationTime, created_at, updated_at
) VALUES (
#{tracompany}, #{tradepartment}, #{traorganization}, #{trarole},
#{trauserName}, #{traassistant}, #{userId}, #{operationEvent},
#{operationTime}, #{createdAt}, #{updatedAt}
)
</insert>
<select id="selectByUserId" resultMap="informationTraMap">
SELECT * FROM informationtra WHERE userId = #{userId}
</select>
</mapper>
```
### 3.3 Service设计
#### InformationTraService.java
```java
package com.example.web.service;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.entity.InformationTra;
import com.example.web.mapper.*;
import com.example.web.config.DynamicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class InformationTraService {
@Autowired
private InformationTraMapper informationTraMapper;
@Autowired
private UsersMapper usersMapper;
@Autowired
private ContactsMapper contactsMapper;
/**
* 记录操作事件
*/
public boolean recordOperationEvent(String phoneNumber, String operationEvent, ManagerAuthInfo authInfo) {
try {
// 1. 从两个数据源查询客户信息
String userId = null;
// 查询wechat数据源的users表
DynamicDataSource.setDataSourceKey("wechat");
com.example.web.entity.Users wechatUser = usersMapper.selectByPhoneNumber(phoneNumber);
if (wechatUser != null) {
userId = wechatUser.getUserId();
} else {
// 查询primary数据源的contacts表
DynamicDataSource.setDataSourceKey("primary");
com.example.web.entity.Contacts contact = contactsMapper.selectByPhoneNumber(phoneNumber);
if (contact != null) {
userId = contact.getId();
}
}
// 如果都没找到,返回失败
if (userId == null) {
return false;
}
// 2. 构造操作记录
InformationTra informationTra = new InformationTra();
informationTra.setTracompany(authInfo.getManagercompany());
informationTra.setTradepartment(authInfo.getManagerdepartment());
informationTra.setTraorganization(authInfo.getOrganization());
informationTra.setTrarole(authInfo.getRole());
informationTra.setTrauserName(authInfo.getUserName());
informationTra.setTraassistant(authInfo.getAssistant());
informationTra.setUserId(userId);
informationTra.setOperationEvent(operationEvent);
informationTra.setOperationTime(LocalDateTime.now());
informationTra.setCreatedAt(LocalDateTime.now());
informationTra.setUpdatedAt(LocalDateTime.now());
// 3. 写入wechat数据源的informationtra表
DynamicDataSource.setDataSourceKey("wechat");
int result = informationTraMapper.insertInformationTra(informationTra);
return result > 0;
} finally {
// 清除数据源标识
DynamicDataSource.clearDataSourceKey();
}
}
}
```
### 3.4 Controller设计
#### InformationTraController.java
```java
package com.example.web.controller;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.service.InformationTraService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/information-tracking")
public class InformationTraController {
@Autowired
private InformationTraService informationTraService;
/**
* 记录操作事件
*/
@PostMapping("/record")
public ResponseEntity<Map<String, Object>> recordOperationEvent(
@RequestBody Map<String, String> requestBody,
HttpServletRequest request) {
Map<String, Object> response = new HashMap<>();
try {
// 获取请求参数
String phoneNumber = requestBody.get("phoneNumber");
String operationEvent = requestBody.get("operationEvent");
// 验证参数
if (phoneNumber == null || phoneNumber.isEmpty() || operationEvent == null || operationEvent.isEmpty()) {
response.put("success", false);
response.put("message", "电话号码和操作事件不能为空");
return ResponseEntity.badRequest().body(response);
}
// 获取当前用户认证信息
String isSupplySideParam = request.getParameter("isSupplySide");
boolean isSupplySide = !"false".equalsIgnoreCase(isSupplySideParam);
ManagerAuthInfo authInfo = null;
if (isSupplySide) {
// 供应端获取认证信息的逻辑
authInfo = getManagerAuthInfoFromRequest(request, true);
} else {
// 销售端获取认证信息的逻辑
authInfo = getManagerAuthInfoFromRequest(request, false);
}
if (authInfo == null) {
response.put("success", false);
response.put("message", "用户未登录或认证信息缺失");
return ResponseEntity.status(401).body(response);
}
// 记录操作事件
boolean success = informationTraService.recordOperationEvent(phoneNumber, operationEvent, authInfo);
if (success) {
response.put("success", true);
response.put("message", "操作记录成功");
return ResponseEntity.ok(response);
} else {
response.put("success", false);
response.put("message", "未找到对应的客户信息");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
response.put("success", false);
response.put("message", "记录操作事件失败: " + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
/**
* 从请求中获取认证信息(复用现有逻辑)
*/
private ManagerAuthInfo getManagerAuthInfoFromRequest(HttpServletRequest request, boolean isSupplySide) {
// 复用SupplyCustomerController或CustomerController中的现有逻辑
// 这里需要根据实际情况实现
return null;
}
}
```
### 3.5 前端调用示例
```javascript
// 前端调用API示例
function recordOperation(phoneNumber, operationEvent) {
fetch('/api/information-tracking/record?isSupplySide=true', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
phoneNumber: phoneNumber,
operationEvent: operationEvent
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('操作记录成功');
} else {
console.error('操作记录失败:', data.message);
}
})
.catch(error => {
console.error('API调用失败:', error);
});
}
// 查看客户详情时调用
recordOperation('13800138000', '查看客户详情');
// 编辑客户信息后调用
recordOperation('13800138000', '修改客户信息');
// 跟进客户后调用
recordOperation('13800138000', '更新客户跟进');
```
## 4. 实现步骤
1. 创建InformationTra实体类
2. 创建InformationTraMapper接口和XML文件
3. 创建InformationTraService
4. 创建InformationTraController
5. 在前端页面添加API调用逻辑
6. 测试功能完整性
## 5. 注意事项
1. 确保数据源切换正确,避免数据查询错误
2. 验证电话号码格式,确保查询准确性
3. 处理并发情况,确保操作记录的完整性
4. 添加适当的日志记录,便于调试和监控
5. 考虑添加权限控制,确保只有授权用户可以调用API
## 6. 扩展建议
1. 添加操作记录查询功能,便于查看历史操作
2. 实现操作记录统计分析,提供数据可视化
3. 添加操作记录导出功能,支持Excel或PDF格式
4. 实现操作记录告警功能,对异常操作进行告警
5. 添加操作记录审计功能,满足合规要求

184
LATEST_DEPLOYMENT_GUIDE.md

@ -1,184 +0,0 @@
# Spring Boot应用最新部署指南(性能优化版)
## 版本信息
- **版本**: 2.0
- **日期**: 2024
- **更新内容**: 包含数据库性能优化、SQL查询优化、分页功能和缓存配置
## 一、优化内容概述
### 1.1 已完成的性能优化
#### SQL查询优化
- 将`SELECT *`查询替换为显式字段列表,减少数据传输量
- 优化复杂JOIN查询,减少不必要的表连接
#### 分页功能实现
- 新增`selectAllManagersWithPagination`方法实现分页查询
- 添加`getManagersCount`方法获取总数用于分页计算
#### 缓存配置
- 为ManagersMapper添加LRU二级缓存配置
- 提升重复查询性能
#### 数据库索引
- 为关键字段创建索引,提升查询性能
- 详细索引脚本位于`CREATE_OPTIMIZATION_INDEXES.sql`
## 二、部署前准备
### 2.1 确认构建产物
- **WAR文件**: `target\web-0.0.1-SNAPSHOT.war`
- **索引脚本**: `CREATE_OPTIMIZATION_INDEXES.sql`
- **验证文件**: `src\main\resources\mapper`目录下的优化后的XML文件
### 2.2 环境要求
- **Tomcat**: 10.1.48(Jakarta EE 10兼容)
- **JDK**: 17或更高
- **数据库**: 支持索引创建的关系型数据库
## 三、部署流程
### 3.1 本地准备部署文件
```bash
# 1. 清理并构建项目
mvn clean package -DskipTests
# 2. 重命名WAR文件
copy target\web-0.0.1-SNAPSHOT.war target\DL.war
```
### 3.2 数据库索引优化(关键步骤)
在数据库服务器上执行索引创建脚本:
```sql
-- 执行CREATE_OPTIMIZATION_INDEXES.sql文件
-- 或直接执行以下索引创建语句
-- 为managers表创建索引
CREATE INDEX idx_managers_enterprise_id ON managers(enterprise_id);
CREATE INDEX idx_managers_user_name ON managers(user_name);
-- 为users表创建索引
CREATE INDEX idx_users_user_id ON users(user_id);
CREATE INDEX idx_users_user_name ON users(user_name);
CREATE INDEX idx_users_status ON users(status);
-- 为usermanagements表创建索引
CREATE INDEX idx_usermanagements_user_id ON usermanagements(user_id);
CREATE INDEX idx_usermanagements_role_id ON usermanagements(role_id);
CREATE INDEX idx_usermanagements_permission_level ON usermanagements(permission_level);
```
### 3.3 上传部署文件
使用SFTP或SCP上传文件到服务器:
```bash
# 上传WAR文件
scp target\DL.war user@your-server:/opt/tomcat/webapps/
# 上传索引脚本(用于备份)
scp CREATE_OPTIMIZATION_INDEXES.sql user@your-server:/opt/tomcat/
```
### 3.4 服务器部署操作
登录服务器后执行:
```bash
# 1. 停止Tomcat
cd /opt/tomcat/bin
./shutdown.sh
# 2. 等待Tomcat完全停止(约30秒)
# 3. 备份旧应用
cp -r /opt/tomcat/webapps/DL /opt/tomcat/backup/
# 4. 删除旧部署文件
rm -rf /opt/tomcat/webapps/DL*
# 5. 确保权限正确
chown -R tomcat:tomcat /opt/tomcat/webapps/
chmod -R 755 /opt/tomcat/webapps/
# 6. 启动Tomcat
./startup.sh
# 7. 监控部署日志
tail -f /opt/tomcat/logs/catalina.out
```
## 四、部署后验证
### 4.1 功能验证
- **访问应用**: `http://your-server-ip:8080/DL`
- **登录验证**: 确认可以正常登录
- **数据操作**: 验证增删改查功能正常
### 4.2 性能优化验证
#### 分页功能验证
- 访问负责人管理页面
- 确认列表显示已启用分页
- 验证翻页功能正常工作
- 检查数据加载速度是否提升
#### SQL查询性能验证
- 执行常用查询操作
- 确认响应时间明显改善
- 监控数据库慢查询日志
#### 缓存效果验证
- 连续访问相同数据页面
- 确认第二次及后续访问速度更快
## 五、回滚方案
如遇部署问题,请按以下步骤回滚:
```bash
# 1. 停止Tomcat
cd /opt/tomcat/bin
./shutdown.sh
# 2. 删除新部署文件
rm -rf /opt/tomcat/webapps/DL*
# 3. 恢复备份
cp -r /opt/tomcat/backup/DL /opt/tomcat/webapps/
# 4. 启动Tomcat
./startup.sh
```
## 六、批处理部署脚本
项目中提供了Windows批处理脚本`deploy_with_optimization.bat`,可以帮助准备部署文件。在项目根目录执行:
```bash
# 运行批处理脚本
.\deploy_with_optimization.bat
```
## 七、注意事项
1. **备份**: 部署前务必备份应用数据和数据库
2. **维护窗口**: 选择低流量时段进行部署
3. **索引优化**: 必须执行数据库索引创建脚本
4. **性能监控**: 部署后持续监控系统性能
5. **日志检查**: 定期检查应用日志,及时发现问题
6. **权限问题**: 确保Tomcat用户对所有必要目录有正确权限
## 八、相关文档
- **性能优化详细说明**: `PERFORMANCE_OPTIMIZATION_GUIDE.md`
- **Mapper XML部署指南**: `MAPPER_XML_DEPLOYMENT_GUIDE.txt`
- **数据库索引脚本**: `CREATE_OPTIMIZATION_INDEXES.sql`
---
> 部署完成后,请务必执行性能优化验证步骤,确保所有优化措施生效。

146
MAPPER_SERVICE_UPDATE_GUIDE.txt

@ -1,146 +0,0 @@
# Mapper和Service代码更新部署指南
## 构建状态确认
✅ 所有Mapper和Service Java文件已成功编译为class文件
### Mapper文件(最后编译时间:2025/11/6 13:50)
- 位置:`target\classes\com\example\web\mapper`
- 文件数量:20个class文件
- 包含:Cart_itemsMapper、EnterpriseMapper、ManagersMapper等所有必要的Mapper接口实现
### Service文件(最后编译时间:2025/11/6 13:50)
- 位置:`target\classes\com\example\web\service`
- 文件数量:11个class文件
- 包含:CustomerService、EnterpriseService、LoginService等所有Service实现类
## 部署方案
### 方案一:完整WAR包部署(推荐)
这种方法最安全、最可靠,确保所有组件版本一致。
1. **准备WAR文件**
```bash
# 确保WAR文件已生成
ls -la target/DL.war
```
2. **上传WAR文件到服务器**
```bash
# 使用SCP上传文件
scp target/DL.war user@server:/path/to/tomcat/webapps/
```
3. **设置文件权限**
```bash
# 在服务器上执行
sudo chown tomcat:tomcat /path/to/tomcat/webapps/DL.war
sudo chmod 644 /path/to/tomcat/webapps/DL.war
```
4. **重启Tomcat服务**
```bash
# 在服务器上执行
sudo systemctl restart tomcat
# 或者
sudo service tomcat restart
# 或者直接使用Tomcat的脚本
/path/to/tomcat/bin/shutdown.sh
/path/to/tomcat/bin/startup.sh
```
### 方案二:仅更新Mapper和Service的class文件(风险较高)
如果只修改了Mapper和Service代码,可以只更新这些class文件,但要注意版本兼容性问题。
1. **在服务器上定位目标目录**
```bash
# 在服务器上执行,找到应用部署目录
cd /path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/
```
2. **备份当前文件**
```bash
# 在服务器上执行
mkdir -p ~/backup/mapper ~/backup/service
cp -r mapper/* ~/backup/mapper/
cp -r service/* ~/backup/service/
```
3. **上传新的class文件**
```bash
# 上传mapper文件
scp target/classes/com/example/web/mapper/*.class user@server:/path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/mapper/
# 上传service文件
scp target/classes/com/example/web/service/*.class user@server:/path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/service/
```
4. **设置文件权限**
```bash
# 在服务器上执行
sudo chown -R tomcat:tomcat /path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/mapper/
sudo chown -R tomcat:tomcat /path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/service/
sudo chmod 644 /path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/mapper/*.class
sudo chmod 644 /path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/service/*.class
```
5. **重载应用(可选)**
- 方法1:重启Tomcat(最可靠)
```bash
sudo systemctl restart tomcat
```
- 方法2:使用Tomcat Manager或JMX热重载(风险较高)
## 验证部署
1. **检查Tomcat日志**
```bash
# 在服务器上执行
tail -f /path/to/tomcat/logs/catalina.out
```
2. **测试应用功能**
- 访问应用的关键功能
- 执行涉及更新的Mapper和Service的操作
## 回滚方案
如果遇到问题,按照以下步骤回滚:
1. **方案一回滚**
```bash
# 在服务器上执行
sudo rm -f /path/to/tomcat/webapps/DL.war
sudo rm -rf /path/to/tomcat/webapps/DL/
# 上传之前的备份WAR文件
scp backup/DL.war user@server:/path/to/tomcat/webapps/
sudo chown tomcat:tomcat /path/to/tomcat/webapps/DL.war
sudo systemctl restart tomcat
```
2. **方案二回滚**
```bash
# 在服务器上执行
cp -r ~/backup/mapper/* /path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/mapper/
cp -r ~/backup/service/* /path/to/tomcat/webapps/DL/WEB-INF/classes/com/example/web/service/
sudo systemctl restart tomcat
```
## 注意事项
1. **版本兼容性**:确保Mapper和Service的修改与其他组件兼容
2. **事务一致性**:特别注意涉及多个Mapper的事务操作
3. **数据库变更**:如果Mapper修改涉及数据库结构变更,请先执行数据库迁移
4. **缓存清理**:如有必要,清除相关缓存
5. **生产环境建议**:在生产环境中,强烈推荐使用方案一进行完整部署
## 后续维护建议
1. **建立部署文档**:记录每次部署的变更内容
2. **制定回滚计划**:为每次更新准备回滚方案
3. **监控应用性能**:部署后密切关注应用性能指标
4. **测试完整性**:执行完整的功能测试套件
---
此指南由自动化工具生成,最后更新时间:2025/11/6 13:51

163
MAPPER_XML_DEPLOYMENT_GUIDE.txt

@ -1,163 +0,0 @@
# MyBatis Mapper XML文件部署指南(含性能优化)
## 文件状态确认
✅ 所有MyBatis Mapper XML映射文件已成功复制到构建目录
### Mapper XML文件(最后修改时间与源代码一致)
- 源文件位置:`src\main\resources\mapper`
- 构建后位置:`target\classes\mapper`
- 文件数量:16个XML文件
- 包含:Cart_itemsMapper.xml、EnterpriseMapper.xml、ManagersMapper.xml等所有必要的MyBatis映射文件
## 部署方案
### 方案一:完整WAR包部署(推荐)
这种方法最安全、最可靠,确保所有组件版本一致。
1. **准备WAR文件**
```bash
# 确保WAR文件已生成
ls -la target/DL.war
```
2. **上传WAR文件到服务器**
```bash
# 使用SCP上传文件
scp target/DL.war user@server:/path/to/tomcat/webapps/
```
3. **设置文件权限**
```bash
# 在服务器上执行
sudo chown tomcat:tomcat /path/to/tomcat/webapps/DL.war
sudo chmod 644 /path/to/tomcat/webapps/DL.war
```
4. **重启Tomcat服务**
```bash
# 在服务器上执行
sudo systemctl restart tomcat
# 或者
sudo service tomcat restart
# 或者直接使用Tomcat的脚本
/path/to/tomcat/bin/shutdown.sh
/path/to/tomcat/bin/startup.sh
```
### 方案二:仅更新Mapper XML文件(中等风险)
如果只修改了Mapper XML文件,可以只更新这些文件,但要注意版本兼容性问题。
1. **在服务器上定位目标目录**
```bash
# 在服务器上执行,找到应用部署目录
cd /path/to/tomcat/webapps/DL/WEB-INF/classes/mapper/
```
2. **备份当前文件**
```bash
# 在服务器上执行
mkdir -p ~/backup/mapper_xml
cp -r * ~/backup/mapper_xml/
```
3. **上传新的XML文件**
```bash
# 上传所有XML文件
scp target/classes/mapper/*.xml user@server:/path/to/tomcat/webapps/DL/WEB-INF/classes/mapper/
```
4. **设置文件权限**
```bash
# 在服务器上执行
sudo chown -R tomcat:tomcat /path/to/tomcat/webapps/DL/WEB-INF/classes/mapper/
sudo chmod 644 /path/to/tomcat/webapps/DL/WEB-INF/classes/mapper/*.xml
```
5. **重载应用**
- 方法1:重启Tomcat(最可靠)
```bash
sudo systemctl restart tomcat
```
- 方法2:使用Tomcat Manager重新加载应用(较快)
```bash
# 使用curl命令重新加载应用
curl -u username:password "http://localhost:8080/manager/text/reload?path=/DL"
```
## 性能优化内容
1. **SQL查询优化**:
- 将SELECT *查询替换为显式字段列表
- 移除不必要的连接操作
2. **分页功能**:
- 添加selectAllManagersWithPagination方法实现分页查询
- 新增getManagersCount方法获取总数
3. **二级缓存配置**:
- 为ManagersMapper添加LRU二级缓存
4. **数据库索引**:
- 为关键字段创建索引,提升查询性能
- 详细索引创建脚本请参考CREATE_OPTIMIZATION_INDEXES.sql
## 验证部署
1. **检查Tomcat日志**
```bash
# 在服务器上执行
tail -f /path/to/tomcat/logs/catalina.out
```
2. **测试数据库操作**
- 访问负责人管理页面,验证分页功能是否正常
- 测试数据查询操作,确认响应速度提升
- 检查应用日志,确保没有错误
- 监控数据库性能,确认索引正常使用
## 回滚方案
如果遇到问题,按照以下步骤回滚:
1. **方案一回滚**
```bash
# 在服务器上执行
sudo rm -f /path/to/tomcat/webapps/DL.war
sudo rm -rf /path/to/tomcat/webapps/DL/
# 上传之前的备份WAR文件
scp backup/DL.war user@server:/path/to/tomcat/webapps/
sudo chown tomcat:tomcat /path/to/tomcat/webapps/DL.war
sudo systemctl restart tomcat
```
2. **方案二回滚**
```bash
# 在服务器上执行
cp -r ~/backup/mapper_xml/* /path/to/tomcat/webapps/DL/WEB-INF/classes/mapper/
sudo systemctl restart tomcat
```
## 注意事项
1. **版本兼容性**:确保Mapper XML的修改与对应的Java接口兼容
2. **SQL语法检查**:部署前确认XML中的SQL语句语法正确
3. **数据库结构**:如果SQL修改涉及表结构变更,请先执行数据库迁移
4. **缓存清理**:MyBatis可能缓存映射文件,重启Tomcat可以确保清除缓存
5. **事务一致性**:特别注意涉及多个Mapper的事务操作
6. **参数类型**:确保XML中定义的参数类型与Java代码中的参数类型匹配
7. **数据库备份**:确保在部署前备份数据库
8. **索引优化**:部署新版本后务必执行数据库索引优化脚本
9. **分页验证**:验证分页功能是否正常工作
10. **性能监控**:持续监控数据库查询性能,必要时进行进一步优化
## 后续维护建议
1. **版本控制**:对Mapper XML文件进行版本控制,记录每次变更
2. **自动化部署**:考虑使用CI/CD工具自动化部署过程
3. **监控查询性能**:部署后监控查询性能,特别是对于复杂的SQL语句
4. **备份策略**:建立定期备份策略,包括数据库和应用配置
---
此指南由自动化工具生成,最后更新时间:2025/11/6

280
PERFORMANCE_OPTIMIZATION_GUIDE.md

@ -1,280 +0,0 @@
# 数据库性能优化指南
## 问题分析
根据对Mapper XML文件的检查,发现以下可能导致数据返回慢的主要问题:
### 1. 使用 SELECT * 查询所有字段
- **问题**:`SELECT *` 查询会返回表中的所有列,增加网络传输量和处理时间
- **影响文件**:ManagersMapper.xml中的多个查询
### 2. 复杂的JOIN和嵌套WHERE条件
- **问题**:复杂的JOIN操作和多层嵌套条件会增加数据库解析和执行时间
- **影响文件**:UsersMapper.xml中的`getAuthorizedCustomers`和`getAuthorizedUserIds`查询
### 3. 可能缺少必要的索引
- **问题**:频繁用于查询条件的字段可能缺少索引,导致全表扫描
- **影响字段**:userId, phoneNumber, type, level, managerId等
### 4. 未使用分页查询
- **问题**:查询大量数据时未使用分页,一次性返回过多数据
- **影响文件**:selectAllManagers等查询
## 优化方案
### 1. 优化SQL查询语句
#### 1.1 替换 SELECT * 为具体字段
**修改前** (ManagersMapper.xml):
```xml
<select id="selectAllManagers" resultMap="managersMap">
SELECT * FROM managers ORDER BY userName
</select>
```
**修改后**:
```xml
<select id="selectAllManagers" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers ORDER BY userName
</select>
```
对其他使用 `SELECT *` 的查询也进行类似修改。
#### 1.2 优化复杂的JOIN查询
**修改建议** (UsersMapper.xml):
- 将复杂的嵌套条件拆分为更简单的部分
- 使用子查询代替部分复杂JOIN
- 考虑使用临时表缓存中间结果
#### 1.3 添加分页查询
**修改前**:
```xml
<select id="selectAllManagers" resultMap="managersMap">
SELECT * FROM managers ORDER BY userName
</select>
```
**修改后**:
```xml
<select id="selectAllManagersWithPagination" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers
ORDER BY userName
LIMIT #{limit} OFFSET #{offset}
</select>
```
同时在Mapper接口中添加对应的方法。
### 2. 添加必要的数据库索引
建议在以下字段上添加索引:
```sql
-- 在users表上添加索引
CREATE INDEX idx_users_userId ON users(userId);
CREATE INDEX idx_users_phoneNumber ON users(phoneNumber);
CREATE INDEX idx_users_type ON users(type);
CREATE INDEX idx_users_level ON users(level);
CREATE INDEX idx_users_created_at ON users(created_at);
-- 在managers表上添加索引
CREATE INDEX idx_managers_id ON managers(id);
CREATE INDEX idx_managers_userName ON managers(userName);
CREATE INDEX idx_managers_managerId ON managers(managerId);
-- 在usermanagements表上添加索引
CREATE INDEX idx_usermanagements_userId ON usermanagements(userId);
CREATE INDEX idx_usermanagements_managerId ON usermanagements(managerId);
CREATE INDEX idx_usermanagements_userName ON usermanagements(userName);
CREATE INDEX idx_usermanagements_organization ON usermanagements(organization);
CREATE INDEX idx_usermanagements_managerdepartment ON usermanagements(managerdepartment);
```
### 3. 添加缓存机制
#### 3.1 在MyBatis中添加二级缓存
在Mapper XML文件中添加缓存配置:
```xml
<mapper namespace="com.example.web.mapper.ManagersMapper">
<!-- 添加二级缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
<!-- 现有映射内容 -->
<!-- ... -->
</mapper>
```
#### 3.2 在Service层添加本地缓存
在Service类中使用Guava Cache或Caffeine等本地缓存库。
### 4. 优化结果集映射
#### 4.1 优化ResultMap定义
确保ResultMap只映射必要的字段,避免不必要的映射。
#### 4.2 使用Constructor Args映射
对于频繁使用的DTO,可以考虑使用构造函数参数映射,提高性能。
## 具体优化实施
### 优化ManagersMapper.xml
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.ManagersMapper">
<!-- 添加二级缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
<resultMap id="managersMap" type="com.example.web.entity.Managers">
<id column="manager_id" property="manager_id"/>
<result column="id" property="id"/>
<result column="managerId" property="managerId"/>
<result column="managercompany" property="managercompany"/>
<result column="managerdepartment" property="managerdepartment"/>
<result column="organization" property="organization"/>
<result column="role" property="role"/>
<result column="root" property="root"/>
<result column="created_at" property="created_at"/>
<result column="updated_at" property="updated_at"/>
<result column="userName" property="userName"/>
<result column="assistant" property="assistant"/>
</resultMap>
<!-- 在 ManagersMapper.xml 中添加 -->
<select id="selectByUserNameAndManagerId" parameterType="map" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers
WHERE userName = #{userName} AND managerId = #{managerId}
</select>
<!-- 根据企业ID查询负责人信息 -->
<select id="selectManagersByEnterpriseId" parameterType="String" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers WHERE id = #{id}
</select>
<!-- 查询所有负责人信息 -->
<select id="selectAllManagers" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers ORDER BY userName
</select>
<!-- 分页查询负责人信息 -->
<select id="selectAllManagersWithPagination" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers
ORDER BY userName
LIMIT #{limit} OFFSET #{offset}
</select>
<!-- 根据负责人姓名查询 -->
<select id="selectByUserName" parameterType="String" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers WHERE userName = #{userName}
</select>
<!-- 插入负责人信息 -->
<insert id="insertManagers" parameterType="com.example.web.entity.Managers">
INSERT INTO managers (
id, managerId, managercompany, managerdepartment,
organization, role, root, created_at, updated_at,
userName, assistant
) VALUES (
#{id}, #{managerId}, #{managercompany}, #{managerdepartment},
#{organization}, #{role}, #{root}, #{created_at}, #{updated_at},
#{userName}, #{assistant}
)
</insert>
<!-- 更新负责人信息 -->
<update id="updateManagers" parameterType="com.example.web.entity.Managers">
UPDATE managers
SET
managercompany = #{managercompany},
managerdepartment = #{managerdepartment},
organization = #{organization},
role = #{role},
userName = #{userName},
assistant = #{assistant},
updated_at = #{updated_at}
WHERE id = #{id}
</update>
</mapper>
```
### 优化UsersMapper接口添加分页方法
在UsersMapper.java中添加分页查询方法:
```java
// 分页查询授权客户
List<UserProductCartDTO> getAuthorizedCustomersWithPagination(Map<String, Object> params);
// 获取授权客户总数
int getAuthorizedCustomersCount(Map<String, Object> params);
```
## 验证优化效果
优化后,建议通过以下方式验证性能改进:
1. **使用EXPLAIN分析SQL执行计划**
```sql
EXPLAIN SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers WHERE userName = 'someName';
```
2. **添加性能监控日志**
在Service层添加方法执行时间记录。
3. **压力测试**
使用JMeter等工具进行压力测试,比较优化前后的响应时间。
## 额外建议
1. **考虑读写分离**
对于高并发场景,考虑实现数据库读写分离。
2. **使用数据库连接池**
确保使用高效的连接池管理数据库连接。
3. **定期清理和优化数据库**
定期执行VACUUM(PostgreSQL)或OPTIMIZE TABLE(MySQL)等操作。
4. **考虑使用NoSQL缓存热点数据**
对于频繁访问的数据,可以考虑使用Redis等NoSQL数据库进行缓存。
---
此优化指南由自动化工具生成,建议根据实际情况进行调整和测试。

118
TOMCAT_DEPLOYMENT_STEPS.txt

@ -1,118 +0,0 @@
# TOMCAT 10.1.48 DEPLOYMENT STEPS
## PREREQUISITES
1. Tomcat 10.1.48 installed at /opt/tomcat
2. JDK 17 or higher installed on the server
3. Generated DL.war file in target directory
## DEPLOYMENT STEPS
### 1. Prepare Deployment File
The DL.war file has been successfully created and is ready for deployment.
### 2. Upload WAR File to Server
```bash
# From Windows using WinSCP or FileZilla:
# - Connect to your server
# - Upload d:\java\project\web(8)\web\target\DL.war to /opt/tomcat/webapps/
# Or from command line using scp (open Command Prompt as Administrator):
scp "d:\java\project\web(8)\web\target\DL.war" user@your-server:/opt/tomcat/webapps/
```
### 3. Set Permissions on Server
Connect to your server via SSH and run:
```bash
cd /opt/tomcat
sudo chown -R tomcat:tomcat webapps/
sudo chmod -R 755 webapps/
```
### 4. Configure Tomcat (Recommended)
```bash
# Edit context.xml to prevent resource locking issues
sudo nano /opt/tomcat/conf/context.xml
# Add antiResourceLocking and antiJARLocking attributes to Context element
<Context antiResourceLocking="true" antiJARLocking="true">
<!-- keep existing configuration -->
</Context>
# Save and exit (Ctrl+O, Enter, Ctrl+X)
# Create setenv.sh for memory optimization
sudo nano /opt/tomcat/bin/setenv.sh
# Add these lines:
#!/bin/bash
JAVA_OPTS="-Xms512m -Xmx1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.lang=ALL-UNNAMED"
# Save and exit
# Make it executable
sudo chmod +x /opt/tomcat/bin/setenv.sh
```
### 5. Restart Tomcat
```bash
cd /opt/tomcat/bin
# Stop Tomcat
sudo ./shutdown.sh
# Wait for Tomcat to stop completely
sleep 30
# Start Tomcat
sudo ./startup.sh
```
### 6. Verify Deployment
```bash
# Monitor Tomcat logs
tail -f /opt/tomcat/logs/catalina.out
# Access the application in a browser
# http://your-server-ip:8080/DL
# http://your-server-ip:8080/DL/loginmm.html (for login page)
```
## TROUBLESHOOTING
1. **Application not accessible**:
- Check if Tomcat is running: `ps aux | grep tomcat`
- Verify WAR file was deployed: `ls -la /opt/tomcat/webapps/`
- Look for errors in logs: `cat /opt/tomcat/logs/catalina.out`
2. **Database connection issues**:
- Ensure database server is reachable from Tomcat server
- Verify database credentials in application.yaml
3. **Port conflicts**:
- If port 8080 is in use, change it in server.xml:
`sudo nano /opt/tomcat/conf/server.xml`
Find and modify: `<Connector port="8080" ...`
4. **Tomcat 10 compatibility issues**:
- Tomcat 10 uses Jakarta EE (jakarta.* packages instead of javax.*)
- Ensure all dependencies are compatible with Jakarta EE
## UPDATING THE APPLICATION
1. Stop Tomcat: `sudo /opt/tomcat/bin/shutdown.sh`
2. Remove old deployment: `sudo rm -rf /opt/tomcat/webapps/DL*`
3. Upload new DL.war file
4. Start Tomcat: `sudo /opt/tomcat/bin/startup.sh`
---
DEPLOYMENT GUIDE CREATED: 2024
Tomcat Version: 10.1.48
Application Context: DL

123
UPDATE_DEPLOYMENT_GUIDE.txt

@ -1,123 +0,0 @@
# 更新部署指南:Controller类文件更新
## 已完成的工作
✅ **Controller代码编译完成**:
- 所有更新后的Controller代码已成功编译成class文件
- 生成时间:2025/11/6 13:41
- 所有8个Controller类文件均已更新:
- CustomerController.class
- EnterpriseController.class
- LoginController.class
- PoolCustomerController.class
- SupplyCustomerController.class
- SupplyCustomerRecycleController.class
- SupplyEnterpriseController.class
- SupplyPoolCustomerController.class
✅ **WAR文件已准备就绪**:
- 已更新的WAR文件:`target\DL.war`
## 更新部署步骤
### 方法1:直接部署整个WAR文件(推荐)
1. **上传新的WAR文件到服务器**:
```bash
# 使用scp命令上传
scp "d:\java\project\web(8)\web\target\DL.war" user@your-server:/opt/tomcat/webapps/
```
2. **设置正确权限**:
```bash
cd /opt/tomcat
sudo chown -R tomcat:tomcat webapps/
sudo chmod -R 755 webapps/
```
3. **重启Tomcat以加载新代码**:
```bash
cd /opt/tomcat/bin
sudo ./shutdown.sh
sleep 30
sudo ./startup.sh
```
### 方法2:只更新Controller的class文件(可选,风险较高)
如果只想更新controller的class文件而不重新部署整个WAR:
1. **找出Tomcat中已解压的应用目录**:
```bash
cd /opt/tomcat/webapps/DL/WEB-INF/classes/com/example/web/controller
```
2. **备份当前的controller文件**:
```bash
mkdir -p backup
cp *.class backup/
```
3. **上传新的controller class文件**:
```bash
# 本地准备命令
# 创建临时目录复制所有controller文件
mkdir -p temp_controller
cp target\classes\com\example\web\controller\*.class temp_controller\
# 然后将整个目录上传
scp -r temp_controller user@your-server:/opt/tomcat/webapps/DL/WEB-INF/classes/com/example/web/
# 服务器上执行
cd /opt/tomcat/webapps/DL/WEB-INF/classes/com/example/web/
mv temp_controller/* controller/
rmdir temp_controller
```
4. **设置权限**:
```bash
sudo chown tomcat:tomcat controller/*.class
sudo chmod 644 controller/*.class
```
5. **重启Tomcat**(即使只更新class文件,为确保线程安全也建议重启):
```bash
cd /opt/tomcat/bin
sudo ./shutdown.sh
sleep 30
sudo ./startup.sh
```
## 验证部署
1. **检查Tomcat日志确认部署成功**:
```bash
tail -f /opt/tomcat/logs/catalina.out
```
2. **验证应用是否正常运行**:
- 访问:http://服务器IP:8080/DL
- 尝试使用更新的Controller功能
## 回滚方案
如果更新出现问题,可以快速回滚:
1. **方法1回滚**:删除新WAR文件,恢复旧WAR文件
```bash
cd /opt/tomcat/webapps
sudo rm -rf DL.war DL/
sudo cp backup/DL.war .
sudo ./bin/startup.sh
```
2. **方法2回滚**:
```bash
cd /opt/tomcat/webapps/DL/WEB-INF/classes/com/example/web/controller
cp backup/*.class .
sudo ./opt/tomcat/bin/startup.sh
```
---
**注意**:推荐使用方法1完整部署WAR文件,以确保所有依赖关系正确加载,避免潜在的类加载问题。

47
deploy_optimization_changes.bat

@ -1,47 +0,0 @@
@echo off
rem ==================================================
rem 性能优化部署脚本
rem 此脚本用于部署数据库索引和更新Mapper XML文件
rem ==================================================
echo 开始部署性能优化更改...
rem 1. 编译项目,确保最新的Mapper XML文件被打包
echo 正在编译项目...
mvn clean package -DskipTests
if %ERRORLEVEL% NEQ 0 (
echo 编译失败,请检查错误信息!
pause
exit /b 1
)
rem 2. 提示用户执行数据库索引脚本
echo.
echo ==================================================
echo 请在数据库中执行以下脚本创建必要的索引:
echo CREATE_OPTIMIZATION_INDEXES.sql
echo ==================================================
echo.
pause
rem 3. 部署更新后的WAR文件
echo.
echo 正在复制WAR文件到部署目录...
copy target\web-0.0.1-SNAPSHOT.war DL.war
if %ERRORLEVEL% NEQ 0 (
echo WAR文件复制失败!
pause
exit /b 1
)
echo 部署完成!请按照以下步骤进行:
echo 1. 将DL.war文件上传到Tomcat的webapps目录
echo 2. 重启Tomcat服务器
echo 3. 验证应用性能是否有所改善
echo.
echo 性能优化部署脚本执行完毕!
pause

59
deploy_to_tomcat.bat

@ -1,59 +0,0 @@
@echo off
cls
echo Spring Boot应用部署到Tomcat 10.1.48
====================================
:: 简单版本,避免复杂路径操作
set "SOURCE_WAR=target\web-0.0.1-SNAPSHOT.war"
set "TARGET_WAR=target\DL.war"
:: 检查源文件是否存在
if not exist "%SOURCE_WAR%" (
echo 错误: 找不到源WAR文件
echo 请先运行 'mvn clean package -DskipTests' 构建项目
pause
exit /b 1
)
:: 复制文件(使用基本copy命令)
echo 正在准备部署文件...
copy "%SOURCE_WAR%" "%TARGET_WAR%"
:: 检查复制是否成功
if exist "%TARGET_WAR%" (
echo 文件准备成功!
echo.
echo ====== Tomcat部署步骤 ======
echo 1. 上传文件:
echo 请将 "%TARGET_WAR%" 上传到服务器的 /opt/tomcat/webapps/ 目录
echo 推荐使用WinSCP或FileZilla进行上传
echo.
echo 2. 设置服务器权限:
echo 连接到服务器后执行以下命令:
echo cd /opt/tomcat
echo sudo chown -R tomcat:tomcat webapps/
echo sudo chmod -R 755 webapps/
echo.
echo 3. 重启Tomcat:
echo cd /opt/tomcat/bin
echo sudo ./shutdown.sh
echo sleep 30
echo sudo ./startup.sh
echo.
echo 4. 验证部署:
echo 访问: http://服务器IP:8080/DL
echo 查看日志: tail -f /opt/tomcat/logs/catalina.out
echo.
echo 重要说明:
echo - 确保服务器上的Tomcat版本为10.1.48
echo - 确保数据库连接信息正确
echo - 如有问题,请查看Tomcat日志文件
) else (
echo 错误: 文件准备失败
)
echo.
pause

56
deploy_to_tomcat.sh

@ -1,56 +0,0 @@
#!/bin/bash
# Spring Boot应用部署到Tomcat 10.1.48脚本
# 避免路径中的括号导致的bash语法错误
echo "===== Spring Boot应用部署到Tomcat 10.1.48 ====="
# 进入项目目录(使用相对路径避免括号问题)
cd "$(dirname "$0")"
PROJECT_DIR="$(pwd)"
WAR_SOURCE="${PROJECT_DIR}/target/web-0.0.1-SNAPSHOT.war"
WAR_TARGET="${PROJECT_DIR}/target/DL.war"
echo "项目目录: $PROJECT_DIR"
echo "源WAR文件: $WAR_SOURCE"
echo "目标WAR文件: $WAR_TARGET"
# 检查源WAR文件是否存在
if [ ! -f "$WAR_SOURCE" ]; then
echo "错误: 找不到源WAR文件 $WAR_SOURCE"
echo "请先运行 'mvn clean package -DskipTests' 构建项目"
exit 1
fi
# 复制并重命名WAR文件
echo "正在复制并重命名WAR文件..."
cp "$WAR_SOURCE" "$WAR_TARGET"
if [ $? -eq 0 ]; then
echo "WAR文件重命名成功!"
echo "
部署步骤:
1. 上传WAR文件到服务器:"
echo " scp "$WAR_TARGET" user@your-server:/opt/tomcat/webapps/"
echo "
2. 在服务器上设置权限:"
echo " cd /opt/tomcat"
echo " sudo chown -R tomcat:tomcat webapps/"
echo " sudo chmod -R 755 webapps/"
echo "
3. 重启Tomcat:"
echo " cd /opt/tomcat/bin"
echo " sudo ./shutdown.sh"
echo " sleep 30"
echo " sudo ./startup.sh"
echo "
4. 验证部署:"
echo " 访问 http://your-server-ip:8080/DL"
echo " 查看日志: tail -f /opt/tomcat/logs/catalina.out"
else
echo "错误: WAR文件复制失败"
exit 1
fi
echo "
===== 部署准备完成 ====="

69
deploy_with_optimization.bat

@ -1,69 +0,0 @@
@echo off
rem ====================================================
rem 部署脚本 - 包含性能优化部署步骤
rem 版本:2.0
rem 日期:2024
rem ====================================================
echo 开始Spring Boot应用部署流程(含性能优化)...
rem 1. 设置变量
set "WAR_FILE=target\web-0.0.1-SNAPSHOT.war"
set "DEPLOY_WAR=target\DL.war"
set "BACKUP_DIR=d:\java\project\web(8)\web\backup"
set "INDEX_SCRIPT=CREATE_OPTIMIZATION_INDEXES.sql"
rem 2. 检查WAR文件是否存在
if not exist "%WAR_FILE%" (
echo 错误:WAR文件不存在,请先执行 mvn clean package -DskipTests
pause
exit /b 1
)
echo 找到WAR文件:%WAR_FILE%
rem 3. 创建备份目录
if not exist "%BACKUP_DIR%" mkdir "%BACKUP_DIR%"
echo 创建备份目录:%BACKUP_DIR%
rem 4. 重命名WAR文件
copy "%WAR_FILE%" "%DEPLOY_WAR%" > nul
if %errorlevel% neq 0 (
echo 错误:重命名WAR文件失败
pause
exit /b 1
)
echo 已将WAR文件重命名为:%DEPLOY_WAR%
rem 5. 复制索引脚本到备份目录
if exist "%INDEX_SCRIPT%" (
copy "%INDEX_SCRIPT%" "%BACKUP_DIR%\%INDEX_SCRIPT%" > nul
echo 已复制数据库索引脚本到备份目录
)
rem 6. 提示数据库索引优化步骤
echo.
echo ====================================
echo 数据库索引优化(重要)
echo ====================================
echo 请在数据库服务器上执行以下操作:
echo 1. 登录数据库服务器
echo 2. 执行索引创建脚本:
echo source %BACKUP_DIR%\%INDEX_SCRIPT%
echo 或直接复制脚本内容执行
echo.
echo ====================================
echo 部署准备完成!
echo ====================================
echo 部署文件:%DEPLOY_WAR%
echo 备份位置:%BACKUP_DIR%
echo.
echo 下一步操作:
echo 1. 上传 %DEPLOY_WAR% 到Tomcat服务器的webapps目录
echo 2. 在数据库服务器上执行索引优化脚本
echo 3. 重启Tomcat服务器
echo 4. 验证性能优化效果
echo.
pause

295
mvnw

@ -1,295 +0,0 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.4
#
# Optional ENV vars
# -----------------
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
# MVNW_REPOURL - repo url base for downloading maven distribution
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x
# OS specific support.
native_path() { printf %s\\n "$1"; }
case "$(uname)" in
CYGWIN* | MINGW*)
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
native_path() { cygpath --path --windows "$1"; }
;;
esac
# set JAVACMD and JAVACCMD
set_java_home() {
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
if [ -n "${JAVA_HOME-}" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACCMD="$JAVA_HOME/jre/sh/javac"
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACCMD="$JAVA_HOME/bin/javac"
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
return 1
fi
fi
else
JAVACMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v java
)" || :
JAVACCMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v javac
)" || :
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
return 1
fi
fi
}
# hash string like Java String::hashCode
hash_string() {
str="${1:-}" h=0
while [ -n "$str" ]; do
char="${str%"${str#?}"}"
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
str="${str#?}"
done
printf %x\\n $h
}
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
scriptDir="$(dirname "$0")"
scriptName="$(basename "$0")"
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
*)
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
distributionPlatform=linux-amd64
;;
esac
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
fi
case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac
# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
trap clean HUP INT TERM EXIT
else
die "cannot create temp dir"
fi
mkdir -p -- "${MAVEN_HOME%/*}"
# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
verbose "Found wget ... using wget"
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
verbose "Found curl ... using curl"
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
verbose "Falling back to use Java to download"
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
cat >"$javaSource" <<-END
public class Downloader extends java.net.Authenticator
{
protected java.net.PasswordAuthentication getPasswordAuthentication()
{
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
}
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
verbose " - Compiling Downloader.java ..."
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
verbose " - Running Downloader.java ..."
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi
# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
distributionSha256Result=false
if [ "$MVN_CMD" = mvnd.sh ]; then
echo "Checksum validation is not supported for maven-mvnd." >&2
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $distributionSha256Result = false ]; then
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
# unzip and move
if command -v unzip >/dev/null; then
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
# Find the actual extracted directory name (handles snapshots where filename != directory name)
actualDistributionDir=""
# First try the expected directory name (for regular distributions)
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
actualDistributionDir="$distributionUrlNameMain"
fi
fi
# If not found, search for any directory with the Maven executable (for snapshots)
if [ -z "$actualDistributionDir" ]; then
# enable globbing to iterate over items
set +f
for dir in "$TMP_DOWNLOAD_DIR"/*; do
if [ -d "$dir" ]; then
if [ -f "$dir/bin/$MVN_CMD" ]; then
actualDistributionDir="$(basename "$dir")"
break
fi
fi
done
set -f
fi
if [ -z "$actualDistributionDir" ]; then
verbose "Contents of $TMP_DOWNLOAD_DIR:"
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
die "Could not find Maven distribution directory in extracted archive"
fi
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"

189
mvnw.cmd

@ -1,189 +0,0 @@
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
"maven-mvnd-*" {
$USE_MVND = $true
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
$MVN_CMD = "mvnd.cmd"
break
}
default {
$USE_MVND = $false
$MVN_CMD = $script -replace '^mvnw','mvn'
break
}
}
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_M2_PATH = "$HOME/.m2"
if ($env:MAVEN_USER_HOME) {
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
}
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
}
$MAVEN_WRAPPER_DISTS = $null
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
} else {
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
}
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
# Find the actual extracted directory name (handles snapshots where filename != directory name)
$actualDistributionDir = ""
# First try the expected directory name (for regular distributions)
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
$actualDistributionDir = $distributionUrlNameMain
}
# If not found, search for any directory with the Maven executable (for snapshots)
if (!$actualDistributionDir) {
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
if (Test-Path -Path $testPath -PathType Leaf) {
$actualDistributionDir = $_.Name
}
}
}
if (!$actualDistributionDir) {
Write-Error "Could not find Maven distribution directory in extracted archive"
}
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"

5
pom.xml

@ -40,6 +40,7 @@
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
<scope>runtime</scope>
</dependency>
@ -116,6 +117,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>

2
src/main/java/com/example/web/WebApplication.java

@ -2,6 +2,7 @@ package com.example.web;
import me.paulschwarz.springdotenv.DotenvPropertySource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
@ -9,6 +10,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@MapperScan("com.example.web.mapper")
@EnableAspectJAutoProxy//启用AOP自动代理
@EnableScheduling//定时任务
/*@PropertySource(value = "classpath:.env", factory = DotenvPropertySource.class)*/
public class WebApplication {

25
src/main/java/com/example/web/config/CharacterEncodingFilterConfig.java

@ -0,0 +1,25 @@
package com.example.web.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
/**
* 字符编码过滤器配置
* 确保所有请求和响应都使用UTF-8编码
*/
@Configuration
public class CharacterEncodingFilterConfig {
@Bean
public FilterRegistrationBean<CharacterEncodingFilter> characterEncodingFilter() {
FilterRegistrationBean<CharacterEncodingFilter> registrationBean = new FilterRegistrationBean<>();
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}

6
src/main/java/com/example/web/config/MyBatisConfig.java

@ -29,6 +29,12 @@ public class MyBatisConfig {
// 设置Mapper XML文件路径
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/*.xml"));
// 添加配置:开启下划线到驼峰命名的自动转换
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
factoryBean.setConfiguration(configuration);
return factoryBean.getObject();
}

38
src/main/java/com/example/web/config/WebSocketConfig.java

@ -0,0 +1,38 @@
package com.example.web.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 配置消息代理,用于广播消息
config.enableSimpleBroker("/topic");
// 配置应用程序前缀,用于处理客户端发送的消息
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 注册WebSocket端点,允许前端连接
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS(); // 支持SockJS,兼容不支持WebSocket的浏览器
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
// 配置WebSocket传输的字符编码为UTF-8
registration.setMessageSizeLimit(64 * 1024); // 64KB
registration.setSendBufferSizeLimit(128 * 1024); // 128KB
registration.setSendTimeLimit(20000); // 20 seconds
}
}

1031
src/main/java/com/example/web/controller/CustomerController.java

File diff suppressed because it is too large

263
src/main/java/com/example/web/controller/CustomerRestController.java

@ -0,0 +1,263 @@
package com.example.web.controller;
import com.example.web.entity.Login;
import com.example.web.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 客户数据REST控制器
*/
@RestController
@RequestMapping("/api/customer")
public class CustomerRestController {
@Autowired
private CustomerService customerService;
/**
* 新增客户数据
* @param customerData 客户数据包含企业联系人和负责人信息
* @return 响应结果
*/
@PostMapping
public Map<String, Object> addCustomer(@RequestParam Map<String, Object> customerData) {
Map<String, Object> response = new HashMap<>();
try {
// 调用服务层方法添加客户
boolean success = customerService.addCustomer(customerData);
if (success) {
response.put("success", true);
response.put("message", "客户添加成功");
} else {
response.put("success", false);
response.put("message", "客户添加失败");
}
} catch (Exception e) {
response.put("success", false);
response.put("message", "客户添加失败: " + e.getMessage());
e.printStackTrace();
}
return response;
}
/**
* 更新客户数据
* @param request 客户数据包含企业联系人和负责人信息使用电话号码作为唯一标识以及操作人信息
* @return 响应结果
*/
@PostMapping("/update")
public Map<String, Object> updateCustomer(@RequestBody Map<String, Object> request) {
Map<String, Object> response = new HashMap<>();
try {
System.out.println("DEBUG: 收到客户更新请求,数据: " + request);
// 获取客户数据
Map<String, Object> customerData = new HashMap<>(request);
// 获取操作人信息
Map<String, Object> operatorInfo = (Map<String, Object>) request.get("operatorInfo");
if (operatorInfo == null) {
operatorInfo = new HashMap<>();
}
// 创建登录者信息对象
Login login = new Login();
login.setProjectName((String) operatorInfo.get("role"));
login.setUserName((String) operatorInfo.get("userName"));
login.setManagercompany((String) operatorInfo.get("company"));
login.setManagerdepartment((String) operatorInfo.get("department"));
login.setOrganization((String) operatorInfo.get("organization"));
// 调用服务层方法更新客户
boolean success = customerService.updateCustomer(customerData, login);
System.out.println("DEBUG: 客户更新结果: " + success);
if (success) {
response.put("success", true);
response.put("message", "客户更新成功");
} else {
response.put("success", false);
response.put("message", "客户更新失败: 未找到客户信息或更新操作失败");
}
} catch (Exception e) {
System.out.println("DEBUG: 客户更新异常,异常信息: " + e.getMessage());
e.printStackTrace();
response.put("success", false);
response.put("message", "客户更新失败: " + e.getMessage());
}
System.out.println("DEBUG: 客户更新响应: " + response);
return response;
}
/**
* 跟进客户
* @param request 跟进请求包含电话号码跟进内容数据源和操作人信息
* @return 响应结果
*/
@PostMapping("/followup")
public Map<String, Object> followupCustomer(@RequestBody Map<String, Object> request) {
Map<String, Object> response = new HashMap<>();
try {
System.out.println("DEBUG: 收到客户跟进请求,数据: " + request);
String phoneNumber = (String) request.get("phoneNumber");
String followupContent = (String) request.get("followup");
String dataSource = (String) request.get("dataSource");
// 获取操作人信息
Map<String, Object> operatorInfo = (Map<String, Object>) request.get("operatorInfo");
if (operatorInfo == null) {
operatorInfo = new HashMap<>();
}
// 创建登录者信息对象
Login login = new Login();
login.setProjectName((String) operatorInfo.get("role"));
login.setUserName((String) operatorInfo.get("userName"));
login.setManagercompany((String) operatorInfo.get("company"));
login.setManagerdepartment((String) operatorInfo.get("department"));
login.setOrganization((String) operatorInfo.get("organization"));
if (phoneNumber == null || phoneNumber.isEmpty() || followupContent == null || dataSource == null) {
response.put("success", false);
response.put("message", "跟进失败: 参数不完整");
return response;
}
// 调用服务层方法跟进客户
boolean success = customerService.followupCustomer(phoneNumber, followupContent, dataSource, login);
System.out.println("DEBUG: 客户跟进结果: " + success);
if (success) {
response.put("success", true);
response.put("message", "跟进成功");
} else {
response.put("success", false);
response.put("message", "跟进失败: 未找到客户信息或更新操作失败");
}
} catch (Exception e) {
System.out.println("DEBUG: 客户跟进异常,异常信息: " + e.getMessage());
e.printStackTrace();
response.put("success", false);
response.put("message", "跟进失败: " + e.getMessage());
}
System.out.println("DEBUG: 客户跟进响应: " + response);
return response;
}
/**
* 记录客户查看操作
* @param request 查看请求包含客户ID和操作人信息
* @return 响应结果
*/
@PostMapping("/view")
public Map<String, Object> recordCustomerView(@RequestBody Map<String, Object> request) {
Map<String, Object> response = new HashMap<>();
try {
System.out.println("DEBUG: 收到客户查看请求,数据: " + request);
String userId = (String) request.get("userId");
// 获取操作人信息
Map<String, Object> operatorInfo = (Map<String, Object>) request.get("operatorInfo");
if (operatorInfo == null) {
operatorInfo = new HashMap<>();
}
// 创建登录者信息对象
Login login = new Login();
login.setProjectName((String) operatorInfo.get("role"));
login.setUserName((String) operatorInfo.get("userName"));
login.setManagercompany((String) operatorInfo.get("company"));
login.setManagerdepartment((String) operatorInfo.get("department"));
login.setOrganization((String) operatorInfo.get("organization"));
if (userId == null || userId.isEmpty()) {
response.put("success", false);
response.put("message", "记录查看操作失败: 参数不完整");
return response;
}
// 调用服务层方法记录客户查看操作
boolean success = customerService.recordCustomerView(userId, login);
System.out.println("DEBUG: 记录客户查看操作结果: " + success);
if (success) {
response.put("success", true);
response.put("message", "记录查看操作成功");
} else {
response.put("success", false);
response.put("message", "记录查看操作失败");
}
} catch (Exception e) {
System.out.println("DEBUG: 记录客户查看操作异常,异常信息: " + e.getMessage());
e.printStackTrace();
response.put("success", false);
response.put("message", "记录查看操作失败: " + e.getMessage());
}
System.out.println("DEBUG: 记录客户查看操作响应: " + response);
return response;
}
/**
* 认领客户
* @param request 认领请求包含客户ID和操作人信息
* @return 响应结果
*/
@PostMapping("/claim")
public Map<String, Object> claimCustomer(@RequestBody Map<String, Object> request) {
Map<String, Object> response = new HashMap<>();
try {
System.out.println("DEBUG: 收到客户认领请求,数据: " + request);
String customerId = (String) request.get("customerId");
// 获取操作人信息
Map<String, Object> operatorInfo = (Map<String, Object>) request.get("operatorInfo");
if (operatorInfo == null) {
operatorInfo = new HashMap<>();
}
// 创建登录者信息对象
Login login = new Login();
login.setProjectName((String) operatorInfo.get("role"));
login.setUserName((String) operatorInfo.get("userName"));
login.setManagercompany((String) operatorInfo.get("company"));
login.setManagerdepartment((String) operatorInfo.get("department"));
login.setOrganization((String) operatorInfo.get("organization"));
if (customerId == null || customerId.isEmpty()) {
response.put("success", false);
response.put("message", "客户认领失败: 参数不完整");
return response;
}
// 调用服务层方法认领客户
boolean success = customerService.claimCustomer(customerId, login);
System.out.println("DEBUG: 客户认领结果: " + success);
if (success) {
response.put("success", true);
response.put("message", "客户认领成功");
} else {
response.put("success", false);
response.put("message", "客户认领失败");
}
} catch (Exception e) {
System.out.println("DEBUG: 客户认领异常,异常信息: " + e.getMessage());
e.printStackTrace();
response.put("success", false);
response.put("message", "客户认领失败: " + e.getMessage());
}
return response;
}
}

67
src/main/java/com/example/web/controller/CustomerWebSocketController.java

@ -0,0 +1,67 @@
package com.example.web.controller;
import com.example.web.entity.CustomerData;
import com.example.web.entity.Login;
import com.example.web.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import java.util.List;
/**
* @Description: 客户数据WebSocket控制器
*/
@Controller
public class CustomerWebSocketController {
@Autowired
private CustomerService customerService;
/**
* 广播所有客户数据
* @return 客户数据列表
*/
@MessageMapping("/customer/all")
@SendTo("/topic/customers")
public List<CustomerData> broadcastAllCustomers() {
return customerService.getAllCustomers();
}
/**
* 根据用户信息获取部门公海池数据
* @param role 用户角色buyer/seller
* @param login 用户登录信息
* @return 部门公海池客户数据
*/
@MessageMapping("/customer/departmentSeaPool/{role}")
@SendTo("/topic/departmentSeaPool/{role}")
public List<CustomerData> getDepartmentSeaPool(@DestinationVariable String role, Login login) {
return customerService.getDepartmentSeaPool(role, login);
}
/**
* 根据用户信息获取组织公海池数据
* @param role 用户角色buyer/seller
* @param login 用户登录信息
* @return 组织公海池客户数据
*/
@MessageMapping("/customer/organizationSeaPool/{role}")
@SendTo("/topic/organizationSeaPool/{role}")
public List<CustomerData> getOrganizationSeaPool(@DestinationVariable String role, Login login) {
return customerService.getOrganizationSeaPool(role, login);
}
/**
* 根据用户角色获取对应的数据销售或采购
* @param role 用户角色buyer/seller
* @return 对应角色的客户数据
*/
@MessageMapping("/customer/role/{role}")
@SendTo("/topic/role/{role}")
public List<CustomerData> getCustomersByRole(@DestinationVariable String role, Login login) {
return customerService.getCustomersByRole(role, login);
}
}

1170
src/main/java/com/example/web/controller/EnterpriseController.java

File diff suppressed because it is too large

219
src/main/java/com/example/web/controller/FollowupController.java

@ -1,219 +0,0 @@
package com.example.web.controller;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.service.FollowUpService;
import com.example.web.service.InformationTraService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/followup")
public class FollowupController {
@Autowired
private FollowUpService followUpService;
@Autowired
private InformationTraService informationTraService;
/**
* 根据电话号码获取跟进信息
* @param phoneNumber 电话号码
* @param dataSource 数据源primary或wechat
* @return 跟进信息
*/
@GetMapping("/get")
public ResponseEntity<String> getFollowUp(
@RequestParam String phoneNumber) {
try {
String followup = followUpService.getFollowUpByPhone(phoneNumber);
return ResponseEntity.ok(followup);
} catch (Exception e) {
return ResponseEntity.internalServerError().body("获取跟进信息失败:" + e.getMessage());
}
}
/**
* 从请求中获取管理员认证信息
* @param request 请求对象
* @return 管理员认证信息
*/
private ManagerAuthInfo getManagerAuthInfoFromRequest(HttpServletRequest request) {
try {
// 记录请求中所有参数,便于调试
System.out.println("🔍 开始获取认证信息,请求参数:");
java.util.Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String paramName = parameterNames.nextElement();
String paramValue = request.getParameter(paramName);
System.out.println(" " + paramName + " = " + paramValue);
}
System.out.println("🔍 请求参数记录完成");
// 记录URL查询参数
System.out.println("🔍 URL查询参数:");
String queryString = request.getQueryString();
System.out.println(" queryString = " + queryString);
// 从请求参数和URL查询参数中获取认证信息
// 优先从请求参数中获取,其次从URL查询参数中获取
String managerId = getParameterFromRequestOrQueryString(request, "managerId");
String organization = getParameterFromRequestOrQueryString(request, "organization");
String role = getParameterFromRequestOrQueryString(request, "role");
String userName = getParameterFromRequestOrQueryString(request, "userName");
String assistant = getParameterFromRequestOrQueryString(request, "assistant");
// 支持两种参数名:managerCompany/managerDepartment 和 company/department
String managerCompany = getParameterFromRequestOrQueryString(request, "managerCompany");
String managerDepartment = getParameterFromRequestOrQueryString(request, "managerDepartment");
// 如果managerCompany或managerDepartment为空,尝试使用company和department参数
if (managerCompany == null || managerCompany.trim().isEmpty()) {
managerCompany = getParameterFromRequestOrQueryString(request, "company");
}
if (managerDepartment == null || managerDepartment.trim().isEmpty()) {
managerDepartment = getParameterFromRequestOrQueryString(request, "department");
}
// 解码URL编码的参数
if (organization != null) {
organization = java.net.URLDecoder.decode(organization, "UTF-8");
}
if (role != null) {
role = java.net.URLDecoder.decode(role, "UTF-8");
}
if (userName != null) {
userName = java.net.URLDecoder.decode(userName, "UTF-8");
}
if (assistant != null) {
assistant = java.net.URLDecoder.decode(assistant, "UTF-8");
}
if (managerCompany != null) {
managerCompany = java.net.URLDecoder.decode(managerCompany, "UTF-8");
}
if (managerDepartment != null) {
managerDepartment = java.net.URLDecoder.decode(managerDepartment, "UTF-8");
}
// 记录解码后的参数值
System.out.println("🔍 解码后的参数值:");
System.out.println(" managerId = " + managerId);
System.out.println(" organization = " + organization);
System.out.println(" role = " + role);
System.out.println(" userName = " + userName);
System.out.println(" assistant = " + assistant);
System.out.println(" managerCompany = " + managerCompany);
System.out.println(" managerDepartment = " + managerDepartment);
// 验证必填参数
if (userName == null || userName.trim().isEmpty()) {
System.out.println("❌ 用户名不能为空");
return null;
}
// 验证公司和部门信息
if (managerCompany == null || managerCompany.trim().isEmpty()) {
System.out.println("❌ 公司信息不能为空");
return null;
}
if (managerDepartment == null || managerDepartment.trim().isEmpty()) {
System.out.println("❌ 部门信息不能为空");
return null;
}
// 使用正确的构造器创建ManagerAuthInfo对象
ManagerAuthInfo authInfo = new ManagerAuthInfo(
managerId,
managerCompany,
managerDepartment,
organization,
role,
userName,
assistant
);
System.out.println("✅ 认证信息获取成功:" + authInfo.toString());
return authInfo;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 从请求参数或URL查询参数中获取参数值
* 优先从请求参数中获取其次从URL查询参数中获取
* @param request 请求对象
* @param paramName 参数名
* @return 参数值
*/
private String getParameterFromRequestOrQueryString(HttpServletRequest request, String paramName) {
// 先从请求参数中获取
String paramValue = request.getParameter(paramName);
if (paramValue != null && !paramValue.trim().isEmpty()) {
return paramValue;
}
// 如果请求参数中没有,尝试从URL查询参数中获取
try {
String queryString = request.getQueryString();
if (queryString != null) {
// 解析queryString
String[] pairs = queryString.split("&");
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2 && keyValue[0].equals(paramName)) {
return keyValue[1];
}
}
}
} catch (Exception e) {
System.err.println("解析URL查询参数失败:" + e.getMessage());
}
return null;
}
/**
* 保存跟进信息
* @param phoneNumber 电话号码
* @param followup 跟进信息
* @return 保存结果
*/
@PostMapping("/save")
public ResponseEntity<String> saveFollowUp(
@RequestParam String phoneNumber,
@RequestParam String followup,
HttpServletRequest request) {
try {
ManagerAuthInfo authInfo = getManagerAuthInfoFromRequest(request);
boolean result = followUpService.saveFollowUp(phoneNumber, followup);
if (result) {
// 只有当authInfo不为null时才记录操作事件,确保负责人信息完整
if (authInfo != null) {
// 记录跟进信息更新操作
informationTraService.recordOperationEvent(
phoneNumber,
"", // 跟进记录可能无法获取客户名称,留空
phoneNumber + "-更新跟进",
authInfo.getManagercompany(),
authInfo.getManagerdepartment(),
authInfo.getOrganization(),
authInfo.getRole(),
authInfo.getUserName(),
authInfo.getAssistant()
);
} else {
System.err.println("❌ 认证信息为空,无法记录操作事件");
}
return ResponseEntity.ok("跟进信息保存成功");
} else {
return ResponseEntity.badRequest().body("跟进信息保存失败");
}
} catch (Exception e) {
return ResponseEntity.internalServerError().body("保存跟进信息失败:" + e.getMessage());
}
}
}

183
src/main/java/com/example/web/controller/InformationTraController.java

@ -1,183 +0,0 @@
package com.example.web.controller;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.service.InformationTraService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* 信息操作记录控制器
* 处理前端操作记录请求包括查看客户详情修改客户信息更新客户跟进等操作
*/
@RestController
@RequestMapping("/api/info-tracking")
public class InformationTraController {
@Autowired
private InformationTraService informationTraService;
/**
* 记录操作事件
* @param request HttpServletRequest对象包含操作信息和认证信息
* @return ResponseEntity<Map<String, Object>> 响应结果
*/
@PostMapping("/record")
public ResponseEntity<Map<String, Object>> recordOperation(HttpServletRequest request) {
Map<String, Object> result = new HashMap<>();
try {
// 获取操作类型
String operationType = request.getParameter("operationType");
// 获取电话号码
String phoneNumber = request.getParameter("phoneNumber");
// 获取客户名称
String customerName = request.getParameter("customerName");
// 验证必要参数
if (operationType == null || operationType.trim().isEmpty()) {
result.put("success", false);
result.put("message", "操作类型不能为空");
return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
if (phoneNumber == null || phoneNumber.trim().isEmpty()) {
result.put("success", false);
result.put("message", "电话号码不能为空");
return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
// 获取认证信息
ManagerAuthInfo authInfo = getManagerAuthInfoFromRequest(request, false); // 默认销售端,后续根据部门自动调整
if (authInfo == null) {
result.put("success", false);
result.put("message", "获取认证信息失败");
return new ResponseEntity<>(result, HttpStatus.UNAUTHORIZED);
}
// 记录操作事件
boolean success = informationTraService.recordOperationEvent(
phoneNumber,
customerName != null ? customerName : "",
operationType,
authInfo.getManagercompany(),
authInfo.getManagerdepartment(),
authInfo.getOrganization(),
authInfo.getRole(),
authInfo.getUserName(),
authInfo.getAssistant()
);
if (success) {
result.put("success", true);
result.put("message", "操作记录成功");
return new ResponseEntity<>(result, HttpStatus.OK);
} else {
result.put("success", false);
result.put("message", "操作记录失败");
return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
}
} catch (Exception e) {
e.printStackTrace();
result.put("success", false);
result.put("message", "操作记录异常: " + e.getMessage());
return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* 从请求中获取管理员认证信息
* @param request HttpServletRequest对象
* @param isSupplySide 是否为采购端
* @return ManagerAuthInfo 认证信息对象
*/
private ManagerAuthInfo getManagerAuthInfoFromRequest(HttpServletRequest request, boolean isSupplySide) {
String managerId = request.getParameter("managerId");
String managercompany = request.getParameter("company");
String managerdepartment = request.getParameter("department");
String organization = request.getParameter("organization");
String role = request.getParameter("role");
String userName = request.getParameter("userName");
String assistant = request.getParameter("assistant");
// URL解码参数
try {
if (managerId != null) managerId = java.net.URLDecoder.decode(managerId, "UTF-8");
if (managercompany != null) managercompany = java.net.URLDecoder.decode(managercompany, "UTF-8");
if (managerdepartment != null) managerdepartment = java.net.URLDecoder.decode(managerdepartment, "UTF-8");
if (organization != null) organization = java.net.URLDecoder.decode(organization, "UTF-8");
if (role != null) role = java.net.URLDecoder.decode(role, "UTF-8");
if (userName != null) userName = java.net.URLDecoder.decode(userName, "UTF-8");
if (assistant != null) assistant = java.net.URLDecoder.decode(assistant, "UTF-8");
} catch (java.io.UnsupportedEncodingException e) {
System.err.println("URL解码失败: " + e.getMessage());
}
// 检查必要参数
if (userName == null || userName.trim().isEmpty()) {
System.out.println("❌ 用户名为空,无法获取认证信息");
return null;
}
// 部门检查
if (managerdepartment == null) {
System.out.println("⚠️ 部门信息为空,使用默认值");
managerdepartment = "";
}
// 智能判断实际端类型:根据部门信息自动调整isSupplySide值
boolean actualIsSupplySide = isSupplySide;
if (managerdepartment.contains("销售")) {
actualIsSupplySide = false; // 销售部门自动判定为销售端
System.out.println("🔄 部门包含'销售',自动调整为销售端");
} else if (managerdepartment.contains("采购")) {
actualIsSupplySide = true; // 采购部门自动判定为采购端
System.out.println("🔄 部门包含'采购',自动调整为采购端");
}
System.out.println("🔍 认证信息检查,实际端类型: " + (actualIsSupplySide ? "采购端" : "销售端") +
",部门: '" + managerdepartment + "'");
System.out.println("✅ 认证信息检查通过");
// 验证公司信息一致性
if (managercompany == null || managercompany.trim().isEmpty()) {
System.out.println("❌ 公司信息为空");
return null;
}
ManagerAuthInfo authInfo = new ManagerAuthInfo(
managerId != null ? managerId : "",
managercompany != null ? managercompany : "",
managerdepartment != null ? managerdepartment : "",
organization != null ? organization : "",
role != null ? role : "",
userName,
assistant != null ? assistant : "");
// 设置实际的端类型
try {
// 尝试通过反射或setter方法设置supplySide属性
java.lang.reflect.Field field = authInfo.getClass().getDeclaredField("supplySide");
field.setAccessible(true);
field.set(authInfo, actualIsSupplySide);
} catch (Exception e) {
System.out.println("ℹ️ 无法设置supplySide属性: " + e.getMessage());
}
System.out.println("🎯 认证信息详情: " +
"系统类型=" + (actualIsSupplySide ? "采购端" : "销售端") +
", 公司=" + authInfo.getManagercompany() +
", 部门=" + authInfo.getManagerdepartment() +
", 组织=" + authInfo.getOrganization() +
", 角色=" + authInfo.getRole() +
", 负责人=" + authInfo.getUserName() +
", 协助人=" + authInfo.getAssistant());
return authInfo;
}
}

70
src/main/java/com/example/web/controller/LoginController.java

@ -1,39 +1,67 @@
package com.example.web.controller;
import com.example.web.entity.Login;
import com.example.web.entity.Personnel;
import com.example.web.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/")
/**
* @Description: 登录控制器
* @Author:
* @Date: 2025-12-17
*/
@Controller
public class LoginController {
@Autowired
private LoginService loginService;
/**
* 处理登录请求
* @param projectName 工位名
* @param userName 用户名
* @param password 密码
* @return 包含登录结果和用户信息的Map
*/
@PostMapping("/logins")
public Map<String, Object> login(@RequestParam String projectName,
@RequestParam String userName,
@ResponseBody
public Map<String, Object> login(@RequestParam String projectName,
@RequestParam String userName,
@RequestParam String password) {
System.out.println("=== Controller收到登录请求 ===");
System.out.println("工位名: " + projectName + ", 用户名: " + userName + ", 密码: " + password);
Map<String, Object> result = loginService.authenticate(projectName, userName, password);
System.out.println("Controller返回结果: " + result);
System.out.println("成功状态: " + result.get("success"));
if (result.get("success").equals(true)) {
System.out.println("权限等级: " + result.get("root"));
System.out.println("Token: " + result.get("token"));
System.out.println("系统类型: " + (Boolean.TRUE.equals(result.get("isSupplySide")) ? "采购端" : "销售端"));
Map<String, Object> user = (Map<String, Object>) result.get("user");
System.out.println("用户信息: " + user);
// 创建响应结果Map
Map<String, Object> result = new HashMap<>();
try {
// 验证登录信息
Login login = loginService.verifyLogin(projectName, userName, password);
if (login != null) {
// 登录成功,获取员工详细信息
Personnel personnel = loginService.getPersonnelInfo(login.getManagerId());
// 设置响应结果
result.put("success", true);
result.put("userInfo", personnel);
result.put("message", "登录成功");
} else {
// 登录失败
result.put("success", false);
result.put("message", "用户名或密码不正确");
}
} catch (Exception e) {
// 处理异常
result.put("success", false);
result.put("message", "登录失败,请稍后重试");
e.printStackTrace();
}
System.out.println(result.get("user"));
return result;
}
}

28
src/main/java/com/example/web/controller/PersonnelController.java

@ -0,0 +1,28 @@
package com.example.web.controller;
import com.example.web.entity.Personnel;
import com.example.web.service.PersonnelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description: 员工信息控制器
*/
@RestController
public class PersonnelController {
@Autowired
private PersonnelService personnelService;
/**
* 根据负责人ID获取员工信息
* @param managerId 负责人ID
* @return Personnel对象
*/
@GetMapping("/api/personnel")
public Personnel getPersonnelByManagerId(@RequestParam String managerId) {
return personnelService.getPersonnelByManagerId(managerId);
}
}

72
src/main/java/com/example/web/controller/PoolCustomerController.java

@ -1,72 +0,0 @@
package com.example.web.controller;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.service.PoolCustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/pool/")
public class PoolCustomerController {
@Autowired
private PoolCustomerService poolCustomerService;
/**
* 根据用户ID获取客户信息
* GET /pool/customers/{userId}
*/
@GetMapping("/customersss/{userId}")
public ResponseEntity<?> getCustomerById(@PathVariable String userId) {
try {
UserProductCartDTO customerInfo = poolCustomerService.getCustomerInfo(userId);
return ResponseEntity.ok(customerInfo);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(createErrorResponse("参数错误", e.getMessage()));
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createErrorResponse("服务器错误", "获取客户信息失败"));
}
}
/**
* 查询客户信息支持按类型过滤
* GET /pool/customers?type=seller&page=1&size=10
*/
@GetMapping("/customersss")
public ResponseEntity<?> getCustomers(
@RequestParam(required = false) String type,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
try {
Map<String, Object> response = new HashMap<>();
response.put("page", page);
response.put("size", size);
response.put("type", type);
response.put("message", "分页查询功能待实现");
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createErrorResponse("查询失败", e.getMessage()));
}
}
/**
* 创建统一的错误响应格式
*/
private Map<String, Object> createErrorResponse(String error, String message) {
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("error", error);
errorResponse.put("message", message);
errorResponse.put("timestamp", System.currentTimeMillis());
return errorResponse;
}
}

996
src/main/java/com/example/web/controller/SupplyCustomerController.java

@ -1,996 +0,0 @@
package com.example.web.controller;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.dto.UnifiedCustomerDTO;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.entity.UsersManagements;
import com.example.web.mapper.SupplyUsersManagementsMapper;
import com.example.web.service.InformationTraService;
import com.example.web.service.SupplyCustomerService;
import com.example.web.service.SupplyPoolCustomerService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/supply/pool")
@CrossOrigin(origins = "*")
public class SupplyCustomerController {
@Autowired
private SupplyCustomerService supplyCustomerService;
@Autowired
private SupplyPoolCustomerService supplyPoolCustomerService;
@Autowired
private SupplyUsersManagementsMapper supplyUsersManagementsMapper;
@Autowired
private com.example.web.mapper.SupplyUsersMapper supplyUsersMapper;
@Autowired
private InformationTraService informationTraService;
/**
* 采购端 - 更新客户通知状态
*/
@PutMapping("/customers/{id}/notice")
public ResponseEntity<Map<String, Object>> updateCustomerNotice(@PathVariable String id) {
try {
// 直接调用mapper更新通知状态
System.out.println("🔄 采购端手动更新通知状态, userId: " + id + ", 从banold改为old");
supplyUsersMapper.updateNotice(id, "old");
// 返回成功响应
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "通知状态更新成功");
return ResponseEntity.ok(response);
} catch (Exception e) {
System.err.println("❌ 采购端更新通知状态失败: " + e.getMessage());
// 返回失败响应
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "通知状态更新失败: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}
/**
* 根据公司ID查询客户详情 - 优先处理wechat数据源
* GET /supply/pool/customers/{id}
*/
@GetMapping("/customers/{id}")
public ResponseEntity<Map<String, Object>> getById(@PathVariable String id, HttpServletRequest request) {
System.out.println("====================================================");
System.out.println("🔍 查询客户: " + id);
System.out.println("====================================================");
Map<String, Object> response = new HashMap<>();
try {
// 从URL参数获取用户类型,与前端保持一致
String isSupplySideParam = request.getParameter("isSupplySide");
boolean isSupplySide = !"false".equalsIgnoreCase(isSupplySideParam);
System.out.println("🎯 根据URL参数选择认证类型: " + (isSupplySide ? "采购端" : "销售端"));
// 从URL参数中获取ManagerAuthInfo
ManagerAuthInfo authInfo = getManagerAuthInfoFromRequest(request, isSupplySide);
if (authInfo == null) {
System.out.println("❌ 未找到用户认证信息");
response.put("success", false);
response.put("message", "用户未登录或认证信息缺失");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
}
System.out.println("✅ 获取到用户认证信息: " + authInfo.getUserName());
// 🎯 重构:完全基于数据存在性判断,彻底消除对ID格式的依赖
System.out.println("🎯 开始查询双数据源(优先wechat)...");
// 1. 首先尝试查询wechat数据源
System.out.println("📊 优先查询 WECHAT 数据源...");
UserProductCartDTO userInfo = supplyPoolCustomerService.getPublicSeaCustomerInfo(id);
if (userInfo != null) {
System.out.println("✅ 在wechat数据源中找到客户基础信息");
// 使用统一的公海池判断逻辑
boolean isPublicSea = supplyPoolCustomerService.isPublicSeaCustomerPublic(userInfo, authInfo);
System.out.println("📊 公海池判断结果: " + isPublicSea);
if (isPublicSea) {
System.out.println("🎯 识别为公海池客户,返回 WECHAT 数据源数据");
UnifiedCustomerDTO customer = convertToUnifiedDTOForPublicSea(userInfo, request);
customer.setDataSource("wechat");
response.put("success", true);
response.put("data", customer);
// 记录查看客户详情操作
informationTraService.recordOperationEvent(
customer.getPhoneNumber(),
customer.getUserName() != null ? customer.getUserName() : "",
customer.getPhoneNumber() + "-查看客户",
authInfo.getManagercompany(),
authInfo.getManagerdepartment(),
authInfo.getOrganization(),
authInfo.getRole(),
authInfo.getUserName(),
authInfo.getAssistant()
);
return ResponseEntity.ok(response);
}
// 如果不是公海池客户,检查权限并返回wechat数据源的非公海池客户
System.out.println("📊 处理 WECHAT 数据源非公海池客户...");
boolean hasPermission = supplyCustomerService.hasPermissionToViewWechatCustomer(userInfo, authInfo);
if (hasPermission) {
System.out.println("✅ 有权限查看非公海池客户,返回 WECHAT 数据源数据");
UnifiedCustomerDTO customer = convertToUnifiedDTOForPublicSea(userInfo, request);
customer.setDataSource("wechat");
response.put("success", true);
response.put("data", customer);
// 记录查看客户详情操作
informationTraService.recordOperationEvent(
customer.getPhoneNumber(),
customer.getUserName() != null ? customer.getUserName() : "",
customer.getPhoneNumber() + "-查看客户",
authInfo.getManagercompany(),
authInfo.getManagerdepartment(),
authInfo.getOrganization(),
authInfo.getRole(),
authInfo.getUserName(),
authInfo.getAssistant()
);
return ResponseEntity.ok(response);
} else {
System.out.println("❌ 无权限查看wechat数据源的非公海池客户");
// 继续查询默认数据源
}
} else {
System.out.println("ℹ️ 在wechat数据源中未找到客户");
}
// 2. 如果wechat数据源没找到或无权限,再尝试查询默认数据源
System.out.println("📊 查询 DEFAULT 数据源...");
UnifiedCustomerDTO defaultCustomer = supplyCustomerService.getCustomerById(id, authInfo);
if (defaultCustomer != null) {
System.out.println("✅ 在默认数据源中找到客户");
response.put("success", true);
response.put("data", defaultCustomer);
// 记录查看客户详情操作
informationTraService.recordOperationEvent(
defaultCustomer.getPhoneNumber(),
defaultCustomer.getUserName() != null ? defaultCustomer.getUserName() : "",
defaultCustomer.getPhoneNumber() + "-查看客户",
authInfo.getManagercompany(),
authInfo.getManagerdepartment(),
authInfo.getOrganization(),
authInfo.getRole(),
authInfo.getUserName(),
authInfo.getAssistant()
);
return ResponseEntity.ok(response);
}
// 3. 如果两个数据源都没找到
System.out.println("❌ 未在任何数据源中找到客户信息");
response.put("success", false);
response.put("message", "未找到对应的客户信息");
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} catch (Exception e) {
System.err.println("❌ 服务器错误: " + e.getMessage());
e.printStackTrace();
response.put("success", false);
response.put("message", "服务器错误:" + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
/**
* 从请求中获取ManagerAuthInfo - 增强版本严格区分销售端和采购端
*/
private ManagerAuthInfo getManagerAuthInfoFromRequest(HttpServletRequest request, boolean isSupplySide) {
String managerId = request.getParameter("managerId");
String managercompany = request.getParameter("company");
String managerdepartment = request.getParameter("department");
String organization = request.getParameter("organization");
String role = request.getParameter("role");
String userName = request.getParameter("userName");
String assistant = request.getParameter("assistant");
// URL解码参数
try {
if (managerId != null) managerId = java.net.URLDecoder.decode(managerId, "UTF-8");
if (managercompany != null) managercompany = java.net.URLDecoder.decode(managercompany, "UTF-8");
if (managerdepartment != null) managerdepartment = java.net.URLDecoder.decode(managerdepartment, "UTF-8");
if (organization != null) organization = java.net.URLDecoder.decode(organization, "UTF-8");
if (role != null) role = java.net.URLDecoder.decode(role, "UTF-8");
if (userName != null) userName = java.net.URLDecoder.decode(userName, "UTF-8");
if (assistant != null) assistant = java.net.URLDecoder.decode(assistant, "UTF-8");
} catch (java.io.UnsupportedEncodingException e) {
System.err.println("URL解码失败: " + e.getMessage());
}
// 检查必要参数
if (userName == null || userName.trim().isEmpty()) {
System.out.println("❌ 用户名为空,无法获取认证信息");
return null;
}
// 部门检查
if (managerdepartment == null) {
System.out.println("⚠️ 部门信息为空,使用默认值");
managerdepartment = "";
}
// 智能判断实际端类型:根据部门信息自动调整isSupplySide值
boolean actualIsSupplySide = isSupplySide;
if (managerdepartment.contains("销售")) {
actualIsSupplySide = false; // 销售部门自动判定为销售端
System.out.println("🔄 部门包含'销售',自动调整为销售端");
} else if (managerdepartment.contains("采购")) {
actualIsSupplySide = true; // 采购部门自动判定为采购端
System.out.println("🔄 部门包含'采购',自动调整为采购端");
}
System.out.println("🔍 认证信息检查,实际端类型: " + (actualIsSupplySide ? "采购端" : "销售端") +
",部门: '" + managerdepartment + "'");
System.out.println("✅ 认证信息检查通过");
// 验证公司信息一致性
if (managercompany == null || managercompany.trim().isEmpty()) {
System.out.println("❌ 公司信息为空");
return null;
}
ManagerAuthInfo authInfo = new ManagerAuthInfo(
managerId != null ? managerId : "",
managercompany != null ? managercompany : "",
managerdepartment != null ? managerdepartment : "",
organization != null ? organization : "",
role != null ? role : "",
userName,
assistant != null ? assistant : "");
// 设置实际的端类型
try {
// 尝试通过反射或setter方法设置supplySide属性
java.lang.reflect.Field field = authInfo.getClass().getDeclaredField("supplySide");
field.setAccessible(true);
field.set(authInfo, actualIsSupplySide);
} catch (Exception e) {
System.out.println("ℹ️ 无法设置supplySide属性: " + e.getMessage());
}
System.out.println("🎯 认证信息详情: " +
"系统类型=" + (actualIsSupplySide ? "采购端" : "销售端") +
", 公司=" + authInfo.getManagercompany() +
", 部门=" + authInfo.getManagerdepartment() +
", 组织=" + authInfo.getOrganization() +
", 角色=" + authInfo.getRole() +
", 负责人=" + authInfo.getUserName() +
", 协助人=" + authInfo.getAssistant());
return authInfo;
}
/**
* 查找选中的产品项 - 修复版本
* 优先使用前端传递的选中ID如果没有则使用第一个
*/
private UserProductCartDTO.ProductInfo findSelectedProduct(UserProductCartDTO userInfo, String targetProductItemId) {
if (userInfo.getProducts() == null || userInfo.getProducts().isEmpty()) {
return null;
}
// 🔥 关键修复:优先使用前端传递的选中产品项ID
if (targetProductItemId != null && !targetProductItemId.trim().isEmpty()) {
for (UserProductCartDTO.ProductInfo product : userInfo.getProducts()) {
if (targetProductItemId.equals(product.getProductId())) {
System.out.println("✅ 找到前端选中的产品项: " + product.getProductId());
return product;
}
}
System.out.println("⚠️ 未找到前端指定的产品项: " + targetProductItemId);
}
// 🔥 其次检查是否有标记为选中的产品项
for (UserProductCartDTO.ProductInfo product : userInfo.getProducts()) {
if (isProductSelected(product)) {
System.out.println("✅ 找到标记为选中的产品项: " + product.getProductId());
return product;
}
}
// 🔥 最后才返回第一个产品项
UserProductCartDTO.ProductInfo firstItem = userInfo.getProducts().get(0);
System.out.println("🔄 使用第一个产品项作为默认: " + firstItem.getProductId());
return firstItem;
}
/**
* 判断产品项是否被选中根据业务逻辑
*/
private boolean isProductSelected(UserProductCartDTO.ProductInfo product) {
// 这里可以根据业务需求实现选中逻辑
// 例如:通过某个字段标识,或者外部传入的参数
// 临时方案:默认返回false,使用第一个产品项
return false;
}
/**
* 专门为公海池客户转换DTO的方法 - 采购端使用产品信息修复版本
* 新增HttpServletRequest参数用于获取选中的产品项ID
*/
private UnifiedCustomerDTO convertToUnifiedDTOForPublicSea(UserProductCartDTO userInfo, HttpServletRequest request) {
UnifiedCustomerDTO dto = new UnifiedCustomerDTO();
// 设置用户基础信息
dto.setId(userInfo.getUserId() != null ? userInfo.getUserId() : "");
dto.setPhoneNumber(userInfo.getPhoneNumber() != null ? userInfo.getPhoneNumber() : "");
// 设置公司信息
dto.setCompany(userInfo.getCompany() != null ? userInfo.getCompany() : "");
dto.setRegion(userInfo.getRegion() != null ? userInfo.getRegion() : "");
// 设置需求和规格
dto.setDemand(userInfo.getDemand() != null ? userInfo.getDemand() : "");
dto.setSpec(userInfo.getSpec() != null ? userInfo.getSpec() : "");
// 类型转换
String frontendType = convertDatabaseTypeToFrontend(userInfo.getType());
dto.setType(frontendType != null ? frontendType : "");
dto.setNickName(userInfo.getNickName() != null ? userInfo.getNickName() : "");
dto.setLevel(userInfo.getLevel() != null ? userInfo.getLevel() : "公海池");
dto.setCreated_at(userInfo.getCreated_at());
dto.setUpdated_at(userInfo.getUpdated_at());
// 添加通知信息转换
dto.setNotice(userInfo.getNotice() != null ? userInfo.getNotice() : "");
// 关键:设置数据源标识为wechat
dto.setDataSource("wechat");
// 设置联系人信息 - 从usersContacts获取
setContactInfoSafely(dto, userInfo);
// 设置负责人信息
setManagerInfoSafely(dto, userInfo.getUserId());
// 设置公海需求信息 - 从产品表获取(采购端)
if (userInfo.getProducts() != null && !userInfo.getProducts().isEmpty()) {
// 🔥 关键修复:从HttpServletRequest中获取目标产品项ID
String targetProductItemId = getTargetProductItemIdFromRequest(request);
UserProductCartDTO.ProductInfo selectedProduct = findSelectedProduct(userInfo, targetProductItemId);
// 设置到单个字段 - 使用选中的产品项
dto.setProductName(selectedProduct.getProductName() != null ? selectedProduct.getProductName() : "");
dto.setVariety(selectedProduct.getVariety() != null ? selectedProduct.getVariety() : "");
dto.setSpecification(selectedProduct.getSpecification() != null ? selectedProduct.getSpecification() : "");
dto.setQuantity(selectedProduct.getQuantity() != null ? selectedProduct.getQuantity() : 0);
dto.setGrossWeight(selectedProduct.getGrossWeight() != null ? selectedProduct.getGrossWeight() : "");
dto.setYolk(selectedProduct.getYolk() != null ? selectedProduct.getYolk() : "");
// 🔥 关键修改:设置选中的产品项ID
dto.setTargetProductItemId(selectedProduct.getProductId() != null ? selectedProduct.getProductId() : "");
// 设置customDetails为产品信息
dto.setCustomDetails(userInfo.getProducts().toArray());
System.out.println("✅ 成功设置选中产品项到DTO: " + selectedProduct.getProductName() +
", 产品项ID: " + selectedProduct.getProductId() +
", 是否前端指定: " + (targetProductItemId != null));
} else {
// 如果没有产品信息,设置默认值
setDefaultProductValues(dto);
System.out.println("⚠️ 没有产品信息,使用默认值");
}
// 设置 productItems 到 UnifiedCustomerDTO - 采购端关键修复
if (userInfo.getProducts() != null && !userInfo.getProducts().isEmpty()) {
List<UnifiedCustomerDTO.ProductItem> unifiedProductItems = userInfo.getProducts().stream()
.map(product -> {
UnifiedCustomerDTO.ProductItem unifiedItem = new UnifiedCustomerDTO.ProductItem();
unifiedItem.setProductId(product.getProductId() != null ? product.getProductId() : "");
unifiedItem.setProductName(product.getProductName() != null ? product.getProductName() : "");
unifiedItem.setVariety(product.getVariety() != null ? product.getVariety() : "");
unifiedItem.setSpecification(product.getSpecification() != null ? product.getSpecification() : "");
unifiedItem.setQuantity(product.getQuantity() != null ? product.getQuantity() : 0);
unifiedItem.setGrossWeight(product.getGrossWeight() != null ? product.getGrossWeight() : "");
unifiedItem.setYolk(product.getYolk() != null ? product.getYolk() : "");
return unifiedItem;
})
.collect(Collectors.toList());
dto.setProductItems(unifiedProductItems);
System.out.println("✅ 成功设置 productItems 到 UnifiedCustomerDTO,数量: " + unifiedProductItems.size());
} else {
System.out.println("❌ 没有产品数据可设置到 productItems");
}
return dto;
}
/**
* 从请求参数中获取目标产品项ID - 完整实现
*/
private String getTargetProductItemIdFromRequest(HttpServletRequest request) {
try {
if (request == null) {
System.out.println("⚠️ HttpServletRequest为null");
return null;
}
// 1. 首先从URL查询参数中获取
String targetProductItemId = request.getParameter("targetProductItemId");
if (targetProductItemId != null && !targetProductItemId.trim().isEmpty()) {
System.out.println("🎯 从URL参数获取到目标产品项ID: " + targetProductItemId);
return targetProductItemId.trim();
}
// 2. 如果URL参数中没有,尝试从请求属性中获取
Object attributeValue = request.getAttribute("targetProductItemId");
if (attributeValue != null) {
String productItemId = attributeValue.toString();
System.out.println("🎯 从请求属性获取到目标产品项ID: " + productItemId);
return productItemId;
}
// 3. 尝试从Header中获取
String headerValue = request.getHeader("X-Target-ProductItem-Id");
if (headerValue != null && !headerValue.trim().isEmpty()) {
System.out.println("🎯 从Header获取到目标产品项ID: " + headerValue);
return headerValue.trim();
}
System.out.println("⚠️ 未从请求中获取到目标产品项ID");
return null;
} catch (Exception e) {
System.err.println("❌ 获取目标产品项ID失败: " + e.getMessage());
return null;
}
}
/**
* 安全设置负责人信息
*/
private void setManagerInfoSafely(UnifiedCustomerDTO dto, String userId) {
try {
System.out.println("🔍 查询用户负责人信息,用户ID: " + userId);
UsersManagements userManager = supplyUsersManagementsMapper.findByUserId(userId);
if (userManager != null) {
System.out.println("✅ 找到负责人信息,设置到DTO");
// 设置负责人信息到统一的DTO字段
dto.setManagercompany(userManager.getManagercompany() != null ? userManager.getManagercompany() : "");
dto.setManagerdepartment(userManager.getManagerdepartment() != null ? userManager.getManagerdepartment() : "");
dto.setOrganization(userManager.getOrganization() != null ? userManager.getOrganization() : "");
dto.setRole(userManager.getRole() != null ? userManager.getRole() : "");
dto.setUserName(userManager.getUserName() != null ? userManager.getUserName() : "");
dto.setAssistant(userManager.getAssistant() != null ? userManager.getAssistant() : "");
// 调试日志
System.out.println("📝 负责人信息详情: " +
"公司=" + dto.getManagercompany() +
", 部门=" + dto.getManagerdepartment() +
", 组织=" + dto.getOrganization() +
", 角色=" + dto.getRole() +
", 负责人=" + dto.getUserName() +
", 协助人=" + dto.getAssistant());
} else {
System.out.println("⚠️ 未找到负责人信息,用户可能为公海池客户");
// 设置默认值
setDefaultManagerValues(dto);
}
} catch (Exception e) {
System.err.println("❌ 查询负责人信息失败: " + e.getMessage());
// 发生异常时设置默认值
setDefaultManagerValues(dto);
}
}
/**
* 设置默认负责人值
*/
private void setDefaultManagerValues(UnifiedCustomerDTO dto) {
dto.setManagercompany("");
dto.setManagerdepartment("");
dto.setOrganization("");
dto.setRole("");
dto.setUserName("");
dto.setAssistant("");
}
/**
* 设置默认产品值
*/
private void setDefaultProductValues(UnifiedCustomerDTO dto) {
dto.setProductName("");
dto.setVariety("");
dto.setSpecification("");
dto.setQuantity(0);
dto.setGrossWeight("");
dto.setYolk("");
}
/**
* 安全设置联系人信息
*/
private void setContactInfoSafely(UnifiedCustomerDTO dto, UserProductCartDTO userInfo) {
if (userInfo.getUsersContacts() != null && !userInfo.getUsersContacts().isEmpty()) {
UserProductCartDTO.UsersContacts contact = userInfo.getUsersContacts().get(0);
dto.setWechat(contact.getWechat() != null ? contact.getWechat() : "");
dto.setAccount(contact.getAccount() != null ? contact.getAccount() : "");
dto.setAccountNumber(contact.getAccountNumber() != null ? contact.getAccountNumber() : "");
dto.setBank(contact.getBank() != null ? contact.getBank() : "");
dto.setAddress(contact.getAddress() != null ? contact.getAddress() : "");
} else {
setDefaultContactValues(dto);
}
}
/**
* 设置默认联系人值
*/
private void setDefaultContactValues(UnifiedCustomerDTO dto) {
dto.setWechat("");
dto.setAccount("");
dto.setAccountNumber("");
dto.setBank("");
dto.setAddress("");
}
/**
* 数据库类型转前端类型
*/
private String convertDatabaseTypeToFrontend(String databaseType) {
if (databaseType == null) {
return null;
}
switch (databaseType) {
case "seller":
return "供应端";
case "buyer":
return "客户端";
case "both":
return "BOTH";
default:
return databaseType;
}
}
/**
* 根据手机号查询客户详情
*/
@GetMapping("/customers/by-phone")
public ResponseEntity<Map<String, Object>> getByPhone(@RequestParam String phoneNumber, HttpServletRequest request) {
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
System.out.println("getByPhone: ---------------------------------------------------" + phoneNumber);
Map<String, Object> response = new HashMap<>();
try {
// 从URL参数获取用户类型,与前端保持一致
String isSupplySideParam = request.getParameter("isSupplySide");
boolean isSupplySide = !"false".equalsIgnoreCase(isSupplySideParam);
System.out.println("🎯 根据URL参数选择认证类型: " + (isSupplySide ? "采购端" : "销售端"));
// 从URL参数获取认证信息
ManagerAuthInfo authInfo = getManagerAuthInfoFromRequest(request, isSupplySide);
if (authInfo == null) {
response.put("success", false);
response.put("message", "用户未登录或认证信息缺失");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
}
UnifiedCustomerDTO customer = supplyCustomerService.getCustomerByPhone(phoneNumber, authInfo);
// 检查客户等级,如果是公海池客户,确保数据源正确
if (customer != null && customer.getLevel() != null &&
(customer.getLevel().contains("sea-pools") || "公海池".equals(customer.getLevel()))) {
customer.setDataSource("wechat");
}
response.put("success", true);
response.put("data", customer);
return ResponseEntity.ok(response);
} catch (IllegalArgumentException e) {
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
} catch (RuntimeException e) {
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.notFound().build();
} catch (Exception e) {
response.put("success", false);
response.put("message", "服务器错误:" + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
/**
* 更新基于电话号码的客户信息专门用于公海池客户- 采购员权限只允许更新为seller和both类型
*/
@PutMapping("/phone-customers/update")
public ResponseEntity<Map<String, Object>> updatePhoneCustomer(@RequestBody UnifiedCustomerDTO updatedDTO, HttpServletRequest request) {
Map<String, Object> response = new HashMap<>();
try {
// 从URL参数获取用户类型,与前端保持一致
String isSupplySideParam = request.getParameter("isSupplySide");
boolean isSupplySide = !"false".equalsIgnoreCase(isSupplySideParam);
System.out.println("🎯 根据URL参数选择认证类型: " + (isSupplySide ? "采购端" : "销售端"));
// 获取认证信息
ManagerAuthInfo authInfo = getManagerAuthInfoFromRequest(request, isSupplySide);
if (authInfo == null) {
response.put("success", false);
response.put("message", "用户未登录或认证信息缺失");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
}
System.out.println("======= 开始处理公海池客户更新(wechat数据源)- 采购端 =======");
System.out.println("手机号: " + updatedDTO.getPhoneNumber());
System.out.println("原始等级: " + updatedDTO.getLevel());
System.out.println("负责人信息: " + authInfo.getUserName());
// 🔥 在Controller层先进行类型转换
System.out.println("🔄 Controller层类型转换前: " + updatedDTO.getType());
String originalType = updatedDTO.getType();
String convertedType = convertCustomerTypeInController(originalType);
updatedDTO.setType(convertedType);
System.out.println("🔄 Controller层类型转换后: " + originalType + " → " + convertedType);
System.out.println("🔐 负责人认证信息: " +
"公司=" + authInfo.getManagercompany() +
", 部门=" + authInfo.getManagerdepartment() +
", 组织=" + authInfo.getOrganization() +
", 角色=" + authInfo.getRole() +
", 负责人=" + authInfo.getUserName() +
", 协助人=" + authInfo.getAssistant());
// 采购员权限:校验客户类型,只允许seller和both
if (!"seller".equals(updatedDTO.getType()) && !"both".equals(updatedDTO.getType())) {
System.out.println("❌ 权限校验失败:采购员只能更新为seller或both类型,当前类型: " + updatedDTO.getType());
response.put("success", false);
response.put("message", "采购员权限只能更新为供应端类型客户");
return ResponseEntity.badRequest().body(response);
}
// 调用服务层 - 传递authInfo用于负责人信息更新
System.out.println("🔄 调用Service更新方法...");
boolean success = supplyCustomerService.updatePhoneBasedCustomer(updatedDTO, authInfo);
System.out.println("✅ Service返回结果: " + success);
if (success) {
System.out.println("🎉 公海池客户信息更新成功");
response.put("success", true);
response.put("message", "客户信息更新成功");
// 记录修改客户信息操作
informationTraService.recordOperationEvent(
updatedDTO.getPhoneNumber(),
updatedDTO.getUserName() != null ? updatedDTO.getUserName() : "",
updatedDTO.getPhoneNumber() + "-更新客户",
authInfo.getManagercompany(),
authInfo.getManagerdepartment(),
authInfo.getOrganization(),
authInfo.getRole(),
authInfo.getUserName(),
authInfo.getAssistant()
);
return ResponseEntity.ok(response);
} else {
System.out.println("❌ 公海池客户信息更新失败");
response.put("success", false);
response.put("message", "客户信息更新失败");
return ResponseEntity.internalServerError().body(response);
}
} catch (Exception e) {
System.err.println("💥 更新公海池客户信息异常: " + e.getMessage());
e.printStackTrace();
response.put("success", false);
response.put("message", "服务器错误:" + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
/**
* Controller层的类型转换方法 - 采购端
*/
private String convertCustomerTypeInController(String originalType) {
if (originalType == null) {
return "seller";
}
switch (originalType.toUpperCase()) {
case "BOTH":
return "both";
case "客户端":
case "BUYER":
return "buyer";
case "供应端":
case "SELLER":
return "seller";
default:
return originalType;
}
}
/**
* 更新基于电话号码的客户信息专门用于公海池客户- 采购员权限只允许更新为seller和both类型
*/
@PutMapping("/customers/update")
public ResponseEntity<Map<String, Object>> updateCustomer(
@RequestBody UnifiedCustomerDTO updatedDTO, HttpServletRequest request) {
Map<String, Object> response = new HashMap<>();
try {
System.out.println("======= 🔄 开始更新非公海池客户信息(采购端) =======");
System.out.println("数据源: " + updatedDTO.getDataSource());
System.out.println("客户ID: " + updatedDTO.getId());
System.out.println("手机号: " + updatedDTO.getPhoneNumber());
System.out.println("客户类型(转换前): " + updatedDTO.getType());
System.out.println("客户等级: " + updatedDTO.getLevel());
System.out.println("公司: " + updatedDTO.getCompany());
System.out.println("区域: " + updatedDTO.getRegion());
System.out.println("昵称: " + updatedDTO.getNickName());
System.out.println("微信: " + updatedDTO.getWechat());
System.out.println("地址: " + updatedDTO.getAddress());
// 打印负责人信息
System.out.println("=== 负责人信息 ===");
System.out.println("负责人公司: " + updatedDTO.getManagercompany());
System.out.println("负责人部门: " + updatedDTO.getManagerdepartment());
System.out.println("负责人组织: " + updatedDTO.getOrganization());
System.out.println("负责人角色: " + updatedDTO.getRole());
System.out.println("负责人姓名: " + updatedDTO.getUserName());
System.out.println("协助人: " + updatedDTO.getAssistant());
// 🎯 关键修复:先进行数据源验证和修正
String originalDataSource = updatedDTO.getDataSource();
String verifiedDataSource = verifyAndCorrectDataSource(updatedDTO);
if (!originalDataSource.equals(verifiedDataSource)) {
System.out.println("🔄 数据源自动修正: " + originalDataSource + " → " + verifiedDataSource);
updatedDTO.setDataSource(verifiedDataSource);
}
// 🎯 关键修复:从URL参数获取用户类型,与前端保持一致
String isSupplySideParam = request.getParameter("isSupplySide");
boolean isSupplySide = !"false".equalsIgnoreCase(isSupplySideParam);
System.out.println("🎯 根据URL参数选择认证类型: " + (isSupplySide ? "采购端" : "销售端"));
// 获取认证信息 - 根据URL参数选择
ManagerAuthInfo authInfo = getManagerAuthInfoFromRequest(request, isSupplySide);
if (authInfo == null) {
response.put("success", false);
response.put("message", "用户未登录或认证信息缺失");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
}
// 关键修复:添加客户类型转换逻辑
System.out.println("🔄 Controller层类型转换前: " + updatedDTO.getType());
String originalType = updatedDTO.getType();
String convertedType = convertCustomerTypeInController(originalType);
updatedDTO.setType(convertedType);
System.out.println("🔄 Controller层类型转换后: " + originalType + " → " + convertedType);
System.out.println("=== 数据验证 ===");
System.out.println("手机号是否为空: " + (updatedDTO.getPhoneNumber() == null || updatedDTO.getPhoneNumber().trim().isEmpty()));
System.out.println("客户类型是否有效: " + ("seller".equals(updatedDTO.getType()) || "both".equals(updatedDTO.getType())));
System.out.println("=========================================");
// 使用修正后的数据源
String dataSource = verifiedDataSource;
System.out.println("🔄 使用修正后的数据源: " + dataSource);
// 权限校验
System.out.println("🔐 ====== 开始权限校验 ======");
System.out.println("🔐 当前类型: " + updatedDTO.getType());
if (!"seller".equals(updatedDTO.getType()) && !"both".equals(updatedDTO.getType())) {
System.out.println("❌ 权限校验失败:采购员只能更新为seller或both类型,当前类型: " + updatedDTO.getType());
response.put("success", false);
response.put("message", "采购员权限只能更新为供应端类型客户");
return ResponseEntity.badRequest().body(response);
}
System.out.println("✅ 权限校验通过");
boolean success;
// 🎯 关键修复:根据修正后的数据源处理更新
if ("wechat".equals(dataSource)) {
System.out.println("🎯 使用wechat数据源更新客户信息");
if (updatedDTO.getPhoneNumber() == null || updatedDTO.getPhoneNumber().trim().isEmpty()) {
throw new IllegalArgumentException("更新微信数据源时手机号不能为空");
}
// 🚨 重要:检查客户是否真的在wechat数据源中
UserProductCartDTO wechatCustomer = supplyPoolCustomerService.getWechatCustomerByPhone(updatedDTO.getPhoneNumber());
if (wechatCustomer == null) {
System.out.println("❌ wechat数据源中未找到该客户,手机号: " + updatedDTO.getPhoneNumber());
response.put("success", false);
response.put("message", "微信数据源中未找到该客户信息");
return ResponseEntity.badRequest().body(response);
}
// 🎯 关键修复:确保传递正确的负责人信息处理标志
success = supplyCustomerService.updateWechatCustomer(updatedDTO, authInfo, true);
} else if ("default".equals(dataSource)) {
System.out.println("🎯 使用默认数据源更新");
if (updatedDTO.getId() == null || updatedDTO.getId().trim().isEmpty()) {
throw new IllegalArgumentException("更新默认数据源时公司ID不能为空");
}
// 检查default数据源中客户是否存在
boolean exists = supplyCustomerService.checkDefaultCustomerExists(updatedDTO.getId());
if (!exists) {
System.out.println("❌ default数据源中不存在ID为" + updatedDTO.getId() + "的客户");
// 尝试检查该客户是否存在于wechat数据源
boolean existsInWechat = false;
try {
// 先尝试通过手机号查找
if (updatedDTO.getPhoneNumber() != null && !updatedDTO.getPhoneNumber().trim().isEmpty()) {
System.out.println("🔍 尝试通过手机号在wechat数据源中查找客户: " + updatedDTO.getPhoneNumber());
UnifiedCustomerDTO wechatCustomerDTO = supplyCustomerService.getCustomerByPhone(updatedDTO.getPhoneNumber(), authInfo);
if (wechatCustomerDTO != null) {
existsInWechat = true;
System.out.println("✅ 在wechat数据源中找到客户");
}
}
// 如果手机号查找失败,尝试通过ID查找
if (!existsInWechat && updatedDTO.getId() != null && !updatedDTO.getId().trim().isEmpty()) {
System.out.println("🔍 尝试通过ID在wechat数据源中查找客户: " + updatedDTO.getId());
UnifiedCustomerDTO wechatCustomerDTO = supplyCustomerService.getWechatCustomerById(updatedDTO.getId(), authInfo);
if (wechatCustomerDTO != null) {
existsInWechat = true;
System.out.println("✅ 在wechat数据源中找到客户");
}
}
} catch (Exception e) {
System.err.println("⚠️ 检查wechat数据源时出错: " + e.getMessage());
}
if (existsInWechat) {
System.out.println("🔄 自动切换到wechat数据源进行更新");
updatedDTO.setDataSource("wechat");
success = supplyCustomerService.updateWechatCustomer(updatedDTO, authInfo, true);
response.put("success", true);
response.put("message", "客户信息更新成功(自动切换到正确的数据源)");
return ResponseEntity.ok(response);
} else {
response.put("success", false);
response.put("message", "默认数据源中未找到该客户,请检查ID或数据源是否正确");
return ResponseEntity.badRequest().body(response);
}
}
success = supplyCustomerService.updateDefaultCustomer(updatedDTO, authInfo);
// 验证更新结果
if (!success) {
System.out.println("❌ SQL执行成功但未更新任何记录,可能数据未变化或记录不存在");
response.put("success", false);
response.put("message", "更新失败,未找到可更新的客户记录(可能数据无变化或ID错误)");
return ResponseEntity.internalServerError().body(response);
}
} else {
System.out.println("❌ 不支持的数据源: " + dataSource);
response.put("success", false);
response.put("message", "不支持的数据源: " + dataSource);
return ResponseEntity.badRequest().body(response);
}
// 记录更新客户信息操作
informationTraService.recordOperationEvent(
updatedDTO.getPhoneNumber(),
updatedDTO.getNickName() != null ? updatedDTO.getNickName() : "",
updatedDTO.getPhoneNumber() + "-更新客户",
updatedDTO.getManagercompany() != null ? updatedDTO.getManagercompany() : "",
updatedDTO.getManagerdepartment() != null ? updatedDTO.getManagerdepartment() : "",
updatedDTO.getOrganization() != null ? updatedDTO.getOrganization() : "",
updatedDTO.getRole() != null ? updatedDTO.getRole() : "",
updatedDTO.getUserName() != null ? updatedDTO.getUserName() : "",
updatedDTO.getAssistant() != null ? updatedDTO.getAssistant() : ""
);
// 🎯 关键修复:返回修正后的数据源信息给前端
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("message", "客户信息更新成功");
result.put("correctedDataSource", verifiedDataSource);
result.put("originalDataSource", originalDataSource);
if (!originalDataSource.equals(verifiedDataSource)) {
result.put("dataSourceCorrected", true);
result.put("message", "客户信息更新成功(数据源已自动修正)");
}
return ResponseEntity.ok(result);
} catch (IllegalArgumentException e) {
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
} catch (RuntimeException e) {
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.notFound().build();
} catch (Exception e) {
System.err.println("更新客户信息失败: " + e.getMessage());
e.printStackTrace();
response.put("success", false);
response.put("message", "服务器错误:" + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
/**
* 验证并修正数据源
* 根据客户ID和手机号自动检测客户实际所在的数据源
*/
private String verifyAndCorrectDataSource(UnifiedCustomerDTO updatedDTO) {
String dataSource = updatedDTO.getDataSource();
String customerId = updatedDTO.getId();
String phoneNumber = updatedDTO.getPhoneNumber();
System.out.println("🔍 开始验证数据源...");
System.out.println("📱 手机号: " + phoneNumber);
System.out.println("🆔 客户ID: " + customerId);
System.out.println("📊 前端指定数据源: " + dataSource);
// 🔥 关键修复:尊重前端指定的数据源,只在明确错误时修正
if ("default".equals(dataSource)) {
// 检查default数据源中客户是否存在
boolean existsInDefault = supplyCustomerService.checkDefaultCustomerExists(customerId);
if (existsInDefault) {
System.out.println("✅ 客户确实在default数据源中,保持数据源不变");
return "default";
} else {
System.out.println("⚠️ default数据源中未找到客户,尝试检查wechat数据源");
// 只有在default数据源不存在时才检查wechat
try {
UserProductCartDTO wechatCustomer = supplyPoolCustomerService.getWechatCustomerByPhone(phoneNumber);
if (wechatCustomer != null) {
System.out.println("🔄 检测到客户存在于wechat数据源,自动修正数据源");
return "wechat";
}
} catch (Exception e) {
System.err.println("❌ 检查wechat数据源失败: " + e.getMessage());
}
}
}
// 保持原数据源
System.out.println("✅ 保持原数据源: " + dataSource);
return dataSource;
}
}

63
src/main/java/com/example/web/controller/SupplyCustomerRecycleController.java

@ -1,63 +0,0 @@
package com.example.web.controller;
import com.example.web.service.SupplyCustomerRecycleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/supply/pool")
public class SupplyCustomerRecycleController {
@Autowired
private SupplyCustomerRecycleService supplycustomerRecycleService;
/**
* 手动触发客户回流
* POST /supply/pool/customers/recycle
*/
@PostMapping("/customers/recycle")
public ResponseEntity<Map<String, Object>> manualRecycle() {
Map<String, Object> response = new HashMap<>();
try {
supplycustomerRecycleService.manualRecycle();
response.put("success", true);
response.put("message", "客户回流任务执行成功");
response.put("config", supplycustomerRecycleService.getRecycleConfigInfo());
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("message", "客户回流任务执行失败: " + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
/**
* 获取回流配置信息
* GET /supply/pool/customers/recycle/config
*/
@GetMapping("/customers/recycle/config")
public ResponseEntity<Map<String, Object>> getRecycleConfig() {
Map<String, Object> response = new HashMap<>();
try {
response.put("success", true);
response.put("config", supplycustomerRecycleService.getRecycleConfigInfo());
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("message", "获取回流配置失败: " + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}
}

1107
src/main/java/com/example/web/controller/SupplyEnterpriseController.java

File diff suppressed because it is too large

73
src/main/java/com/example/web/controller/SupplyPoolCustomerController.java

@ -1,73 +0,0 @@
package com.example.web.controller;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.service.PoolCustomerService;
import com.example.web.service.SupplyPoolCustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/supply/pool/")
public class SupplyPoolCustomerController {
@Autowired
private SupplyPoolCustomerService supplypoolCustomerService;
/**
* 根据用户ID获取客户信息
* GET /pool/customers/{userId}
*/
@GetMapping("/customersss/{userId}")
public ResponseEntity<?> getCustomerById(@PathVariable String userId) {
try {
UserProductCartDTO customerInfo = supplypoolCustomerService.getCustomerInfo(userId);
return ResponseEntity.ok(customerInfo);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(createErrorResponse("参数错误", e.getMessage()));
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createErrorResponse("服务器错误", "获取客户信息失败"));
}
}
/**
* 查询客户信息支持按类型过滤
* GET /pool/customers?type=seller&page=1&size=10
*/
@GetMapping("/customersss")
public ResponseEntity<?> getCustomers(
@RequestParam(required = false) String type,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
try {
Map<String, Object> response = new HashMap<>();
response.put("page", page);
response.put("size", size);
response.put("type", type);
response.put("message", "分页查询功能待实现");
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createErrorResponse("查询失败", e.getMessage()));
}
}
/**
* 创建统一的错误响应格式
*/
private Map<String, Object> createErrorResponse(String error, String message) {
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("error", error);
errorResponse.put("message", message);
errorResponse.put("timestamp", System.currentTimeMillis());
return errorResponse;
}
}

22
src/main/java/com/example/web/dao/LoginMapper.java

@ -0,0 +1,22 @@
package com.example.web.dao;
import com.example.web.entity.Login;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* @Description: 登录Mapper接口
*/
@Mapper
public interface LoginMapper {
/**
* 验证用户登录信息
* @param projectName 职位名称
* @param userName 用户名
* @param password 密码
* @return Login对象如果存在则返回否则返回null
*/
@Select("SELECT * FROM login WHERE projectName = #{projectName} AND userName = #{userName} AND password = #{password}")
Login validateLogin(@Param("projectName") String projectName, @Param("userName") String userName, @Param("password") String password);
}

20
src/main/java/com/example/web/dao/PersonnelMapper.java

@ -0,0 +1,20 @@
package com.example.web.dao;
import com.example.web.entity.Personnel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* @Description: 员工信息Mapper接口
*/
@Mapper
public interface PersonnelMapper {
/**
* 根据负责人ID获取员工信息
* @param managerId 负责人ID
* @return Personnel对象如果存在则返回否则返回null
*/
@Select("SELECT * FROM personnel WHERE managerId = #{managerId}")
Personnel getPersonnelByManagerId(@Param("managerId") String managerId);
}

53
src/main/java/com/example/web/dto/EnterpriseInfoDTO.java

@ -3,45 +3,16 @@ package com.example.web.dto;
import com.example.web.entity.Contacts;
import com.example.web.entity.Enterprise;
import com.example.web.entity.Managers;
// 企业信息数据传输对象(DTO),用于封装企业完整信息(企业基本信息+联系人+负责人等)
// getter和setter:提供属性的获取和设置方法,用于在各层之间传递数据
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description: 企业信息DTO包含企业联系人和负责人信息
*/
@Data
@NoArgsConstructor
public class EnterpriseInfoDTO {
private Enterprise enterprise;
private Contacts contacts;
private Managers managers;
// 构造方法
public EnterpriseInfoDTO() {}
public EnterpriseInfoDTO(Enterprise enterprise, Contacts contacts, Managers managers) {
this.enterprise = enterprise;
this.contacts = contacts;
this.managers = managers;
}
// getter和setter
public Enterprise getEnterprise() {
return enterprise;
}// 获取企业基本信息对象(包含公司名称、地区、类型等核心字段)
public void setEnterprise(Enterprise enterprise) {
this.enterprise = enterprise;
}
public Contacts getContacts() {
return contacts;
}
public void setContacts(Contacts contacts) {
this.contacts = contacts;
}
public Managers getManagers() {
return managers;
}
public void setManagers(Managers managers) {
this.managers = managers;
}
}
private Enterprise enterprise;
private Contacts contacts;
private Managers managers;
}

80
src/main/java/com/example/web/dto/ManagerAuthInfo.java

@ -1,80 +0,0 @@
package com.example.web.dto;
import lombok.Data;
@Data
public class ManagerAuthInfo {
private String managerId;
private String managercompany;
private String managerdepartment;
private String organization;
private String role;
private String userName;
private String assistant;
public ManagerAuthInfo(String managerId, String managercompany, String managerdepartment, String organization, String role, String userName, String assistant) {
this.managerId = managerId;
this.managercompany = managercompany;
this.managerdepartment = managerdepartment;
this.organization = organization;
this.role = role;
this.userName = userName;
this.assistant = assistant;
}
public String getManagerId() {
return managerId;
}
public void setManagerId(String managerId) {
this.managerId = managerId;
}
public String getManagercompany() {
return managercompany;
}
public void setManagercompany(String managercompany) {
this.managercompany = managercompany;
}
public String getManagerdepartment() {
return managerdepartment;
}
public void setManagerdepartment(String managerdepartment) {
this.managerdepartment = managerdepartment;
}
public String getOrganization() {
return organization;
}
public void setOrganization(String organization) {
this.organization = organization;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAssistant() {
return assistant;
}
public void setAssistant(String assistant) {
this.assistant = assistant;
}
}

55
src/main/java/com/example/web/dto/NotificationDTO.java

@ -0,0 +1,55 @@
package com.example.web.dto;
import java.time.LocalDateTime;
/**
* WebSocket通知DTO
*/
public class NotificationDTO {
private String type; // 通知类型
private String title; // 通知标题
private String message; // 通知内容
private String customerId; // 相关客户ID
private LocalDateTime timestamp; // 时间戳
// getter和setter方法
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
}

331
src/main/java/com/example/web/dto/UnifiedCustomerDTO.java

@ -1,331 +0,0 @@
package com.example.web.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
public class UnifiedCustomerDTO {
private String id = "";
private String company = "";
private String region = "";
private String level = "";
private String type = "";
private String demand = "";
private String spec = "";
private String nickName = "";
@NotBlank(message = "手机号不能为空")
private String phoneNumber = "";
// 向后兼容的单个联系人字段
private String wechat = "";
private String account = "";
private String accountNumber = "";
private String bank = "";
private String address = "";
// 向后兼容的单个产品字段
private String productName = "";
private String variety = "";
private String specification = "";
private Integer quantity = 0;
private String grossWeight = "";
private String yolk = "";
// 负责人信息字段
private String managerId =""; //负责人id
private String managercompany = ""; // 负责人公司
private String managerdepartment = ""; // 负责人部门
private String organization = ""; // 负责人组织
private String role = ""; // 负责人角色
private String userName = ""; // 负责人用户名
private String assistant = ""; // 协助人
private String notice = ""; // 通知信息
// 时间信息
private LocalDateTime created_at;
private LocalDateTime updated_at;
// 扩展信息
private Object[] customDetails = new Object[0];
private String dataSource; // "wechat" 或 "default" 或 "mixed"
// ==================== 一对多字段 ====================
private List<ContactInfo> contacts = new ArrayList<>();
private List<CartItem> cartItems = new ArrayList<>(); // 销售端使用
private List<ProductItem> productItems = new ArrayList<>(); // 采购端使用
// ==================== 精确修改字段 ====================
private String targetContactId; // 要修改的联系人ID
private String targetCartItemId; // 要修改的购物车项ID
private String targetProductItemId; // 要修改的产品项ID
// ==================== 更新数据字段 ====================
// 修改字段名以保持与Service的一致性
private ContactInfo updateContactData; // 联系人更新数据(与Service中的使用保持一致)
private CartItem updateCartItemData; // 购物车项更新数据(与Service中的使用保持一致)
private ProductItem updateProductItemData; // 产品项更新数据
// ==================== 内部类定义 ====================
/**
* 联系人信息
*/
public static class ContactInfo {
private String contactId = "";
private String wechat = "";
private String account = "";
private String accountNumber = "";
private String bank = "";
private String address = "";
// Getter 和 Setter 方法
public String getContactId() { return contactId; }
public void setContactId(String contactId) { this.contactId = contactId; }
public String getWechat() { return wechat; }
public void setWechat(String wechat) { this.wechat = wechat; }
public String getAccount() { return account; }
public void setAccount(String account) { this.account = account; }
public String getAccountNumber() { return accountNumber; }
public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; }
public String getBank() { return bank; }
public void setBank(String bank) { this.bank = bank; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
}
/**
* 购物车项信息销售端使用
*/
public static class CartItem {
private String cartItemId = "";
private String productId = "";
private String productName = "";
private String specification = "";
private Integer quantity = 0;
private String grossWeight = "";
private String yolk = "";
// Getter 和 Setter 方法
public String getCartItemId() { return cartItemId; }
public void setCartItemId(String cartItemId) { this.cartItemId = cartItemId; }
public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public String getSpecification() { return specification; }
public void setSpecification(String specification) { this.specification = specification; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public String getGrossWeight() { return grossWeight; }
public void setGrossWeight(String grossWeight) { this.grossWeight = grossWeight; }
public String getYolk() { return yolk; }
public void setYolk(String yolk) { this.yolk = yolk; }
}
/**
* 产品项信息采购端使用
*/
public static class ProductItem {
private String productId = "";
private String productName = "";
private String variety = "";
private String specification = "";
private Integer quantity = 0;
private String grossWeight = "";
private String yolk = "";
// Getter 和 Setter 方法
public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public String getVariety() { return variety; }
public void setVariety(String variety) { this.variety = variety; }
public String getSpecification() { return specification; }
public void setSpecification(String specification) { this.specification = specification; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public String getGrossWeight() { return grossWeight; }
public void setGrossWeight(String grossWeight) { this.grossWeight = grossWeight; }
public String getYolk() { return yolk; }
public void setYolk(String yolk) { this.yolk = yolk; }
}
// ==================== Getter 和 Setter 方法 ====================
// 一对多字段的getter/setter
public List<ContactInfo> getContacts() { return contacts; }
public void setContacts(List<ContactInfo> contacts) { this.contacts = contacts; }
public List<CartItem> getCartItems() { return cartItems; }
public void setCartItems(List<CartItem> cartItems) { this.cartItems = cartItems; }
public List<ProductItem> getProductItems() { return productItems; }
public void setProductItems(List<ProductItem> productItems) { this.productItems = productItems; }
// 精确修改字段的getter/setter
public String getTargetContactId() { return targetContactId; }
public void setTargetContactId(String targetContactId) { this.targetContactId = targetContactId; }
public String getTargetCartItemId() { return targetCartItemId; }
public void setTargetCartItemId(String targetCartItemId) { this.targetCartItemId = targetCartItemId; }
public String getTargetProductItemId() { return targetProductItemId; }
public void setTargetProductItemId(String targetProductItemId) { this.targetProductItemId = targetProductItemId; }
// 更新数据字段的getter/setter
public ContactInfo getUpdateContactData() { return updateContactData; }
public void setUpdateContactData(ContactInfo updateContactData) { this.updateContactData = updateContactData; }
public CartItem getUpdateCartItemData() { return updateCartItemData; }
public void setUpdateCartItemData(CartItem updateCartItemData) { this.updateCartItemData = updateCartItemData; }
public ProductItem getUpdateProductItemData() { return updateProductItemData; }
public void setUpdateProductItemData(ProductItem updateProductItemData) { this.updateProductItemData = updateProductItemData; }
// 向后兼容:为旧的updateContact字段提供兼容性(如果需要)
/**
* @deprecated 请使用 getUpdateContactData()
*/
public ContactInfo getUpdateContact() { return updateContactData; }
/**
* @deprecated 请使用 setUpdateContactData()
*/
public void setUpdateContact(ContactInfo updateContact) { this.updateContactData = updateContact; }
/**
* @deprecated 请使用 getUpdateCartItemData()
*/
public CartItem getUpdateCartItem() { return updateCartItemData; }
/**
* @deprecated 请使用 setUpdateCartItemData()
*/
public void setUpdateCartItem(CartItem updateCartItem) { this.updateCartItemData = updateCartItem; }
/**
* @deprecated 请使用 getUpdateProductItemData()
*/
public ProductItem getUpdateProductItem() { return updateProductItemData; }
/**
* @deprecated 请使用 setUpdateProductItemData()
*/
public void setUpdateProductItem(ProductItem updateProductItem) { this.updateProductItemData = updateProductItem; }
// 基础字段的getter/setter
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getCompany() { return company; }
public void setCompany(String company) { this.company = company; }
public String getRegion() { return region; }
public void setRegion(String region) { this.region = region; }
public String getLevel() { return level; }
public void setLevel(String level) { this.level = level; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getDemand() { return demand; }
public void setDemand(String demand) { this.demand = demand; }
public String getSpec() { return spec; }
public void setSpec(String spec) { this.spec = spec; }
public String getNickName() { return nickName; }
public void setNickName(String nickName) { this.nickName = nickName; }
public String getPhoneNumber() { return phoneNumber; }
public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
public String getWechat() { return wechat; }
public void setWechat(String wechat) { this.wechat = wechat; }
public String getAccount() { return account; }
public void setAccount(String account) { this.account = account; }
public String getAccountNumber() { return accountNumber; }
public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; }
public String getBank() { return bank; }
public void setBank(String bank) { this.bank = bank; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public String getVariety() { return variety; }
public void setVariety(String variety) { this.variety = variety; }
public String getSpecification() { return specification; }
public void setSpecification(String specification) { this.specification = specification; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public String getGrossWeight() { return grossWeight; }
public void setGrossWeight(String grossWeight) { this.grossWeight = grossWeight; }
public String getYolk() { return yolk; }
public void setYolk(String yolk) { this.yolk = yolk; }
public String getManagerId() {
return managerId;
}
public void setManagerId(String managerId) {
this.managerId = managerId;
}
public String getManagercompany() { return managercompany; }
public void setManagercompany(String managercompany) { this.managercompany = managercompany; }
public String getManagerdepartment() { return managerdepartment; }
public void setManagerdepartment(String managerdepartment) { this.managerdepartment = managerdepartment; }
public String getOrganization() { return organization; }
public void setOrganization(String organization) { this.organization = organization; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public String getAssistant() { return assistant; }
public void setAssistant(String assistant) {
this.assistant = assistant;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
public LocalDateTime getCreated_at() { return created_at; }
public void setCreated_at(LocalDateTime created_at) { this.created_at = created_at; }
public LocalDateTime getUpdated_at() { return updated_at; }
public void setUpdated_at(LocalDateTime updated_at) { this.updated_at = updated_at; }
public Object[] getCustomDetails() { return customDetails; }
public void setCustomDetails(Object[] customDetails) { this.customDetails = customDetails; }
public String getDataSource() { return dataSource; }
public void setDataSource(String dataSource) { this.dataSource = dataSource; }
}

432
src/main/java/com/example/web/dto/UserProductCartDTO.java

@ -1,432 +0,0 @@
package com.example.web.dto;
import com.example.web.entity.UsersManagements;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
public class UserProductCartDTO {
private String userId;//用户id
private String phoneNumber;//用户手机号
private String type;//用户类型
private String nickName;//用户昵称
private String company;//客户公司
private String region;//客户地区
private String level;//客户等级
private String demand;//客户需求
private String spec;//规格
private LocalDateTime created_at;//创建时间
private LocalDateTime updated_at;//更新时间
private String notice;//通知状态
private List<ProductInfo> products= new ArrayList<>();;//产品信息
private List<CartItem> cartItems= new ArrayList<>();;//购物车信息
private List<UsersContacts> usersContacts= new ArrayList<>();;//联系人信息
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getDemand() {
return demand;
}
public void setDemand(String demand) {
this.demand = demand;
}
public String getSpec() {
return spec;
}
public void setSpec(String spec) {
this.spec = spec;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
public LocalDateTime getCreated_at() {
return created_at;
}
public void setCreated_at(LocalDateTime created_at) {
this.created_at = created_at;
}
public LocalDateTime getUpdated_at() {
return updated_at;
}
public void setUpdated_at(LocalDateTime updated_at) {
this.updated_at = updated_at;
}
public List<ProductInfo> getProducts() {
return products;
}
public void setProducts(List<ProductInfo> products) {
this.products = products;
}
public List<CartItem> getCartItems() {
return cartItems;
}
public void setCartItems(List<CartItem> cartItems) {
this.cartItems = cartItems;
}
public List<UsersContacts> getUsersContacts() {
return usersContacts;
}
public void setUsersContacts(List<UsersContacts> usersContacts) {
this.usersContacts = usersContacts;
}
@Data
public static class ProductInfo {
private String productName = "";
private String variety = "";
private String specification = "";
private Integer quantity = 0;
private String grossWeight = "";
private String yolk = "";
private String productId = "";
private String price = "";
private String sellerId = "";
private String sellerNickName = "";
private String sellerPhone = "";
private LocalDateTime created_at;
private LocalDateTime updated_at;
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getVariety() {
return variety;
}
public void setVariety(String variety) {
this.variety = variety;
}
public String getSpecification() {
return specification;
}
public void setSpecification(String specification) {
this.specification = specification;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public String getGrossWeight() {
return grossWeight;
}
public void setGrossWeight(String grossWeight) {
this.grossWeight = grossWeight;
}
public String getYolk() {
return yolk;
}
public void setYolk(String yolk) {
this.yolk = yolk;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getSellerId() {
return sellerId;
}
public void setSellerId(String sellerId) {
this.sellerId = sellerId;
}
public String getSellerNickName() {
return sellerNickName;
}
public void setSellerNickName(String sellerNickName) {
this.sellerNickName = sellerNickName;
}
public String getSellerPhone() {
return sellerPhone;
}
public void setSellerPhone(String sellerPhone) {
this.sellerPhone = sellerPhone;
}
public LocalDateTime getCreated_at() {
return created_at;
}
public void setCreated_at(LocalDateTime created_at) {
this.created_at = created_at;
}
public LocalDateTime getUpdated_at() {
return updated_at;
}
public void setUpdated_at(LocalDateTime updated_at) {
this.updated_at = updated_at;
}
}
@Data
public static class CartItem {
private String cartItemId = ""; // 新增:购物车项唯一标识
private String productName = "";
private String variety = "";
private String specification = "";
private Integer quantity = 0;
private String grossWeight = "";
private String yolk = "";
private String productId = "";
private String price = "";
private Boolean selected = false;
private LocalDateTime added_at;
public String getCartItemId() {
return cartItemId;
}
public void setCartItemId(String cartItemId) {
this.cartItemId = cartItemId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getSpecification() {
return specification;
}
public void setSpecification(String specification) {
this.specification = specification;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public String getGrossWeight() {
return grossWeight;
}
public void setGrossWeight(String grossWeight) {
this.grossWeight = grossWeight;
}
public String getYolk() {
return yolk;
}
public void setYolk(String yolk) {
this.yolk = yolk;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public Boolean getSelected() {
return selected;
}
public void setSelected(Boolean selected) {
this.selected = selected;
}
public LocalDateTime getAdded_at() {
return added_at;
}
public void setAdded_at(LocalDateTime added_at) {
this.added_at = added_at;
}
}
@Data
public static class UsersContacts {
private String contactId = ""; // 新增:联系人唯一标识
private String wechat = "";
private String account = "";
private String accountNumber = "";
private String bank = "";
private String address = "";
public UsersContacts(String wechat, String account, String accountNumber, String bank, String address) {
this.wechat = wechat;
this.account = account;
this.accountNumber = accountNumber;
this.bank = bank;
this.address = address;
}
public UsersContacts() {}
public String getContactId() {
return contactId;
}
public void setContactId(String contactId) {
this.contactId = contactId;
}
public String getWechat() {
return wechat;
}
public void setWechat(String wechat) {
this.wechat = wechat;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public String getBank() {
return bank;
}
public void setBank(String bank) {
this.bank = bank;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
}

12
src/main/java/com/example/web/entity/Contacts.java

@ -21,6 +21,7 @@ public class Contacts {
private String bank;//联系人银行
private String address;//联系人地址
private String followup;//跟进信息
private LocalDateTime followup_at;//最后跟进时间
private LocalDateTime created_at;//创建时间
private LocalDateTime updated_at;//更新时间
@ -43,7 +44,7 @@ public class Contacts {
public Contacts() {
}
public Contacts(String contact_id, String id, String nickName, String phoneNumber, String wechat, String account, String accountNumber, String bank, String address, String followup) {
public Contacts(String contact_id, String id, String nickName, String phoneNumber, String wechat, String account, String accountNumber, String bank, String address, String followup, LocalDateTime followup_at) {
this.contact_id = contact_id;
this.id = id;
this.nickName = nickName;
@ -54,6 +55,7 @@ public class Contacts {
this.bank = bank;
this.address = address;
this.followup = followup;
this.followup_at = followup_at;
}
public String getContact_id() {
@ -135,4 +137,12 @@ public class Contacts {
public void setFollowup(String followup) {
this.followup = followup;
}
public LocalDateTime getFollowup_at() {
return followup_at;
}
public void setFollowup_at(LocalDateTime followup_at) {
this.followup_at = followup_at;
}
}

66
src/main/java/com/example/web/entity/CustomerData.java

@ -0,0 +1,66 @@
package com.example.web.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
/**
* @Description: 客户数据统一实体类用于WebSocket通信
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerData {
// 基本信息
private String id; // 客户ID
private String nickName; // 姓名
private String phoneNumber; // 电话号码
private String wechat; // 微信号
private String account; // 账户
private String accountNumber; // 账号
private String bank; // 银行
private String address; // 地址
private String followup; // 跟进
private String notice; // 通知状态
private LocalDateTime followup_at; // 最后跟进时间
private LocalDateTime created_at; // 创建时间
private LocalDateTime updated_at; // 更新时间
// 企业信息
private String company; // 客户公司
private String region; // 客户地区
private String level; // 客户等级
private String type; // 客户类型
private String demand; // 客户需求
private String spec; // 规格
// 负责人信息
private String managercompany; // 负责人公司
private String managerdepartment; // 负责人部门
private String organization; // 负责人组织
private String projectName; // 职位
private String userName; // 负责人姓名
private String assistant; // 协助人
private String role; // 用户角色
// 产品收藏信息
private List<ProductFavorite> favorites; // 收藏的产品列表
/**
* 产品收藏内部类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ProductFavorite {
private String productName; // 产品名称
private String price; // 价格
private String quantity; // 数量
private String grossWeight; // 毛重
private String yolk; // 蛋黄
private LocalDateTime favoriteDate; // 收藏时间
}
}

52
src/main/java/com/example/web/entity/Favorites.java

@ -0,0 +1,52 @@
package com.example.web.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* @Description: 用户收藏实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Favorites {
private Integer id; // 控制唯一值
private String user_phone; // 用户手机号
private String productId; // 产品ID
private LocalDateTime date; // 收藏的时间
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUser_phone() {
return user_phone;
}
public void setUser_phone(String user_phone) {
this.user_phone = user_phone;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public LocalDateTime getDate() {
return date;
}
public void setDate(LocalDateTime date) {
this.date = date;
}
}

27
src/main/java/com/example/web/entity/InformationTra.java

@ -15,6 +15,9 @@ public class InformationTra {
private LocalDateTime operationTime;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String originalData; // 原始数据JSON
private String modifiedData; // 修改后数据JSON
private String changedFields; // 变更字段列表JSON
// Getters and Setters
public Integer getId() {
@ -112,4 +115,28 @@ public class InformationTra {
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
public String getOriginalData() {
return originalData;
}
public void setOriginalData(String originalData) {
this.originalData = originalData;
}
public String getModifiedData() {
return modifiedData;
}
public void setModifiedData(String modifiedData) {
this.modifiedData = modifiedData;
}
public String getChangedFields() {
return changedFields;
}
public void setChangedFields(String changedFields) {
this.changedFields = changedFields;
}
}

36
src/main/java/com/example/web/entity/Login.java

@ -15,6 +15,10 @@ public class Login {
private String userName;//用户名
private String password;//密码
private Integer id;//用户id
private String managerId;//负责人ID
private String managercompany; // 负责人公司
private String managerdepartment; // 负责人部门
private String organization; // 负责人组织
public String getProjectName() {
@ -48,4 +52,36 @@ public class Login {
public void setUserName(String userName) {
this.userName = userName;
}
public String getManagerId() {
return managerId;
}
public void setManagerId(String managerId) {
this.managerId = managerId;
}
public String getManagercompany() {
return managercompany;
}
public void setManagercompany(String managercompany) {
this.managercompany = managercompany;
}
public String getManagerdepartment() {
return managerdepartment;
}
public void setManagerdepartment(String managerdepartment) {
this.managerdepartment = managerdepartment;
}
public String getOrganization() {
return organization;
}
public void setOrganization(String organization) {
this.organization = organization;
}
}

23
src/main/java/com/example/web/entity/Notification.java

@ -0,0 +1,23 @@
package com.example.web.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 通知实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Notification {
private Long id;
private String title;
private String message;
private String type;
private String userId;
private boolean read;
private LocalDateTime created_at;
}

28
src/main/java/com/example/web/entity/Personnel.java

@ -0,0 +1,28 @@
package com.example.web.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.sql.Timestamp;
/**
* @Description: 员工信息实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Personnel {
private Integer id; // 员工ID
private String managerId; // 负责人ID
private String managercompany; // 负责公司
private String managerdepartment; // 负责部门
private String organization; // 负责小组
private String projectName; // 职位名称
private String alias; // 负责人别名
private String name; // 负责人名
private String phoneNumber; // 负责人电话
private Timestamp createdAt; // 创建时间
private Timestamp updatedAt; // 更新时间
private String avatarUrl; // 头像
}

12
src/main/java/com/example/web/entity/Users.java

@ -34,8 +34,9 @@ public class Users {
private String spec;//规格
private String followup;//跟进信息
private String notice;//通知状态
private LocalDateTime followup_at;//最后跟进时间
public Users(Integer id, String openid, String userId, String nickName, String avatarUrl, String phoneNumber, String type, Integer gender, String country, String province, String city, String language, String session_key, LocalDateTime created_at, LocalDateTime updated_at, String company, String region, String level, String demand, String spec, String followup, String notice) {
public Users(Integer id, String openid, String userId, String nickName, String avatarUrl, String phoneNumber, String type, Integer gender, String country, String province, String city, String language, String session_key, LocalDateTime created_at, LocalDateTime updated_at, String company, String region, String level, String demand, String spec, String followup, String notice, LocalDateTime followup_at) {
this.id = id;
this.openid = openid;
this.userId = userId;
@ -58,6 +59,7 @@ public class Users {
this.spec = spec;
this.followup = followup;
this.notice = notice;
this.followup_at = followup_at;
}
public Integer getId() {
@ -235,4 +237,12 @@ public class Users {
public void setNotice(String notice) {
this.notice = notice;
}
public LocalDateTime getFollowup_at() {
return followup_at;
}
public void setFollowup_at(LocalDateTime followup_at) {
this.followup_at = followup_at;
}
}

24
src/main/java/com/example/web/entity/UsersContacts.java

@ -1,14 +1,14 @@
package com.example.web.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
public class UsersContacts {
private String userId;//用户id
private String id;//联系人id
private String nickName;//联系人姓名
private String phoneNumber;//联系人电话
private String wechat;//联系人微信
private String account;//联系人账号
private String accountNumber;//联系人账号
@ -18,11 +18,9 @@ public class UsersContacts {
private LocalDateTime updated_at;//更新时间
public UsersContacts(String userId, String id, String nickName, String phoneNumber, String wechat, String account, String accountNumber, String bank, String address, LocalDateTime created_at, LocalDateTime updated_at) {
public UsersContacts(String userId, String id, String wechat, String account, String accountNumber, String bank, String address, LocalDateTime created_at, LocalDateTime updated_at) {
this.userId = userId;
this.id = id;
this.nickName = nickName;
this.phoneNumber = phoneNumber;
this.wechat = wechat;
this.account = account;
this.accountNumber = accountNumber;
@ -48,22 +46,6 @@ public class UsersContacts {
this.id = id;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getWechat() {
return wechat;
}

5
src/main/java/com/example/web/mapper/ContactsMapper.java

@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
@ -30,6 +31,6 @@ public interface ContactsMapper {
String getFollowUpByPhone(@Param("phoneNumber") String phoneNumber);
// 更新跟进信息
@Update("UPDATE contacts SET followup = #{followup} WHERE phoneNumber = #{phoneNumber}")
int updateFollowUpByPhone(@Param("phoneNumber") String phoneNumber, @Param("followup") String followup);
@Update("UPDATE contacts SET followup = #{followup}, updated_at = #{updatedAt} WHERE phoneNumber = #{phoneNumber}")
int updateFollowUpByPhone(@Param("phoneNumber") String phoneNumber, @Param("followup") String followup, @Param("updatedAt") LocalDateTime updatedAt);
}

136
src/main/java/com/example/web/mapper/CustomerMapper.java

@ -0,0 +1,136 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.entity.Contacts;
import com.example.web.entity.Enterprise;
import com.example.web.entity.Managers;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Description: 客户数据Mapper接口primary数据源
*/
@Mapper
@DataSource("primary")
public interface CustomerMapper {
/**
* 获取所有联系人信息
* @return 联系人列表
*/
List<Contacts> getAllContacts();
/**
* 根据联系人ID获取企业信息
* @param contactId 联系人ID
* @return 企业信息
*/
Enterprise getEnterpriseByContactId(@Param("contactId") String contactId);
/**
* 根据联系人ID获取负责人信息
* @param contactId 联系人ID
* @return 负责人信息
*/
Managers getManagerByContactId(@Param("contactId") String contactId);
/**
* 获取部门公海池数据
* @param role 用户角色buyer/seller
* @param managercompany 负责人公司
* @param managerdepartment 负责人部门
* @return 部门公海池客户数据
*/
List<Contacts> getDepartmentSeaPoolContacts(
@Param("role") String role,
@Param("managercompany") String managercompany,
@Param("managerdepartment") String managerdepartment);
/**
* 获取组织公海池数据
* @param role 用户角色buyer/seller
* @param managercompany 负责人公司
* @param managerdepartment 负责人部门
* @param organization 负责人组织
* @return 组织公海池客户数据
*/
List<Contacts> getOrganizationSeaPoolContacts(
@Param("role") String role,
@Param("managercompany") String managercompany,
@Param("managerdepartment") String managerdepartment,
@Param("organization") String organization);
/**
* 插入企业信息
* @param enterprise 企业信息
* @return 插入结果
*/
int insertEnterprise(Enterprise enterprise);
/**
* 插入联系人信息
* @param contacts 联系人信息
* @return 插入结果
*/
int insertContacts(Contacts contacts);
/**
* 插入负责人信息
* @param managers 负责人信息
* @return 插入结果
*/
int insertManagers(Managers managers);
/**
* 根据电话号码获取联系人信息
* @param phoneNumber 电话号码
* @return 联系人信息
*/
Contacts getContactsByPhoneNumber(@Param("phoneNumber") String phoneNumber);
/**
* 根据ID获取联系人信息
* @param id 联系人ID
* @return 联系人信息
*/
Contacts getContactsById(@Param("id") String id);
/**
* 更新联系人信息
* @param contacts 联系人信息
* @return 更新结果
*/
int updateContacts(Contacts contacts);
/**
* 更新企业信息
* @param enterprise 企业信息
* @return 更新结果
*/
int updateEnterprise(Enterprise enterprise);
/**
* 根据角色和登录信息获取客户数据
* @param role 角色
* @param userName 用户名
* @param managercompany 负责人公司
* @param managerdepartment 负责人部门
* @param organization 负责人组织
* @return 客户数据列表
*/
List<Contacts> getCustomersByRole(
@Param("role") String role,
@Param("userName") String userName,
@Param("managercompany") String managercompany,
@Param("managerdepartment") String managerdepartment,
@Param("organization") String organization);
/**
* 更新负责人信息
* @param managers 负责人信息
* @return 更新结果
*/
int updateManagers(Managers managers);
}

24
src/main/java/com/example/web/mapper/EnterpriseMapper.java

@ -1,24 +0,0 @@
package com.example.web.mapper;
import com.example.web.dto.EnterpriseInfoDTO;
import com.example.web.entity.Enterprise;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface EnterpriseMapper {
// 查询所有企业及其联系人和负责人信息
List<EnterpriseInfoDTO> selectAllEnterpriseInfo();
// 根据企业ID查询详细信息
EnterpriseInfoDTO selectEnterpriseInfoById(String id);
// 查询所有企业基本信息
List<Enterprise> selectAllEnterprises();
// 新增企业
int insertEnterprise(Enterprise enterprise);
//编辑修改
int updateEnterprise(Enterprise enterprise);
}

18
src/main/java/com/example/web/mapper/InformationTraMapper.java

@ -1,18 +1,20 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.entity.InformationTra;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @Description: 信息跟踪Mapper接口
*/
@Mapper
@DataSource("wechat")
public interface InformationTraMapper {
/**
* 插入操作记录
* 插入信息跟踪记录
* @param informationTra 信息跟踪记录
* @return 插入结果
*/
int insertInformationTra(InformationTra informationTra);
/**
* 根据userId查询操作记录
*/
InformationTra selectByUserId(@Param("userId") String userId);
}
}

22
src/main/java/com/example/web/mapper/LoginMapper.java

@ -5,18 +5,18 @@ import com.example.web.entity.Login;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Description: 登录Mapper接口
* @TableName: login
*/
@Mapper
@DataSource("primary")
public interface LoginMapper {
// 移除 @Select 注解,使用 XML 配置
Login findByProjectNameAndUserName(@Param("projectName") String projectName,
@Param("userName") String userName);
// 新增方法 - 支持动态查询
List<Login> findByConditions(@Param("projectName") String projectName,
@Param("userName") String userName);
/**
* 根据用户名和工位名查询用户
* @param userName 用户名
* @param projectName 工位名
* @return Login对象
*/
Login selectByUserNameAndProjectName(@Param("userName") String userName, @Param("projectName") String projectName);
}

21
src/main/java/com/example/web/mapper/PersonnelMapper.java

@ -0,0 +1,21 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.entity.Personnel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @Description: 员工信息Mapper接口
* @TableName: personnel
*/
@Mapper
@DataSource("primary")
public interface PersonnelMapper {
/**
* 根据负责人ID查询员工信息
* @param managerId 负责人ID
* @return Personnel对象
*/
Personnel selectByManagerId(@Param("managerId") String managerId);
}

10
src/main/java/com/example/web/mapper/SupplyCart_itemsMapper.java

@ -1,10 +0,0 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import org.apache.ibatis.annotations.Mapper;
@Mapper
@DataSource("wechat")
public interface SupplyCart_itemsMapper {
}

24
src/main/java/com/example/web/mapper/SupplyContactsMapper.java

@ -1,24 +0,0 @@
package com.example.web.mapper;
import com.example.web.entity.Contacts;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SupplyContactsMapper {
// 根据企业ID查询联系人信息
List<Contacts> selectContactsByEnterpriseId(String id);
// 查询所有联系人信息
List<Contacts> selectAllContacts();
// 新增联系人
int insertContacts(Contacts contacts);
// 新增:根据电话号码查询记录数(用于判断是否重复)
int countByPhoneNumber(String phoneNumber);
// 新增:根据手机号查询联系人(关联企业信息)
Contacts selectByPhoneNumber(String phoneNumber);
// 修改编辑功能
int updateContacts(Contacts contacts);
}

24
src/main/java/com/example/web/mapper/SupplyEnterpriseMapper.java

@ -1,24 +0,0 @@
package com.example.web.mapper;
import com.example.web.dto.EnterpriseInfoDTO;
import com.example.web.entity.Enterprise;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SupplyEnterpriseMapper {
// 查询所有企业及其联系人和负责人信息
List<EnterpriseInfoDTO> selectAllEnterpriseInfo();
// 根据企业ID查询详细信息
EnterpriseInfoDTO selectEnterpriseInfoById(String id);
// 查询所有企业基本信息
List<Enterprise> selectAllEnterprises();
// 新增企业
int insertEnterprise(Enterprise enterprise);
//编辑修改
int updateEnterprise(Enterprise enterprise);
}

28
src/main/java/com/example/web/mapper/SupplyManagersMapper.java

@ -1,28 +0,0 @@
package com.example.web.mapper;
import com.example.web.entity.Managers;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SupplyManagersMapper {
List<Managers> selectManagersByEnterpriseId(String id);
// 查询所有负责人信息
List<Managers> selectAllManagers();
// 新增负责人
int insertManagers(Managers managers);
//编辑修改
int updateManagers(Managers managers);
// 根据负责人姓名查询
List<Managers> selectByUserName(@Param("userName") String userName);
// 🔥 新增:根据用户名和managerId查询
List<Managers> selectByUserNameAndManagerId(@Param("userName") String userName,
@Param("managerId") String managerId);
}

12
src/main/java/com/example/web/mapper/SupplyProductsMapper.java

@ -1,12 +0,0 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import org.apache.ibatis.annotations.Mapper;
@Mapper
@DataSource("wechat")
public interface SupplyProductsMapper {
}

64
src/main/java/com/example/web/mapper/SupplyUsersManagementsMapper.java

@ -1,64 +0,0 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.entity.UsersManagements;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* 负责人信息Mapper接口
* 对应wechat_app数据源中的负责人信息表操作
* 使用@DataSource("wechat")注解指定数据源
*/
@Mapper
@DataSource("wechat")
public interface SupplyUsersManagementsMapper {
// 负责人信息相关的数据库操作方法将在这里定义
// 🔥 新增:根据用户名和managerId查询
List<UsersManagements> findByUserNameAndManagerId(@Param("userName") String userName,
@Param("managerId") String managerId);
// 新增方法
List<UsersManagements> findByDepartment(@Param("department") String department);
List<UsersManagements> findByConditions(@Param("userName") String userName,
@Param("department") String department,
@Param("company") String company,
@Param("role") String role);
/**
* 根据用户ID查询负责人信息
*/
UsersManagements findByUserId(@Param("userId") String userId);
/**
* 插入负责人信息
*/
int insertUsersManagements(UsersManagements usersManagements);
/**
* 更新负责人信息
*/
int updateUsersManagements(UsersManagements usersManagements);
// 🔥 新增:更新负责人信息的更新时间(不改变负责人本身)
boolean updateManagerUpdateTime(@Param("userId") String userId, @Param("updatedAt") LocalDateTime updatedAt);
// 🔥 新增:查询指定客户的完整负责人信息
UsersManagements findCompleteManagerInfoByUserId(String userId);
// 新增:根据用户ID和认证信息直接查询匹配的负责人
UsersManagements findByUserIdAndAuthInfo(@Param("userId") String userId,
@Param("authInfo") ManagerAuthInfo authInfo);
// 新增:批量查询用户权限
List<String> findAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo);
// 🔥 新增:批量查询用户负责人信息
List<UsersManagements> findByUserIds(@Param("userIds") List<String> userIds);
}

270
src/main/java/com/example/web/mapper/SupplyUsersMapper.java

@ -1,270 +0,0 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.dto.UserProductCartDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
@DataSource("wechat")
public interface SupplyUsersMapper {
// 获取用户基本信息
UserProductCartDTO getUserBasicInfo(@Param("userId") String userId);
// 获取卖家的产品信息
List<UserProductCartDTO.ProductInfo> getSellerProducts(@Param("sellerId") String sellerId);
/**
* 获取所有用户的基本信息
*/
List<UserProductCartDTO> getAllUserBasicInfo();
// 新增根据手机号查询用户的方法
UserProductCartDTO selectByPhone(@Param("phoneNumber") String phoneNumber);
// 新增:根据用户ID精确查询用户
UserProductCartDTO selectByUserId(@Param("userId") String userId);
// 新增:更新公海需求信息
int updateProduct(
@Param("sellerId") String sellerId,
@Param("productId") String productId,
@Param("product") UserProductCartDTO.ProductInfo product
);
/**
* 根据手机号更新用户信息
*/
int updateByPhone(UserProductCartDTO user);
/**
* 更新卖家产品信息
*/
int updateProductBySellerId(
@Param("sellerId") String sellerId,
@Param("productName") String productName,
@Param("variety") String variety,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
/**
* 更新用户联系人信息 - 更新第一个联系人向后兼容
*/
int updateContactsByUserId(
@Param("userId") String userId,
@Param("wechat") String wechat,
@Param("account") String account,
@Param("accountNumber") String accountNumber,
@Param("bank") String bank,
@Param("address") String address
);
/**
* 获取用户联系人信息 - 获取第一个联系人向后兼容
*/
List<UserProductCartDTO.UsersContacts> getUserContacts(@Param("userId") String userId);
/**
* 更新或插入联系人信息
*/
int updateOrInsertContact(
@Param("userId") String userId,
@Param("contactId") String contactId,
@Param("wechat") String wechat,
@Param("account") String account,
@Param("accountNumber") String accountNumber,
@Param("bank") String bank,
@Param("address") String address
);
/**
* 采购端更新或插入产品信息类似购物车的updateOrInsertCartItem
*/
int updateOrInsertProduct(
@Param("sellerId") String sellerId,
@Param("productId") String productId,
@Param("productName") String productName,
@Param("variety") String variety,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk,
@Param("price") String price
);
/**
* 更新特定联系人信息 - 一对多逻辑新增
*/
int updateSpecificContact(
@Param("userId") String userId,
@Param("contactId") String contactId,
@Param("wechat") String wechat,
@Param("account") String account,
@Param("accountNumber") String accountNumber,
@Param("bank") String bank,
@Param("address") String address
);
/**
* 采购端更新特定产品信息 - 一对多逻辑新增
*/
int updateSpecificProduct(
@Param("sellerId") String sellerId,
@Param("productId") String productId,
@Param("productName") String productName,
@Param("variety") String variety,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
/**
* 获取用户所有联系人 - 一对多逻辑新增
*/
List<ContactInfo> getUserAllContacts(@Param("userId") String userId);
/**
* 采购端获取用户所有产品信息 - 一对多逻辑新增
*/
List<ProductInfo> getUserAllProducts(@Param("sellerId") String sellerId);
// 联系人信息内部类 - 用于一对多查询
class ContactInfo {
private String contactId;
private String userId;
private String wechat;
private String account;
private String accountNumber;
private String bank;
private String address;
public String getContactId() { return contactId; }
public void setContactId(String contactId) { this.contactId = contactId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getWechat() { return wechat; }
public void setWechat(String wechat) { this.wechat = wechat; }
public String getAccount() { return account; }
public void setAccount(String account) { this.account = account; }
public String getAccountNumber() { return accountNumber; }
public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; }
public String getBank() { return bank; }
public void setBank(String bank) { this.bank = bank; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
}
// 产品信息内部类 - 用于一对多查询
class ProductInfo {
private String productId;
private String userId;
private String productName;
private String variety;
private String specification;
private Integer quantity;
private String grossWeight;
private String yolk;
public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public String getVariety() { return variety; }
public void setVariety(String variety) { this.variety = variety; }
public String getSpecification() { return specification; }
public void setSpecification(String specification) { this.specification = specification; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public String getGrossWeight() { return grossWeight; }
public void setGrossWeight(String grossWeight) { this.grossWeight = grossWeight; }
public String getYolk() { return yolk; }
public void setYolk(String yolk) { this.yolk = yolk; }
}
/**
* 根据产品ID精确更新产品信息
*/
int updateProductByProductId(
@Param("productId") String productId,
@Param("productName") String productName,
@Param("variety") String variety,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
// 在 SupplyUsersMapper.java 中添加以下方法
/**
* 查询超过指定时间的未分级客户
*/
List<UserProductCartDTO> findUnclassifiedCustomersOlderThan(LocalDateTime thresholdTime);
/**
* 查询超过指定时间的组织公海池客户
*/
List<UserProductCartDTO> findOrganizationSeaPoolsCustomersOlderThan(LocalDateTime thresholdTime);
/**
* 更新客户等级
*/
@Update("UPDATE users SET level = #{level}, updated_at = #{updateTime} WHERE user_id = #{userId}")
boolean updateCustomerLevel(@Param("userId") String userId,
@Param("level") String level,
@Param("updateTime") LocalDateTime updateTime);
// 🔥 新增:查询最近回流的客户
List<UserProductCartDTO> findRecentlyRecycledCustomers(@Param("sinceTime") LocalDateTime sinceTime);
// 新增:直接查询有权限的客户列表(支持分页)
List<UserProductCartDTO> getAuthorizedCustomers(@Param("authInfo") ManagerAuthInfo authInfo, @Param("limit") int limit, @Param("offset") int offset);
/**
* 根据认证信息获取有权限的用户ID列表
*/
List<String> getAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo);
// 获取授权客户总数
int getAuthorizedCustomersCount(@Param("authInfo") ManagerAuthInfo authInfo);
/**
* 根据手机号和权限查询用户
*/
UserProductCartDTO selectByPhoneWithAuth(@Param("phoneNumber") String phoneNumber,
@Param("authInfo") ManagerAuthInfo authInfo);
// 🔥 新增:批量查询用户联系人信息
List<UsersMapper.ContactInfo> getUserContactsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户产品信息
List<UserProductCartDTO.ProductInfo> getSellerProductsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户基本信息
List<UserProductCartDTO> getUserBasicInfoByUserIds(@Param("userIds") List<String> userIds);
// 查询跟进信息
@Select("SELECT followup FROM users WHERE phoneNumber = #{phoneNumber}")
String getFollowUpByPhone(@Param("phoneNumber") String phoneNumber);
// 更新跟进信息
@Update("UPDATE users SET followup = #{followup} WHERE phoneNumber = #{phoneNumber}")
int updateFollowUpByPhone(@Param("phoneNumber") String phoneNumber, @Param("followup") String followup);
// 🔥 新增:更新用户通知状态
@Update("UPDATE users SET notice = #{notice} WHERE userId = #{userId}")
int updateNotice(@Param("userId") String userId, @Param("notice") String notice);
}

63
src/main/java/com/example/web/mapper/UsersManagementsMapper.java

@ -1,63 +0,0 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.entity.UsersManagements;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* 负责人信息Mapper接口
* 对应wechat_app数据源中的负责人信息表操作
* 使用@DataSource("wechat")注解指定数据源
*/
@Mapper
@DataSource("wechat")
public interface UsersManagementsMapper {
// 负责人信息相关的数据库操作方法将在这里定义
// 🔥 新增:根据用户名和managerId查询
List<UsersManagements> findByUserNameAndManagerId(@Param("userName") String userName,
@Param("managerId") String managerId);
// 新增方法
List<UsersManagements> findByDepartment(@Param("department") String department);
List<UsersManagements> findByConditions(@Param("userName") String userName,
@Param("department") String department,
@Param("company") String company,
@Param("role") String role);
/**
* 根据用户ID查询负责人信息
*/
UsersManagements findByUserId(@Param("userId") String userId);
/**
* 插入负责人信息
*/
int insertUsersManagements(UsersManagements usersManagements);
/**
* 更新负责人信息
*/
int updateUsersManagements(UsersManagements usersManagements);
// 🔥 新增:更新负责人信息的更新时间(不改变负责人本身)
boolean updateManagerUpdateTime(@Param("userId") String userId, @Param("updatedAt") LocalDateTime updatedAt);
// 🔥 新增:查询指定客户的完整负责人信息
UsersManagements findCompleteManagerInfoByUserId(String userId);
// 新增:根据用户ID和认证信息直接查询匹配的负责人
UsersManagements findByUserIdAndAuthInfo(@Param("userId") String userId,
@Param("authInfo") ManagerAuthInfo authInfo);
// 新增:批量查询用户权限
List<String> findAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo);
// 🔥 新增:批量查询用户负责人信息
List<UsersManagements> findByUserIds(@Param("userIds") List<String> userIds);
}

276
src/main/java/com/example/web/mapper/UsersMapper.java

@ -1,276 +0,0 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.dto.UserProductCartDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
@DataSource("wechat")
public interface UsersMapper {
// 获取用户基本信息
UserProductCartDTO getUserBasicInfo(@Param("userId") String userId);
// 获取卖家的产品信息
List<UserProductCartDTO.ProductInfo> getSellerProducts(@Param("sellerId") String sellerId);
// 销售端:获取买家的购物车信息(公海需求)
List<UserProductCartDTO.CartItem> getBuyerCartItems(@Param("userId") String userId);
/**
* 销售端获取所有买家用户的基本信息类型为buyer和both
*/
List<UserProductCartDTO> getAllUserBasicInfo();
// 销售端:根据手机号查询买家用户
UserProductCartDTO selectByPhone(@Param("phoneNumber") String phoneNumber);
// 新增:根据用户ID精确查询用户
UserProductCartDTO selectByUserId(@Param("userId") String userId);
// 新增:更新公海需求信息
int updateProduct(
@Param("sellerId") String sellerId,
@Param("productId") String productId,
@Param("product") UserProductCartDTO.ProductInfo product
);
/**
* 根据手机号更新用户信息
*/
int updateByPhone(UserProductCartDTO user);
/**
* 更新卖家产品信息
*/
int updateProductBySellerId(
@Param("sellerId") String sellerId,
@Param("productName") String productName,
@Param("variety") String variety,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
/**
* 销售端更新买家购物车信息 - 更新第一个购物车项向后兼容
*/
int updateCartItemByBuyerId(
@Param("buyerId") String buyerId,
@Param("productId") String productId,
@Param("productName") String productName,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
/**
* 更新用户联系人信息 - 更新第一个联系人向后兼容
*/
int updateContactsByUserId(
@Param("userId") String userId,
@Param("wechat") String wechat,
@Param("account") String account,
@Param("accountNumber") String accountNumber,
@Param("bank") String bank,
@Param("address") String address
);
/**
* 获取用户联系人信息 - 获取第一个联系人向后兼容
*/
List<UserProductCartDTO.UsersContacts> getUserContacts(@Param("userId") String userId);
/**
* 更新或插入联系人信息
*/
int updateOrInsertContact(
@Param("userId") String userId,
@Param("contactId") String contactId,
@Param("wechat") String wechat,
@Param("account") String account,
@Param("accountNumber") String accountNumber,
@Param("bank") String bank,
@Param("address") String address
);
/**
* 更新或插入购物车项
*/
int updateOrInsertCartItem(
@Param("userId") String userId,
@Param("cartItemId") String cartItemId,
@Param("productId") String productId,
@Param("productName") String productName,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
/**
* 更新特定联系人信息 - 一对多逻辑新增
*/
int updateSpecificContact(
@Param("userId") String userId,
@Param("contactId") String contactId,
@Param("wechat") String wechat,
@Param("account") String account,
@Param("accountNumber") String accountNumber,
@Param("bank") String bank,
@Param("address") String address
);
/**
* 更新特定购物车项信息 - 一对多逻辑新增
*/
int updateSpecificCartItem(
@Param("userId") String userId,
@Param("cartItemId") String cartItemId,
@Param("productId") String productId,
@Param("productName") String productName,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
/**
* 获取用户所有联系人 - 一对多逻辑新增
*/
List<ContactInfo> getUserAllContacts(@Param("userId") String userId);
/**
* 获取用户所有购物车项 - 一对多逻辑新增
*/
List<CartItem> getUserAllCartItems(@Param("userId") String userId);
// 联系人信息内部类 - 用于一对多查询
class ContactInfo {
private String contactId;
private String userId;
private String wechat;
private String account;
private String accountNumber;
private String bank;
private String address;
public String getContactId() { return contactId; }
public void setContactId(String contactId) { this.contactId = contactId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getWechat() { return wechat; }
public void setWechat(String wechat) { this.wechat = wechat; }
public String getAccount() { return account; }
public void setAccount(String account) { this.account = account; }
public String getAccountNumber() { return accountNumber; }
public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; }
public String getBank() { return bank; }
public void setBank(String bank) { this.bank = bank; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
}
// 购物车项内部类 - 用于一对多查询
class CartItem {
private String cartItemId;
private String userId;
private String productId;
private String productName;
private String specification;
private Integer quantity;
private String grossWeight;
private String yolk;
public String getCartItemId() { return cartItemId; }
public void setCartItemId(String cartItemId) { this.cartItemId = cartItemId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public String getSpecification() { return specification; }
public void setSpecification(String specification) { this.specification = specification; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public String getGrossWeight() { return grossWeight; }
public void setGrossWeight(String grossWeight) { this.grossWeight = grossWeight; }
public String getYolk() { return yolk; }
public void setYolk(String yolk) { this.yolk = yolk; }
}
/**
* 根据产品ID精确更新产品信息
*/
int updateProductByProductId(
@Param("productId") String productId,
@Param("productName") String productName,
@Param("variety") String variety,
@Param("specification") String specification,
@Param("quantity") Integer quantity,
@Param("grossWeight") String grossWeight,
@Param("yolk") String yolk
);
// 在 SupplyUsersMapper.java 中添加以下方法
/**
* 查询超过指定时间的未分级客户
*/
List<UserProductCartDTO> findUnclassifiedCustomersOlderThan(LocalDateTime thresholdTime);
/**
* 查询超过指定时间的组织公海池客户
*/
List<UserProductCartDTO> findOrganizationSeaPoolsCustomersOlderThan(LocalDateTime thresholdTime);
/**
* 更新客户等级
*/
@Update("UPDATE users SET level = #{level}, updated_at = #{updateTime} WHERE user_id = #{userId}")
boolean updateCustomerLevel(@Param("userId") String userId,
@Param("level") String level,
@Param("updateTime") LocalDateTime updateTime);
// 🔥 新增:查询最近回流的客户
List<UserProductCartDTO> findRecentlyRecycledCustomers(@Param("sinceTime") LocalDateTime sinceTime);
// 新增:直接查询有权限的客户列表(支持分页)
List<UserProductCartDTO> getAuthorizedCustomers(@Param("authInfo") ManagerAuthInfo authInfo, @Param("limit") int limit, @Param("offset") int offset);
/**
* 根据认证信息获取有权限的用户ID列表
*/
List<String> getAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo);
// 获取授权客户总数
int getAuthorizedCustomersCount(@Param("authInfo") ManagerAuthInfo authInfo);
/**
* 根据手机号和权限查询用户
*/
UserProductCartDTO selectByPhoneWithAuth(@Param("phoneNumber") String phoneNumber,
@Param("authInfo") ManagerAuthInfo authInfo);
// 🔥 新增:批量查询用户联系人信息
List<UsersMapper.ContactInfo> getUserContactsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户购物车信息
List<UserProductCartDTO.CartItem> getBuyerCartItemsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户购物车信息(兼容旧方法名)
List<UsersMapper.CartItem> getCartItemsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户基本信息
List<UserProductCartDTO> getUserBasicInfoByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:更新用户通知状态
@Update("UPDATE users SET notice = #{notice} WHERE userId = #{userId}")
int updateNotice(@Param("userId") String userId, @Param("notice") String notice);
}

145
src/main/java/com/example/web/mapper/WechatCustomerMapper.java

@ -0,0 +1,145 @@
package com.example.web.mapper;
import com.example.web.annotation.DataSource;
import com.example.web.entity.Favorites;
import com.example.web.entity.Products;
import com.example.web.entity.Users;
import com.example.web.entity.UsersContacts;
import com.example.web.entity.UsersManagements;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Description: 微信客户数据Mapper接口wechat数据源
*/
@Mapper
@DataSource("wechat")
public interface WechatCustomerMapper {
/**
* 获取所有用户信息
* @return 用户列表
*/
List<Users> getAllUsers();
/**
* 根据用户手机号获取联系人信息
* @param phoneNumber 用户手机号
* @return 联系人信息
*/
UsersContacts getContactsByPhoneNumber(@Param("phoneNumber") String phoneNumber);
/**
* 根据用户ID获取联系人信息
* @param userId 用户ID
* @return 联系人信息
*/
UsersContacts getContactsByUserId(@Param("userId") String userId);
/**
* 根据用户手机号获取负责人信息
* @param phoneNumber 用户手机号
* @return 负责人信息
*/
UsersManagements getManagementsByPhoneNumber(@Param("phoneNumber") String phoneNumber);
/**
* 根据用户ID获取负责人信息
* @param userId 用户ID
* @return 负责人信息
*/
UsersManagements getManagementsByUserId(@Param("userId") String userId);
/**
* 根据用户手机号获取收藏的产品ID列表
* @param userPhone 用户手机号
* @return 产品收藏列表
*/
List<Favorites> getFavoritesByUserPhone(@Param("userPhone") String userPhone);
/**
* 根据产品ID获取产品信息
* @param productId 产品ID
* @return 产品信息
*/
Products getProductById(@Param("productId") String productId);
/**
* 获取部门公海池数据微信数据源
* @param role 用户角色buyer/seller
* @param managercompany 负责人公司
* @param managerdepartment 负责人部门
* @return 部门公海池客户数据
*/
List<Users> getDepartmentSeaPoolUsers(
@Param("role") String role,
@Param("managercompany") String managercompany,
@Param("managerdepartment") String managerdepartment);
/**
* 获取组织公海池数据微信数据源
* @param role 用户角色buyer/seller
* @param managercompany 负责人公司
* @param managerdepartment 负责人部门
* @param organization 负责人组织
* @return 组织公海池客户数据
*/
List<Users> getOrganizationSeaPoolUsers(
@Param("role") String role,
@Param("managercompany") String managercompany,
@Param("managerdepartment") String managerdepartment,
@Param("organization") String organization);
/**
* 根据电话号码获取用户信息
* @param phoneNumber 电话号码
* @return 用户信息
*/
Users getUsersByPhoneNumber(@Param("phoneNumber") String phoneNumber);
/**
* 更新用户信息
* @param users 用户信息
* @return 更新结果
*/
int updateUsers(Users users);
/**
* 根据角色和登录信息获取客户数据
* @param role 角色
* @param userName 用户名
* @param managercompany 负责人公司
* @param managerdepartment 负责人部门
* @param organization 负责人组织
* @return 客户数据列表
*/
List<Users> getCustomersByRole(
@Param("role") String role,
@Param("userName") String userName,
@Param("managercompany") String managercompany,
@Param("managerdepartment") String managerdepartment,
@Param("organization") String organization);
/**
* 更新联系人信息
* @param contacts 联系人信息
* @return 更新结果
*/
int updateUsersContacts(UsersContacts contacts);
/**
* 更新负责人信息
* @param managements 负责人信息
* @return 更新结果
*/
int updateUsersManagements(UsersManagements managements);
/**
* 根据用户ID获取用户信息
* @param userId 用户ID
* @return 用户信息
*/
Users getUsersByUserId(@Param("userId") String userId);
}

203
src/main/java/com/example/web/scheduler/CustomerStatusScheduler.java

@ -0,0 +1,203 @@
package com.example.web.scheduler;
import com.example.web.entity.Contacts;
import com.example.web.entity.CustomerData;
import com.example.web.entity.Enterprise;
import com.example.web.entity.Users;
import com.example.web.mapper.CustomerMapper;
import com.example.web.mapper.WechatCustomerMapper;
import com.example.web.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
/**
* 客户状态流转定时任务
*/
@Component
public class CustomerStatusScheduler {
@Autowired
private CustomerService customerService;
@Autowired
private CustomerMapper customerMapper;
@Autowired
private WechatCustomerMapper wechatCustomerMapper;
/**
* 执行客户状态流转
* 每天凌晨执行
*/
@Scheduled(cron = "0 0 0 * * ?")
public void executeCustomerStatusFlow() {
System.out.println("DEBUG: 开始执行客户状态流转定时任务,当前时间: " + LocalDateTime.now());
// 获取当前时间
LocalDateTime now = LocalDateTime.now();
// 1. 处理primary数据源
processPrimaryDataSource(now);
// 2. 处理wechat数据源
processWechatDataSource(now);
System.out.println("DEBUG: 客户状态流转定时任务执行完成,当前时间: " + LocalDateTime.now());
}
/**
* 处理primary数据源的客户状态流转
* @param now 当前时间
*/
private void processPrimaryDataSource(LocalDateTime now) {
System.out.println("DEBUG: 开始处理primary数据源");
// 获取所有联系人
List<Contacts> contactsList = customerMapper.getAllContacts();
System.out.println("DEBUG: primary数据源联系人总数: " + contactsList.size());
for (Contacts contacts : contactsList) {
try {
// 获取企业信息
Enterprise enterprise = customerMapper.getEnterpriseByContactId(contacts.getContact_id());
if (enterprise == null) {
continue;
}
// 获取客户等级
String currentLevel = enterprise.getLevel();
// 计算跟进时间差
LocalDateTime followupAt = contacts.getFollowup_at();
if (followupAt == null) {
followupAt = contacts.getCreated_at();
}
long daysSinceFollowup = ChronoUnit.DAYS.between(followupAt, now);
// 状态流转逻辑
boolean needUpdate = false;
// 第一次筛选:查找 followup_at ≤ 3天前的客户
if (daysSinceFollowup >= 3 && "unclassified".equals(currentLevel)) {
// 当前状态为"未分级" → 变更为"组织公海池客户"
enterprise.setLevel("organization-sea-pools");
needUpdate = true;
System.out.println("DEBUG: primary数据源 - 客户 " + contacts.getNickName() + " 从 未分级 转为 组织公海池客户,已" + daysSinceFollowup + "天未跟进");
}
// 第二次筛选:查找 followup_at ≤ 6天前的客户
if (daysSinceFollowup >= 6 && "organization-sea-pools".equals(currentLevel)) {
// 当前状态为"组织公海池客户" → 变更为"部门公海池"
enterprise.setLevel("department-sea-pools");
needUpdate = true;
System.out.println("DEBUG: primary数据源 - 客户 " + contacts.getNickName() + " 从 组织公海池客户 转为 部门公海池,已" + daysSinceFollowup + "天未跟进");
}
// 如果需要更新,执行更新操作
if (needUpdate) {
// 更新企业信息
customerMapper.updateEnterprise(enterprise);
// 更新联系人的updated_at为当前时间
contacts.setUpdated_at(now);
customerMapper.updateContacts(contacts);
}
// 提前一天提醒功能:如果距离回流还有1天,发送通知
if (daysSinceFollowup == 2 && "unclassified".equals(currentLevel)) {
// 距离转为组织公海池还有1天
System.out.println("DEBUG: 提醒 - 客户 " + contacts.getNickName() + " 将在明天转为组织公海池,请及时跟进");
// 这里可以添加通知逻辑,比如发送消息给相关业务员
}
if (daysSinceFollowup == 5 && "organization-sea-pools".equals(currentLevel)) {
// 距离转为部门公海池还有1天
System.out.println("DEBUG: 提醒 - 客户 " + contacts.getNickName() + " 将在明天转为部门公海池,请及时跟进");
// 这里可以添加通知逻辑,比如发送消息给相关业务员
}
} catch (Exception e) {
System.out.println("DEBUG: 处理primary数据源客户 " + contacts.getNickName() + " 时出错: " + e.getMessage());
e.printStackTrace();
}
}
System.out.println("DEBUG: primary数据源处理完成");
}
/**
* 处理wechat数据源的客户状态流转
* @param now 当前时间
*/
private void processWechatDataSource(LocalDateTime now) {
System.out.println("DEBUG: 开始处理wechat数据源");
// 获取所有用户
List<Users> usersList = wechatCustomerMapper.getAllUsers();
System.out.println("DEBUG: wechat数据源用户总数: " + usersList.size());
for (Users user : usersList) {
try {
// 获取客户等级
String currentLevel = user.getLevel();
// 计算跟进时间差
LocalDateTime followupAt = user.getFollowup_at();
if (followupAt == null) {
followupAt = user.getCreated_at();
}
long daysSinceFollowup = ChronoUnit.DAYS.between(followupAt, now);
// 状态流转逻辑
boolean needUpdate = false;
// 第一次筛选:查找 followup_at ≤ 3天前的客户
if (daysSinceFollowup >= 3 && "unclassified".equals(currentLevel)) {
// 当前状态为"未分级" → 变更为"组织公海池客户"
user.setLevel("organization-sea-pools");
needUpdate = true;
System.out.println("DEBUG: wechat数据源 - 客户 " + user.getNickName() + " 从 未分级 转为 组织公海池客户,已" + daysSinceFollowup + "天未跟进");
}
// 第二次筛选:查找 followup_at ≤ 6天前的客户
if (daysSinceFollowup >= 6 && "organization-sea-pools".equals(currentLevel)) {
// 当前状态为"组织公海池客户" → 变更为"部门公海池"
user.setLevel("department-sea-pools");
needUpdate = true;
System.out.println("DEBUG: wechat数据源 - 客户 " + user.getNickName() + " 从 组织公海池客户 转为 部门公海池,已" + daysSinceFollowup + "天未跟进");
}
// 如果需要更新,执行更新操作
if (needUpdate) {
// 更新updated_at为当前时间
user.setUpdated_at(now);
wechatCustomerMapper.updateUsers(user);
}
// 提前一天提醒功能:如果距离回流还有1天,发送通知
if (daysSinceFollowup == 2 && "unclassified".equals(currentLevel)) {
// 距离转为组织公海池还有1天
System.out.println("DEBUG: 提醒 - 客户 " + user.getNickName() + " 将在明天转为组织公海池,请及时跟进");
// 这里可以添加通知逻辑,比如发送消息给相关业务员
}
if (daysSinceFollowup == 5 && "organization-sea-pools".equals(currentLevel)) {
// 距离转为部门公海池还有1天
System.out.println("DEBUG: 提醒 - 客户 " + user.getNickName() + " 将在明天转为部门公海池,请及时跟进");
// 这里可以添加通知逻辑,比如发送消息给相关业务员
}
} catch (Exception e) {
System.out.println("DEBUG: 处理wechat数据源客户 " + user.getNickName() + " 时出错: " + e.getMessage());
e.printStackTrace();
}
}
System.out.println("DEBUG: wechat数据源处理完成");
}
}

1364
src/main/java/com/example/web/service/CustomerService.java

File diff suppressed because it is too large

501
src/main/java/com/example/web/service/EnterpriseService.java

@ -1,501 +0,0 @@
package com.example.web.service;
import com.example.web.dto.EnterpriseInfoDTO;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.dto.UnifiedCustomerDTO;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.entity.Enterprise;
import com.example.web.entity.Contacts;
import com.example.web.entity.Managers;
import com.example.web.mapper.EnterpriseMapper;
import com.example.web.mapper.ContactsMapper;
import com.example.web.mapper.ManagersMapper;
import com.example.web.mapper.UsersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Service// 标识为Spring服务层组件,用于处理业务逻辑
public class EnterpriseService {
@Autowired// 自动注入数据访问层(Mapper)对象,用于数据库操作
private EnterpriseMapper enterpriseMapper;
@Autowired
private ContactsMapper contactsMapper;
@Autowired
private ManagersMapper managersMapper;
@Autowired
private UsersMapper usersMapper;
// 修改:检查电话号码是否重复(同时检查两个数据源)
public boolean isPhoneNumberDuplicate(String phoneNumber) {
// 1. 检查微信数据源中的电话号码是否重复
boolean wechatDuplicate = false;
try {
UserProductCartDTO existingUser = usersMapper.selectByPhone(phoneNumber);
wechatDuplicate = existingUser != null;
if (wechatDuplicate) {
System.out.println("❌ 电话号码在微信数据源中已存在: " + phoneNumber);
}
} catch (Exception e) {
System.err.println("查询微信数据源电话号码失败: " + e.getMessage());
}
// 2. 检查默认数据源(userlogin)中的电话号码是否重复
boolean defaultDuplicate = false;
try {
int count = contactsMapper.countByPhoneNumber(phoneNumber);
defaultDuplicate = count > 0;
if (defaultDuplicate) {
System.out.println("❌ 电话号码在默认数据源中已存在: " + phoneNumber);
}
} catch (Exception e) {
System.err.println("查询默认数据源电话号码失败: " + e.getMessage());
}
// 3. 返回最终结果:任意一个数据源中存在都算重复
boolean isDuplicate = wechatDuplicate || defaultDuplicate;
System.out.println("📞 电话号码重复检查结果: " + phoneNumber +
" -> 微信数据源:" + wechatDuplicate +
", 默认数据源:" + defaultDuplicate +
", 最终结果:" + isDuplicate);
return isDuplicate;
}
private String convertFrontendTypeToDatabase(String frontendType) {
if (frontendType == null) {
return null;
}
switch (frontendType) {
case "供应端":
return "seller";
case "客户端":
return "buyer";
case "BOTH":
return "both"; // 改为小写
default:
return frontendType;
}
}
private String convertDatabaseTypeToFrontend(String databaseType) {
if (databaseType == null) {
return null;
}
switch (databaseType) {
case "seller":
return "供应端";
case "buyer":
return "客户端";
case "both": // 匹配小写的数据库存储
return "BOTH";
default:
return databaseType;
}
}
// 新增客户:保存企业、联系人、负责人信息(默认数据源)
@Transactional(rollbackFor = Exception.class)
public boolean addCustomer(UnifiedCustomerDTO dto) {
System.out.println("====================================================");
System.out.println("➕ 开始新增客户到默认数据源");
System.out.println("====================================================");
System.out.println("📋 客户信息: 手机=" + dto.getPhoneNumber() +
", 公司=" + dto.getCompany() +
", 类型=" + dto.getType() +
", 等级=" + dto.getLevel());
// 1. 转换前端类型为数据库类型
String databaseType = convertFrontendTypeToDatabase(dto.getType());
dto.setType(databaseType);
System.out.println("🔄 类型转换: 前端=" + dto.getType() + " → 数据库=" + databaseType);
// 2. 权限校验
if (!"buyer".equals(dto.getType()) && !"both".equals(dto.getType())) {
System.out.println("❌ 权限校验失败: 销售端只能新增客户端类型和BOTH客户");
throw new IllegalArgumentException("采购端权限只能新增客户端类型客户和BOTH类型客户");
}
System.out.println("✅ 权限校验通过");
// 3. 等级校验
if (!StringUtils.hasText(dto.getLevel())) {
System.out.println("❌ 等级校验失败: 客户等级不能为空");
throw new IllegalArgumentException("客户等级不能为空");
}
System.out.println("✅ 等级校验通过");
// 4. 检查电话号码是否重复
System.out.println("🔍 检查电话号码重复性...");
if (isPhoneNumberDuplicate(dto.getPhoneNumber())) {
System.out.println("❌ 电话号码重复: " + dto.getPhoneNumber());
throw new IllegalArgumentException("电话号码已存在");
}
System.out.println("✅ 电话号码检查通过");
// 5. 封装并保存企业信息
System.out.println("💾 保存企业信息...");
Enterprise enterprise = new Enterprise();
enterprise.setId(dto.getId());
enterprise.setCompany(dto.getCompany());
enterprise.setRegion(dto.getRegion());
enterprise.setLevel(dto.getLevel());
enterprise.setType(dto.getType());
enterprise.setDemand(dto.getDemand());
enterprise.setSpec(dto.getSpec());
int enterpriseRows = enterpriseMapper.insertEnterprise(enterprise);
System.out.println("✅ 企业信息保存结果: " + enterpriseRows + " 行受影响");
// 6. 封装联系人信息
System.out.println("💾 保存联系人信息...");
Contacts contacts = new Contacts();
String contactId = UUID.randomUUID().toString().replaceAll("-", "");
contacts.setContact_id(contactId);
contacts.setId(dto.getId());
contacts.setNickName(dto.getNickName());
contacts.setPhoneNumber(dto.getPhoneNumber());
contacts.setWechat(dto.getWechat());
contacts.setAccount(dto.getAccount());
contacts.setAccountNumber(dto.getAccountNumber());
contacts.setBank(dto.getBank());
contacts.setAddress(dto.getAddress());
contacts.setCreated_at(dto.getCreated_at() != null ? dto.getCreated_at() : LocalDateTime.now());
contacts.setUpdated_at(dto.getUpdated_at() != null ? dto.getUpdated_at() : LocalDateTime.now());
int contactsRows = contactsMapper.insertContacts(contacts);
System.out.println("✅ 联系人信息保存结果: " + contactsRows + " 行受影响");
// 7. 封装负责人信息 - 使用前端传递的认证信息(更新为新表结构)
System.out.println("💾 保存负责人信息...");
Managers managers = new Managers();
managers.setId(dto.getId());
managers.setManagerId(StringUtils.hasText(dto.getManagerId()) ? dto.getManagerId() : "未分配");
managers.setManagercompany(StringUtils.hasText(dto.getManagercompany()) ? dto.getManagercompany() : "未分配");
managers.setManagerdepartment(StringUtils.hasText(dto.getManagerdepartment()) ? dto.getManagerdepartment() : "未分配");
managers.setOrganization(StringUtils.hasText(dto.getOrganization()) ? dto.getOrganization() : "未分配");
managers.setRole(StringUtils.hasText(dto.getRole()) ? dto.getRole() : "未分配");
managers.setRoot("3"); // 默认权限
managers.setUserName(StringUtils.hasText(dto.getUserName()) ? dto.getUserName() : "未分配");
managers.setAssistant(StringUtils.hasText(dto.getAssistant()) ? dto.getAssistant() : "无");
managers.setCreated_at(LocalDateTime.now());
managers.setUpdated_at(LocalDateTime.now());
int managersRows = managersMapper.insertManagers(managers);
System.out.println("✅ 负责人信息保存结果: " + managersRows + " 行受影响");
boolean success = enterpriseRows > 0 && contactsRows > 0 && managersRows > 0;
if (success) {
System.out.println("🎉 新增客户成功,负责人信息已自动填充");
// 记录负责人信息详情
System.out.println("📝 负责人信息详情: " +
"负责人id=" + managers.getManagerId() +
"负责公司=" + managers.getManagercompany() +
", 负责部门=" + managers.getManagerdepartment() +
", 组织=" + managers.getOrganization() +
", 角色=" + managers.getRole() +
", 负责人=" + managers.getUserName() +
", 协助人=" + managers.getAssistant());
} else {
System.out.println("❌ 新增客户失败");
}
System.out.println("====================================================");
return success;
}
/**
* 获取所有企业的完整信息企业信息 + 联系人信息 + 负责人信息
*/
public List<EnterpriseInfoDTO> getAllEnterpriseInfo() {
try {
return enterpriseMapper.selectAllEnterpriseInfo();
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 根据企业ID获取详细信息
*/
public EnterpriseInfoDTO getEnterpriseInfoById(String id) {
try {
return enterpriseMapper.selectEnterpriseInfoById(id);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取所有企业基本信息
*/
public List<Enterprise> getAllEnterprises() {
try {
return enterpriseMapper.selectAllEnterprises();
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 根据企业ID获取联系人信息
*/
public List<Contacts> getContactsByEnterpriseId(String id) {
try {
return contactsMapper.selectContactsByEnterpriseId(id);
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 根据企业ID获取负责人信息
*/
public List<Managers> getManagersByEnterpriseId(String id) {
try {
return managersMapper.selectManagersByEnterpriseId(id);
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 搜索企业信息根据企业名称地区类型等
*/
public List<EnterpriseInfoDTO> searchEnterprises(String keyword) {
try {
// 这里可以扩展更复杂的搜索逻辑
List<EnterpriseInfoDTO> allInfo = enterpriseMapper.selectAllEnterpriseInfo();
if (keyword == null || keyword.trim().isEmpty()) {
return allInfo;
}
List<EnterpriseInfoDTO> result = new ArrayList<>();
for (EnterpriseInfoDTO info : allInfo) {
if (info.getEnterprise() != null) {
String company = info.getEnterprise().getCompany() != null ? info.getEnterprise().getCompany().toLowerCase() : "";
String region = info.getEnterprise().getRegion() != null ? info.getEnterprise().getRegion().toLowerCase() : "";
String type = info.getEnterprise().getType() != null ? info.getEnterprise().getType().toLowerCase() : "";
if (company.contains(keyword.toLowerCase()) ||
region.contains(keyword.toLowerCase()) ||
type.contains(keyword.toLowerCase())) {
result.add(info);
}
}
}
return result;
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 更新默认数据源客户信息企业联系人负责人
*/
@Transactional(rollbackFor = Exception.class)
public boolean updateDefaultCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo) {
// 转换前端类型为数据库类型
String databaseType = convertFrontendTypeToDatabase(dto.getType());
dto.setType(databaseType);
// 销售员权限:校验客户类型,只允许buyer和both
if (!"buyer".equals(dto.getType()) && !"both".equals(dto.getType())) {
throw new IllegalArgumentException("销售员权限只能更新为客户端类型客户");
}
// 1. 更新企业信息
Enterprise enterprise = new Enterprise();
enterprise.setId(dto.getId());
enterprise.setCompany(dto.getCompany());
enterprise.setRegion(dto.getRegion());
enterprise.setLevel(dto.getLevel());
enterprise.setType(dto.getType());
enterprise.setDemand(dto.getDemand());
enterprise.setSpec(dto.getSpec());
int enterpriseRows = enterpriseMapper.updateEnterprise(enterprise);
if (enterpriseRows <= 0) {
throw new RuntimeException("更新企业信息失败,企业ID: " + dto.getId());
}
// 2. 更新联系人信息 - 修复逻辑
Contacts contacts = new Contacts();
// 先根据企业ID查询原始联系人信息
List<Contacts> originalContactsList = contactsMapper.selectContactsByEnterpriseId(dto.getId());
if (originalContactsList != null && !originalContactsList.isEmpty()) {
Contacts originalContacts = originalContactsList.get(0);
contacts.setContact_id(originalContacts.getContact_id());
contacts.setId(dto.getId());
contacts.setNickName(dto.getNickName());
// 关键:使用原始电话号码,禁止修改
contacts.setPhoneNumber(originalContacts.getPhoneNumber());
contacts.setWechat(dto.getWechat());
contacts.setAccount(dto.getAccount());
contacts.setAccountNumber(dto.getAccountNumber());
contacts.setBank(dto.getBank());
contacts.setAddress(dto.getAddress());
int contactsRows = contactsMapper.updateContacts(contacts);
if (contactsRows <= 0) {
throw new RuntimeException("更新联系人信息失败,联系人ID: " + originalContacts.getContact_id());
}
} else {
// 如果没有找到联系人记录,创建新的联系人
System.out.println("未找到现有联系人,创建新的联系人记录");
contacts.setContact_id(UUID.randomUUID().toString().replaceAll("-", ""));
contacts.setId(dto.getId());
contacts.setNickName(dto.getNickName());
contacts.setPhoneNumber(""); // 新创建的联系人电话号码为空
contacts.setWechat(dto.getWechat());
contacts.setAccount(dto.getAccount());
contacts.setAccountNumber(dto.getAccountNumber());
contacts.setBank(dto.getBank());
contacts.setAddress(dto.getAddress());
contacts.setCreated_at(LocalDateTime.now());
contacts.setUpdated_at(LocalDateTime.now());
int contactsRows = contactsMapper.insertContacts(contacts);
if (contactsRows <= 0) {
throw new RuntimeException("创建联系人信息失败");
}
}
// 🔥 修改:检查是否需要更新负责人信息
boolean hasFrontendManagerInfo = StringUtils.hasText(dto.getManagercompany()) ||
StringUtils.hasText(dto.getManagerdepartment()) ||
StringUtils.hasText(dto.getOrganization()) ||
StringUtils.hasText(dto.getRole()) ||
StringUtils.hasText(dto.getUserName()) ||
StringUtils.hasText(dto.getAssistant());
// 定义非公海池等级
Set<String> nonPublicSeaLevels = Set.of("important", "normal", "low-value", "logistics", "unclassified");
boolean isNonPublicSeaCustomer = nonPublicSeaLevels.contains(dto.getLevel());
System.out.println("🎯 负责人信息更新检查: " +
"是否非公海池客户=" + isNonPublicSeaCustomer +
", 前端是否提交负责人信息=" + hasFrontendManagerInfo);
// 🔥 只有非公海池客户且前端提交了负责人信息时才更新
if (isNonPublicSeaCustomer && hasFrontendManagerInfo) {
System.out.println("✅ 需要更新负责人信息");
Managers managers = new Managers();
managers.setId(dto.getId());
System.out.println("✅ 前端提交了负责人信息,使用前端数据更新");
// 使用前端提交的数据
managers.setManagercompany(dto.getManagercompany());
managers.setManagerdepartment(dto.getManagerdepartment());
managers.setOrganization(dto.getOrganization());
managers.setRole(dto.getRole());
managers.setUserName(dto.getUserName());
managers.setAssistant(dto.getAssistant());
managers.setUpdated_at(LocalDateTime.now());
int managersRows = managersMapper.updateManagers(managers);
if (managersRows <= 0) {
// 如果没有更新到记录,尝试插入
System.out.println("未找到现有负责人记录,创建新的负责人记录");
managers.setManagerId(authInfo.getManagerId());
managers.setRoot("3");
managers.setCreated_at(LocalDateTime.now());
managersRows = managersMapper.insertManagers(managers);
if (managersRows <= 0) {
throw new RuntimeException("更新负责人信息失败");
}
}
System.out.println("✅ 负责人信息更新成功");
} else {
System.out.println("ℹ️ 无需更新负责人信息 - " +
(!isNonPublicSeaCustomer ? "客户不是非公海池客户" : "") +
(!hasFrontendManagerInfo ? "前端未提交负责人信息" : ""));
}
return true;
}
/**
* 更新wechat数据源的客户信息用户和产品信息
*/
public boolean updateWechatCustomer(UnifiedCustomerDTO dto) {
// 修复:确保类型转换正确
String type = dto.getType();
if ("BOTH".equals(type)) {
type = "both";
dto.setType(type);
} else if ("客户端".equals(type)) {
type = "buyer";
dto.setType(type);
} else if ("供应端".equals(type)) {
type = "seller";
dto.setType(type);
}
// 销售员权限:校验客户类型,只允许buyer和both
if (!"buyer".equals(dto.getType()) && !"both".equals(dto.getType())) {
throw new IllegalArgumentException("销售权限只能更新为供应端类型客户和BOTH类型客户");
}
// 1. 根据手机号查询wechat用户ID(关联用户与产品)
UserProductCartDTO user = usersMapper.selectByPhone(dto.getPhoneNumber());
if (user == null) {
// 若该用户在wechat数据源中不存在,无需更新
return true;
}
// 2. 更新用户基本信息
UserProductCartDTO updateUser = new UserProductCartDTO();
updateUser.setUserId(user.getUserId());
updateUser.setPhoneNumber(dto.getPhoneNumber());
updateUser.setNickName(dto.getNickName());
updateUser.setType(dto.getType());
updateUser.setUpdated_at(LocalDateTime.now());
int rows = usersMapper.updateByPhone(updateUser);
System.out.println("更新用户基本信息影响行数: " + rows);
// 3. 如果有产品信息需要更新
if (("buyer".equals(dto.getType()) || "both".equals(dto.getType())) &&
dto.getProductName() != null && !dto.getProductName().trim().isEmpty()) {
System.out.println("更新产品信息");
int productRows = usersMapper.updateProductBySellerId(
user.getUserId(),
dto.getProductName(),
dto.getVariety(),
dto.getSpecification(),
dto.getQuantity(),
dto.getGrossWeight(),
dto.getYolk()
);
System.out.println("更新产品信息影响行数: " + productRows);
}
return rows > 0;
}
}

65
src/main/java/com/example/web/service/FollowUpService.java

@ -1,65 +0,0 @@
package com.example.web.service;
import com.example.web.entity.Contacts;
import com.example.web.entity.Users;
import com.example.web.mapper.ContactsMapper;
import com.example.web.mapper.SupplyContactsMapper;
import com.example.web.mapper.SupplyUsersMapper;
import com.example.web.mapper.UsersMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class FollowUpService {
private final ContactsMapper contactsMapper;
private final UsersMapper usersMapper;
private final SupplyContactsMapper supplyContactsMapper;
private final SupplyUsersMapper supplyUsersMapper;
public FollowUpService(ContactsMapper contactsMapper, UsersMapper usersMapper, SupplyContactsMapper supplyContactsMapper, SupplyUsersMapper supplyUsersMapper) {
this.contactsMapper = contactsMapper;
this.usersMapper = usersMapper;
this.supplyContactsMapper = supplyContactsMapper;
this.supplyUsersMapper = supplyUsersMapper;
}
/**
* 根据电话号码查询跟进信息
* @param phoneNumber 电话号码
* @return 跟进信息
*/
public String getFollowUpByPhone(String phoneNumber) {
try {
// 先尝试在contacts表中查询
String followup = contactsMapper.getFollowUpByPhone(phoneNumber);
if (followup != null) {
return followup;
}
// 如果contacts表中没有,尝试在users表中查询
return supplyUsersMapper.getFollowUpByPhone(phoneNumber);
} catch (Exception e) {
return "";
}
}
/**
* 保存跟进信息
* @param phoneNumber 电话号码
* @param followup 跟进信息
* @return 是否成功
*/
public boolean saveFollowUp(String phoneNumber, String followup) {
try {
// 先尝试在contacts表中保存
if (contactsMapper.updateFollowUpByPhone(phoneNumber, followup) > 0) {
return true;
}
// 如果contacts表中没有,尝试在users表中保存
return supplyUsersMapper.updateFollowUpByPhone(phoneNumber, followup) > 0;
} catch (Exception e) {
throw e;
}
}
}

105
src/main/java/com/example/web/service/InformationTraService.java

@ -1,105 +0,0 @@
package com.example.web.service;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.entity.Contacts;
import com.example.web.entity.InformationTra;
import com.example.web.mapper.*;
import com.example.web.config.DynamicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class InformationTraService {
@Autowired
private InformationTraMapper informationTraMapper;
@Autowired
private UsersMapper usersMapper;
@Autowired
private ContactsMapper contactsMapper;
/**
* 记录操作事件
*/
public boolean recordOperationEvent(String phoneNumber, String customerName, String operationEvent,
String managerCompany, String managerDepartment, String organization,
String role, String userName, String assistant) {
// 保存原始数据源,以便在finally块中恢复
String originalDataSource = DynamicDataSource.getCurrentDataSourceKey();
try {
// 1. 从两个数据源查询客户信息
String userId = null;
// 查询wechat数据源的users表
DynamicDataSource.setDataSourceKey("wechat");
UserProductCartDTO wechatUser = usersMapper.selectByPhone(phoneNumber);
if (wechatUser != null) {
userId = wechatUser.getUserId();
System.out.println("ℹ️ 从wechat数据源获取到userId: " + userId);
} else {
System.out.println("ℹ️ 在wechat数据源中未找到客户");
// 查询primary数据源的contacts表
DynamicDataSource.setDataSourceKey("primary");
Contacts contact = contactsMapper.selectByPhoneNumber(phoneNumber);
if (contact != null) {
System.out.println("✅ 在primary数据源中找到客户: " + contact.getNickName());
userId = contact.getId();
System.out.println("ℹ️ 使用primary数据源的contact.id作为userId: " + userId);
}
}
// 如果都没找到,返回失败
if (userId == null) {
System.err.println("❌ 无法获取客户ID,操作记录失败");
return false;
}
// 2. 获取当前时间
LocalDateTime now = LocalDateTime.now();
// 3. 构造操作记录
InformationTra informationTra = new InformationTra();
informationTra.setTracompany(managerCompany);
informationTra.setTradepartment(managerDepartment);
informationTra.setTraorganization(organization);
informationTra.setTrarole(role);
informationTra.setTrauserName(userName);
informationTra.setTraassistant(assistant);
informationTra.setUserId(userId);
informationTra.setOperationEvent(operationEvent);
informationTra.setOperationTime(now);
informationTra.setCreatedAt(now);
informationTra.setUpdatedAt(now);
// 4. 始终写入wechat数据源的informationtra表
DynamicDataSource.setDataSourceKey("wechat");
System.out.println("🔄 设置数据源为wechat,准备插入信息跟踪记录");
int result = informationTraMapper.insertInformationTra(informationTra);
if (result > 0) {
System.out.println("✅ 插入信息跟踪记录成功,影响行数: " + result);
return true;
} else {
System.err.println("❌ 插入信息跟踪记录失败,影响行数: " + result);
return false;
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("❌ 操作记录异常: " + e.getMessage());
return false;
} finally {
// 恢复原始数据源,避免线程池复用问题
if (originalDataSource != null) {
DynamicDataSource.setDataSourceKey(originalDataSource);
} else {
DynamicDataSource.clearDataSourceKey();
}
System.out.println("🔄 恢复数据源为原始值: " + (originalDataSource != null ? originalDataSource : "默认数据源"));
}
}
}

265
src/main/java/com/example/web/service/LoginService.java

@ -1,259 +1,28 @@
package com.example.web.service;
import com.example.web.entity.Login;
import com.example.web.entity.Rootdb;
import com.example.web.entity.UsersManagements;
import com.example.web.entity.Managers;
import com.example.web.mapper.LoginMapper;
import com.example.web.mapper.RootdbMapper;
import com.example.web.mapper.UsersManagementsMapper;
import com.example.web.mapper.SupplyUsersManagementsMapper;
import com.example.web.mapper.ManagersMapper;
import com.example.web.mapper.SupplyManagersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.web.entity.Personnel;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class LoginService {
@Autowired
private LoginMapper loginMapper;
@Autowired
private RootdbMapper rootdbMapper;
@Autowired
private UsersManagementsMapper usersManagementsMapper; // 销售端 usermanagements
@Autowired
private SupplyUsersManagementsMapper supplyUsersManagementsMapper; // 采购端 usermanagements
@Autowired
private ManagersMapper managersMapper; // 销售端 managers
@Autowired
private SupplyManagersMapper supplyManagersMapper; // 采购端 managers
public Map<String, Object> authenticate(String projectName, String userName, String password) {
Map<String, Object> result = new HashMap<>();
try {
System.out.println("=== 开始认证过程 ===");
System.out.println("工位名: " + projectName + ", 用户名: " + userName);
// 1. 验证登录信息
Login loginInfo = loginMapper.findByProjectNameAndUserName(projectName, userName);
System.out.println("登录信息查询结果: " + (loginInfo != null ? "成功" : "失败"));
if (loginInfo == null) {
result.put("success", false);
result.put("message", "用户名或工位名不存在");
result.put("errorType", "user");
return result;
}
// 2. 验证密码
boolean passwordCorrect = loginInfo.getPassword().equals(password);
System.out.println("密码验证: " + (passwordCorrect ? "正确" : "错误"));
if (!passwordCorrect) {
result.put("success", false);
result.put("message", "密码错误");
result.put("errorType", "password");
return result;
}
// 3. 获取权限信息
Rootdb rootInfo = rootdbMapper.findByProjectName(projectName);
System.out.println("权限信息查询结果: " + (rootInfo != null ? "成功" : "失败"));
System.out.println("权限等级: " + (rootInfo != null ? rootInfo.getRoot() : "无"));
if (rootInfo == null) {
result.put("success", false);
result.put("message", "未找到该职位的权限信息");
return result;
}
// 4. 🔥 关键修改:使用 loginId 来查询负责人信息
String loginId = String.valueOf(loginInfo.getId()); // 获取 login 表的 id
System.out.println("登录ID (loginId): " + loginId);
boolean isSupplySide = isSupplySideProject(projectName);
System.out.println("🔄 系统识别: " + (isSupplySide ? "采购端" : "销售端"));
UsersManagements userManagement = null;
boolean fromManagersTable = false;
// 4.1 首先尝试从 managers 表查询(使用 loginId 作为 managerId)
List<Managers> managersList;
if (isSupplySide) {
managersList = supplyManagersMapper.selectByUserNameAndManagerId(userName, loginId);
} else {
managersList = managersMapper.selectByUserNameAndManagerId(userName, loginId);
}
if (managersList != null && !managersList.isEmpty()) {
// 从 managers 表找到数据
Managers manager = managersList.get(0);
if (managersList.size() > 1) {
System.out.println("找到多个 managers 记录,数量: " + managersList.size());
// 选择最新的记录
manager = managersList.stream()
.sorted((m1, m2) -> {
if (m1.getUpdated_at() != null && m2.getUpdated_at() != null) {
return m2.getUpdated_at().compareTo(m1.getUpdated_at());
}
return 0;
})
.findFirst()
.orElse(managersList.get(0));
}
// 将 Managers 转换为 UsersManagements
userManagement = convertManagerToUserManagement(manager);
fromManagersTable = true;
System.out.println("✅ 从 managers 表获取用户管理信息");
} else {
// 4.2 如果 managers 表没有数据,再从 usermanagements 表查询(使用 loginId 作为 managerId)
List<UsersManagements> userManagementsList;
if (isSupplySide) {
userManagementsList = supplyUsersManagementsMapper.findByUserNameAndManagerId(userName, loginId);
} else {
userManagementsList = usersManagementsMapper.findByUserNameAndManagerId(userName, loginId);
}
if (userManagementsList != null && !userManagementsList.isEmpty()) {
if (userManagementsList.size() > 1) {
System.out.println("找到多个 usermanagements 记录,数量: " + userManagementsList.size());
userManagement = userManagementsList.stream()
.sorted((u1, u2) -> {
if (u1.getUpdated_at() != null && u2.getUpdated_at() != null) {
return u2.getUpdated_at().compareTo(u1.getUpdated_at());
}
return 0;
})
.findFirst()
.orElse(userManagementsList.get(0));
} else {
userManagement = userManagementsList.get(0);
}
System.out.println("✅ 从 usermanagements 表获取用户管理信息");
}
}
if (userManagement == null) {
result.put("success", false);
result.put("message", "未找到对应的负责人信息,请检查用户名和密码是否正确!");
return result;
}
// 打印用户管理信息详情
System.out.println("用户管理信息详情:");
System.out.println(" - 数据来源: " + (fromManagersTable ? "managers表" : "usermanagements表"));
System.out.println(" - managerId: " + userManagement.getManagerId());
System.out.println(" - managercompany: " + userManagement.getManagercompany());
System.out.println(" - managerdepartment: " + userManagement.getManagerdepartment());
System.out.println(" - organization: " + userManagement.getOrganization());
System.out.println(" - role: " + userManagement.getRole());
System.out.println(" - userName: " + userManagement.getUserName());
System.out.println(" - assistant: " + userManagement.getAssistant());
// 5. 生成token(包含端信息)
String token = generateToken(userName, projectName, isSupplySide);
System.out.println("生成的token: " + token);
// 6. 构建返回结果
result.put("success", true);
result.put("token", token);
result.put("root", rootInfo.getRoot());
result.put("user", buildUserResponse(userManagement));
result.put("isSupplySide", isSupplySide);
result.put("fromManagersTable", fromManagersTable); // 新增:标识数据来源
result.put("loginId", loginId); // 返回 loginId
// 返回完整的认证信息供前端使用
Map<String, Object> authInfo = new HashMap<>();
authInfo.put("managerId", userManagement.getManagerId() != null ? userManagement.getManagerId() : "");
authInfo.put("company", userManagement.getManagercompany() != null ? userManagement.getManagercompany() : "");
authInfo.put("department", userManagement.getManagerdepartment() != null ? userManagement.getManagerdepartment() : "");
authInfo.put("organization", userManagement.getOrganization() != null ? userManagement.getOrganization() : "");
authInfo.put("role", userManagement.getRole() != null ? userManagement.getRole() : "");
authInfo.put("userName", userManagement.getUserName() != null ? userManagement.getUserName() : "");
authInfo.put("assistant", userManagement.getAssistant() != null ? userManagement.getAssistant() : "");
result.put("authInfo", authInfo);
System.out.println("✅ 返回完整认证信息: " + authInfo);
System.out.println("最终返回结果: " + result);
System.out.println("=== 认证过程结束 ===");
} catch (Exception e) {
System.err.println("认证过程中发生异常: " + e.getMessage());
e.printStackTrace();
result.put("success", false);
result.put("message", "登录过程中发生错误: " + e.getMessage());
}
return result;
}
/**
* @Description: 登录服务接口
* @Author:
* @Date: 2025-12-17
*/
public interface LoginService {
/**
* Managers 实体转换为 UsersManagements 实体
* 验证用户登录信息
* @param projectName 工位名
* @param userName 用户名
* @param password 密码
* @return Login对象如果验证成功则返回用户信息否则返回null
*/
private UsersManagements convertManagerToUserManagement(Managers manager) {
UsersManagements userManagement = new UsersManagements();
userManagement.setId(manager.getManager_id());
userManagement.setUserId(manager.getId()); // 使用企业id作为userId
userManagement.setManagerId(manager.getManagerId());
userManagement.setManagercompany(manager.getManagercompany());
userManagement.setManagerdepartment(manager.getManagerdepartment());
userManagement.setOrganization(manager.getOrganization());
userManagement.setRole(manager.getRole());
userManagement.setRoot(manager.getRoot());
userManagement.setCreated_at(manager.getCreated_at());
userManagement.setUpdated_at(manager.getUpdated_at());
userManagement.setUserName(manager.getUserName());
userManagement.setAssistant(manager.getAssistant());
return userManagement;
}
Login verifyLogin(String projectName, String userName, String password);
/**
* 判断是否为采购端工位
* 根据managerId获取员工详细信息
* @param managerId 负责人ID
* @return Personnel对象包含员工详细信息
*/
private boolean isSupplySideProject(String projectName) {
if (projectName == null) {
return false;
}
String lowerProjectName = projectName.toLowerCase();
return lowerProjectName.contains("supply") ||
lowerProjectName.contains("purchase") ||
lowerProjectName.contains("procurement") ||
lowerProjectName.contains("采购");
}
/**
* 生成token包含端信息
*/
private String generateToken(String userName, String projectName, boolean isSupplySide) {
String side = isSupplySide ? "supply" : "sales";
return System.currentTimeMillis() + "-" + userName + "-" + projectName + "-" + side;
}
private Map<String, Object> buildUserResponse(UsersManagements userManagement) {
Map<String, Object> user = new HashMap<>();
user.put("managerId", userManagement.getManagerId() != null ? userManagement.getManagerId() : "");
user.put("managercompany", userManagement.getManagercompany() != null ? userManagement.getManagercompany() : "");
user.put("managerdepartment", userManagement.getManagerdepartment() != null ? userManagement.getManagerdepartment() : "");
user.put("organization", userManagement.getOrganization() != null ? userManagement.getOrganization() : "");
user.put("role", userManagement.getRole() != null ? userManagement.getRole() : "");
user.put("userName", userManagement.getUserName() != null ? userManagement.getUserName() : "");
user.put("assistant", userManagement.getAssistant() != null ? userManagement.getAssistant() : "");
System.out.println("构建的用户响应: " + user);
return user;
}
Personnel getPersonnelInfo(String managerId);
}

48
src/main/java/com/example/web/service/NotificationService.java

@ -0,0 +1,48 @@
package com.example.web.service;
import com.example.web.dto.NotificationDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* WebSocket通知服务
*/
@Service
public class NotificationService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
/**
* 广播通知给所有用户
*/
public void broadcastNotification(NotificationDTO notification) {
messagingTemplate.convertAndSend("/topic/notifications", notification);
}
/**
* 发送通知给特定用户
*/
public void sendNotificationToUser(String userId, NotificationDTO notification) {
messagingTemplate.convertAndSendToUser(userId, "/queue/notifications", notification);
}
/**
* 发送客户数据更新通知
*/
public void sendCustomerUpdateNotification(String customerId, String message) {
NotificationDTO notification = new NotificationDTO();
notification.setType("CUSTOMER_UPDATE");
notification.setTitle("客户数据更新");
notification.setMessage(message);
notification.setCustomerId(customerId);
notification.setTimestamp(LocalDateTime.now(ZoneOffset.UTC));
// 广播给所有用户
messagingTemplate.convertAndSend("/topic/customers/" + customerId, notification);
}
}

15
src/main/java/com/example/web/service/PersonnelService.java

@ -0,0 +1,15 @@
package com.example.web.service;
import com.example.web.entity.Personnel;
/**
* @Description: 员工信息服务接口
*/
public interface PersonnelService {
/**
* 根据负责人ID获取员工信息
* @param managerId 负责人ID
* @return Personnel对象
*/
Personnel getPersonnelByManagerId(String managerId);
}

818
src/main/java/com/example/web/service/PoolCustomerService.java

@ -1,818 +0,0 @@
package com.example.web.service;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.entity.UsersManagements;
import com.example.web.mapper.UsersManagementsMapper;
import com.example.web.mapper.UsersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class PoolCustomerService {
@Autowired
private UsersMapper usersMapper;
@Autowired
private UsersManagementsMapper usersManagementsMapper;
/**
* 公共方法判断是否为公海池客户供Controller调用
*/
public boolean isPublicSeaCustomerPublic(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) {
return isPublicSeaCustomer(userInfo, authInfo);
}
/**
* 根据手机号查询微信用户信息销售端权限只处理buyer和both类型- 支持一对多
*/
public UserProductCartDTO getWechatCustomerByPhone(String phoneNumber) {
if (!StringUtils.hasText(phoneNumber)) {
throw new IllegalArgumentException("手机号不能为空");
}
// 1. 根据手机号查询微信用户基本信息
UserProductCartDTO userInfo = usersMapper.selectByPhone(phoneNumber);
if (userInfo == null) {
throw new RuntimeException("未找到手机号对应的微信用户:" + phoneNumber);
}
System.out.println("获取到用户基础信息,用户ID: " + userInfo.getUserId() +
", 类型: " + userInfo.getType() +
", 公司: " + userInfo.getCompany() +
", 需求: " + userInfo.getDemand() +
", 规格: " + userInfo.getSpec());
// 2. 获取所有联系人信息(一对多)
try {
List<UsersMapper.ContactInfo> contacts = usersMapper.getUserAllContacts(userInfo.getUserId());
// 转换为UserProductCartDTO.UsersContacts格式以保持兼容
if (contacts != null && !contacts.isEmpty()) {
List<UserProductCartDTO.UsersContacts> userContacts = contacts.stream()
.map(contact -> {
UserProductCartDTO.UsersContacts userContact = new UserProductCartDTO.UsersContacts(
contact.getWechat(),
contact.getAccount(),
contact.getAccountNumber(),
contact.getBank(),
contact.getAddress()
);
// 设置contactId
userContact.setContactId(contact.getContactId());
return userContact;
})
.collect(Collectors.toList());
userInfo.setUsersContacts(userContacts);
} else {
userInfo.setUsersContacts(Collections.emptyList());
}
System.out.println("获取用户所有联系人信息,数量: " + (userInfo.getUsersContacts() != null ? userInfo.getUsersContacts().size() : 0));
} catch (Exception e) {
System.err.println("获取用户联系人信息失败: " + e.getMessage());
userInfo.setUsersContacts(Collections.emptyList());
}
// 3. 获取所有购物车项信息(一对多)
if ("buyer".equals(userInfo.getType()) || "both".equals(userInfo.getType())) {
try {
List<UsersMapper.CartItem> cartItems = usersMapper.getUserAllCartItems(userInfo.getUserId());
// 转换为UserProductCartDTO.CartItem格式以保持兼容
if (cartItems != null && !cartItems.isEmpty()) {
List<UserProductCartDTO.CartItem> userCartItems = cartItems.stream()
.map(item -> {
UserProductCartDTO.CartItem cartItem = new UserProductCartDTO.CartItem();
cartItem.setCartItemId(item.getCartItemId());
cartItem.setProductId(item.getProductId());
cartItem.setProductName(item.getProductName());
cartItem.setSpecification(item.getSpecification());
cartItem.setQuantity(item.getQuantity());
cartItem.setGrossWeight(item.getGrossWeight());
cartItem.setYolk(item.getYolk());
return cartItem;
})
.collect(Collectors.toList());
userInfo.setCartItems(userCartItems);
} else {
userInfo.setCartItems(Collections.emptyList());
}
System.out.println("获取用户所有购物车项信息,数量: " + (userInfo.getCartItems() != null ? userInfo.getCartItems().size() : 0));
} catch (Exception e) {
System.err.println("获取购物车信息失败: " + e.getMessage());
userInfo.setCartItems(Collections.emptyList());
}
userInfo.setProducts(Collections.emptyList());
}
return userInfo;
}
/**
* 根据用户ID获取用户信息和相关产品/购物车数据
*/
public UserProductCartDTO getCustomerInfo(String userId) {
if (!StringUtils.hasText(userId)) {
throw new IllegalArgumentException("用户ID不能为空");
}
// 1. 获取用户基本信息
UserProductCartDTO userInfo = usersMapper.getUserBasicInfo(userId);
if (userInfo == null) {
throw new RuntimeException("用户不存在: " + userId);
}
// 2. 根据用户类型查询不同的数据
if ("seller".equals(userInfo.getType())) {
// 卖家:查询产品信息
List<UserProductCartDTO.ProductInfo> products = usersMapper.getSellerProducts(userId);
userInfo.setProducts(products != null ? products : Collections.emptyList());
userInfo.setCartItems(Collections.emptyList());
} else {
// 买家:查询购物车信息
List<UserProductCartDTO.CartItem> cartItems = usersMapper.getBuyerCartItems(userId);
userInfo.setCartItems(cartItems != null ? cartItems : Collections.emptyList());
userInfo.setProducts(Collections.emptyList());
}
// 3. 获取联系人信息 - 安全处理
try {
List<UserProductCartDTO.UsersContacts> contacts = usersMapper.getUserContacts(userId);
userInfo.setUsersContacts(contacts != null ? contacts : Collections.emptyList());
} catch (Exception e) {
System.err.println("获取用户联系人信息失败: " + e.getMessage());
userInfo.setUsersContacts(Collections.emptyList());
}
return userInfo;
}
/**
* 获取所有客户信息 - 使用数据库层面权限过滤销售端版本支持分页
*/
public List<UserProductCartDTO> getAllCustomers(ManagerAuthInfo authInfo) {
try {
System.out.println("====================================================");
System.out.println("🚀 开始获取所有微信用户数据(数据库权限过滤,销售端)...");
// 移除固定的分页限制,获取所有数据
// 注意:这里可以根据实际需要调整,或者改为支持传入分页参数的方法
int limit = Integer.MAX_VALUE; // 获取所有数据
int offset = 0; // 从第一条开始
// 1. 获取授权客户总数
System.out.println("📊 查询授权客户总数...");
int totalCount = usersMapper.getAuthorizedCustomersCount(authInfo);
System.out.println("✅ 授权客户总数: " + totalCount);
// 2. 使用数据库层面权限过滤并分页
System.out.println("📋 查询授权客户基础信息(分页查询)...");
List<UserProductCartDTO> authorizedUsers = usersMapper.getAuthorizedCustomers(authInfo, limit, offset);
System.out.println("✅ 授权客户数据查询完成");
System.out.println("📊 当前页获取到授权客户数据条数: " + (authorizedUsers != null ? authorizedUsers.size() : "null"));
// 统计通知状态为banold的用户数量
long banoldCount = authorizedUsers != null ? authorizedUsers.stream().filter(user -> "banold".equals(user.getNotice())).count() : 0;
System.out.println("📊 通知状态为banold的用户数量: " + banoldCount);
List<UserProductCartDTO> result = new ArrayList<>();
if (authorizedUsers != null && !authorizedUsers.isEmpty()) {
// 🔥 修改:收集所有用户ID用于批量查询
List<String> userIds = authorizedUsers.stream()
.map(UserProductCartDTO::getUserId)
.filter(Objects::nonNull)
.filter(userId -> !userId.trim().isEmpty())
.distinct()
.collect(Collectors.toList());
System.out.println("🔍 需要批量查询的用户数量: " + userIds.size());
// 🔥 修改:批量查询所有相关数据
Map<String, UsersManagements> managerMap = batchQueryManagers(userIds);
Map<String, List<UsersMapper.ContactInfo>> contactsMap = batchQueryContacts(userIds);
Map<String, List<UsersMapper.CartItem>> cartItemsMap = batchQueryCartItems(userIds);
// 2. 为每个授权用户构建完整信息
System.out.println("🔄 开始处理 " + authorizedUsers.size() + " 条授权用户数据...");
for (int i = 0; i < authorizedUsers.size(); i++) {
UserProductCartDTO user = authorizedUsers.get(i);
try {
// 🔥 修改:使用批量查询的数据构建完整用户信息
UserProductCartDTO fullUserInfo = buildUserInfoFromBatchData(user, managerMap, contactsMap, cartItemsMap);
if (fullUserInfo != null) {
result.add(fullUserInfo);
// 打印前几个客户的详细信息
if (i < 3) {
System.out.println("📝 授权客户样例 " + (i + 1) + ": " +
"ID=" + fullUserInfo.getUserId() +
", 手机=" + fullUserInfo.getPhoneNumber() +
", 公司=" + fullUserInfo.getCompany() +
", 联系人数量=" + (fullUserInfo.getUsersContacts() != null ? fullUserInfo.getUsersContacts().size() : 0) +
", 购物车数量=" + (fullUserInfo.getCartItems() != null ? fullUserInfo.getCartItems().size() : 0));
}
}
} catch (Exception e) {
System.err.println("❌ 构建用户 " + user.getUserId() + " 的详细信息时出错: " + e.getMessage());
}
}
}
System.out.println("====================================================");
System.out.println("🎉 数据获取完成(数据库权限过滤,销售端)");
System.out.println("📊 返回记录数: " + result.size());
System.out.println("====================================================");
return result;
} catch (Exception e) {
System.err.println("❌ 获取授权客户信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 检查是否有权限查看客户
*/
private boolean hasPermissionToViewCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) {
// 使用新的公海池判断逻辑
if (isPublicSeaCustomer(userInfo, authInfo)) {
return true;
}
// 非公海池客户需要检查负责人权限
return hasUserManagerPermission(userInfo.getUserId(), authInfo);
}
/**
* 判断是否为公海池客户 - 根据负责人信息中的认证字段是否为空判断
*/
private boolean isPublicSeaCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) {
// 定义公海池等级
Set<String> publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池");
// 检查等级是否为公海池
boolean isPublicSeaLevel = publicSeaLevels.contains(userInfo.getLevel());
System.out.println("🔍 PoolCustomerService - 客户等级检查: " + userInfo.getLevel() + " → 是否公海池等级: " + isPublicSeaLevel);
if (!isPublicSeaLevel) {
return false;
}
// 根据不同类型的公海池使用不同判断逻辑
String level = userInfo.getLevel();
if ("company-sea-pools".equals(level) || "公海池".equals(level)) {
// 公司公海池:必须没有负责人信息
boolean result = !hasManagerAuthInfo(userInfo.getUserId());
System.out.println("🏢 公司公海池检查结果: " + result);
return result;
} else if ("organization-sea-pools".equals(level)) {
// 组织公海池:必须有负责人信息且组织匹配
boolean hasManager = hasManagerAuthInfo(userInfo.getUserId());
if (!hasManager) {
System.out.println("🏢 组织公海池:无负责人信息,不允许查看");
return false;
}
boolean sameOrganization = hasSameOrganization(userInfo.getUserId(), authInfo);
System.out.println("🏢 组织公海池检查结果 - 有负责人: " + hasManager + ", 组织匹配: " + sameOrganization + " → 结果: " + sameOrganization);
return sameOrganization;
} else if ("department-sea-pools".equals(level)) {
// 部门公海池:必须有负责人信息且部门匹配
boolean hasManager = hasManagerAuthInfo(userInfo.getUserId());
if (!hasManager) {
System.out.println("🏢 部门公海池:无负责人信息,不允许查看");
return false;
}
boolean sameDepartment = hasSameDepartment(userInfo.getUserId(), authInfo);
System.out.println("🏢 部门公海池检查结果 - 有负责人: " + hasManager + ", 部门匹配: " + sameDepartment + " → 结果: " + sameDepartment);
return sameDepartment;
}
return false;
}
/**
* 检查是否有负责人认证信息
*/
private boolean hasManagerAuthInfo(String userId) {
try {
UsersManagements userManager = usersManagementsMapper.findByUserId(userId);
if (userManager == null) {
return false;
}
// 检查认证字段是否为空 - 只要有一个认证字段有值,就有负责人信息
boolean hasAuthInfo = StringUtils.hasText(userManager.getManagerId()) ||
StringUtils.hasText(userManager.getManagercompany()) ||
StringUtils.hasText(userManager.getManagerdepartment()) ||
StringUtils.hasText(userManager.getOrganization()) ||
StringUtils.hasText(userManager.getRole()) ||
StringUtils.hasText(userManager.getUserName()) ||
StringUtils.hasText(userManager.getAssistant());
System.out.println("📋 负责人认证信息检查结果: " + (hasAuthInfo ? "有认证信息" : "无认证信息"));
return hasAuthInfo;
} catch (Exception e) {
System.err.println("❌ 检查负责人认证信息失败: " + e.getMessage());
return false;
}
}
/**
* 检查是否同一组织
*/
private boolean hasSameOrganization(String userId, ManagerAuthInfo authInfo) {
try {
UsersManagements userManager = usersManagementsMapper.findByUserId(userId);
if (userManager == null) {
return false;
}
boolean sameOrganization = StringUtils.hasText(userManager.getOrganization()) &&
userManager.getOrganization().equals(authInfo.getOrganization());
System.out.println("🏢 组织匹配检查: " + userManager.getOrganization() + " vs " + authInfo.getOrganization() + " → " + sameOrganization);
return sameOrganization;
} catch (Exception e) {
System.err.println("❌ 检查组织信息失败: " + e.getMessage());
return false;
}
}
/**
* 检查是否同一部门
*/
private boolean hasSameDepartment(String userId, ManagerAuthInfo authInfo) {
try {
UsersManagements userManager = usersManagementsMapper.findByUserId(userId);
if (userManager == null) {
return false;
}
boolean sameDepartment = StringUtils.hasText(userManager.getManagerdepartment()) &&
userManager.getManagerdepartment().equals(authInfo.getManagerdepartment());
System.out.println("🏢 部门匹配检查: " + userManager.getManagerdepartment() + " vs " + authInfo.getManagerdepartment() + " → " + sameDepartment);
return sameDepartment;
} catch (Exception e) {
System.err.println("❌ 检查部门信息失败: " + e.getMessage());
return false;
}
}
/**
* 检查用户负责人权限 - 优化版本数据库层面直接匹配
*/
private boolean hasUserManagerPermission(String userId, ManagerAuthInfo authInfo) {
try {
System.out.println("🔐 检查用户负责人权限,用户ID: " + userId);
// 🔥 优化:直接在数据库层面查询匹配的负责人记录,避免内存比较
UsersManagements userManager = usersManagementsMapper.findByUserIdAndAuthInfo(userId, authInfo);
// 🔥 优化:如果没有找到匹配的负责人记录,检查是否为公海池客户
if (userManager == null) {
System.out.println("🔍 未找到匹配的负责人记录,检查客户等级...");
// 获取用户信息检查等级
UserProductCartDTO userInfo = usersMapper.getUserBasicInfo(userId);
if (userInfo != null) {
// 定义公海池等级
Set<String> publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池");
boolean isPublicSea = publicSeaLevels.contains(userInfo.getLevel());
if (isPublicSea) {
System.out.println("✅ 公海池客户,允许查看");
return true;
}
}
System.out.println("❌ 非公海池客户且无匹配负责人,拒绝访问");
return false;
}
// 🔥 优化:数据库已经完成匹配,直接返回true
System.out.println("✅ 找到匹配的负责人记录,允许查看");
System.out.println("📝 负责人信息: " +
"公司=" + userManager.getManagercompany() +
", 部门=" + userManager.getManagerdepartment() +
", 组织=" + userManager.getOrganization() +
", 负责人=" + userManager.getUserName());
return true;
} catch (Exception e) {
System.err.println("❌ 检查用户负责人权限失败: " + e.getMessage());
// 发生异常时,出于安全考虑返回false
return false;
}
}
/**
* 检查负责人信息是否匹配UsersManagements
*/
private boolean isManagerMatch(UsersManagements userManager, ManagerAuthInfo authInfo) {
boolean match =
(authInfo.getManagercompany() == null || authInfo.getManagercompany().equals(userManager.getManagercompany())) &&
(authInfo.getManagerdepartment() == null || authInfo.getManagerdepartment().equals(userManager.getManagerdepartment())) &&
(authInfo.getOrganization() == null || authInfo.getOrganization().equals(userManager.getOrganization())) &&
(authInfo.getRole() == null || authInfo.getRole().equals(userManager.getRole())) &&
(authInfo.getUserName() == null || authInfo.getUserName().equals(userManager.getUserName()));
System.out.println("🔐 用户负责人权限检查: " + (match ? "✅ 匹配" : "❌ 不匹配"));
return match;
}
/**
* 获取公海池客户完整信息 - 支持一对多单个查询版本
*/
public UserProductCartDTO getPublicSeaCustomerInfo(String userId) {
if (!StringUtils.hasText(userId)) {
System.out.println("⚠️ 用户ID为空");
return null;
}
// 1. 获取用户基本信息
UserProductCartDTO userInfo = usersMapper.getUserBasicInfo(userId);
if (userInfo == null) {
System.out.println("⚠️ 用户不存在: " + userId);
return null;
}
// 2. 如果通知状态为banold,则更新为old
if ("banold".equals(userInfo.getNotice())) {
System.out.println("🔄 更新通知状态, userId: " + userId + ", 从banold改为old");
usersMapper.updateNotice(userId, "old");
userInfo.setNotice("old");
}
// 销售端权限校验
if (!"buyer".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) {
System.out.println("🚫 过滤掉非客户端客户: " + userId + " (类型: " + userInfo.getType() + ")");
return null;
}
// 🔥 恢复:原有的单个查询负责人信息逻辑
try {
UsersManagements userManager = usersManagementsMapper.findByUserId(userId);
if (userManager != null) {
System.out.println("✅ 获取到负责人信息: " +
"负责人=" + userManager.getUserName() +
", 组织=" + userManager.getOrganization() +
", 部门=" + userManager.getManagerdepartment());
} else {
System.out.println("⚠️ 未找到负责人信息");
}
} catch (Exception e) {
System.err.println("❌ 获取负责人信息失败: " + e.getMessage());
}
// 2. 获取联系人信息 - 一对多
try {
List<UsersMapper.ContactInfo> contacts = usersMapper.getUserAllContacts(userId);
if (contacts != null && !contacts.isEmpty()) {
List<UserProductCartDTO.UsersContacts> userContacts = contacts.stream()
.map(contact -> {
UserProductCartDTO.UsersContacts userContact = new UserProductCartDTO.UsersContacts(
contact.getWechat(),
contact.getAccount(),
contact.getAccountNumber(),
contact.getBank(),
contact.getAddress()
);
userContact.setContactId(contact.getContactId());
return userContact;
})
.collect(Collectors.toList());
userInfo.setUsersContacts(userContacts);
} else {
userInfo.setUsersContacts(Collections.emptyList());
}
} catch (Exception e) {
System.err.println("❌ 获取用户联系人信息失败: " + e.getMessage());
userInfo.setUsersContacts(Collections.emptyList());
}
// 3. 销售端:获取购物车信息(公海需求)- 一对多
try {
List<UsersMapper.CartItem> cartItems = usersMapper.getUserAllCartItems(userId);
System.out.println("🔍 查询到的购物车数据条数: " + (cartItems != null ? cartItems.size() : 0));
if (cartItems != null && !cartItems.isEmpty()) {
List<UserProductCartDTO.CartItem> userCartItems = cartItems.stream()
.map(item -> {
UserProductCartDTO.CartItem cartItem = new UserProductCartDTO.CartItem();
cartItem.setCartItemId(item.getCartItemId());
cartItem.setProductId(item.getProductId());
cartItem.setProductName(item.getProductName());
cartItem.setSpecification(item.getSpecification());
cartItem.setQuantity(item.getQuantity());
cartItem.setGrossWeight(item.getGrossWeight());
cartItem.setYolk(item.getYolk());
// 调试日志
System.out.println("📦 转换购物车项: " +
"ID=" + cartItem.getCartItemId() +
", 产品=" + cartItem.getProductName() +
", 规格=" + cartItem.getSpecification());
return cartItem;
})
.collect(Collectors.toList());
userInfo.setCartItems(userCartItems);
System.out.println("✅ 成功设置购物车数据,数量: " + userCartItems.size());
} else {
userInfo.setCartItems(Collections.emptyList());
System.out.println("⚠️ 购物车数据为空");
}
userInfo.setProducts(Collections.emptyList()); // 销售端不使用产品信息
} catch (Exception e) {
System.err.println("❌ 获取购物车信息失败: " + e.getMessage());
e.printStackTrace();
userInfo.setCartItems(Collections.emptyList());
userInfo.setProducts(Collections.emptyList());
}
// 最终调试信息
System.out.println("🎯 最终返回的用户数据: " +
"cartItems数量=" + (userInfo.getCartItems() != null ? userInfo.getCartItems().size() : "null") +
", contacts数量=" + (userInfo.getUsersContacts() != null ? userInfo.getUsersContacts().size() : "null"));
return userInfo;
}
/**
* 获取所有客户信息 - 不进行负责人过滤直接返回所有数据销售端版本
*/
public List<UserProductCartDTO> getAllCustomersWithoutFilter() {
try {
System.out.println("====================================================");
System.out.println("🚀 开始获取所有微信用户数据(无过滤,销售端)...");
// 1. 获取所有用户的基本信息
System.out.println("📋 查询用户基础信息...");
List<UserProductCartDTO> allUsers = usersMapper.getAllUserBasicInfo();
System.out.println("✅ 基础用户数据查询完成");
System.out.println("📊 获取到基础用户数据条数: " + (allUsers != null ? allUsers.size() : "null"));
// 统计通知状态为banold的用户数量
long banoldCount = allUsers != null ? allUsers.stream().filter(user -> "banold".equals(user.getNotice())).count() : 0;
System.out.println("📊 通知状态为banold的用户数量: " + banoldCount);
List<UserProductCartDTO> result = new ArrayList<>();
if (allUsers != null && !allUsers.isEmpty()) {
// 🔥 修改:收集所有用户ID用于批量查询
List<String> userIds = allUsers.stream()
.map(UserProductCartDTO::getUserId)
.filter(Objects::nonNull)
.filter(userId -> !userId.trim().isEmpty())
.distinct()
.collect(Collectors.toList());
System.out.println("🔍 需要批量查询的用户数量: " + userIds.size());
// 🔥 修改:批量查询所有相关数据
Map<String, UsersManagements> managerMap = batchQueryManagers(userIds);
Map<String, List<UsersMapper.ContactInfo>> contactsMap = batchQueryContacts(userIds);
Map<String, List<UsersMapper.CartItem>> cartItemsMap = batchQueryCartItems(userIds);
// 2. 为每个用户构建完整信息
System.out.println("🔄 开始处理 " + allUsers.size() + " 条用户数据...");
for (int i = 0; i < allUsers.size(); i++) {
UserProductCartDTO user = allUsers.get(i);
try {
// 销售端权限:只处理buyer和both类型
if (!"buyer".equals(user.getType()) && !"both".equals(user.getType())) {
System.out.println("🚫 过滤掉非供应端客户: " + user.getUserId() + " (类型: " + user.getType() + ")");
continue;
}
// 🔥 修改:使用批量查询的数据构建完整用户信息
UserProductCartDTO fullUserInfo = buildUserInfoFromBatchData(user, managerMap, contactsMap, cartItemsMap);
if (fullUserInfo != null) {
result.add(fullUserInfo);
// 打印前几个客户的详细信息
if (i < 3) {
System.out.println("📝 客户样例 " + (i + 1) + ": " +
"ID=" + fullUserInfo.getUserId() +
", 手机=" + fullUserInfo.getPhoneNumber() +
", 公司=" + fullUserInfo.getCompany() +
", 联系人数量=" + (fullUserInfo.getUsersContacts() != null ? fullUserInfo.getUsersContacts().size() : 0) +
", 购物车数量=" + (fullUserInfo.getCartItems() != null ? fullUserInfo.getCartItems().size() : 0));
}
}
} catch (Exception e) {
System.err.println("❌ 构建用户 " + user.getUserId() + " 的详细信息时出错: " + e.getMessage());
}
}
}
System.out.println("====================================================");
System.out.println("🎉 数据获取完成(无过滤,销售端)");
System.out.println("📊 返回记录数: " + result.size());
System.out.println("====================================================");
return result;
} catch (Exception e) {
System.err.println("❌ 获取所有客户信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 1.批量查询负责人信息
*/
private Map<String, UsersManagements> batchQueryManagers(List<String> userIds) {
Map<String, UsersManagements> managerMap = new HashMap<>();
if (userIds != null && !userIds.isEmpty()) {
try {
List<UsersManagements> allManagers = usersManagementsMapper.findByUserIds(userIds);
for (UsersManagements manager : allManagers) {
if (manager.getUserId() != null) {
managerMap.put(manager.getUserId(), manager);
}
}
System.out.println("✅ 批量查询负责人信息完成,共获取 " + allManagers.size() + " 条记录");
} catch (Exception e) {
System.err.println("❌ 批量查询负责人信息失败: " + e.getMessage());
}
}
return managerMap;
}
/**
* 2.批量查询联系人信息
*/
private Map<String, List<UsersMapper.ContactInfo>> batchQueryContacts(List<String> userIds) {
Map<String, List<UsersMapper.ContactInfo>> contactsMap = new HashMap<>();
if (userIds != null && !userIds.isEmpty()) {
try {
List<UsersMapper.ContactInfo> allContacts = usersMapper.getUserContactsByUserIds(userIds);
// 按用户ID分组
for (UsersMapper.ContactInfo contact : allContacts) {
String userId = contact.getUserId();
if (userId != null) {
contactsMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(contact);
}
}
System.out.println("✅ 批量查询联系人信息完成,共获取 " + allContacts.size() + " 条记录,涉及 " + contactsMap.size() + " 个用户");
} catch (Exception e) {
System.err.println("❌ 批量查询联系人信息失败: " + e.getMessage());
}
}
return contactsMap;
}
/**
* 3.批量查询购物车信息
*/
private Map<String, List<UsersMapper.CartItem>> batchQueryCartItems(List<String> userIds) {
Map<String, List<UsersMapper.CartItem>> cartItemsMap = new HashMap<>();
if (userIds != null && !userIds.isEmpty()) {
try {
List<UsersMapper.CartItem> allCartItems = usersMapper.getCartItemsByUserIds(userIds);
// 按用户ID分组
for (UsersMapper.CartItem cartItem : allCartItems) {
String userId = cartItem.getUserId();
if (userId != null) {
cartItemsMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(cartItem);
}
}
System.out.println("✅ 批量查询购物车信息完成,共获取 " + allCartItems.size() + " 条记录,涉及 " + cartItemsMap.size() + " 个用户");
} catch (Exception e) {
System.err.println("❌ 批量查询购物车信息失败: " + e.getMessage());
}
}
return cartItemsMap;
}
/**
* 4.从批量查询的数据构建用户完整信息
*/
private UserProductCartDTO buildUserInfoFromBatchData(UserProductCartDTO basicUserInfo,
Map<String, UsersManagements> managerMap,
Map<String, List<UsersMapper.ContactInfo>> contactsMap,
Map<String, List<UsersMapper.CartItem>> cartItemsMap) {
String userId = basicUserInfo.getUserId();
// 销售端权限校验
if (!"buyer".equals(basicUserInfo.getType()) && !"both".equals(basicUserInfo.getType())) {
System.out.println("🚫 过滤掉非客户端客户: " + userId + " (类型: " + basicUserInfo.getType() + ")");
return null;
}
// 🔥 设置负责人信息(记录日志,但不设置到DTO中,因为UserProductCartDTO没有负责人字段)
try {
UsersManagements userManager = managerMap.get(userId);
if (userManager != null) {
System.out.println("✅ 从批量Map中获取到负责人信息: " +
"负责人=" + userManager.getUserName() +
", 组织=" + userManager.getOrganization() +
", 部门=" + userManager.getManagerdepartment());
} else {
System.out.println("⚠️ 未在批量Map中找到负责人信息,用户ID: " + userId);
}
} catch (Exception e) {
System.err.println("❌ 从Map获取负责人信息失败: " + e.getMessage());
}
// 🔥 设置联系人信息 - 从批量查询的Map中获取
try {
List<UsersMapper.ContactInfo> contacts = contactsMap.get(userId);
if (contacts != null && !contacts.isEmpty()) {
List<UserProductCartDTO.UsersContacts> userContacts = contacts.stream()
.map(contact -> {
UserProductCartDTO.UsersContacts userContact = new UserProductCartDTO.UsersContacts(
contact.getWechat(),
contact.getAccount(),
contact.getAccountNumber(),
contact.getBank(),
contact.getAddress()
);
userContact.setContactId(contact.getContactId());
return userContact;
})
.collect(Collectors.toList());
basicUserInfo.setUsersContacts(userContacts);
System.out.println("✅ 成功设置联系人数据,数量: " + userContacts.size() + ",用户ID: " + userId);
} else {
basicUserInfo.setUsersContacts(Collections.emptyList());
System.out.println("⚠️ 未找到联系人信息,用户ID: " + userId);
}
} catch (Exception e) {
System.err.println("❌ 设置联系人信息失败: " + e.getMessage());
basicUserInfo.setUsersContacts(Collections.emptyList());
}
// 🔥 设置购物车信息 - 从批量查询的Map中获取
try {
List<UsersMapper.CartItem> cartItems = cartItemsMap.get(userId);
if (cartItems != null && !cartItems.isEmpty()) {
List<UserProductCartDTO.CartItem> userCartItems = cartItems.stream()
.map(item -> {
UserProductCartDTO.CartItem cartItem = new UserProductCartDTO.CartItem();
cartItem.setCartItemId(item.getCartItemId());
cartItem.setProductId(item.getProductId());
cartItem.setProductName(item.getProductName());
cartItem.setSpecification(item.getSpecification());
cartItem.setQuantity(item.getQuantity());
cartItem.setGrossWeight(item.getGrossWeight());
cartItem.setYolk(item.getYolk());
return cartItem;
})
.collect(Collectors.toList());
basicUserInfo.setCartItems(userCartItems);
System.out.println("✅ 成功设置购物车数据,数量: " + userCartItems.size() + ",用户ID: " + userId);
} else {
basicUserInfo.setCartItems(Collections.emptyList());
System.out.println("⚠️ 购物车数据为空,用户ID: " + userId);
}
basicUserInfo.setProducts(Collections.emptyList()); // 销售端不使用产品信息
} catch (Exception e) {
System.err.println("❌ 设置购物车信息失败: " + e.getMessage());
basicUserInfo.setCartItems(Collections.emptyList());
basicUserInfo.setProducts(Collections.emptyList());
}
return basicUserInfo;
}
/**
* 只检查等级是否为公海池等级不检查负责人信息
*/
private boolean isPublicSeaLevel(String level) {
// 定义公海池等级
Set<String> publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池");
boolean isPublicSeaLevel = publicSeaLevels.contains(level);
System.out.println("🔍 等级检查: " + level + " → 是否公海池等级: " + isPublicSeaLevel);
return isPublicSeaLevel;
}
/**
* 销售端获取客户的购物车信息公海需求
*/
public List<UserProductCartDTO.ProductInfo> getCustomerProducts(String phoneNumber) {
try {
UserProductCartDTO user = usersMapper.selectByPhone(phoneNumber);
if (user != null && ("buyer".equals(user.getType()) || "both".equals(user.getType()))) {
return usersMapper.getSellerProducts(user.getUserId());
}
return new ArrayList<>();
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
}

10
src/main/java/com/example/web/service/RootService.java

@ -1,10 +0,0 @@
package com.example.web.service;
import com.example.web.mapper.RootdbMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RootService {
}

204
src/main/java/com/example/web/service/SupplyCustomerRecycleService.java

@ -1,204 +0,0 @@
package com.example.web.service;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.entity.UsersManagements;
import com.example.web.mapper.SupplyUsersMapper;
import com.example.web.mapper.SupplyUsersManagementsMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.LocalDateTime;
import java.util.List;
@Service
@RequiredArgsConstructor
public class SupplyCustomerRecycleService {
private static final Logger log = LoggerFactory.getLogger(SupplyCustomerRecycleService.class);
@Autowired
private SupplyUsersManagementsMapper supplyUsersManagementsMapper;
@Autowired
private SupplyUsersMapper supplyUsersMapper;
// 从配置文件注入回流时间配置
@Value("${app.recycle.unclassified-to-organization-days:30}")
private int unclassifiedToOrganizationDays;
@Value("${app.recycle.organization-to-department-days:30}")
private int organizationToDepartmentDays;
/**
* 客户回流定时任务 - 每天凌晨2点执行
*/
@Scheduled(cron = "0 0 2 * * ?")
@Transactional(rollbackFor = Exception.class)
public void autoRecycleCustomers() {
log.info("🎯 开始执行客户回流任务...");
log.info("📅 回流配置 - 未分级->组织公海池: {}天, 组织->部门公海池: {}天",
unclassifiedToOrganizationDays, organizationToDepartmentDays);
try {
// 1. 未分级客户回流到组织公海池
recycleUnclassifiedToOrganization();
// 2. 组织公海池客户回流到部门公海池
recycleOrganizationToDepartment();
log.info("✅ 客户回流任务执行完成");
} catch (Exception e) {
log.error("❌ 客户回流任务执行失败", e);
throw e; // 抛出异常确保事务回滚
}
}
/**
* 未分级客户回流到组织公海池 - 修复保留负责人信息
*/
private void recycleUnclassifiedToOrganization() {
log.info("🔄 开始处理未分级客户回流...");
LocalDateTime thresholdTime = LocalDateTime.now().minusDays(unclassifiedToOrganizationDays);
// 查询超过指定天数未更新的未分级客户
List<UserProductCartDTO> unclassifiedCustomers = supplyUsersMapper.findUnclassifiedCustomersOlderThan(thresholdTime);
log.info("📊 找到 {} 个需要回流的未分级客户 (阈值: {}天前)",
unclassifiedCustomers.size(), unclassifiedToOrganizationDays);
int recycledCount = 0;
for (UserProductCartDTO customer : unclassifiedCustomers) {
try {
// 🔥 关键修改:先查询当前的负责人信息
UsersManagements currentManager = supplyUsersManagementsMapper.findByUserId(customer.getUserId());
log.info("🔍 客户 {} 当前负责人信息: {}", customer.getUserId(),
currentManager != null ? currentManager.getUserName() : "无负责人");
// 更新客户等级为组织公海池
boolean success = supplyUsersMapper.updateCustomerLevel(
customer.getUserId(),
"organization-sea-pools",
LocalDateTime.now()
);
if (success) {
// 🔥 关键修改:确保负责人信息保留
if (currentManager != null) {
// 更新负责人信息的更新时间,但不改变负责人本身
boolean managerUpdated = supplyUsersManagementsMapper.updateManagerUpdateTime(
customer.getUserId(), LocalDateTime.now());
log.info("✅ 客户 {} 负责人信息已保留: {}", customer.getUserId(),
managerUpdated ? "成功" : "失败");
}
recycledCount++;
log.info("🔄 客户 {} 从未分级回流到组织公海池, 负责人: {}",
customer.getUserId(),
currentManager != null ? currentManager.getUserName() : "无");
}
} catch (Exception e) {
log.error("❌ 回流客户失败: {}", customer.getUserId(), e);
}
}
log.info("✅ 未分级客户回流完成: {} 个客户已回流到组织公海池", recycledCount);
}
/**
* 组织公海池客户回流到部门公海池 - 修复保留负责人信息
*/
private void recycleOrganizationToDepartment() {
log.info("🔄 开始处理组织公海池客户回流...");
LocalDateTime thresholdTime = LocalDateTime.now().minusDays(organizationToDepartmentDays);
// 查询超过指定天数未更新的组织公海池客户
List<UserProductCartDTO> organizationCustomers = supplyUsersMapper.findOrganizationSeaPoolsCustomersOlderThan(thresholdTime);
log.info("📊 找到 {} 个需要回流的组织公海池客户 (阈值: {}天前)",
organizationCustomers.size(), organizationToDepartmentDays);
int recycledCount = 0;
for (UserProductCartDTO customer : organizationCustomers) {
try {
// 🔥 关键修改:先查询当前的负责人信息
UsersManagements currentManager = supplyUsersManagementsMapper.findByUserId(customer.getUserId());
log.info("🔍 客户 {} 当前负责人信息: {}", customer.getUserId(),
currentManager != null ? currentManager.getUserName() : "无负责人");
// 更新客户等级为部门公海池
boolean success = supplyUsersMapper.updateCustomerLevel(
customer.getUserId(),
"department-sea-pools",
LocalDateTime.now()
);
if (success) {
// 🔥 关键修改:确保负责人信息保留
if (currentManager != null) {
// 更新负责人信息的更新时间,但不改变负责人本身
boolean managerUpdated = supplyUsersManagementsMapper.updateManagerUpdateTime(
customer.getUserId(), LocalDateTime.now());
log.info("✅ 客户 {} 负责人信息已保留: {}", customer.getUserId(),
managerUpdated ? "成功" : "失败");
}
recycledCount++;
log.info("🔄 客户 {} 从组织公海池回流到部门公海池, 负责人: {}",
customer.getUserId(),
currentManager != null ? currentManager.getUserName() : "无");
}
} catch (Exception e) {
log.error("❌ 回流客户失败: {}", customer.getUserId(), e);
}
}
log.info("✅ 组织公海池客户回流完成: {} 个客户已回流到部门公海池", recycledCount);
}
/**
* 手动触发回流任务用于测试或手动执行
*/
public void manualRecycle() {
log.info("🔧 手动触发客户回流任务...");
autoRecycleCustomers();
}
/**
* 获取当前回流配置信息用于调试或监控
*/
public String getRecycleConfigInfo() {
return String.format("回流配置 - 未分级->组织公海池: %d天, 组织->部门公海池: %d天",
unclassifiedToOrganizationDays, organizationToDepartmentDays);
}
/**
* 🔥 新增获取回流客户完整信息用于前端显示
*/
public List<UserProductCartDTO> getRecycledCustomersWithManagerInfo() {
log.info("🔍 获取回流客户完整信息(包含负责人信息)");
// 获取最近回流的客户(例如最近1天内回流的)
LocalDateTime sinceTime = LocalDateTime.now().minusDays(1);
List<UserProductCartDTO> recycledCustomers = supplyUsersMapper.findRecentlyRecycledCustomers(sinceTime);
// 为每个客户加载负责人信息
for (UserProductCartDTO customer : recycledCustomers) {
UsersManagements manager = supplyUsersManagementsMapper.findByUserId(customer.getUserId());
if (manager != null) {
// 将负责人信息设置到DTO中(需要扩展UserProductCartDTO或使用其他方式)
log.info("✅ 客户 {} 回流后负责人信息: {}", customer.getUserId(), manager.getUserName());
}
}
log.info("📊 获取到 {} 个回流客户的完整信息", recycledCustomers.size());
return recycledCustomers;
}
}

1330
src/main/java/com/example/web/service/SupplyCustomerService.java

File diff suppressed because it is too large

515
src/main/java/com/example/web/service/SupplyEnterpriseService.java

@ -1,515 +0,0 @@
package com.example.web.service;
import com.example.web.dto.EnterpriseInfoDTO;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.dto.UnifiedCustomerDTO;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.entity.Contacts;
import com.example.web.entity.Enterprise;
import com.example.web.entity.Managers;
import com.example.web.mapper.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Service
public class SupplyEnterpriseService {
@Autowired
private SupplyEnterpriseMapper supplyenterpriseMapper;
@Autowired
private SupplyContactsMapper supplycontactsMapper;
@Autowired
private SupplyManagersMapper supplymanagersMapper;
@Autowired
private SupplyUsersMapper supplyusersMapper;
@Autowired
private SupplyPoolCustomerService supplypoolCustomerService;
// 修改:检查电话号码是否重复(同时检查两个数据源)
public boolean isPhoneNumberDuplicate(String phoneNumber) {
// 1. 检查微信数据源中的电话号码是否重复
boolean wechatDuplicate = false;
try {
UserProductCartDTO existingUser = supplyusersMapper.selectByPhone(phoneNumber);
wechatDuplicate = existingUser != null;
if (wechatDuplicate) {
System.out.println("❌ [采购端] 电话号码在微信数据源中已存在: " + phoneNumber);
}
} catch (Exception e) {
System.err.println("[采购端] 查询微信数据源电话号码失败: " + e.getMessage());
}
// 2. 检查默认数据源(userlogin)中的电话号码是否重复
boolean defaultDuplicate = false;
try {
int count = supplycontactsMapper.countByPhoneNumber(phoneNumber);
defaultDuplicate = count > 0;
if (defaultDuplicate) {
System.out.println("❌ [采购端] 电话号码在默认数据源中已存在: " + phoneNumber);
}
} catch (Exception e) {
System.err.println("[采购端] 查询默认数据源电话号码失败: " + e.getMessage());
}
// 3. 返回最终结果:任意一个数据源中存在都算重复
boolean isDuplicate = wechatDuplicate || defaultDuplicate;
System.out.println("📞 [采购端] 电话号码重复检查结果: " + phoneNumber +
" -> 微信数据源:" + wechatDuplicate +
", 默认数据源:" + defaultDuplicate +
", 最终结果:" + isDuplicate);
return isDuplicate;
}
// 类型转换方法(保持与销售端一致)
private String convertFrontendTypeToDatabase(String frontendType) {
if (frontendType == null) {
return null;
}
switch (frontendType) {
case "供应端":
return "seller";
case "客户端":
return "buyer";
case "BOTH":
return "both"; // 改为小写
default:
return frontendType;
}
}
private String convertDatabaseTypeToFrontend(String databaseType) {
if (databaseType == null) {
return null;
}
switch (databaseType) {
case "seller":
return "供应端";
case "buyer":
return "客户端";
case "both": // 匹配小写的数据库存储
return "BOTH";
default:
return databaseType;
}
}
// 新增客户:保存企业、联系人、负责人信息(默认数据源)
@Transactional(rollbackFor = Exception.class)
public boolean addCustomer(UnifiedCustomerDTO dto) {
System.out.println("====================================================");
System.out.println("➕ [采购端] 开始新增客户到默认数据源");
System.out.println("====================================================");
System.out.println("📋 客户信息: 手机=" + dto.getPhoneNumber() +
", 公司=" + dto.getCompany() +
", 类型=" + dto.getType() +
", 等级=" + dto.getLevel());
// 1. 转换前端类型为数据库类型
String databaseType = convertFrontendTypeToDatabase(dto.getType());
dto.setType(databaseType);
System.out.println("🔄 [采购端] 类型转换: 前端=" + dto.getType() + " → 数据库=" + databaseType);
// 2. 权限校验(采购端:仅允许seller/both)
if (!"seller".equals(dto.getType()) && !"both".equals(dto.getType())) {
System.out.println("❌ [采购端] 权限校验失败: 采购端只能新增供应端类型客户和BOTH类型客户");
throw new IllegalArgumentException("采购端权限只能新增供应端类型客户和BOTH类型客户");
}
System.out.println("✅ [采购端] 权限校验通过");
// 3. 等级校验
if (!StringUtils.hasText(dto.getLevel())) {
System.out.println("❌ [采购端] 等级校验失败: 客户等级不能为空");
throw new IllegalArgumentException("客户等级不能为空");
}
System.out.println("✅ [采购端] 等级校验通过");
// 4. 检查电话号码是否重复
System.out.println("🔍 [采购端] 检查电话号码重复性...");
if (isPhoneNumberDuplicate(dto.getPhoneNumber())) {
System.out.println("❌ [采购端] 电话号码重复: " + dto.getPhoneNumber());
throw new IllegalArgumentException("电话号码已存在");
}
System.out.println("✅ [采购端] 电话号码检查通过");
// 5. 封装并保存企业信息
System.out.println("💾 [采购端] 保存企业信息...");
Enterprise enterprise = new Enterprise();
enterprise.setId(dto.getId());
enterprise.setCompany(dto.getCompany());
enterprise.setRegion(dto.getRegion());
enterprise.setLevel(dto.getLevel());
enterprise.setType(dto.getType());
enterprise.setDemand(dto.getDemand());
enterprise.setSpec(dto.getSpec());
int enterpriseRows = supplyenterpriseMapper.insertEnterprise(enterprise);
System.out.println("✅ [采购端] 企业信息保存结果: " + enterpriseRows + " 行受影响");
// 6. 封装联系人信息
System.out.println("💾 [采购端] 保存联系人信息...");
Contacts contacts = new Contacts();
String contactId = UUID.randomUUID().toString().replaceAll("-", "");
contacts.setContact_id(contactId);
contacts.setId(dto.getId());
contacts.setNickName(dto.getNickName());
contacts.setPhoneNumber(dto.getPhoneNumber());
contacts.setWechat(dto.getWechat());
contacts.setAccount(dto.getAccount());
contacts.setAccountNumber(dto.getAccountNumber());
contacts.setBank(dto.getBank());
contacts.setAddress(dto.getAddress());
contacts.setCreated_at(dto.getCreated_at() != null ? dto.getCreated_at() : LocalDateTime.now());
contacts.setUpdated_at(dto.getUpdated_at() != null ? dto.getUpdated_at() : LocalDateTime.now());
int contactsRows = supplycontactsMapper.insertContacts(contacts);
System.out.println("✅ [采购端] 联系人信息保存结果: " + contactsRows + " 行受影响");
// 7. 封装负责人信息 - 使用前端传递的认证信息(更新为新表结构)
System.out.println("💾 [采购端] 保存负责人信息...");
Managers managers = new Managers();
managers.setId(dto.getId());
managers.setManagerId(StringUtils.hasText(dto.getManagerId()) ? dto.getManagerId() : "未分配");
managers.setManagercompany(StringUtils.hasText(dto.getManagercompany()) ? dto.getManagercompany() : "未分配");
managers.setManagerdepartment(StringUtils.hasText(dto.getManagerdepartment()) ? dto.getManagerdepartment() : "未分配");
managers.setOrganization(StringUtils.hasText(dto.getOrganization()) ? dto.getOrganization() : "未分配");
managers.setRole(StringUtils.hasText(dto.getRole()) ? dto.getRole() : "未分配");
managers.setRoot("2"); // 采购端默认权限
managers.setUserName(StringUtils.hasText(dto.getUserName()) ? dto.getUserName() : "未分配");
managers.setAssistant(StringUtils.hasText(dto.getAssistant()) ? dto.getAssistant() : "无");
managers.setCreated_at(LocalDateTime.now());
managers.setUpdated_at(LocalDateTime.now());
int managersRows = supplymanagersMapper.insertManagers(managers);
System.out.println("✅ [采购端] 负责人信息保存结果: " + managersRows + " 行受影响");
boolean success = enterpriseRows > 0 && contactsRows > 0 && managersRows > 0;
if (success) {
System.out.println("🎉 [采购端] 新增客户成功,负责人信息已自动填充");
// 记录负责人信息详情
System.out.println("📝 [采购端] 负责人信息详情: " +
"负责人id=" + managers.getManagerId() +
"负责公司=" + managers.getManagercompany() +
", 负责部门=" + managers.getManagerdepartment() +
", 组织=" + managers.getOrganization() +
", 角色=" + managers.getRole() +
", 负责人=" + managers.getUserName() +
", 协助人=" + managers.getAssistant());
} else {
System.out.println("❌ [采购端] 新增客户失败");
}
System.out.println("====================================================");
return success;
}
/**
* 获取所有企业的完整信息企业信息 + 联系人信息 + 负责人信息
*/
public List<EnterpriseInfoDTO> getAllEnterpriseInfo() {
try {
return supplyenterpriseMapper.selectAllEnterpriseInfo();
} catch (Exception e) {
System.err.println("[采购端] 获取所有企业信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 根据企业ID获取详细信息
*/
public EnterpriseInfoDTO getEnterpriseInfoById(String id) {
try {
System.out.println("🔍 [采购端] 根据企业ID查询信息: " + id);
return supplyenterpriseMapper.selectEnterpriseInfoById(id);
} catch (Exception e) {
System.err.println("[采购端] 根据企业ID查询信息失败: " + e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* 获取所有企业基本信息
*/
public List<Enterprise> getAllEnterprises() {
try {
return supplyenterpriseMapper.selectAllEnterprises();
} catch (Exception e) {
System.err.println("[采购端] 获取所有企业基本信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 根据企业ID获取联系人信息
*/
public List<Contacts> getContactsByEnterpriseId(String id) {
try {
return supplycontactsMapper.selectContactsByEnterpriseId(id);
} catch (Exception e) {
System.err.println("[采购端] 根据企业ID获取联系人信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 根据企业ID获取负责人信息
*/
public List<Managers> getManagersByEnterpriseId(String id) {
try {
return supplymanagersMapper.selectManagersByEnterpriseId(id);
} catch (Exception e) {
System.err.println("[采购端] 根据企业ID获取负责人信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 搜索企业信息根据企业名称地区类型等
*/
public List<EnterpriseInfoDTO> searchEnterprises(String keyword) {
try {
System.out.println("🔍 [采购端] 搜索企业信息,关键词: " + keyword);
// 这里可以扩展更复杂的搜索逻辑
List<EnterpriseInfoDTO> allInfo = supplyenterpriseMapper.selectAllEnterpriseInfo();
if (keyword == null || keyword.trim().isEmpty()) {
return allInfo;
}
List<EnterpriseInfoDTO> result = new ArrayList<>();
for (EnterpriseInfoDTO info : allInfo) {
if (info.getEnterprise() != null) {
String company = info.getEnterprise().getCompany() != null ? info.getEnterprise().getCompany().toLowerCase() : "";
String region = info.getEnterprise().getRegion() != null ? info.getEnterprise().getRegion().toLowerCase() : "";
String type = info.getEnterprise().getType() != null ? info.getEnterprise().getType().toLowerCase() : "";
if (company.contains(keyword.toLowerCase()) ||
region.contains(keyword.toLowerCase()) ||
type.contains(keyword.toLowerCase())) {
result.add(info);
}
}
}
return result;
} catch (Exception e) {
System.err.println("[采购端] 搜索企业信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 更新默认数据源客户信息企业联系人负责人- 同步销售端增加ManagerAuthInfo参数
*/
@Transactional(rollbackFor = Exception.class)
public boolean updateDefaultCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo) {
// 转换前端类型为数据库类型
String databaseType = convertFrontendTypeToDatabase(dto.getType());
dto.setType(databaseType);
// 采购员权限:校验客户类型,只允许seller和both
if (!"seller".equals(dto.getType()) && !"both".equals(dto.getType())) {
throw new IllegalArgumentException("采购员权限只能更新为供应端类型客户和BOTH类型客户");
}
// 1. 更新企业信息
Enterprise enterprise = new Enterprise();
enterprise.setId(dto.getId());
enterprise.setCompany(dto.getCompany());
enterprise.setRegion(dto.getRegion());
enterprise.setLevel(dto.getLevel());
enterprise.setType(dto.getType());
enterprise.setDemand(dto.getDemand());
enterprise.setSpec(dto.getSpec());
int enterpriseRows = supplyenterpriseMapper.updateEnterprise(enterprise);
if (enterpriseRows <= 0) {
throw new RuntimeException("更新企业信息失败,企业ID: " + dto.getId());
}
// 2. 更新联系人信息 - 修复逻辑(与销售端一致)
Contacts contacts = new Contacts();
// 先根据企业ID查询原始联系人信息
List<Contacts> originalContactsList = supplycontactsMapper.selectContactsByEnterpriseId(dto.getId());
if (originalContactsList != null && !originalContactsList.isEmpty()) {
Contacts originalContacts = originalContactsList.get(0);
contacts.setContact_id(originalContacts.getContact_id());
contacts.setId(dto.getId());
contacts.setNickName(dto.getNickName());
// 关键:使用原始电话号码,禁止修改
contacts.setPhoneNumber(originalContacts.getPhoneNumber());
contacts.setWechat(dto.getWechat());
contacts.setAccount(dto.getAccount());
contacts.setAccountNumber(dto.getAccountNumber());
contacts.setBank(dto.getBank());
contacts.setAddress(dto.getAddress());
int contactsRows = supplycontactsMapper.updateContacts(contacts);
if (contactsRows <= 0) {
throw new RuntimeException("更新联系人信息失败,联系人ID: " + originalContacts.getContact_id());
}
} else {
// 如果没有找到联系人记录,创建新的联系人
System.out.println("[采购端] 未找到现有联系人,创建新的联系人记录");
contacts.setContact_id(UUID.randomUUID().toString().replaceAll("-", ""));
contacts.setId(dto.getId());
contacts.setNickName(dto.getNickName());
contacts.setPhoneNumber(""); // 新创建的联系人电话号码为空
contacts.setWechat(dto.getWechat());
contacts.setAccount(dto.getAccount());
contacts.setAccountNumber(dto.getAccountNumber());
contacts.setBank(dto.getBank());
contacts.setAddress(dto.getAddress());
contacts.setCreated_at(LocalDateTime.now());
contacts.setUpdated_at(LocalDateTime.now());
int contactsRows = supplycontactsMapper.insertContacts(contacts);
if (contactsRows <= 0) {
throw new RuntimeException("创建联系人信息失败");
}
}
// 3. 更新负责人信息 - 只有非公海池客户且前端提交了负责人信息时才更新
// 定义公海池和非公海池等级
Set<String> publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池");
Set<String> nonPublicSeaLevels = Set.of("important", "normal", "low-value", "logistics", "unclassified");
// 检查是否是非公海池客户且前端提交了负责人信息
boolean isNonPublicSeaCustomer = nonPublicSeaLevels.contains(dto.getLevel());
boolean hasFrontendManagerInfo = StringUtils.hasText(dto.getManagercompany()) ||
StringUtils.hasText(dto.getManagerdepartment()) ||
StringUtils.hasText(dto.getOrganization()) ||
StringUtils.hasText(dto.getRole()) ||
StringUtils.hasText(dto.getUserName()) ||
StringUtils.hasText(dto.getAssistant());
System.out.println("🎯 [采购端] 默认数据源负责人更新检查: " +
"客户等级=" + dto.getLevel() +
", 是否非公海池=" + isNonPublicSeaCustomer +
", 前端提交负责人信息=" + hasFrontendManagerInfo);
// 只有非公海池客户且前端提交了负责人信息时才更新
if (isNonPublicSeaCustomer && hasFrontendManagerInfo) {
System.out.println("✅ [采购端] 更新默认数据源负责人信息");
Managers managers = new Managers();
managers.setId(dto.getId());
// 使用前端提交的数据
managers.setManagercompany(dto.getManagercompany());
managers.setManagerdepartment(dto.getManagerdepartment());
managers.setOrganization(dto.getOrganization());
managers.setRole(dto.getRole());
managers.setUserName(dto.getUserName());
managers.setAssistant(dto.getAssistant());
managers.setUpdated_at(LocalDateTime.now());
int managersRows = supplymanagersMapper.updateManagers(managers);
if (managersRows <= 0) {
// 如果没有更新到记录,尝试插入
System.out.println("[采购端] 未找到现有负责人记录,创建新的负责人记录");
managers.setManagerId(authInfo.getManagerId());
managers.setRoot("2"); // 采购端默认权限
managers.setCreated_at(LocalDateTime.now());
managersRows = supplymanagersMapper.insertManagers(managers);
if (managersRows <= 0) {
throw new RuntimeException("更新负责人信息失败");
}
}
System.out.println("✅ [采购端] 默认数据源负责人信息更新完成,影响行数: " + managersRows);
} else {
System.out.println("ℹ️ [采购端] 跳过默认数据源负责人信息更新");
if (!isNonPublicSeaCustomer) {
System.out.println(" 原因: 客户是公海池客户,等级=" + dto.getLevel());
} else if (!hasFrontendManagerInfo) {
System.out.println(" 原因: 前端未提交负责人信息");
}
}
return true;
}
/**
* 更新wechat数据源的客户信息用户和产品信息
*/
public boolean updateWechatCustomer(UnifiedCustomerDTO dto) {
// 修复:确保类型转换正确
String type = dto.getType();
if ("BOTH".equals(type)) {
type = "both";
dto.setType(type);
} else if ("客户端".equals(type)) {
type = "buyer";
dto.setType(type);
} else if ("供应端".equals(type)) {
type = "seller";
dto.setType(type);
}
// 采购员权限:校验客户类型,只允许seller和both
if (!"seller".equals(dto.getType()) && !"both".equals(dto.getType())) {
throw new IllegalArgumentException("采购权限只能更新为供应端类型客户和BOTH类型客户");
}
// 1. 根据手机号查询wechat用户ID(关联用户与产品)
UserProductCartDTO user = supplyusersMapper.selectByPhone(dto.getPhoneNumber());
if (user == null) {
// 若该用户在wechat数据源中不存在,无需更新
return true;
}
// 2. 更新用户基本信息
UserProductCartDTO updateUser = new UserProductCartDTO();
updateUser.setUserId(user.getUserId());
updateUser.setPhoneNumber(dto.getPhoneNumber());
updateUser.setNickName(dto.getNickName());
updateUser.setType(dto.getType());
updateUser.setUpdated_at(LocalDateTime.now());
int rows = supplyusersMapper.updateByPhone(updateUser);
System.out.println("[采购端] 更新用户基本信息影响行数: " + rows);
// 3. 如果有产品信息需要更新
if (("seller".equals(dto.getType()) || "both".equals(dto.getType())) &&
dto.getProductName() != null && !dto.getProductName().trim().isEmpty()) {
System.out.println("[采购端] 更新产品信息");
int productRows = supplyusersMapper.updateProductBySellerId(
user.getUserId(),
dto.getProductName(),
dto.getVariety(),
dto.getSpecification(),
dto.getQuantity(),
dto.getGrossWeight(),
dto.getYolk()
);
System.out.println("[采购端] 更新产品信息影响行数: " + productRows);
}
return rows > 0;
}
}

816
src/main/java/com/example/web/service/SupplyPoolCustomerService.java

@ -1,816 +0,0 @@
package com.example.web.service;
import com.example.web.dto.ManagerAuthInfo;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.entity.UsersManagements;
import com.example.web.mapper.SupplyUsersManagementsMapper;
import com.example.web.mapper.SupplyUsersMapper;
import com.example.web.mapper.UsersManagementsMapper;
import com.example.web.mapper.UsersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class SupplyPoolCustomerService {
@Autowired
private SupplyUsersMapper supplyusersMapper;
@Autowired
private SupplyUsersManagementsMapper supplyUsersManagementsMapper;
/**
* 根据手机号查询微信用户信息采购端权限只处理seller和both类型- 支持一对多
*/
public UserProductCartDTO getWechatCustomerByPhone(String phoneNumber) {
if (!StringUtils.hasText(phoneNumber)) {
throw new IllegalArgumentException("手机号不能为空");
}
// 1. 根据手机号查询微信用户基本信息
UserProductCartDTO userInfo = supplyusersMapper.selectByPhone(phoneNumber);
if (userInfo == null) {
throw new RuntimeException("未找到手机号对应的微信用户:" + phoneNumber);
}
System.out.println("🔍 获取到用户基础信息,用户ID: " + userInfo.getUserId() +
", 类型: " + userInfo.getType() +
", 公司: " + userInfo.getCompany() +
", 需求: " + userInfo.getDemand() +
", 规格: " + userInfo.getSpec());
// 采购端权限校验:只处理seller和both类型
if (!"seller".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) {
System.out.println("❌ 权限校验失败: 采购端只能处理供应端和BOTH类型客户");
throw new IllegalArgumentException("采购端权限只能处理供应端和BOTH类型客户");
}
// 2. 获取所有联系人信息(一对多)
try {
List<SupplyUsersMapper.ContactInfo> contacts = supplyusersMapper.getUserAllContacts(userInfo.getUserId());
// 转换为UserProductCartDTO.UsersContacts格式以保持兼容
if (contacts != null && !contacts.isEmpty()) {
List<UserProductCartDTO.UsersContacts> userContacts = contacts.stream()
.map(contact -> {
UserProductCartDTO.UsersContacts userContact = new UserProductCartDTO.UsersContacts(
contact.getWechat(),
contact.getAccount(),
contact.getAccountNumber(),
contact.getBank(),
contact.getAddress()
);
// 设置contactId
userContact.setContactId(contact.getContactId());
return userContact;
})
.collect(Collectors.toList());
userInfo.setUsersContacts(userContacts);
} else {
userInfo.setUsersContacts(Collections.emptyList());
}
System.out.println("✅ 获取用户所有联系人信息,数量: " + (userInfo.getUsersContacts() != null ? userInfo.getUsersContacts().size() : 0));
} catch (Exception e) {
System.err.println("❌ 获取用户联系人信息失败: " + e.getMessage());
userInfo.setUsersContacts(Collections.emptyList());
}
// 3. 采购端:获取所有产品项信息(一对多)- 公海需求
if ("seller".equals(userInfo.getType()) || "both".equals(userInfo.getType())) {
try {
List<SupplyUsersMapper.ProductInfo> products = supplyusersMapper.getUserAllProducts(userInfo.getUserId());
// 转换为UserProductCartDTO.ProductInfo格式以保持兼容
if (products != null && !products.isEmpty()) {
List<UserProductCartDTO.ProductInfo> userProducts = products.stream()
.map(item -> {
UserProductCartDTO.ProductInfo product = new UserProductCartDTO.ProductInfo();
product.setProductId(item.getProductId());
product.setProductName(item.getProductName());
product.setVariety(item.getVariety());
product.setSpecification(item.getSpecification());
product.setQuantity(item.getQuantity());
product.setGrossWeight(item.getGrossWeight());
product.setYolk(item.getYolk());
return product;
})
.collect(Collectors.toList());
userInfo.setProducts(userProducts);
} else {
userInfo.setProducts(Collections.emptyList());
}
System.out.println("✅ 获取用户所有产品项信息,数量: " + (userInfo.getProducts() != null ? userInfo.getProducts().size() : 0));
} catch (Exception e) {
System.err.println("❌ 获取产品信息失败: " + e.getMessage());
userInfo.setProducts(Collections.emptyList());
}
userInfo.setCartItems(Collections.emptyList()); // 采购端不使用购物车信息
}
return userInfo;
}
/**
* 根据用户ID获取用户信息和相关产品数据采购端版本
*/
public UserProductCartDTO getCustomerInfo(String userId) {
if (!StringUtils.hasText(userId)) {
throw new IllegalArgumentException("用户ID不能为空");
}
// 1. 获取用户基本信息
UserProductCartDTO userInfo = supplyusersMapper.getUserBasicInfo(userId);
if (userInfo == null) {
throw new RuntimeException("用户不存在: " + userId);
}
// 采购端权限校验
if (!"seller".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) {
throw new IllegalArgumentException("采购端权限只能查看供应端和BOTH类型客户");
}
// 2. 采购端:查询产品信息(公海需求)
List<UserProductCartDTO.ProductInfo> products = supplyusersMapper.getSellerProducts(userId);
userInfo.setProducts(products != null ? products : Collections.emptyList());
userInfo.setCartItems(Collections.emptyList()); // 采购端不使用购物车
// 3. 获取联系人信息 - 安全处理
try {
List<UserProductCartDTO.UsersContacts> contacts = supplyusersMapper.getUserContacts(userId);
userInfo.setUsersContacts(contacts != null ? contacts : Collections.emptyList());
} catch (Exception e) {
System.err.println("❌ 获取用户联系人信息失败: " + e.getMessage());
userInfo.setUsersContacts(Collections.emptyList());
}
return userInfo;
}
/**
* 获取所有客户信息 - 使用数据库层面权限过滤采购端版本- 优化版支持分页
*/
public List<UserProductCartDTO> getAllCustomers(ManagerAuthInfo authInfo) {
try {
System.out.println("====================================================");
System.out.println("🚀 开始获取所有微信用户数据(数据库权限过滤,采购端)...");
System.out.println("🔐 负责人过滤条件: " +
"公司=" + authInfo.getManagercompany() +
", 部门=" + authInfo.getManagerdepartment());
// 移除固定的分页限制,获取所有数据
int limit = Integer.MAX_VALUE; // 获取所有数据
int offset = 0; // 从第一条开始
// 1. 获取授权客户总数
System.out.println("📊 查询授权客户总数...");
int totalCount = supplyusersMapper.getAuthorizedCustomersCount(authInfo);
System.out.println("✅ 授权客户总数: " + totalCount);
// 2. 使用数据库层面权限过滤并分页
System.out.println("📋 查询授权客户基础信息(分页查询)...");
List<UserProductCartDTO> authorizedUsers = supplyusersMapper.getAuthorizedCustomers(authInfo, limit, offset);
System.out.println("✅ 授权客户数据查询完成");
System.out.println("📊 当前页获取到授权客户数据条数: " + (authorizedUsers != null ? authorizedUsers.size() : "null"));
// 统计通知状态为banold的授权客户数量
if (authorizedUsers != null) {
long banoldCount = authorizedUsers.stream().filter(user -> "banold".equals(user.getNotice())).count();
System.out.println("📊 通知状态为banold的授权客户数量: " + banoldCount);
}
List<UserProductCartDTO> result = new ArrayList<>();
if (authorizedUsers != null && !authorizedUsers.isEmpty()) {
// 🔥 新增:收集所有用户ID用于批量查询
List<String> userIds = authorizedUsers.stream()
.map(UserProductCartDTO::getUserId)
.filter(Objects::nonNull)
.filter(userId -> !userId.trim().isEmpty())
.distinct()
.collect(Collectors.toList());
System.out.println("🔍 需要批量查询的授权用户数量: " + userIds.size());
// 🔥 新增:批量查询所有相关数据
Map<String, UsersManagements> managerMap = batchQueryManagers(userIds);
Map<String, List<UsersMapper.ContactInfo>> contactsMap = batchQueryContacts(userIds);
Map<String, List<UserProductCartDTO.ProductInfo>> productsMap = batchQueryProducts(userIds);
// 2. 为每个授权用户构建完整信息(使用批量数据)
System.out.println("🔄 开始处理 " + authorizedUsers.size() + " 条授权用户数据...");
for (int i = 0; i < authorizedUsers.size(); i++) {
UserProductCartDTO user = authorizedUsers.get(i);
try {
// 🎯 关键修改:使用批量查询的数据构建完整用户信息
UserProductCartDTO fullUserInfo = buildSupplyUserInfoFromBatchData(user, managerMap, contactsMap, productsMap);
if (fullUserInfo != null) {
// 数据库已经过滤了权限,这里直接添加
result.add(fullUserInfo);
// 打印前几个客户的详细信息
if (i < 3) {
System.out.println("📝 授权客户样例 " + (i + 1) + ": " +
"ID=" + fullUserInfo.getUserId() +
", 手机=" + fullUserInfo.getPhoneNumber() +
", 公司=" + fullUserInfo.getCompany() +
", 类型=" + fullUserInfo.getType() +
", 联系人数量=" + (fullUserInfo.getUsersContacts() != null ? fullUserInfo.getUsersContacts().size() : 0) +
", 产品数量=" + (fullUserInfo.getProducts() != null ? fullUserInfo.getProducts().size() : 0) +
", 是否公海池=" + isPublicSeaCustomer(fullUserInfo, authInfo));
}
}
} catch (Exception e) {
System.err.println("❌ 构建用户 " + user.getUserId() + " 的详细信息时出错: " + e.getMessage());
}
}
}
System.out.println("====================================================");
System.out.println("🎉 数据获取完成(数据库权限过滤,采购端)");
System.out.println("📊 返回记录数: " + result.size());
System.out.println("====================================================");
return result;
} catch (Exception e) {
System.err.println("❌ 获取授权客户信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 检查是否有权限查看客户
*/
private boolean hasPermissionToViewCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) {
// 公海池客户使用特殊权限检查
if (isPublicSeaCustomer(userInfo, authInfo)) {
return true;
}
// 非公海池客户需要检查负责人权限
return hasUserManagerPermission(userInfo.getUserId(), authInfo);
}
/**
* 判断是否为公海池客户 - 根据不同类型使用不同逻辑
*/
private boolean isPublicSeaCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) {
// 定义公海池等级
Set<String> publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池");
// 检查等级是否为公海池
boolean isPublicSeaLevel = publicSeaLevels.contains(userInfo.getLevel());
System.out.println("🔍 客户等级检查: " + userInfo.getLevel() + " → 是否公海池等级: " + isPublicSeaLevel);
if (!isPublicSeaLevel) {
return false;
}
// 根据不同类型的公海池使用不同判断逻辑
String level = userInfo.getLevel();
if ("company-sea-pools".equals(level) || "公海池".equals(level)) {
// 公司公海池:必须没有负责人信息
boolean result = !hasManagerAuthInfo(userInfo.getUserId());
System.out.println("🏢 公司公海池检查结果: " + result);
return result;
} else if ("organization-sea-pools".equals(level)) {
// 组织公海池:必须有负责人信息且组织匹配
boolean hasManager = hasManagerAuthInfo(userInfo.getUserId());
if (!hasManager) {
System.out.println("🏢 组织公海池:无负责人信息,不允许查看");
return false;
}
boolean sameOrganization = hasSameOrganization(userInfo.getUserId(), authInfo);
System.out.println("🏢 组织公海池检查结果 - 有负责人: " + hasManager + ", 组织匹配: " + sameOrganization + " → 结果: " + sameOrganization);
return sameOrganization;
} else if ("department-sea-pools".equals(level)) {
// 部门公海池:必须有负责人信息且部门匹配
boolean hasManager = hasManagerAuthInfo(userInfo.getUserId());
if (!hasManager) {
System.out.println("🏢 部门公海池:无负责人信息,不允许查看");
return false;
}
boolean sameDepartment = hasSameDepartment(userInfo.getUserId(), authInfo);
System.out.println("🏢 部门公海池检查结果 - 有负责人: " + hasManager + ", 部门匹配: " + sameDepartment + " → 结果: " + sameDepartment);
return sameDepartment;
}
return false;
}
/**
* 公共方法判断是否为公海池客户供Controller调用
*/
public boolean isPublicSeaCustomerPublic(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) {
return isPublicSeaCustomer(userInfo, authInfo);
}
/**
* 检查是否有负责人认证信息
*/
private boolean hasManagerAuthInfo(String userId) {
try {
UsersManagements userManager = supplyUsersManagementsMapper.findByUserId(userId);
if (userManager == null) {
return false;
}
// 检查认证字段是否为空 - 只要有一个认证字段有值,就有负责人信息
boolean hasAuthInfo = StringUtils.hasText(userManager.getManagerId()) ||
StringUtils.hasText(userManager.getManagercompany()) ||
StringUtils.hasText(userManager.getManagerdepartment()) ||
StringUtils.hasText(userManager.getOrganization()) ||
StringUtils.hasText(userManager.getRole()) ||
StringUtils.hasText(userManager.getUserName()) ||
StringUtils.hasText(userManager.getAssistant());
System.out.println("📋 负责人认证信息检查结果: " + (hasAuthInfo ? "有认证信息" : "无认证信息"));
return hasAuthInfo;
} catch (Exception e) {
System.err.println("❌ 检查负责人认证信息失败: " + e.getMessage());
return false;
}
}
/**
* 检查是否同一组织
*/
private boolean hasSameOrganization(String userId, ManagerAuthInfo authInfo) {
try {
UsersManagements userManager = supplyUsersManagementsMapper.findByUserId(userId);
if (userManager == null) {
return false;
}
boolean sameOrganization = StringUtils.hasText(userManager.getOrganization()) &&
userManager.getOrganization().equals(authInfo.getOrganization());
System.out.println("🏢 组织匹配检查: " + userManager.getOrganization() + " vs " + authInfo.getOrganization() + " → " + sameOrganization);
return sameOrganization;
} catch (Exception e) {
System.err.println("❌ 检查组织信息失败: " + e.getMessage());
return false;
}
}
/**
* 检查是否同一部门
*/
private boolean hasSameDepartment(String userId, ManagerAuthInfo authInfo) {
try {
UsersManagements userManager = supplyUsersManagementsMapper.findByUserId(userId);
if (userManager == null) {
return false;
}
boolean sameDepartment = StringUtils.hasText(userManager.getManagerdepartment()) &&
userManager.getManagerdepartment().equals(authInfo.getManagerdepartment());
System.out.println("🏢 部门匹配检查: " + userManager.getManagerdepartment() + " vs " + authInfo.getManagerdepartment() + " → " + sameDepartment);
return sameDepartment;
} catch (Exception e) {
System.err.println("❌ 检查部门信息失败: " + e.getMessage());
return false;
}
}
/**
* 检查用户负责人权限 - 优化版本数据库层面直接匹配
*/
private boolean hasUserManagerPermission(String userId, ManagerAuthInfo authInfo) {
try {
System.out.println("🔐 检查用户负责人权限,用户ID: " + userId);
// 🔥 优化:直接在数据库层面查询匹配的负责人记录,避免内存比较
UsersManagements userManager = supplyUsersManagementsMapper.findByUserIdAndAuthInfo(userId, authInfo);
// 🔥 优化:如果没有找到匹配的负责人记录,检查是否为公海池客户
if (userManager == null) {
System.out.println("🔍 未找到匹配的负责人记录,检查客户等级...");
// 获取用户信息检查等级
UserProductCartDTO userInfo = supplyusersMapper.getUserBasicInfo(userId);
if (userInfo != null) {
// 定义公海池等级
Set<String> publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池");
boolean isPublicSea = publicSeaLevels.contains(userInfo.getLevel());
if (isPublicSea) {
System.out.println("✅ 公海池客户,允许查看");
return true;
}
}
System.out.println("❌ 非公海池客户且无匹配负责人,拒绝访问");
return false;
}
// 🔥 优化:数据库已经完成匹配,直接返回true
System.out.println("✅ 找到匹配的负责人记录,允许查看");
System.out.println("📝 负责人信息: " +
"公司=" + userManager.getManagercompany() +
", 部门=" + userManager.getManagerdepartment() +
", 组织=" + userManager.getOrganization() +
", 负责人=" + userManager.getUserName());
return true;
} catch (Exception e) {
System.err.println("❌ 检查用户负责人权限失败: " + e.getMessage());
// 发生异常时,出于安全考虑返回false
return false;
}
}
/**
* 检查负责人信息是否匹配UsersManagements
*/
private boolean isManagerMatch(UsersManagements userManager, ManagerAuthInfo authInfo) {
boolean match =
(authInfo.getManagerId() == null || authInfo.getManagerId().equals(userManager.getManagerId())) &&
(authInfo.getManagercompany() == null || authInfo.getManagercompany().equals(userManager.getManagercompany())) &&
(authInfo.getManagerdepartment() == null || authInfo.getManagerdepartment().equals(userManager.getManagerdepartment())) &&
(authInfo.getOrganization() == null || authInfo.getOrganization().equals(userManager.getOrganization())) &&
(authInfo.getRole() == null || authInfo.getRole().equals(userManager.getRole())) &&
(authInfo.getUserName() == null || authInfo.getUserName().equals(userManager.getUserName()));
System.out.println("🔐 用户负责人权限检查: " + (match ? "✅ 匹配" : "❌ 不匹配"));
return match;
}
/**
* 获取公海池客户完整信息 - 支持一对多采购端版本
*/
public UserProductCartDTO getPublicSeaCustomerInfo(String userId) {
if (!StringUtils.hasText(userId)) {
System.out.println("⚠️ 用户ID为空");
return null;
}
// 1. 获取用户基本信息
UserProductCartDTO userInfo = supplyusersMapper.getUserBasicInfo(userId);
if (userInfo == null) {
System.out.println("⚠️ 用户不存在: " + userId);
return null; // 返回null而不是抛出异常
}
// 采购端权限校验
if (!"seller".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) {
System.out.println("🚫 过滤掉非供应端客户: " + userId + " (类型: " + userInfo.getType() + ")");
return null; // 返回null而不是抛出异常
}
// 如果通知状态为banold,则更新为old
if ("banold".equals(userInfo.getNotice())) {
System.out.println("🔄 更新通知状态: " + userId + " 从banold改为old");
supplyusersMapper.updateNotice(userId, "old");
userInfo.setNotice("old");
}
// 🔥 新增:查询负责人信息
try {
UsersManagements userManager = supplyUsersManagementsMapper.findByUserId(userId);
if (userManager != null) {
System.out.println("✅ 获取到负责人信息: " +
"负责人=" + userManager.getUserName() +
", 组织=" + userManager.getOrganization() +
", 部门=" + userManager.getManagerdepartment());
// 将负责人信息设置到DTO中(需要扩展UserProductCartDTO或使用其他方式)
// 这里可以创建一个新的字段来存储负责人信息,或者使用现有的扩展机制
// 暂时记录日志,后续需要DTO支持负责人字段
} else {
System.out.println("⚠️ 未找到负责人信息");
}
} catch (Exception e) {
System.err.println("❌ 获取负责人信息失败: " + e.getMessage());
}
// 2. 获取联系人信息 - 一对多
try {
List<SupplyUsersMapper.ContactInfo> contacts = supplyusersMapper.getUserAllContacts(userId);
if (contacts != null && !contacts.isEmpty()) {
List<UserProductCartDTO.UsersContacts> userContacts = contacts.stream()
.map(contact -> {
UserProductCartDTO.UsersContacts userContact = new UserProductCartDTO.UsersContacts(
contact.getWechat(),
contact.getAccount(),
contact.getAccountNumber(),
contact.getBank(),
contact.getAddress()
);
userContact.setContactId(contact.getContactId());
return userContact;
})
.collect(Collectors.toList());
userInfo.setUsersContacts(userContacts);
} else {
userInfo.setUsersContacts(Collections.emptyList());
}
} catch (Exception e) {
System.err.println("❌ 获取用户联系人信息失败: " + e.getMessage());
userInfo.setUsersContacts(Collections.emptyList());
}
// 3. 采购端:获取产品信息(公海需求)- 一对多
try {
List<SupplyUsersMapper.ProductInfo> products = supplyusersMapper.getUserAllProducts(userId);
System.out.println("🔍 查询到的产品数据条数: " + (products != null ? products.size() : 0));
if (products != null && !products.isEmpty()) {
List<UserProductCartDTO.ProductInfo> userProducts = products.stream()
.map(item -> {
UserProductCartDTO.ProductInfo product = new UserProductCartDTO.ProductInfo();
product.setProductId(item.getProductId());
product.setProductName(item.getProductName());
product.setVariety(item.getVariety());
product.setSpecification(item.getSpecification());
product.setQuantity(item.getQuantity());
product.setGrossWeight(item.getGrossWeight());
product.setYolk(item.getYolk());
// 调试日志
System.out.println("📦 转换产品项: " +
"ID=" + product.getProductId() +
", 产品=" + product.getProductName() +
", 规格=" + product.getSpecification());
return product;
})
.collect(Collectors.toList());
userInfo.setProducts(userProducts);
System.out.println("✅ 成功设置产品数据,数量: " + userProducts.size());
} else {
userInfo.setProducts(Collections.emptyList());
System.out.println("⚠️ 产品数据为空");
}
userInfo.setCartItems(Collections.emptyList()); // 采购端不使用购物车信息
} catch (Exception e) {
System.err.println("❌ 获取产品信息失败: " + e.getMessage());
e.printStackTrace();
userInfo.setProducts(Collections.emptyList());
userInfo.setCartItems(Collections.emptyList());
}
// 最终调试信息
System.out.println("🎯 最终返回的用户数据: " +
"products数量=" + (userInfo.getProducts() != null ? userInfo.getProducts().size() : "null") +
", contacts数量=" + (userInfo.getUsersContacts() != null ? userInfo.getUsersContacts().size() : "null"));
return userInfo;
}
/**
* 获取所有客户信息 - 不进行负责人过滤直接返回所有数据采购端版本 - 优化版
*/
public List<UserProductCartDTO> getAllCustomersWithoutFilter() {
try {
System.out.println("====================================================");
System.out.println("🚀 开始获取所有微信用户数据(无过滤,采购端)...");
// 1. 获取所有用户的基本信息(采购端:只获取seller和both类型)
System.out.println("📋 查询用户基础信息...");
List<UserProductCartDTO> allUsers = supplyusersMapper.getAllUserBasicInfo();
System.out.println("✅ 基础用户数据查询完成");
System.out.println("📊 获取到基础用户数据条数: " + (allUsers != null ? allUsers.size() : "null"));
// 统计通知状态为banold的用户数量
if (allUsers != null) {
long banoldCount = allUsers.stream().filter(user -> "banold".equals(user.getNotice())).count();
System.out.println("📊 通知状态为banold的用户数量: " + banoldCount);
}
List<UserProductCartDTO> result = new ArrayList<>();
if (allUsers != null && !allUsers.isEmpty()) {
// 🔥 修改:收集所有用户ID用于批量查询
List<String> userIds = allUsers.stream()
.map(UserProductCartDTO::getUserId)
.filter(Objects::nonNull)
.filter(userId -> !userId.trim().isEmpty())
.distinct()
.collect(Collectors.toList());
System.out.println("🔍 需要批量查询的用户数量: " + userIds.size());
// 🔥 修改:批量查询所有相关数据(采购端需要产品信息)
Map<String, UsersManagements> managerMap = batchQueryManagers(userIds);
Map<String, List<UsersMapper.ContactInfo>> contactsMap = batchQueryContacts(userIds);
Map<String, List<UserProductCartDTO.ProductInfo>> productsMap = batchQueryProducts(userIds); // 采购端需要产品信息
// 2. 为每个用户构建完整信息
System.out.println("🔄 开始处理 " + allUsers.size() + " 条用户数据...");
for (int i = 0; i < allUsers.size(); i++) {
UserProductCartDTO user = allUsers.get(i);
try {
// 采购端权限:只处理seller和both类型
if (!"seller".equals(user.getType()) && !"both".equals(user.getType())) {
System.out.println("🚫 过滤掉非供应端客户: " + user.getUserId() + " (类型: " + user.getType() + ")");
continue;
}
// 🔥 修改:使用批量查询的数据构建完整用户信息(采购端版本)
UserProductCartDTO fullUserInfo = buildSupplyUserInfoFromBatchData(user, managerMap, contactsMap, productsMap);
if (fullUserInfo != null) {
result.add(fullUserInfo);
// 打印前几个客户的详细信息
if (i < 3) {
System.out.println("📝 采购端客户样例 " + (i + 1) + ": " +
"ID=" + fullUserInfo.getUserId() +
", 手机=" + fullUserInfo.getPhoneNumber() +
", 公司=" + fullUserInfo.getCompany() +
", 类型=" + fullUserInfo.getType() +
", 联系人数量=" + (fullUserInfo.getUsersContacts() != null ? fullUserInfo.getUsersContacts().size() : 0) +
", 产品数量=" + (fullUserInfo.getProducts() != null ? fullUserInfo.getProducts().size() : 0));
}
}
} catch (Exception e) {
System.err.println("❌ 构建用户 " + user.getUserId() + " 的详细信息时出错: " + e.getMessage());
}
}
}
System.out.println("====================================================");
System.out.println("🎉 数据获取完成(无过滤,采购端)");
System.out.println("📊 返回记录数: " + result.size());
System.out.println("====================================================");
return result;
} catch (Exception e) {
System.err.println("❌ 获取所有客户信息失败: " + e.getMessage());
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* 1.批量查询负责人信息与销售端复用
*/
private Map<String, UsersManagements> batchQueryManagers(List<String> userIds) {
Map<String, UsersManagements> managerMap = new HashMap<>();
if (userIds != null && !userIds.isEmpty()) {
try {
// 🔥 注意:采购端可能需要使用不同的Mapper
List<UsersManagements> allManagers = supplyUsersManagementsMapper.findByUserIds(userIds);
for (UsersManagements manager : allManagers) {
if (manager.getUserId() != null) {
managerMap.put(manager.getUserId(), manager);
}
}
System.out.println("✅ 采购端批量查询负责人信息完成,共获取 " + allManagers.size() + " 条记录");
} catch (Exception e) {
System.err.println("❌ 采购端批量查询负责人信息失败: " + e.getMessage());
}
}
return managerMap;
}
/**
* 2.批量查询联系人信息与销售端复用
*/
private Map<String, List<UsersMapper.ContactInfo>> batchQueryContacts(List<String> userIds) {
Map<String, List<UsersMapper.ContactInfo>> contactsMap = new HashMap<>();
if (userIds != null && !userIds.isEmpty()) {
try {
// 🔥 注意:采购端需要使用采购端的Mapper
List<UsersMapper.ContactInfo> allContacts = supplyusersMapper.getUserContactsByUserIds(userIds);
for (UsersMapper.ContactInfo contact : allContacts) {
String userId = contact.getUserId();
if (userId != null) {
contactsMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(contact);
}
}
System.out.println("✅ 采购端批量查询联系人信息完成,共获取 " + allContacts.size() + " 条记录");
} catch (Exception e) {
System.err.println("❌ 采购端批量查询联系人信息失败: " + e.getMessage());
}
}
return contactsMap;
}
/**
* 3.批量查询产品信息采购端专用
*/
private Map<String, List<UserProductCartDTO.ProductInfo>> batchQueryProducts(List<String> userIds) {
Map<String, List<UserProductCartDTO.ProductInfo>> productsMap = new HashMap<>();
if (userIds != null && !userIds.isEmpty()) {
try {
List<UserProductCartDTO.ProductInfo> allProducts = supplyusersMapper.getSellerProductsByUserIds(userIds);
for (UserProductCartDTO.ProductInfo product : allProducts) {
String userId = product.getSellerId();
if (userId != null) {
productsMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(product);
}
}
System.out.println("✅ 批量查询产品信息完成,共获取 " + allProducts.size() + " 条记录");
} catch (Exception e) {
System.err.println("❌ 批量查询产品信息失败: " + e.getMessage());
}
}
return productsMap;
}
/**
* 4.从批量查询的数据构建采购端用户完整信息采购端专用
*/
private UserProductCartDTO buildSupplyUserInfoFromBatchData(UserProductCartDTO basicUserInfo,
Map<String, UsersManagements> managerMap,
Map<String, List<UsersMapper.ContactInfo>> contactsMap,
Map<String, List<UserProductCartDTO.ProductInfo>> productsMap) {
String userId = basicUserInfo.getUserId();
// 采购端权限校验
if (!"seller".equals(basicUserInfo.getType()) && !"both".equals(basicUserInfo.getType())) {
System.out.println("🚫 过滤掉非供应端客户: " + userId + " (类型: " + basicUserInfo.getType() + ")");
return null;
}
// 设置负责人信息
try {
UsersManagements userManager = managerMap.get(userId);
if (userManager != null) {
System.out.println("✅ 从批量Map中获取到负责人信息: " +
"负责人=" + userManager.getUserName() +
", 组织=" + userManager.getOrganization() +
", 部门=" + userManager.getManagerdepartment());
}
} catch (Exception e) {
System.err.println("❌ 从Map获取负责人信息失败: " + e.getMessage());
}
// 设置联系人信息
try {
List<UsersMapper.ContactInfo> contacts = contactsMap.get(userId);
if (contacts != null && !contacts.isEmpty()) {
List<UserProductCartDTO.UsersContacts> userContacts = contacts.stream()
.map(contact -> {
UserProductCartDTO.UsersContacts userContact = new UserProductCartDTO.UsersContacts(
contact.getWechat(),
contact.getAccount(),
contact.getAccountNumber(),
contact.getBank(),
contact.getAddress()
);
userContact.setContactId(contact.getContactId());
return userContact;
})
.collect(Collectors.toList());
basicUserInfo.setUsersContacts(userContacts);
} else {
basicUserInfo.setUsersContacts(Collections.emptyList());
}
} catch (Exception e) {
System.err.println("❌ 设置联系人信息失败: " + e.getMessage());
basicUserInfo.setUsersContacts(Collections.emptyList());
}
// 设置产品信息(采购端关键)
try {
List<UserProductCartDTO.ProductInfo> products = productsMap.get(userId);
if (products != null && !products.isEmpty()) {
basicUserInfo.setProducts(products);
} else {
basicUserInfo.setProducts(Collections.emptyList());
}
basicUserInfo.setCartItems(Collections.emptyList()); // 采购端不使用购物车信息
} catch (Exception e) {
System.err.println("❌ 设置产品信息失败: " + e.getMessage());
basicUserInfo.setProducts(Collections.emptyList());
basicUserInfo.setCartItems(Collections.emptyList());
}
return basicUserInfo;
}
/**
* 只检查等级是否为公海池等级不检查负责人信息
*/
private boolean isPublicSeaLevel(String level) {
// 定义公海池等级
Set<String> publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池");
boolean isPublicSeaLevel = publicSeaLevels.contains(level);
System.out.println("🔍 等级检查: " + level + " → 是否公海池等级: " + isPublicSeaLevel);
return isPublicSeaLevel;
}
/**
* 采购端获取客户的产品信息公海需求
*/
public List<UserProductCartDTO.ProductInfo> getCustomerProducts(String phoneNumber) {
try {
UserProductCartDTO user = supplyusersMapper.selectByPhone(phoneNumber);
if (user != null && ("seller".equals(user.getType()) || "both".equals(user.getType()))) {
return supplyusersMapper.getSellerProducts(user.getUserId());
}
return new ArrayList<>();
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
}

8
src/main/java/com/example/web/service/SupplyUserService.java

@ -1,8 +0,0 @@
package com.example.web.service;
import org.springframework.stereotype.Service;
@Service
public class SupplyUserService {
}

13
src/main/java/com/example/web/service/UserService.java

@ -1,13 +0,0 @@
package com.example.web.service;
import com.example.web.dto.UserProductCartDTO;
import com.example.web.mapper.UsersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
}

1708
src/main/java/com/example/web/service/impl/CustomerServiceImpl.java

File diff suppressed because it is too large

63
src/main/java/com/example/web/service/impl/LoginServiceImpl.java

@ -0,0 +1,63 @@
package com.example.web.service.impl;
import com.example.web.entity.Login;
import com.example.web.entity.Personnel;
import com.example.web.mapper.LoginMapper;
import com.example.web.mapper.PersonnelMapper;
import com.example.web.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Description: 登录服务实现类
* @Author:
* @Date: 2025-12-17
*/
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private LoginMapper loginMapper;
@Autowired
private PersonnelMapper personnelMapper;
/**
* 验证用户登录信息
* @param projectName 工位名
* @param userName 用户名
* @param password 密码
* @return Login对象如果验证成功则返回用户信息否则返回null
*/
@Override
public Login verifyLogin(String projectName, String userName, String password) {
// 根据用户名和工位名查询用户
Login login = loginMapper.selectByUserNameAndProjectName(userName, projectName);
// 如果用户存在且密码正确,则返回用户信息
if (login != null && login.getPassword().equals(password)) {
// 从Personnel表中获取负责人的公司和部门信息
Personnel personnel = getPersonnelInfo(login.getManagerId());
if (personnel != null) {
login.setManagercompany(personnel.getManagercompany());
login.setManagerdepartment(personnel.getManagerdepartment());
login.setOrganization(personnel.getOrganization());
}
return login;
}
// 否则返回null表示验证失败
return null;
}
/**
* 根据managerId获取员工详细信息
* @param managerId 负责人ID
* @return Personnel对象包含员工详细信息
*/
@Override
public Personnel getPersonnelInfo(String managerId) {
// 根据managerId查询员工详细信息
return personnelMapper.selectByManagerId(managerId);
}
}

27
src/main/java/com/example/web/service/impl/PersonnelServiceImpl.java

@ -0,0 +1,27 @@
package com.example.web.service.impl;
import com.example.web.entity.Personnel;
import com.example.web.mapper.PersonnelMapper;
import com.example.web.service.PersonnelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Description: 员工信息服务实现类
*/
@Service
public class PersonnelServiceImpl implements PersonnelService {
@Autowired
private PersonnelMapper personnelMapper;
/**
* 根据负责人ID获取员工信息
* @param managerId 负责人ID
* @return Personnel对象
*/
@Override
public Personnel getPersonnelByManagerId(String managerId) {
return personnelMapper.selectByManagerId(managerId);
}
}

138
src/main/java/com/example/web/task/CustomerStatusTask.java

@ -0,0 +1,138 @@
package com.example.web.task;
import com.example.web.entity.Contacts;
import com.example.web.entity.Enterprise;
import com.example.web.entity.Users;
import com.example.web.mapper.CustomerMapper;
import com.example.web.mapper.WechatCustomerMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
/**
* @Description: 客户状态流转定时任务
*/
@Component
public class CustomerStatusTask {
@Autowired
private CustomerMapper customerMapper;
@Autowired
private WechatCustomerMapper wechatCustomerMapper;
/**
* 每分钟执行一次客户状态流转处理
*/
@Scheduled(cron = "0 * * */5 * ?")
public void processCustomerStatus() {
System.out.println("DEBUG: 开始执行客户状态流转定时任务,当前时间: " + LocalDateTime.now());
// 获取当前时间
LocalDateTime now = LocalDateTime.now();
// 计算3天前和6天前的时间
LocalDateTime threeDaysAgo = now.minusDays(3);
LocalDateTime sixDaysAgo = now.minusDays(6);
// 处理primary数据源的客户
processPrimaryCustomers(threeDaysAgo, sixDaysAgo, now);
// 处理wechat数据源的客户
processWechatCustomers(threeDaysAgo, sixDaysAgo, now);
System.out.println("DEBUG: 客户状态流转定时任务执行完成");
}
/**
* 处理primary数据源的客户状态流转
* @param threeDaysAgo 3天前的时间
* @param sixDaysAgo 6天前的时间
* @param now 当前时间
*/
private void processPrimaryCustomers(LocalDateTime threeDaysAgo, LocalDateTime sixDaysAgo, LocalDateTime now) {
try {
System.out.println("DEBUG: 开始处理primary数据源的客户状态流转");
// 获取所有联系人信息
List<Contacts> contactsList = customerMapper.getAllContacts();
for (Contacts contacts : contactsList) {
// 获取企业信息
Enterprise enterprise = customerMapper.getEnterpriseByContactId(contacts.getId());
if (enterprise == null) {
continue;
}
String currentLevel = enterprise.getLevel();
LocalDateTime followupAt = contacts.getFollowup_at();
// 第一次筛选:查找 followup_at ≤ 3天前的客户,当前状态为"未分级" → 变更为"组织公海池客户"
if ("unclassified".equals(currentLevel) && followupAt != null && followupAt.isBefore(threeDaysAgo)) {
enterprise.setLevel("organization-sea-pools");
customerMapper.updateEnterprise(enterprise);
System.out.println("DEBUG: primary数据源客户ID: " + contacts.getId() + " 从 未分级 变更为 组织公海池客户");
}
// 第二次筛选:查找 followup_at ≤ 6天前的客户,当前状态为"组织公海池客户" → 变更为"部门公海池"
else if ("organization-sea-pools".equals(currentLevel) && followupAt != null && followupAt.isBefore(sixDaysAgo)) {
enterprise.setLevel("department-sea-pools");
customerMapper.updateEnterprise(enterprise);
System.out.println("DEBUG: primary数据源客户ID: " + contacts.getId() + " 从 组织公海池客户 变更为 部门公海池");
}
}
System.out.println("DEBUG: primary数据源的客户状态流转处理完成");
} catch (Exception e) {
System.out.println("DEBUG: 处理primary数据源的客户状态流转失败,异常信息: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 处理wechat数据源的客户状态流转
* @param threeDaysAgo 3天前的时间
* @param sixDaysAgo 6天前的时间
* @param now 当前时间
*/
private void processWechatCustomers(LocalDateTime threeDaysAgo, LocalDateTime sixDaysAgo, LocalDateTime now) {
try {
System.out.println("DEBUG: 开始处理wechat数据源的客户状态流转");
// 获取所有用户信息
List<Users> usersList = wechatCustomerMapper.getAllUsers();
for (Users user : usersList) {
// 跳过type为manager的用户
if ("manager".equals(user.getType())) {
continue;
}
String currentLevel = user.getLevel();
LocalDateTime followupAt = user.getFollowup_at();
// 第一次筛选:查找 followup_at ≤ 3天前的客户,当前状态为"未分级" → 变更为"组织公海池客户"
if ("unclassified".equals(currentLevel) && followupAt != null && followupAt.isBefore(threeDaysAgo)) {
user.setLevel("organization-sea-pools");
user.setUpdated_at(now);
wechatCustomerMapper.updateUsers(user);
System.out.println("DEBUG: wechat数据源客户ID: " + user.getUserId() + " 从 未分级 变更为 组织公海池客户");
}
// 第二次筛选:查找 followup_at ≤ 6天前的客户,当前状态为"组织公海池客户" → 变更为"部门公海池"
else if ("organization-sea-pools".equals(currentLevel) && followupAt != null && followupAt.isBefore(sixDaysAgo)) {
user.setLevel("department-sea-pools");
user.setUpdated_at(now);
wechatCustomerMapper.updateUsers(user);
System.out.println("DEBUG: wechat数据源客户ID: " + user.getUserId() + " 从 组织公海池客户 变更为 部门公海池");
}
}
System.out.println("DEBUG: wechat数据源的客户状态流转处理完成");
} catch (Exception e) {
System.out.println("DEBUG: 处理wechat数据源的客户状态流转失败,异常信息: " + e.getMessage());
e.printStackTrace();
}
}
}

121
src/main/java/com/example/web/utils/CustomerTraceUtil.java

@ -0,0 +1,121 @@
package com.example.web.utils;
import com.example.web.entity.InformationTra;
import com.example.web.mapper.InformationTraMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @Description: 客户踪迹追踪工具类
*/
@Component
public class CustomerTraceUtil {
@Autowired
private InformationTraMapper informationTraMapper;
/**
* 记录客户操作
* @param userId 客户ID
* @param tracompany 负责人公司
* @param tradepartment 负责人部门
* @param traorganization 负责人组织
* @param trarole 负责人角色
* @param trauserName 负责人姓名
* @param traassistant 协助人
* @param operationEvent 操作事件
* @param originalData 原始数据
* @param modifiedData 修改后数据
* @param changedFields 变更字段
* @return 是否记录成功
*/
public boolean recordCustomerOperation(String userId, String tracompany, String tradepartment, String traorganization,
String trarole, String trauserName, String traassistant, String operationEvent,
String originalData, String modifiedData, String changedFields) {
try {
// 创建信息跟踪记录
InformationTra informationTra = new InformationTra();
informationTra.setUserId(userId);
informationTra.setTracompany(tracompany);
informationTra.setTradepartment(tradepartment);
informationTra.setTraorganization(traorganization);
informationTra.setTrarole(trarole);
informationTra.setTrauserName(trauserName);
informationTra.setTraassistant(traassistant);
informationTra.setOperationEvent(operationEvent);
informationTra.setOperationTime(LocalDateTime.now());
informationTra.setCreatedAt(LocalDateTime.now());
informationTra.setUpdatedAt(LocalDateTime.now());
informationTra.setOriginalData(originalData);
informationTra.setModifiedData(modifiedData);
informationTra.setChangedFields(changedFields);
// 保存到数据库
int result = informationTraMapper.insertInformationTra(informationTra);
System.out.println("DEBUG: 记录客户操作,用户ID: " + userId + ", 操作人: " + trauserName + ", 操作事件: " + operationEvent + ", 结果: " + result);
return result > 0;
} catch (Exception e) {
System.out.println("DEBUG: 记录客户操作失败,异常信息: " + e.getMessage());
e.printStackTrace();
return false;
}
}
/**
* 记录客户查看详情操作
* @param userId 客户ID
* @param tracompany 负责人公司
* @param tradepartment 负责人部门
* @param traorganization 负责人组织
* @param trarole 负责人角色
* @param trauserName 负责人姓名
* @return 是否记录成功
*/
public boolean recordCustomerView(String userId, String tracompany, String tradepartment, String traorganization,
String trarole, String trauserName) {
return recordCustomerOperation(userId, tracompany, tradepartment, traorganization, trarole, trauserName,
null, "查看客户详情", null, null, null);
}
/**
* 记录客户编辑操作
* @param userId 客户ID
* @param tracompany 负责人公司
* @param tradepartment 负责人部门
* @param traorganization 负责人组织
* @param trarole 负责人角色
* @param trauserName 负责人姓名
* @param originalData 原始数据
* @param modifiedData 修改后数据
* @param changedFields 变更字段
* @return 是否记录成功
*/
public boolean recordCustomerEdit(String userId, String tracompany, String tradepartment, String traorganization,
String trarole, String trauserName, String originalData, String modifiedData,
String changedFields) {
return recordCustomerOperation(userId, tracompany, tradepartment, traorganization, trarole, trauserName,
null, "编辑客户信息", originalData, modifiedData, changedFields);
}
/**
* 记录客户跟进操作
* @param userId 客户ID
* @param tracompany 负责人公司
* @param tradepartment 负责人部门
* @param traorganization 负责人组织
* @param trarole 负责人角色
* @param trauserName 负责人姓名
* @param originalData 原始数据
* @param modifiedData 修改后数据
* @param changedFields 变更字段
* @return 是否记录成功
*/
public boolean recordCustomerFollowup(String userId, String tracompany, String tradepartment, String traorganization,
String trarole, String trauserName, String originalData, String modifiedData,
String changedFields) {
return recordCustomerOperation(userId, tracompany, tradepartment, traorganization, trarole, trauserName,
null, "跟进客户", originalData, modifiedData, changedFields);
}
}

70
src/main/resources/application.yaml

@ -1,32 +1,79 @@
spring:
main:
allow-bean-definition-overriding: true
datasource:
# userlogin数据库
primary:
jdbc-url: jdbc:mysql://1.95.162.61:3306/userlogin?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
jdbc-url: jdbc:mysql://1.95.162.61:3306/userlogin?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&maxReconnects=10&allowPublicKeyRetrieval=true
username: root
password: schl@2025
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
connection-timeout: 120000
max-lifetime: 3600000
maximum-pool-size: 15
minimum-idle: 8
validation-timeout: 5000
test-while-idle: true
test-on-borrow: true
test-on-return: true
connection-test-query: SELECT 1
initialization-fail-timeout: -1
keepalive-time: 300000
leak-detection-threshold: 30000
# wechat_app数据库
wechat:
jdbc-url: jdbc:mysql://1.95.162.61:3306/wechat_app?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
jdbc-url: jdbc:mysql://1.95.162.61:3306/wechat_app?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&maxReconnects=10&allowPublicKeyRetrieval=true
username: root
password: schl@2025
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
connection-timeout: 120000
max-lifetime: 3600000
maximum-pool-size: 20
minimum-idle: 10
validation-timeout: 5000
test-while-idle: true
test-on-borrow: true
test-on-return: true
connection-test-query: SELECT 1
initialization-fail-timeout: -1
keepalive-time: 300000
leak-detection-threshold: 30000
# 应用配置
app:
recycle:
# 未分级客户回流到组织公海池的天数阈值
unclassified-to-organization-days: 30
unclassified-to-organization-days: 3
# 组织公海池客户回流到部门公海池的天数阈值
organization-to-department-days: 30
organization-to-department-days: 3
# Spring Boot Actuator配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
health:
datasource:
enabled: true
server:
port: 8081
port: 8080
servlet:
context-path: /DL
# 在Tomcat中部署时,端口由Tomcat配置决定,这里不需要指定
# address属性在Tomcat部署中不生效,由容器控制
encoding:
charset: UTF-8
enabled: true
force: true
tomcat:
uri-encoding: UTF-8
# 在Tomcat中部署时,端口由Tomcat配置决定,这里不需要指定
# address属性在Tomcat部署中不生效,由容器控制
mybatis:
type-aliases-package: com.example.web.entity
@ -36,10 +83,13 @@ mybatis:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
call-setters-on-nulls: true
jdbc-type-for-null: null
map-underscore-to-camel-case: true
logging:
level:
com.example.web.mapper: DEBUG
com.example.web.config: DEBUG
com.example.web.aspect: DEBUG
com.example.web.mapper: TRACE
com.example.web.config: TRACE
com.example.web.aspect: TRACE
org.apache.ibatis: TRACE
org.mybatis.spring: TRACE

21
src/main/resources/mapper/Cart_itemsMapper.xml

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.Cart_itemsMapper">
<resultMap id="cart_timeMap" type="com.example.web.entity.Cart_items">
<id column="id" property="id"/>
<result column="userId" property="userId"/>
<result column="productId" property="productId"/>
<result column="productName" property="productName"/>
<result column="specification" property="specification"/>
<result column="quantity" property="quantity"/>
<result column="grossWeight" property="grossWeight"/>
<result column="price" property="price"/>
<result column="selected" property="selected"/>
<result column="added_at" property="added_at"/>
</resultMap>
</mapper>

160
src/main/resources/mapper/CustomerMapper.xml

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.CustomerMapper">
<resultMap id="ContactsResultMap" type="com.example.web.entity.Contacts">
<result column="contact_id" property="contact_id" />
<result column="id" property="id" />
<result column="nickName" property="nickName" />
<result column="phoneNumber" property="phoneNumber" />
<result column="wechat" property="wechat" />
<result column="account" property="account" />
<result column="accountNumber" property="accountNumber" />
<result column="bank" property="bank" />
<result column="address" property="address" />
<result column="followup" property="followup" />
<result column="created_at" property="created_at" />
<result column="updated_at" property="updated_at" />
</resultMap>
<resultMap id="EnterpriseResultMap" type="com.example.web.entity.Enterprise">
<result column="id" property="id" />
<result column="company" property="company" />
<result column="region" property="region" />
<result column="level" property="level" />
<result column="type" property="type" />
<result column="demand" property="demand" />
<result column="spec" property="spec" />
</resultMap>
<resultMap id="ManagersResultMap" type="com.example.web.entity.Managers">
<result column="manager_id" property="manager_id" />
<result column="id" property="id" />
<result column="managerId" property="managerId" />
<result column="managercompany" property="managercompany" />
<result column="managerdepartment" property="managerdepartment" />
<result column="organization" property="organization" />
<result column="role" property="role" />
<result column="root" property="root" />
<result column="created_at" property="created_at" />
<result column="updated_at" property="updated_at" />
<result column="userName" property="userName" />
<result column="assistant" property="assistant" />
</resultMap>
<!-- 获取所有联系人信息 -->
<select id="getAllContacts" resultMap="ContactsResultMap">
SELECT contact_id, id, nickName, phoneNumber, wechat, account, accountNumber, bank, address, followup, created_at, updated_at
FROM contacts
</select>
<!-- 根据联系人ID获取企业信息 -->
<select id="getEnterpriseByContactId" parameterType="String" resultMap="EnterpriseResultMap">
SELECT id, company, region, level, type, demand, spec
FROM enterprise
WHERE id = #{contactId}
</select>
<!-- 根据联系人ID获取负责人信息 -->
<select id="getManagerByContactId" parameterType="String" resultMap="ManagersResultMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment, organization, role, root, created_at, updated_at, userName, assistant
FROM managers
WHERE id = #{contactId}
</select>
<!-- 获取部门公海池数据 -->
<select id="getDepartmentSeaPoolContacts" resultMap="ContactsResultMap">
SELECT c.contact_id, c.id, c.nickName, c.phoneNumber, c.wechat, c.account, c.accountNumber, c.bank, c.address, c.followup, c.created_at, c.updated_at
FROM contacts c
LEFT JOIN managers m ON c.id = m.id
LEFT JOIN enterprise e ON c.id = e.id
WHERE m.managercompany = #{managercompany}
AND m.managerdepartment = #{managerdepartment}
AND e.level = 'department-sea-pools'
AND ((#{role} = 'seller' AND (e.type = 'seller' OR e.type = 'both')) OR (#{role} = 'buyer' AND (e.type = 'buyer' OR e.type = 'both')))
</select>
<!-- 获取组织公海池数据 -->
<select id="getOrganizationSeaPoolContacts" resultMap="ContactsResultMap">
SELECT c.contact_id, c.id, c.nickName, c.phoneNumber, c.wechat, c.account, c.accountNumber, c.bank, c.address, c.followup, c.created_at, c.updated_at
FROM contacts c
LEFT JOIN managers m ON c.id = m.id
LEFT JOIN enterprise e ON c.id = e.id
WHERE m.managercompany = #{managercompany}
AND m.managerdepartment = #{managerdepartment}
AND m.organization = #{organization}
AND e.level = 'organization-sea-pools'
AND ((#{role} = 'seller' AND (e.type = 'seller' OR e.type = 'both')) OR (#{role} = 'buyer' AND (e.type = 'buyer' OR e.type = 'both')))
</select>
<!-- 插入企业信息 -->
<insert id="insertEnterprise" parameterType="com.example.web.entity.Enterprise">
INSERT INTO enterprise (id, company, region, level, type, demand, spec)
VALUES (#{id}, #{company}, #{region}, #{level}, #{type}, #{demand}, #{spec})
</insert>
<!-- 插入联系人信息 -->
<insert id="insertContacts" parameterType="com.example.web.entity.Contacts">
INSERT INTO contacts (contact_id, id, nickName, phoneNumber, wechat, account, accountNumber, bank, address, followup, created_at, updated_at)
VALUES (#{contact_id}, #{id}, #{nickName}, #{phoneNumber}, #{wechat}, #{account}, #{accountNumber}, #{bank}, #{address}, #{followup}, #{created_at}, #{updated_at})
</insert>
<!-- 插入负责人信息 -->
<insert id="insertManagers" parameterType="com.example.web.entity.Managers">
INSERT INTO managers (id, managerId, managercompany, managerdepartment, organization, role, root, created_at, updated_at, userName, assistant)
VALUES (#{id}, #{managerId}, #{managercompany}, #{managerdepartment}, #{organization}, #{role}, #{root}, #{created_at}, #{updated_at}, #{userName}, #{assistant})
</insert>
<!-- 根据角色和登录信息获取客户数据 -->
<select id="getCustomersByRole" resultMap="ContactsResultMap">
SELECT c.contact_id, c.id, c.nickName, c.phoneNumber, c.wechat, c.account, c.accountNumber, c.bank, c.address, c.followup, c.created_at, c.updated_at
FROM contacts c
LEFT JOIN managers m ON c.id = m.id
LEFT JOIN enterprise e ON c.id = e.id
WHERE m.userName = #{userName}
AND m.managercompany = #{managercompany}
AND m.managerdepartment = #{managerdepartment}
AND m.organization = #{organization}
AND e.level NOT IN ('department-sea-pools', 'organization-sea-pools')
AND ((#{role} = 'seller' AND (e.type = 'seller' OR e.type = 'both')) OR (#{role} = 'buyer' AND (e.type = 'buyer' OR e.type = 'both')))
</select>
<!-- 根据电话号码获取联系人信息 -->
<select id="getContactsByPhoneNumber" parameterType="String" resultMap="ContactsResultMap">
SELECT contact_id, id, nickName, phoneNumber, wechat, account, accountNumber, bank, address, followup, created_at, updated_at
FROM contacts
WHERE phoneNumber = #{phoneNumber}
</select>
<!-- 根据ID获取联系人信息 -->
<select id="getContactsById" parameterType="String" resultMap="ContactsResultMap">
SELECT contact_id, id, nickName, phoneNumber, wechat, account, accountNumber, bank, address, followup, created_at, updated_at
FROM contacts
WHERE id = #{id}
</select>
<!-- 更新联系人信息 -->
<update id="updateContacts" parameterType="com.example.web.entity.Contacts">
UPDATE contacts
SET nickName = #{nickName}, wechat = #{wechat}, account = #{account},
accountNumber = #{accountNumber}, bank = #{bank}, address = #{address},
updated_at = #{updated_at}
WHERE contact_id = #{contact_id}
</update>
<!-- 更新企业信息 -->
<update id="updateEnterprise" parameterType="com.example.web.entity.Enterprise">
UPDATE enterprise
SET company = #{company}, region = #{region}, level = #{level},
type = #{type}, demand = #{demand}, spec = #{spec}
WHERE id = #{id}
</update>
<!-- 更新负责人信息 -->
<update id="updateManagers" parameterType="com.example.web.entity.Managers">
UPDATE managers
SET managercompany = #{managercompany}, managerdepartment = #{managerdepartment},
organization = #{organization}, role = #{role}, userName = #{userName},
assistant = #{assistant}, updated_at = #{updated_at}
WHERE id = #{id}
</update>
</mapper>

51
src/main/resources/mapper/InformationTraMapper.xml

@ -1,35 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.InformationTraMapper">
<resultMap id="informationTraMap" type="com.example.web.entity.InformationTra">
<id property="id" column="id"/>
<result property="tracompany" column="tracompany"/>
<result property="tradepartment" column="tradepartment"/>
<result property="traorganization" column="traorganization"/>
<result property="trarole" column="trarole"/>
<result property="trauserName" column="trauserName"/>
<result property="traassistant" column="traassistant"/>
<result property="userId" column="userId"/>
<result property="operationEvent" column="operationEvent"/>
<result property="operationTime" column="operationTime"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
<resultMap id="InformationTraResultMap" type="com.example.web.entity.InformationTra">
<result column="id" property="id" />
<result column="tracompany" property="tracompany" />
<result column="tradepartment" property="tradepartment" />
<result column="traorganization" property="traorganization" />
<result column="trarole" property="trarole" />
<result column="trauserName" property="trauserName" />
<result column="traassistant" property="traassistant" />
<result column="userId" property="userId" />
<result column="operationEvent" property="operationEvent" />
<result column="operationTime" property="operationTime" />
<result column="created_at" property="createdAt" />
<result column="updated_at" property="updatedAt" />
<result column="originalData" property="originalData" />
<result column="modifiedData" property="modifiedData" />
<result column="changedFields" property="changedFields" />
</resultMap>
<!-- 插入信息跟踪记录 -->
<insert id="insertInformationTra" parameterType="com.example.web.entity.InformationTra">
INSERT INTO informationtra (
tracompany, tradepartment, traorganization, trarole,
trauserName, traassistant, userId, operationEvent,
operationTime, created_at, updated_at
) VALUES (
#{tracompany}, #{tradepartment}, #{traorganization}, #{trarole},
#{trauserName}, #{traassistant}, #{userId}, #{operationEvent},
#{operationTime}, #{createdAt}, #{updatedAt}
)
INSERT INTO informationtra (tracompany, tradepartment, traorganization, trarole, trauserName, traassistant, userId, operationEvent, operationTime, created_at, updated_at, originalData, modifiedData, changedFields)
VALUES (#{tracompany}, #{tradepartment}, #{traorganization}, #{trarole}, #{trauserName}, #{traassistant}, #{userId}, #{operationEvent}, #{operationTime}, #{createdAt}, #{updatedAt}, #{originalData}, #{modifiedData}, #{changedFields})
</insert>
<select id="selectByUserId" resultMap="informationTraMap">
SELECT * FROM informationtra WHERE userId = #{userId}
</select>
</mapper>
</mapper>

38
src/main/resources/mapper/LoginMapper.xml

@ -1,34 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.LoginMapper">
<resultMap id="LoginResultMap" type="com.example.web.entity.Login">
<id property="id" column="id"/>
<result property="projectName" column="projectName"/>
<result property="userName" column="userName"/>
<result property="password" column="password"/>
<resultMap id="BaseResultMap" type="com.example.web.entity.Login">
<result column="id" property="id" />
<result column="userName" property="userName" />
<result column="projectName" property="projectName" />
<result column="password" property="password" />
<result column="managerId" property="managerId" />
</resultMap>
<select id="findByProjectNameAndUserName" resultMap="LoginResultMap">
SELECT id, projectName, userName, password
FROM login
WHERE projectName = #{projectName} AND userName = #{userName}
</select>
<select id="findByConditions" resultMap="LoginResultMap">
SELECT id, projectName, userName, password
FROM login
<where>
<if test="projectName != null">
AND projectName = #{projectName}
</if>
<if test="userName != null">
AND userName = #{userName}
</if>
</where>
ORDER BY id DESC
<select id="selectByUserNameAndProjectName" resultMap="BaseResultMap">
SELECT userName, projectName, password, managerId FROM login
WHERE userName = #{userName} AND projectName = #{projectName}
</select>
</mapper>

27
src/main/resources/mapper/PersonnelMapper.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.PersonnelMapper">
<resultMap id="BaseResultMap" type="com.example.web.entity.Personnel">
<result column="id" property="id" />
<result column="managerId" property="managerId" />
<result column="managercompany" property="managercompany" />
<result column="managerdepartment" property="managerdepartment" />
<result column="organization" property="organization" />
<result column="projectName" property="projectName" />
<result column="alias" property="alias" />
<result column="name" property="name" />
<result column="phoneNumber" property="phoneNumber" />
<result column="createdAt" property="createdAt" />
<result column="updatedAt" property="updatedAt" />
<result column="avatarUrl" property="avatarUrl" />
</resultMap>
<!-- 根据负责人ID查询负责人信息 -->
<select id="selectByManagerId" resultMap="BaseResultMap">
SELECT id, managerId, managercompany, managerdepartment, organization,
projectName, alias, name, phoneNumber,
created_at AS createdAt, updated_at AS updatedAt, avatarUrl
FROM personnel
WHERE managerId = #{managerId}
</select>
</mapper>

26
src/main/resources/mapper/ProductsMapper.xml

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.ProductsMapper">
<resultMap id="productsMap" type="com.example.web.entity.Products">
<id column="id" property="id"/>
<result column="productId" property="productId"/>
<result column="sellerId" property="sellerId"/>
<result column="productName" property="productName"/>
<result column="price" property="price"/>
<result column="quantity" property="quantity"/>
<result column="variety" property="variety"/>
<result column="grossWeight" property="grossWeight"/>
<result column="specification" property="specification"/>
<result column="status" property="status"/>
<result column="created_at" property="created_at"/>
<result column="updated_at" property="updated_at"/>
<result column="yolk" property="yolk"/>
<result column="rejectReason" property="rejectReason"/>
</resultMap>
</mapper>

42
src/main/resources/mapper/RootdbMapper.xml

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.RootdbMapper">
<resultMap id="RootdbResultMap" type="com.example.web.entity.Rootdb">
<result property="root" column="root"/>
<result property="projectName" column="projectName"/>
</resultMap>
<select id="findByProjectName" resultMap="RootdbResultMap">
SELECT root, projectName
FROM rootdb
WHERE projectName = #{projectName}
</select>
<!-- 新增:获取所有权限信息 -->
<select id="findAll" resultMap="RootdbResultMap">
SELECT root, projectName
FROM rootdb
ORDER BY root ASC
</select>
<!-- 新增:根据权限等级查询 -->
<select id="findByRootLevel" parameterType="int" resultMap="RootdbResultMap">
SELECT root, projectName
FROM rootdb
WHERE root = #{root}
</select>
<!-- 新增:批量查询权限信息 -->
<select id="findByProjectNames" parameterType="list" resultMap="RootdbResultMap">
SELECT root, projectName
FROM rootdb
WHERE projectName IN
<foreach collection="projectNames" item="projectName" open="(" separator="," close=")">
#{projectName}
</foreach>
</select>
</mapper>

21
src/main/resources/mapper/SupplyCart_itemsMapper.xml

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.SupplyCart_itemsMapper">
<resultMap id="cart_timeMap" type="com.example.web.entity.Cart_items">
<id column="id" property="id"/>
<result column="userId" property="userId"/>
<result column="productId" property="productId"/>
<result column="productName" property="productName"/>
<result column="specification" property="specification"/>
<result column="quantity" property="quantity"/>
<result column="grossWeight" property="grossWeight"/>
<result column="price" property="price"/>
<result column="selected" property="selected"/>
<result column="added_at" property="added_at"/>
</resultMap>
</mapper>

63
src/main/resources/mapper/SupplyContactsMapper.xml

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.SupplyContactsMapper">
<resultMap id="contactsMap" type="com.example.web.entity.Contacts">
<id column="contact_id" property="contact_id"/>
<result column="id" property="id"/>
<result column="nickName" property="nickName"/>
<result column="phoneNumber" property="phoneNumber"/>
<result column="wechat" property="wechat"/>
<result column="account" property="account"/>
<result column="accountNumber" property="accountNumber"/>
<result column="bank" property="bank"/>
<result column="address" property="address"/>
<result column="created_at" property="created_at"/>
<result column="updated_at" property="updated_at"/>
</resultMap>
<!-- 根据企业ID查询联系人信息 -->
<select id="selectContactsByEnterpriseId" parameterType="String" resultMap="contactsMap">
SELECT * FROM contacts WHERE id = #{id}
</select>
<!-- 查询所有联系人信息 -->
<select id="selectAllContacts" resultMap="contactsMap">
SELECT * FROM contacts ORDER BY nickName
</select>
<insert id="insertContacts" parameterType="com.example.web.entity.Contacts">
INSERT INTO contacts (
contact_id,
id, nickName, phoneNumber, wechat, account, accountNumber, bank, address,created_at,updated_at
) VALUES (
#{contact_id},
#{id}, #{nickName}, #{phoneNumber}, #{wechat}, #{account}, #{accountNumber}, #{bank}, #{address}, #{created_at}, #{updated_at}
)
</insert>
<!-- 新增:查询指定电话号码的记录数 -->
<select id="countByPhoneNumber" parameterType="String" resultType="int">
SELECT COUNT(*) FROM contacts WHERE phoneNumber = #{phoneNumber}
</select>
<select id="selectByPhoneNumber" parameterType="String" resultType="com.example.web.entity.Contacts">
SELECT * FROM contacts WHERE phoneNumber = #{phoneNumber}
</select>
<!-- 修复:移除注释中的SQL语句,避免解析问题 -->
<update id="updateContacts" parameterType="com.example.web.entity.Contacts">
UPDATE contacts
SET
id = #{id},
nickName = #{nickName},
wechat = #{wechat},
account = #{account},
accountNumber = #{accountNumber},
bank = #{bank},
address = #{address}
WHERE contact_id = #{contact_id}
</update>
</mapper>

79
src/main/resources/mapper/SupplyEnterpriseMapper.xml

@ -1,79 +0,0 @@
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.SupplyEnterpriseMapper">
<resultMap id="enterpriseMap" type="com.example.web.entity.Enterprise">
<id column="id" property="id"/>
<result column="company" property="company"/>
<result column="region" property="region"/>
<result column="level" property="level"/>
<result column="type" property="type"/>
<result column="demand" property="demand"/>
<result column="spec" property="spec"/>
</resultMap>
<resultMap id="enterpriseInfoDTOMap" type="com.example.web.dto.EnterpriseInfoDTO">
<association property="enterprise" resultMap="enterpriseMap"/>
<association property="contacts" resultMap="com.example.web.mapper.ContactsMapper.contactsMap"/>
<association property="managers" resultMap="com.example.web.mapper.ManagersMapper.managersMap"/>
</resultMap>
<!-- 查询所有企业及其联系人和负责人信息(销售端:只查询seller和both类型) -->
<select id="selectAllEnterpriseInfo" resultMap="enterpriseInfoDTOMap">
SELECT
e.*,
c.contact_id, c.nickName, c.phoneNumber, c.wechat, c.account, c.accountNumber, c.bank, c.address,
c.created_at, c.updated_at,
m.manager_id, m.managerId, m.managercompany, m.managerdepartment, m.organization, m.role, m.root,
m.created_at as manager_created_at, m.updated_at as manager_updated_at, m.userName, m.assistant
FROM enterprise e
LEFT JOIN contacts c ON e.id = c.id
LEFT JOIN managers m ON e.id = m.id
WHERE e.type IN ('seller', 'both') <!-- 销售端:只查询seller和both类型 -->
ORDER BY e.company
</select>
<!-- 根据企业ID查询详细信息 -->
<select id="selectEnterpriseInfoById" parameterType="String" resultMap="enterpriseInfoDTOMap">
SELECT
e.*,
c.contact_id, c.nickName, c.phoneNumber, c.wechat, c.account, c.accountNumber, c.bank, c.address,
c.created_at, c.updated_at,
m.manager_id, m.managerId, m.managercompany, m.managerdepartment, m.organization, m.role, m.root,
m.created_at as manager_created_at, m.updated_at as manager_updated_at, m.userName, m.assistant
FROM enterprise e
LEFT JOIN contacts c ON e.id = c.id
LEFT JOIN managers m ON e.id = m.id
WHERE e.id = #{id}
AND e.type IN ('seller', 'both') <!-- 销售端:只查询seller和both类型 -->
</select>
<!-- 查询所有企业基本信息(销售员权限:只查询seller和both类型) -->
<select id="selectAllEnterprises" resultMap="enterpriseMap">
SELECT * FROM enterprise
WHERE type IN ('seller', 'both') <!-- 销售员权限:只查询seller和both类型 -->
ORDER BY company
</select>
<insert id="insertEnterprise" parameterType="com.example.web.entity.Enterprise">
INSERT INTO enterprise (
id, company, region, level, type, demand, spec
) VALUES (
#{id}, #{company}, #{region}, #{level}, #{type}, #{demand}, #{spec}
)
</insert>
<update id="updateEnterprise" parameterType="com.example.web.entity.Enterprise">
UPDATE enterprise
SET
company = #{company},
region = #{region},
level = #{level},
type = #{type},
demand = #{demand},
spec = #{spec}
WHERE id = #{id}
</update>
</mapper>

79
src/main/resources/mapper/SupplyManagersMapper.xml

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.SupplyManagersMapper">
<!-- 添加二级缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
<resultMap id="managersMap" type="com.example.web.entity.Managers">
<id column="manager_id" property="manager_id"/>
<result column="id" property="id"/>
<result column="managerId" property="managerId"/>
<result column="managercompany" property="managercompany"/>
<result column="managerdepartment" property="managerdepartment"/>
<result column="organization" property="organization"/>
<result column="role" property="role"/>
<result column="root" property="root"/>
<result column="created_at" property="created_at"/>
<result column="updated_at" property="updated_at"/>
<result column="userName" property="userName"/>
<result column="assistant" property="assistant"/>
</resultMap>
<!-- 在 SupplyManagersMapper.xml 中添加 -->
<select id="selectByUserNameAndManagerId" parameterType="map" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers
WHERE userName = #{userName} AND managerId = #{managerId}
</select>
<!-- 根据企业ID查询负责人信息 -->
<select id="selectManagersByEnterpriseId" parameterType="String" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers WHERE id = #{id}
</select>
<!-- 查询所有负责人信息 -->
<select id="selectAllManagers" resultMap="managersMap">
SELECT * FROM managers ORDER BY userName
</select>
<!-- 根据负责人姓名查询 -->
<select id="selectByUserName" parameterType="String" resultMap="managersMap">
SELECT * FROM managers WHERE userName = #{userName}
</select>
<!-- 插入负责人信息 -->
<insert id="insertManagers" parameterType="com.example.web.entity.Managers">
INSERT INTO managers (
id, managerId, managercompany, managerdepartment,
organization, role, root, created_at, updated_at,
userName, assistant
) VALUES (
#{id}, #{managerId}, #{managercompany}, #{managerdepartment},
#{organization}, #{role}, #{root}, #{created_at}, #{updated_at},
#{userName}, #{assistant}
)
</insert>
<!-- 更新负责人信息 -->
<update id="updateManagers" parameterType="com.example.web.entity.Managers">
UPDATE managers
SET
managercompany = #{managercompany},
managerdepartment = #{managerdepartment},
organization = #{organization},
role = #{role},
userName = #{userName},
assistant = #{assistant},
updated_at = #{updated_at}
WHERE id = #{id}
</update>
</mapper>

26
src/main/resources/mapper/SupplyProductsMapper.xml

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.SupplyProductsMapper">
<resultMap id="productsMap" type="com.example.web.entity.Products">
<id column="id" property="id"/>
<result column="productId" property="productId"/>
<result column="sellerId" property="sellerId"/>
<result column="productName" property="productName"/>
<result column="price" property="price"/>
<result column="quantity" property="quantity"/>
<result column="variety" property="variety"/>
<result column="grossWeight" property="grossWeight"/>
<result column="specification" property="specification"/>
<result column="status" property="status"/>
<result column="created_at" property="created_at"/>
<result column="updated_at" property="updated_at"/>
<result column="yolk" property="yolk"/>
<result column="rejectReason" property="rejectReason"/>
</resultMap>
</mapper>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save