commit 19ea60d6b92905bb5e2aedeb4b7092a808cc63e6 Author: SwTt29 <2055018491@qq.com> Date: Fri Nov 28 13:42:07 2025 +0800 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3b41682 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/CREATE_OPTIMIZATION_INDEXES.sql b/CREATE_OPTIMIZATION_INDEXES.sql new file mode 100644 index 0000000..7251b99 --- /dev/null +++ b/CREATE_OPTIMIZATION_INDEXES.sql @@ -0,0 +1,81 @@ +-- 数据库索引创建脚本 +-- 此脚本用于优化应用性能,为常用查询字段创建索引 + +-- ============================================ +-- 用户相关表索引 +-- ============================================ + +-- 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); +-- 以此类推... \ No newline at end of file diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..18a210a --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -0,0 +1,224 @@ +# 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 + + + WEB-INF/web.xml + ${catalina.base}/conf/web.xml + +``` + +#### 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 + +``` + +### 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 \ No newline at end of file diff --git a/DL.war b/DL.war new file mode 100644 index 0000000..8086386 Binary files /dev/null and b/DL.war differ diff --git a/LATEST_DEPLOYMENT_GUIDE.md b/LATEST_DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..b38fdfe --- /dev/null +++ b/LATEST_DEPLOYMENT_GUIDE.md @@ -0,0 +1,184 @@ +# 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` + +--- + +> 部署完成后,请务必执行性能优化验证步骤,确保所有优化措施生效。 \ No newline at end of file diff --git a/MAPPER_SERVICE_UPDATE_GUIDE.txt b/MAPPER_SERVICE_UPDATE_GUIDE.txt new file mode 100644 index 0000000..4af069c --- /dev/null +++ b/MAPPER_SERVICE_UPDATE_GUIDE.txt @@ -0,0 +1,146 @@ +# 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 \ No newline at end of file diff --git a/MAPPER_XML_DEPLOYMENT_GUIDE.txt b/MAPPER_XML_DEPLOYMENT_GUIDE.txt new file mode 100644 index 0000000..bf9aa1a --- /dev/null +++ b/MAPPER_XML_DEPLOYMENT_GUIDE.txt @@ -0,0 +1,163 @@ +# 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 \ No newline at end of file diff --git a/PERFORMANCE_OPTIMIZATION_GUIDE.md b/PERFORMANCE_OPTIMIZATION_GUIDE.md new file mode 100644 index 0000000..389fc76 --- /dev/null +++ b/PERFORMANCE_OPTIMIZATION_GUIDE.md @@ -0,0 +1,280 @@ +# 数据库性能优化指南 + +## 问题分析 + +根据对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 + +``` + +**修改后**: +```xml + +``` + +对其他使用 `SELECT *` 的查询也进行类似修改。 + +#### 1.2 优化复杂的JOIN查询 + +**修改建议** (UsersMapper.xml): +- 将复杂的嵌套条件拆分为更简单的部分 +- 使用子查询代替部分复杂JOIN +- 考虑使用临时表缓存中间结果 + +#### 1.3 添加分页查询 + +**修改前**: +```xml + +``` + +**修改后**: +```xml + +``` + +同时在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 + + + + + + + +``` + +#### 3.2 在Service层添加本地缓存 + +在Service类中使用Guava Cache或Caffeine等本地缓存库。 + +### 4. 优化结果集映射 + +#### 4.1 优化ResultMap定义 + +确保ResultMap只映射必要的字段,避免不必要的映射。 + +#### 4.2 使用Constructor Args映射 + +对于频繁使用的DTO,可以考虑使用构造函数参数映射,提高性能。 + +## 具体优化实施 + +### 优化ManagersMapper.xml + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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} + ) + + + + + UPDATE managers + SET + managercompany = #{managercompany}, + managerdepartment = #{managerdepartment}, + organization = #{organization}, + role = #{role}, + userName = #{userName}, + assistant = #{assistant}, + updated_at = #{updated_at} + WHERE id = #{id} + + +``` + +### 优化UsersMapper接口添加分页方法 + +在UsersMapper.java中添加分页查询方法: + +```java +// 分页查询授权客户 +List getAuthorizedCustomersWithPagination(Map params); + +// 获取授权客户总数 +int getAuthorizedCustomersCount(Map 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数据库进行缓存。 + +--- + +此优化指南由自动化工具生成,建议根据实际情况进行调整和测试。 \ No newline at end of file diff --git a/TOMCAT_DEPLOYMENT_STEPS.txt b/TOMCAT_DEPLOYMENT_STEPS.txt new file mode 100644 index 0000000..dfc4365 --- /dev/null +++ b/TOMCAT_DEPLOYMENT_STEPS.txt @@ -0,0 +1,118 @@ +# 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 + + + + +# 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: ` 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 \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..bd8896b --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/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-,maven-mvnd--}/ +[ -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 "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..92450f9 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : 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-,maven-mvnd--}/ +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" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..74a8a9b --- /dev/null +++ b/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.4.10 + + + com.example + web + 0.0.1-SNAPSHOT + war + web + web + + + 17 + + + + + org.springframework.boot + spring-boot-starter-web + + + me.paulschwarz + spring-dotenv + 4.0.0 + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + com.mysql + mysql-connector-j + runtime + + + + org.projectlombok + lombok + 1.18.24 + true + + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 3.0.4 + + + + + com.alibaba + druid-spring-boot-starter + 1.2.20 + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.1.0 + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + me.paulschwarz + spring-dotenv + 4.0.0 + + + + org.hibernate.validator + hibernate-validator + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-websocket + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + 3.4.10 + + + + org.projectlombok + lombok + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/example/web/ServletInitializer.java b/src/main/java/com/example/web/ServletInitializer.java new file mode 100644 index 0000000..d1354ab --- /dev/null +++ b/src/main/java/com/example/web/ServletInitializer.java @@ -0,0 +1,13 @@ +package com.example.web; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(WebApplication.class); + } + +} diff --git a/src/main/java/com/example/web/WebApplication.java b/src/main/java/com/example/web/WebApplication.java new file mode 100644 index 0000000..67d0c8a --- /dev/null +++ b/src/main/java/com/example/web/WebApplication.java @@ -0,0 +1,18 @@ +package com.example.web; + +import me.paulschwarz.springdotenv.DotenvPropertySource; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@MapperScan("com.example.web.mapper") +@EnableScheduling//定时任务 +/*@PropertySource(value = "classpath:.env", factory = DotenvPropertySource.class)*/ +public class WebApplication { + public static void main(String[] args) { + SpringApplication.run(WebApplication.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/annotation/DataSource.java b/src/main/java/com/example/web/annotation/DataSource.java new file mode 100644 index 0000000..c34eaa9 --- /dev/null +++ b/src/main/java/com/example/web/annotation/DataSource.java @@ -0,0 +1,14 @@ +// DataSource.java +package com.example.web.annotation; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataSource { + // 数据源名称,对应配置中的"primary"和"wechat" + String value() default "primary"; + + +} \ No newline at end of file diff --git a/src/main/java/com/example/web/aspect/DataSourceAspect.java b/src/main/java/com/example/web/aspect/DataSourceAspect.java new file mode 100644 index 0000000..ac2275e --- /dev/null +++ b/src/main/java/com/example/web/aspect/DataSourceAspect.java @@ -0,0 +1,50 @@ +package com.example.web.aspect; + +import com.example.web.annotation.DataSource; +import com.example.web.config.DynamicDataSource; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Aspect +@Component +@Order(1) // 设置Order为1,确保在事务切面之前执行 +public class DataSourceAspect { + + // 拦截所有标注了 @DataSource 注解的类或方法 + @Pointcut("@annotation(com.example.web.annotation.DataSource) || @within(com.example.web.annotation.DataSource)") + public void dataSourcePointCut() {} + + @Around("dataSourcePointCut()") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + // 获取目标方法和类上的 @DataSource 注解 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + DataSource methodAnnotation = method.getAnnotation(DataSource.class); + DataSource classAnnotation = AnnotationUtils.findAnnotation(joinPoint.getTarget().getClass(), DataSource.class); + + // 方法注解优先于类注解 + String dataSourceKey = methodAnnotation != null ? methodAnnotation.value() : + (classAnnotation != null ? classAnnotation.value() : "primary"); + + try { + // 切换数据源 + DynamicDataSource.setDataSourceKey(dataSourceKey); + // 执行目标方法 + System.out.println("切换到数据源: " + dataSourceKey); + return joinPoint.proceed(); + } finally { + // 清除数据源,避免线程池复用导致的问题 + DynamicDataSource.clearDataSourceKey(); + } + + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/web/config/DataSourceConfig.java b/src/main/java/com/example/web/config/DataSourceConfig.java new file mode 100644 index 0000000..d2f95d8 --- /dev/null +++ b/src/main/java/com/example/web/config/DataSourceConfig.java @@ -0,0 +1,42 @@ +package com.example.web.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class DataSourceConfig { + + // 主数据源(userlogin) + @Bean(name = "primaryDataSource") + @ConfigurationProperties(prefix = "spring.datasource.primary") + public DataSource primaryDataSource() { + return DataSourceBuilder.create().build(); + } + + // 第二个数据源(wechat_app) + @Bean(name = "wechatDataSource") + @ConfigurationProperties(prefix = "spring.datasource.wechat") + public DataSource wechatDataSource() { + return DataSourceBuilder.create().build(); + } + + // 动态数据源配置 + @Primary + @Bean(name = "dynamicDataSource") + public DataSource dynamicDataSource() { + DynamicDataSource dynamicDataSource = new DynamicDataSource(); + dynamicDataSource.setDefaultTargetDataSource(primaryDataSource()); + Map dataSources = new HashMap<>(); + dataSources.put("primary", primaryDataSource()); + dataSources.put("wechat", wechatDataSource()); + dynamicDataSource.setTargetDataSources(dataSources); + return dynamicDataSource; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/config/DataSourceContextHolder.java b/src/main/java/com/example/web/config/DataSourceContextHolder.java new file mode 100644 index 0000000..c96bc3b --- /dev/null +++ b/src/main/java/com/example/web/config/DataSourceContextHolder.java @@ -0,0 +1,33 @@ +package com.example.web.config; + +import org.springframework.stereotype.Component; + +/** + * 数据源上下文持有者 + */ +@Component +public class DataSourceContextHolder { + + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源 + */ + public static void setDataSource(String dataSource) { + CONTEXT_HOLDER.set(dataSource); + } + + /** + * 获取数据源 + */ + public static String getDataSource() { + return CONTEXT_HOLDER.get(); + } + + /** + * 清除数据源 + */ + public static void clearDataSource() { + CONTEXT_HOLDER.remove(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/config/DynamicDataSource.java b/src/main/java/com/example/web/config/DynamicDataSource.java new file mode 100644 index 0000000..2def525 --- /dev/null +++ b/src/main/java/com/example/web/config/DynamicDataSource.java @@ -0,0 +1,26 @@ +// DynamicDataSource.java +package com.example.web.config; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +public class DynamicDataSource extends AbstractRoutingDataSource { + + // 线程本地变量:存储当前线程使用的数据源名称(保证线程安全) + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + // 设置数据源(供AOP切面调用,切换数据源) + public static void setDataSourceKey(String dataSourceKey) { + contextHolder.set(dataSourceKey); + } + + // 清除数据源(线程结束时调用,避免线程池复用导致的数据源混乱) + public static void clearDataSourceKey() { + contextHolder.remove(); + } + + // 路由方法:返回当前线程的数据源名称(Spring会根据此方法选择对应的数据源) + @Override + protected Object determineCurrentLookupKey() { + return contextHolder.get(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/config/MyBatisConfig.java b/src/main/java/com/example/web/config/MyBatisConfig.java new file mode 100644 index 0000000..150b89f --- /dev/null +++ b/src/main/java/com/example/web/config/MyBatisConfig.java @@ -0,0 +1,40 @@ +package com.example.web.config; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; + +import javax.sql.DataSource; + +@Configuration +@MapperScan("com.example.web.mapper") +public class MyBatisConfig { + + private final DataSource dynamicDataSource; + + public MyBatisConfig(DataSource dynamicDataSource) { + this.dynamicDataSource = dynamicDataSource; + } + + @Bean + public SqlSessionFactory sqlSessionFactory() throws Exception { + SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); + // 设置动态数据源 + factoryBean.setDataSource(dynamicDataSource); + // 设置Mapper XML文件路径 + factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources("classpath:mapper/*.xml")); + return factoryBean.getObject(); + } + + @Bean + public PlatformTransactionManager transactionManager() { + // 使用动态数据源作为事务管理器的数据源 + return new DataSourceTransactionManager(dynamicDataSource); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/config/TomcatCompatibilityConfig.java b/src/main/java/com/example/web/config/TomcatCompatibilityConfig.java new file mode 100644 index 0000000..e45c3a7 --- /dev/null +++ b/src/main/java/com/example/web/config/TomcatCompatibilityConfig.java @@ -0,0 +1,38 @@ +package com.example.web.config; + +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; + +/** + * Tomcat 10兼容性配置类 + * 提供Tomcat 10兼容性支持,避免过滤器重复注册问题 + */ +@Configuration +public class TomcatCompatibilityConfig { + + /** + * 配置Servlet上下文参数,确保Tomcat 10兼容性 + */ + @Bean + @ConditionalOnMissingBean + public ServletContextInitializer servletContextInitializer() { + return new ServletContextInitializer() { + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + // 允许反斜杠处理 + servletContext.setInitParameter("org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH", "true"); + // 禁用严格的Servlet合规性检查 + servletContext.setInitParameter("org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "false"); + // 允许编码斜杠 + servletContext.setInitParameter("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); + // 添加multipart配置 + servletContext.setInitParameter("multipart.enabled", "true"); + } + }; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/config/WebConfig.java b/src/main/java/com/example/web/config/WebConfig.java new file mode 100644 index 0000000..07fe6a9 --- /dev/null +++ b/src/main/java/com/example/web/config/WebConfig.java @@ -0,0 +1,38 @@ +package com.example.web.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Arrays; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") // 允许所有域名访问,生产环境建议指定具体域名 + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(false) + .maxAge(3600); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList("*")); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(false); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/CustomerController.java b/src/main/java/com/example/web/controller/CustomerController.java new file mode 100644 index 0000000..3edc25a --- /dev/null +++ b/src/main/java/com/example/web/controller/CustomerController.java @@ -0,0 +1,932 @@ +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.UsersManagementsMapper; +import com.example.web.service.CustomerService; +import com.example.web.service.PoolCustomerService; +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; + +/** + * 销售端客户管理控制器 + * 主要负责销售员的客户查询、更新、权限验证等操作 + * 路由前缀:/pool + */ +@RestController +@RequestMapping("/pool") +@CrossOrigin(origins = "*") +public class CustomerController { + // 核心业务服务注入 + @Autowired + private CustomerService customerService; + + @Autowired + private PoolCustomerService poolCustomerService; + + @Autowired + private UsersManagementsMapper usersManagementsMapper; + + + /** + * 根据公司ID查询客户详情 - 优先处理wechat数据源 + * GET /pool/customers/{id} + */ + @GetMapping("/customers/{id}") + public ResponseEntity> getById(@PathVariable String id, HttpServletRequest request) { + System.out.println("===================================================="); + System.out.println("🔍 查询客户: " + id); + System.out.println("===================================================="); + + Map 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 = poolCustomerService.getPublicSeaCustomerInfo(id); + + if (userInfo != null) { + System.out.println("✅ 在wechat数据源中找到客户基础信息"); + + // 使用统一的公海池判断逻辑 + boolean isPublicSea = poolCustomerService.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); + return ResponseEntity.ok(response); + } + + // 如果不是公海池客户,检查权限并返回wechat数据源的非公海池客户 + System.out.println("📊 处理 WECHAT 数据源非公海池客户..."); + boolean hasPermission = customerService.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); + return ResponseEntity.ok(response); + } else { + System.out.println("❌ 无权限查看wechat数据源的非公海池客户"); + // 继续查询默认数据源 + } + } else { + System.out.println("ℹ️ 在wechat数据源中未找到客户"); + } + + // 2. 如果wechat数据源没找到或无权限,再尝试查询默认数据源 + System.out.println("📊 查询 DEFAULT 数据源..."); + UnifiedCustomerDTO defaultCustomer = customerService.getCustomerById(id, authInfo); + + if (defaultCustomer != null) { + System.out.println("✅ 在默认数据源中找到客户"); + response.put("success", true); + response.put("data", defaultCustomer); + 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("❌ 部门信息为空"); + return null; + } + + // 智能判断实际端类型:根据部门信息自动调整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.CartItem findSelectedCartItem(UserProductCartDTO userInfo, String targetCartItemId) { + if (userInfo.getCartItems() == null || userInfo.getCartItems().isEmpty()) { + return null; + } + + // 🔥 关键修复:优先使用前端传递的选中购物车项ID + if (targetCartItemId != null && !targetCartItemId.trim().isEmpty()) { + for (UserProductCartDTO.CartItem cartItem : userInfo.getCartItems()) { + if (targetCartItemId.equals(cartItem.getCartItemId())) { + System.out.println("✅ 找到前端选中的购物车项: " + cartItem.getCartItemId()); + return cartItem; + } + } + System.out.println("⚠️ 未找到前端指定的购物车项: " + targetCartItemId); + } + + // 🔥 其次检查是否有标记为选中的购物车项 + for (UserProductCartDTO.CartItem cartItem : userInfo.getCartItems()) { + if (isCartItemSelected(cartItem)) { + System.out.println("✅ 找到标记为选中的购物车项: " + cartItem.getCartItemId()); + return cartItem; + } + } + + // 🔥 最后才返回第一个购物车项 + UserProductCartDTO.CartItem firstItem = userInfo.getCartItems().get(0); + System.out.println("🔄 使用第一个购物车项作为默认: " + firstItem.getCartItemId()); + return firstItem; + } + + /** + * 判断购物车项是否被选中(根据业务逻辑) + */ + private boolean isCartItemSelected(UserProductCartDTO.CartItem cartItem) { + // 这里可以根据业务需求实现选中逻辑 + // 例如:通过某个字段标识,或者外部传入的参数 + + // 临时方案:默认返回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()); + + // 关键:设置数据源标识为wechat + dto.setDataSource("wechat"); + + // 设置联系人信息 - 从usersContacts获取 + setContactInfoSafely(dto, userInfo); + + // 设置负责人信息 + setManagerInfoSafely(dto, userInfo.getUserId()); + + // 设置公海需求信息 - 从购物车表获取(销售端) + if (userInfo.getCartItems() != null && !userInfo.getCartItems().isEmpty()) { + // 🔥 关键修复:从HttpServletRequest中获取目标购物车项ID + String targetCartItemId = getTargetCartItemIdFromRequest(request); + + UserProductCartDTO.CartItem selectedCartItem = findSelectedCartItem(userInfo, targetCartItemId); + + // 设置到单个字段 - 使用选中的购物车项 + dto.setProductName(selectedCartItem.getProductName() != null ? selectedCartItem.getProductName() : ""); + dto.setSpecification(selectedCartItem.getSpecification() != null ? selectedCartItem.getSpecification() : ""); + dto.setQuantity(selectedCartItem.getQuantity() != null ? selectedCartItem.getQuantity() : 0); + dto.setGrossWeight(selectedCartItem.getGrossWeight() != null ? selectedCartItem.getGrossWeight() : ""); + dto.setYolk(selectedCartItem.getYolk() != null ? selectedCartItem.getYolk() : ""); + + // 🔥 关键修改:设置选中的购物车项ID + dto.setTargetCartItemId(selectedCartItem.getCartItemId() != null ? selectedCartItem.getCartItemId() : ""); + + // 设置customDetails为购物车信息 + dto.setCustomDetails(userInfo.getCartItems().toArray()); + + System.out.println("✅ 成功设置选中购物车项到DTO: " + selectedCartItem.getProductName() + + ", 购物车项ID: " + selectedCartItem.getCartItemId() + + ", 是否前端指定: " + (targetCartItemId != null)); + } else { + // 如果没有购物车信息,设置默认值 + setDefaultProductValues(dto); + System.out.println("⚠️ 没有购物车信息,使用默认值"); + } + + // 设置 cartItems 到 UnifiedCustomerDTO - 销售端关键修复 + if (userInfo.getCartItems() != null && !userInfo.getCartItems().isEmpty()) { + List unifiedCartItems = userInfo.getCartItems().stream() + .map(cartItem -> { + UnifiedCustomerDTO.CartItem unifiedItem = new UnifiedCustomerDTO.CartItem(); + unifiedItem.setCartItemId(cartItem.getCartItemId() != null ? cartItem.getCartItemId() : ""); + unifiedItem.setProductId(cartItem.getProductId() != null ? cartItem.getProductId() : ""); + unifiedItem.setProductName(cartItem.getProductName() != null ? cartItem.getProductName() : ""); + unifiedItem.setSpecification(cartItem.getSpecification() != null ? cartItem.getSpecification() : ""); + unifiedItem.setQuantity(cartItem.getQuantity() != null ? cartItem.getQuantity() : 0); + unifiedItem.setGrossWeight(cartItem.getGrossWeight() != null ? cartItem.getGrossWeight() : ""); + unifiedItem.setYolk(cartItem.getYolk() != null ? cartItem.getYolk() : ""); + return unifiedItem; + }) + .collect(Collectors.toList()); + dto.setCartItems(unifiedCartItems); + System.out.println("✅ 成功设置 cartItems 到 UnifiedCustomerDTO,数量: " + unifiedCartItems.size()); + } else { + System.out.println("❌ 没有购物车数据可设置到 cartItems"); + } + + return dto; + } + + /** + * 从请求参数中获取目标购物车项ID - 完整实现 + */ + private String getTargetCartItemIdFromRequest() { + try { + // 从当前线程的HttpServletRequest中获取请求参数 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes != null) { + HttpServletRequest request = attributes.getRequest(); + + // 从URL查询参数中获取targetCartItemId + String targetCartItemId = request.getParameter("targetCartItemId"); + + if (targetCartItemId != null && !targetCartItemId.trim().isEmpty()) { + System.out.println("🎯 从请求参数获取到目标购物车项ID: " + targetCartItemId); + return targetCartItemId.trim(); + } + + // 如果URL参数中没有,尝试从请求属性中获取(前端可能通过其他方式设置) + Object attributeValue = request.getAttribute("targetCartItemId"); + if (attributeValue != null) { + String cartItemId = attributeValue.toString(); + System.out.println("🎯 从请求属性获取到目标购物车项ID: " + cartItemId); + return cartItemId; + } + } + + System.out.println("⚠️ 未从请求中获取到目标购物车项ID"); + return null; + + } catch (Exception e) { + System.err.println("❌ 获取目标购物车项ID失败: " + e.getMessage()); + return null; + } + } + + /** + * 重载方法:从指定的HttpServletRequest中获取目标购物车项ID + */ + private String getTargetCartItemIdFromRequest(HttpServletRequest request) { + try { + if (request == null) { + System.out.println("⚠️ HttpServletRequest为null"); + return null; + } + + // 1. 首先从URL查询参数中获取 + String targetCartItemId = request.getParameter("targetCartItemId"); + + if (targetCartItemId != null && !targetCartItemId.trim().isEmpty()) { + System.out.println("🎯 从URL参数获取到目标购物车项ID: " + targetCartItemId); + return targetCartItemId.trim(); + } + + // 2. 如果URL参数中没有,尝试从请求属性中获取 + Object attributeValue = request.getAttribute("targetCartItemId"); + if (attributeValue != null) { + String cartItemId = attributeValue.toString(); + System.out.println("🎯 从请求属性获取到目标购物车项ID: " + cartItemId); + return cartItemId; + } + + // 3. 尝试从Header中获取 + String headerValue = request.getHeader("X-Target-CartItem-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 = usersManagementsMapper.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> getByPhone(@RequestParam String phoneNumber, HttpServletRequest request) { + System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + System.out.println("getByPhone: ---------------------------------------------------" + phoneNumber); + Map response = new HashMap<>(); + try { + //🔥 修复:从URL参数获取认证信息 + ManagerAuthInfo authInfo = getManagerAuthInfoFromRequest(request, false); + + if (authInfo == null) { + response.put("success", false); + response.put("message", "用户未登录或认证信息缺失"); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response); + } + + UnifiedCustomerDTO customer = customerService.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); + } + } + + /** + * 更新基于电话号码的客户信息(专门用于公海池客户)- 销售员权限:只允许更新为buyer和both类型 + */ + @PutMapping("/phone-customers/update") + public ResponseEntity> updatePhoneCustomer(@RequestBody UnifiedCustomerDTO updatedDTO, HttpServletRequest request) { + Map 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()); + + // 销售员权限:校验客户类型,只允许buyer和both + if (!"buyer".equals(updatedDTO.getType()) && !"both".equals(updatedDTO.getType())) { + System.out.println("❌ 权限校验失败:销售员只能更新为buyer或both类型,当前类型: " + updatedDTO.getType()); + response.put("success", false); + response.put("message", "销售员权限只能更新为客户端类型客户"); + return ResponseEntity.badRequest().body(response); + } + + // 调用服务层 - 传递authInfo用于负责人信息更新 + System.out.println("🔄 调用Service更新方法..."); + boolean success = customerService.updatePhoneBasedCustomer(updatedDTO, authInfo); + + System.out.println("✅ Service返回结果: " + success); + + if (success) { + System.out.println("🎉 公海池客户信息更新成功"); + response.put("success", true); + response.put("message", "客户信息更新成功"); + 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 "buyer"; + } + + switch (originalType.toUpperCase()) { + case "BOTH": + return "both"; + case "客户端": + case "BUYER": + return "buyer"; + case "供应端": + case "SELLER": + return "seller"; + default: + return originalType; + } + } + + @PutMapping("/customers/update") + public ResponseEntity> updateCustomer( + @RequestBody UnifiedCustomerDTO updatedDTO, HttpServletRequest request) { + + Map 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); + } + + // 关键修复:添加客户类型转换逻辑(之前遗漏) + 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("客户类型是否有效: " + ("buyer".equals(updatedDTO.getType()) || "both".equals(updatedDTO.getType()))); + System.out.println("========================================="); + + // 验证数据源 + String dataSource = updatedDTO.getDataSource(); + if (dataSource == null || dataSource.trim().isEmpty()) { + System.out.println("❌ 数据源标识不能为空,请明确指定数据源(wechat或default)"); + response.put("success", false); + response.put("message", "数据源标识不能为空,请明确指定数据源(wechat或default)"); + return ResponseEntity.badRequest().body(response); + } + + System.out.println("🔄 使用验证后的数据源: " + dataSource); + + // 从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("🔐 ====== 开始权限校验 ======"); + System.out.println("🔐 当前类型: " + updatedDTO.getType()); + if (!"buyer".equals(updatedDTO.getType()) && !"both".equals(updatedDTO.getType())) { + System.out.println("❌ 权限校验失败:销售员只能更新为buyer或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("更新微信数据源时手机号不能为空"); + } + success = customerService.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 = customerService.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 = customerService.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 = customerService.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 = customerService.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 = customerService.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); + } + + response.put("success", true); + response.put("message", "客户信息更新成功"); + 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) { + 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); + + // 如果已经是wechat数据源,直接返回 + if ("wechat".equals(dataSource)) { + System.out.println("✅ 数据源验证通过:wechat"); + return dataSource; + } + + // 如果指定了default数据源,但客户实际上在wechat数据源中 + if ("default".equals(dataSource)) { + try { + // 优先通过手机号检查wechat数据源 + if (phoneNumber != null && !phoneNumber.trim().isEmpty()) { + System.out.println("🔍 通过手机号检查wechat数据源: " + phoneNumber); + UserProductCartDTO wechatCustomer = poolCustomerService.getWechatCustomerByPhone(phoneNumber); + if (wechatCustomer != null) { + System.out.println("🎯 检测到客户存在于wechat数据源(通过手机号),自动修正数据源"); + return "wechat"; + } + } + + // 其次通过客户ID检查wechat数据源 + if (customerId != null && !customerId.trim().isEmpty()) { + System.out.println("🔍 通过客户ID检查wechat数据源: " + customerId); + UserProductCartDTO wechatCustomer = poolCustomerService.getPublicSeaCustomerInfo(customerId); + if (wechatCustomer != null) { + System.out.println("🎯 检测到客户存在于wechat数据源(通过客户ID),自动修正数据源"); + return "wechat"; + } + } + + // 如果wechat数据源都没找到,保持default数据源 + System.out.println("✅ 数据源验证通过:default(客户确实在default数据源中)"); + return dataSource; + + } catch (Exception e) { + System.err.println("❌ 数据源验证失败: " + e.getMessage()); + // 发生异常时保持原数据源 + return dataSource; + } + } + + // 如果是不支持的数据源,保持原样(后续会报错) + System.out.println("⚠️ 不支持的数据源类型: " + dataSource); + return dataSource; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/EnterpriseController.java b/src/main/java/com/example/web/controller/EnterpriseController.java new file mode 100644 index 0000000..3974e24 --- /dev/null +++ b/src/main/java/com/example/web/controller/EnterpriseController.java @@ -0,0 +1,1168 @@ +package com.example.web.controller; + +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.entity.UsersManagements; +import com.example.web.mapper.UsersManagementsMapper; +import com.example.web.service.CustomerService; +import com.example.web.service.EnterpriseService; +import com.example.web.service.PoolCustomerService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +/** + * 企业客户管理控制器 + * 负责企业客户的全生命周期管理,支持双数据源融合查询 + * 路由前缀:/pool + */ +@RestController +@RequestMapping("/pool") +@CrossOrigin(origins = "*") +public class EnterpriseController { + + @Autowired + private EnterpriseService enterpriseService; + @Autowired + private PoolCustomerService poolCustomerService; + @Autowired + private CustomerService customerService; + @Autowired + private UsersManagementsMapper usersManagementsMapper; + + // 修改类型转换方法 - 前端类型转数据库类型 + 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; + } + } + + // 添加完整的类型转换方法 + private Map convertTypesForFrontend(Map response) { + if (response.containsKey("data")) { + Object data = response.get("data"); + if (data instanceof Map) { + Map dataMap = (Map) data; + for (UnifiedCustomerDTO dto : dataMap.values()) { + String frontendType = convertDatabaseTypeToFrontend(dto.getType()); + dto.setType(frontendType); + // 添加数据源标识 + dto.setDataSource("wechat"); + } + } else if (data instanceof List) { + List dataList = (List) data; + for (Object item : dataList) { + if (item instanceof UnifiedCustomerDTO) { + UnifiedCustomerDTO dto = (UnifiedCustomerDTO) item; + String frontendType = convertDatabaseTypeToFrontend(dto.getType()); + dto.setType(frontendType); + dto.setDataSource("wechat"); + } else if (item instanceof EnterpriseInfoDTO) { + EnterpriseInfoDTO enterpriseInfo = (EnterpriseInfoDTO) item; + if (enterpriseInfo.getEnterprise() != null) { + String frontendType = convertDatabaseTypeToFrontend(enterpriseInfo.getEnterprise().getType()); + enterpriseInfo.getEnterprise().setType(frontendType); + } + } + } + } else if (data instanceof UnifiedCustomerDTO) { + UnifiedCustomerDTO dto = (UnifiedCustomerDTO) data; + String frontendType = convertDatabaseTypeToFrontend(dto.getType()); + dto.setType(frontendType); + dto.setDataSource("wechat"); + } else if (data instanceof EnterpriseInfoDTO) { + EnterpriseInfoDTO enterpriseInfo = (EnterpriseInfoDTO) data; + if (enterpriseInfo.getEnterprise() != null) { + String frontendType = convertDatabaseTypeToFrontend(enterpriseInfo.getEnterprise().getType()); + enterpriseInfo.getEnterprise().setType(frontendType); + } + } + } + return response; + } + + // 新增:电话号码重复检查接口(供前端预校验) + @GetMapping("/customers/check-phone") + public ResponseEntity> checkPhoneNumber(@RequestParam String phoneNumber) { + System.out.println("电话号码重复检查接口(双数据源):------------"+phoneNumber); + Map response = new HashMap<>(); + + // 1. 非空校验 + if (!StringUtils.hasText(phoneNumber)) { + response.put("success", false); + response.put("message", "电话号码不能为空"); + response.put("isDuplicate", false); + return ResponseEntity.badRequest().body(response); + } + + // 2. 格式校验(简单示例:11位数字) + if (!phoneNumber.matches("^1[3-9]\\d{9}$")) { + response.put("success", false); + response.put("message", "请输入有效的11位手机号码"); + response.put("isDuplicate", false); + return ResponseEntity.badRequest().body(response); + } + + // 3. 重复校验 - 现在检查双数据源 + boolean isDuplicate = enterpriseService.isPhoneNumberDuplicate(phoneNumber); + response.put("success", true); + response.put("isDuplicate", isDuplicate); + response.put("message", isDuplicate ? "电话号码已存在" : "电话号码可用"); + return ResponseEntity.ok(response); + } + + // 新增:处理前端提交的客户数据(保存到默认数据源) + @PostMapping("/inputcustomers") + public ResponseEntity> addCustomer(@Valid UnifiedCustomerDTO dto, BindingResult result) { + System.out.println("新增客户信息:------------"); + System.out.println("原始类型: " + dto.getType()); + System.out.println(dto.getPhoneNumber()+"----"+dto.getCreated_at()+"----"+dto.getNickName()+"----"+dto.getUserName()); + Map response = new HashMap<>(); + + // 1. 参数校验(包括手机号非空) + if (result.hasErrors()) { + response.put("success", false); + response.put("message", "参数错误:" + result.getFieldError().getDefaultMessage()); + return ResponseEntity.badRequest().body(response); + } + + + // 在检查重复之前转换类型 + String databaseType = convertFrontendTypeToDatabase(dto.getType()); + dto.setType(databaseType); + System.out.println("转换后类型: " + dto.getType()); + + // 销售端权限:校验客户类型,只允许buyer和both + System.out.println("权限校验 - 当前类型: " + dto.getType() + ", 是否buyer: " + "buyer".equals(dto.getType()) + ", 是否both: " + "both".equals(dto.getType())); + + if (!"buyer".equals(dto.getType()) && !"both".equals(dto.getType())) { + response.put("success", false); + response.put("message", "销售端权限只能新增客户端类型客户和BOTH类型客户"); + return ResponseEntity.badRequest().body(response); + } + + // 2. 检查电话号码是否重复(同时检查双数据源) + if (enterpriseService.isPhoneNumberDuplicate(dto.getPhoneNumber())) { + response.put("success", false); + response.put("message", "电话号码已存在,添加失败"); + response.put("isDuplicate", true); + return ResponseEntity.badRequest().body(response); + } + + // 3. 执行新增逻辑 + try { + boolean success = enterpriseService.addCustomer(dto); + if (success) { + response.put("success", true); + response.put("message", "新增客户成功"); + return ResponseEntity.ok(response); + } else { + response.put("success", false); + response.put("message", "新增客户失败"); + return ResponseEntity.internalServerError().body(response); + } + } catch (Exception e) { + response.put("success", false); + response.put("message", "服务器错误:" + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 获取所有客户完整信息 - 融合双数据查询 + 负责人过滤 + * GET /pool/customers + */ + @ResponseBody + @GetMapping("/customers") + public ResponseEntity> getAllCustomers(HttpServletRequest request) { + System.out.println("===================================================="); + System.out.println("🔄 开始获取所有客户信息 - 双数据源查询 + 负责人过滤"); + System.out.println("===================================================="); + + Map 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()); + System.out.println("🔐 负责人过滤条件: " + + "公司=" + authInfo.getManagercompany() + + ", 部门=" + authInfo.getManagerdepartment()); + + // 1. 创建结果存储容器,优先使用手机号作为键 + Map resultMap = new HashMap<>(); + + // 2. 处理primary数据源(企业信息)- 添加负责人过滤 + System.out.println("🏢 开始获取企业数据..."); + List enterpriseData = enterpriseService.getAllEnterpriseInfo(); + + // 应用负责人过滤 + List filteredEnterpriseData = enterpriseData.stream() + .filter(enterpriseInfo -> hasManagerPermission(enterpriseInfo, authInfo)) + .collect(Collectors.toList()); + + long enterpriseEndTime = System.currentTimeMillis(); + + System.out.println("✅ 企业数据查询完成"); + System.out.println("📊 企业数据条数: " + enterpriseData.size() + " → 过滤后: " + filteredEnterpriseData.size()); + + int enterpriseCount = 0; + if (filteredEnterpriseData != null) { + for (EnterpriseInfoDTO enterpriseInfo : filteredEnterpriseData) { + try { + UnifiedCustomerDTO dto = convertToUnifiedDTOFromEnterprise(enterpriseInfo); + String key = dto.getPhoneNumber() != null && !dto.getPhoneNumber().isEmpty() + ? dto.getPhoneNumber() + : dto.getId(); + + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无有效标识的企业数据"); + continue; + } + + resultMap.put(key, dto); + enterpriseCount++; + + // 打印前几条数据的详细信息 + if (enterpriseCount <= 3) { + System.out.println("📝 企业客户样例 " + enterpriseCount + ": " + + "公司=" + dto.getCompany() + + ", 联系人=" + dto.getNickName() + + ", 手机=" + dto.getPhoneNumber() + + ", 类型=" + dto.getType()); + } + } catch (Exception e) { + System.err.println("❌ 处理企业数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ 企业数据源成功处理: " + enterpriseCount + " 条记录"); + } + + // 3. 处理wechat数据源(用户信息)- 添加负责人过滤 + System.out.println("📱 开始获取wechat数据..."); + long wechatStartTime = System.currentTimeMillis(); + + List userData = poolCustomerService.getAllCustomers(authInfo); + +// 🔥 新增:在Controller层进行负责人过滤之前,先收集所有用户ID用于批量查询 + List allUserIds = new ArrayList<>(); + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + if (userInfo.getUserId() != null && !userInfo.getUserId().isEmpty()) { + allUserIds.add(userInfo.getUserId()); + } + } + } + +// 🔥 新增:批量查询所有用户的负责人信息 + Map managerMap = new HashMap<>(); + if (!allUserIds.isEmpty()) { + try { + List allManagers = usersManagementsMapper.findByUserIds(allUserIds); + 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()); + // 即使批量查询失败,也不影响后续逻辑,只是会使用默认值 + } + } + +// 在Controller层进行负责人过滤 + List filteredUserData = new ArrayList<>(); + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + try { + if (hasPermissionToViewWechatCustomer(userInfo, authInfo)) { + filteredUserData.add(userInfo); + } + } catch (Exception e) { + System.err.println("❌ 检查用户权限时出错: " + e.getMessage()); + } + } + } + + long wechatEndTime = System.currentTimeMillis(); + + System.out.println("✅ Wechat数据查询完成"); + System.out.println("📊 Wechat数据条数: " + userData.size() + " → 过滤后: " + filteredUserData.size()); + System.out.println("⏱️ Wechat查询耗时: " + (wechatEndTime - wechatStartTime) + "ms"); + + int wechatCount = 0; + int updateCount = 0; + if (filteredUserData != null) { + for (UserProductCartDTO userInfo : filteredUserData) { + try { + String key = userInfo.getPhoneNumber() != null ? userInfo.getPhoneNumber() : ""; + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无手机号的用户数据"); + continue; + } + + // 检查是否已存在该手机号的记录 + if (resultMap.containsKey(key)) { + // 更新现有记录(补充wechat数据源的信息) + UnifiedCustomerDTO existingDto = resultMap.get(key); + updateUnifiedDTOWithWechatData(existingDto, userInfo); + updateCount++; + } else { + // 🔥 修改:传入managerMap,避免N+1查询 + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo, managerMap); + resultMap.put(key, dto); + wechatCount++; + } + } catch (Exception e) { + System.err.println("❌ 处理用户数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ Wechat数据源成功处理: " + wechatCount + " 条新记录, " + updateCount + " 条更新记录"); + } + + // 4. 打印最终统计信息和详细信息 + printFinalStatistics(resultMap, enterpriseCount, wechatCount, updateCount); + + // 5. 构建响应 + response.put("success", true); + response.put("data", resultMap); + response.put("message", "获取客户信息成功"); + response.put("total", resultMap.size()); + response.put("enterpriseCount", enterpriseCount); + response.put("wechatCount", wechatCount); + response.put("updateCount", updateCount); + + return ResponseEntity.ok(convertTypesForFrontend(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); + } + } + + /** + * 从请求中获取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("❌ 部门信息为空"); + return null; + } + + // 智能判断实际端类型:根据部门信息自动调整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; + } + + /** + * 检查企业负责人权限 + */ + private boolean hasManagerPermission(EnterpriseInfoDTO enterpriseInfo, ManagerAuthInfo authInfo) { + if (enterpriseInfo.getManagers() == null) { + System.out.println("✅ 企业客户无负责人信息,允许查看"); + return true; + } + + return isManagerMatch(enterpriseInfo.getManagers(), authInfo); + } + + /** + * 检查负责人信息是否匹配(Managers) + */ + private boolean isManagerMatch(Managers manager, ManagerAuthInfo authInfo) { + boolean match = + (authInfo.getManagercompany() == null || authInfo.getManagercompany().equals(manager.getManagercompany())) && + (authInfo.getManagerdepartment() == null || authInfo.getManagerdepartment().equals(manager.getManagerdepartment())) && + (authInfo.getOrganization() == null || authInfo.getOrganization().equals(manager.getOrganization())) && + (authInfo.getRole() == null || authInfo.getRole().equals(manager.getRole())) && + (authInfo.getUserName() == null || authInfo.getUserName().equals(manager.getUserName())); + + System.out.println("🔐 企业负责人权限检查: " + (match ? "✅ 匹配" : "❌ 不匹配")); + return match; + } + + /** + * 检查是否有权限查看微信客户 - 修复版本 + */ + private boolean hasPermissionToViewWechatCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + try { + System.out.println("🔐 开始检查微信客户权限,用户ID: " + userInfo.getUserId()); + + // 公海池客户对所有人可见 + if (isPublicSeaCustomer(userInfo)) { + System.out.println("✅ 公海池客户,无需负责人权限验证"); + return true; + } + + // 非公海池客户需要检查负责人权限 + System.out.println("🔍 非公海池客户,检查负责人权限..."); + boolean hasPermission = customerService.hasPermissionToViewWechatCustomer(userInfo, authInfo); + System.out.println("🔐 微信客户权限检查结果: " + (hasPermission ? "✅ 有权限" : "❌ 无权限")); + return hasPermission; + } catch (Exception e) { + System.err.println("❌ 检查微信客户权限失败: " + e.getMessage()); + // 发生异常时,出于安全考虑返回false + return false; + } + } + + /** + * 判断是否为公海池客户 + */ + private boolean isPublicSeaCustomer(UserProductCartDTO userInfo) { + // 定义公海池等级 + java.util.Set publicSeaLevels = java.util.Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池"); + + // 检查等级是否为公海池 + boolean isPublicSeaLevel = publicSeaLevels.contains(userInfo.getLevel()); + + System.out.println("🔍 客户等级检查: " + userInfo.getLevel() + " → 是否公海池: " + isPublicSeaLevel); + + // 公海池客户:等级是公海池 + return isPublicSeaLevel; + } + + /** + * 从企业数据源转换DTO + */ + private UnifiedCustomerDTO convertToUnifiedDTOFromEnterprise(EnterpriseInfoDTO enterpriseInfo) { + UnifiedCustomerDTO dto = new UnifiedCustomerDTO(); + String companyId = enterpriseInfo.getEnterprise() != null ? enterpriseInfo.getEnterprise().getId() : ""; + + // 设置企业信息 + if (enterpriseInfo.getEnterprise() != null) { + dto.setId(enterpriseInfo.getEnterprise().getId() != null ? enterpriseInfo.getEnterprise().getId() : ""); + dto.setCompany(enterpriseInfo.getEnterprise().getCompany() != null ? enterpriseInfo.getEnterprise().getCompany() : ""); + dto.setRegion(enterpriseInfo.getEnterprise().getRegion() != null ? enterpriseInfo.getEnterprise().getRegion() : ""); + dto.setLevel(enterpriseInfo.getEnterprise().getLevel() != null ? enterpriseInfo.getEnterprise().getLevel() : ""); + String frontendType = convertDatabaseTypeToFrontend(enterpriseInfo.getEnterprise().getType()); + dto.setType(frontendType != null ? frontendType : ""); + dto.setDemand(enterpriseInfo.getEnterprise().getDemand() != null ? enterpriseInfo.getEnterprise().getDemand() : ""); + dto.setSpec(enterpriseInfo.getEnterprise().getSpec() != null ? enterpriseInfo.getEnterprise().getSpec() : ""); + } + + // 设置联系人信息 + if (enterpriseInfo.getContacts() != null) { + dto.setNickName(enterpriseInfo.getContacts().getNickName() != null ? enterpriseInfo.getContacts().getNickName() : ""); + dto.setPhoneNumber(enterpriseInfo.getContacts().getPhoneNumber() != null ? enterpriseInfo.getContacts().getPhoneNumber() : ""); + dto.setWechat(enterpriseInfo.getContacts().getWechat() != null ? enterpriseInfo.getContacts().getWechat() : ""); + dto.setAccount(enterpriseInfo.getContacts().getAccount() != null ? enterpriseInfo.getContacts().getAccount() : ""); + dto.setAccountNumber(enterpriseInfo.getContacts().getAccountNumber() != null ? enterpriseInfo.getContacts().getAccountNumber() : ""); + dto.setBank(enterpriseInfo.getContacts().getBank() != null ? enterpriseInfo.getContacts().getBank() : ""); + dto.setAddress(enterpriseInfo.getContacts().getAddress() != null ? enterpriseInfo.getContacts().getAddress() : ""); + dto.setCreated_at(enterpriseInfo.getContacts().getCreated_at()); + dto.setUpdated_at(enterpriseInfo.getContacts().getUpdated_at()); + } + + // 设置负责人信息(根据新的表结构) + if (enterpriseInfo.getManagers() != null) { + // 使用新的字段名 + dto.setManagercompany(enterpriseInfo.getManagers().getManagercompany() != null ? enterpriseInfo.getManagers().getManagercompany() : ""); + dto.setManagerdepartment(enterpriseInfo.getManagers().getManagerdepartment() != null ? enterpriseInfo.getManagers().getManagerdepartment() : ""); + dto.setOrganization(enterpriseInfo.getManagers().getOrganization() != null ? enterpriseInfo.getManagers().getOrganization() : ""); + dto.setRole(enterpriseInfo.getManagers().getRole() != null ? enterpriseInfo.getManagers().getRole() : ""); + dto.setUserName(enterpriseInfo.getManagers().getUserName() != null ? enterpriseInfo.getManagers().getUserName() : ""); + dto.setAssistant(enterpriseInfo.getManagers().getAssistant() != null ? enterpriseInfo.getManagers().getAssistant() : ""); + } + + // 设置数据源标识 + dto.setDataSource("enterprise"); + + return dto; + } + + /** + * 专门为公海池客户转换DTO的方法 - 补充负责人信息(优化版本) + */ + private UnifiedCustomerDTO convertToUnifiedDTOForPublicSea(UserProductCartDTO userInfo, Map managerMap) { + 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()); + + // 关键:设置数据源标识为wechat + dto.setDataSource("wechat"); + + // 设置联系人信息 - 从usersContacts获取 + setContactInfoSafely(dto, userInfo); + + // 🔥 修改:从批量查询的managerMap中获取负责人信息,避免N+1查询 + setManagerInfoFromMap(dto, userInfo.getUserId(), managerMap); + + // 设置公海需求信息 - 从购物车表获取(销售端) + if (userInfo.getCartItems() != null && !userInfo.getCartItems().isEmpty()) { + UserProductCartDTO.CartItem cartItem = userInfo.getCartItems().get(0); + dto.setProductName(cartItem.getProductName() != null ? cartItem.getProductName() : ""); + dto.setSpecification(cartItem.getSpecification() != null ? cartItem.getSpecification() : ""); + dto.setQuantity(cartItem.getQuantity() != null ? cartItem.getQuantity() : 0); + dto.setGrossWeight(cartItem.getGrossWeight() != null ? cartItem.getGrossWeight() : ""); + dto.setYolk(cartItem.getYolk() != null ? cartItem.getYolk() : ""); + + // 购物车没有品种字段,所以留空或设置默认值 + dto.setVariety(""); + + // 设置customDetails为购物车信息 + dto.setCustomDetails(userInfo.getCartItems().toArray()); + } else { + // 如果没有购物车信息,设置默认值 + setDefaultProductValues(dto); + } + + return dto; + } + + + /** + * 从批量查询的Map中设置负责人信息 - 避免N+1查询 + */ + private void setManagerInfoFromMap(UnifiedCustomerDTO dto, String userId, Map managerMap) { + try { + System.out.println("🔍 从批量Map中查询用户负责人信息,用户ID: " + userId); + UsersManagements userManager = managerMap.get(userId); + + if (userManager != null) { + System.out.println("✅ 从批量Map中找到负责人信息,设置到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("⚠️ 未在批量Map中找到负责人信息,用户可能为公海池客户"); + // 设置默认值 + setDefaultManagerValues(dto); + } + } catch (Exception e) { + System.err.println("❌ 从Map设置负责人信息失败: " + e.getMessage()); + // 发生异常时设置默认值 + setDefaultManagerValues(dto); + } + } + + + /** + * 设置默认负责人值 + */ + private void setDefaultManagerValues(UnifiedCustomerDTO dto) { + dto.setManagercompany(""); + dto.setManagerdepartment(""); + dto.setOrganization(""); + dto.setRole(""); + dto.setUserName(""); + dto.setAssistant(""); + } + + + /** + * 用wechat数据更新现有的DTO(用于数据合并) + */ + private void updateUnifiedDTOWithWechatData(UnifiedCustomerDTO existingDto, UserProductCartDTO userInfo) { + // 补充产品信息(如果企业数据中缺少) + if ((existingDto.getProductName() == null || existingDto.getProductName().isEmpty()) && + userInfo.getCartItems() != null && !userInfo.getCartItems().isEmpty()) { + + UserProductCartDTO.CartItem cartItem = userInfo.getCartItems().get(0); + existingDto.setProductName(cartItem.getProductName() != null ? cartItem.getProductName() : ""); + existingDto.setSpecification(cartItem.getSpecification() != null ? cartItem.getSpecification() : ""); + existingDto.setQuantity(cartItem.getQuantity() != null ? cartItem.getQuantity() : 0); + existingDto.setGrossWeight(cartItem.getGrossWeight() != null ? cartItem.getGrossWeight() : ""); + existingDto.setYolk(cartItem.getYolk() != null ? cartItem.getYolk() : ""); + + // 设置customDetails + existingDto.setCustomDetails(userInfo.getCartItems().toArray()); + } + + // 补充其他可能缺失的字段 + if (existingDto.getLevel() == null || existingDto.getLevel().isEmpty()) { + existingDto.setLevel(userInfo.getLevel() != null ? userInfo.getLevel() : "公海池"); + } + + // 标记为混合数据源 + existingDto.setDataSource("mixed"); + } + + /** + * 打印最终统计信息 + */ + private void printFinalStatistics(Map resultMap, int enterpriseCount, int wechatCount, int updateCount) { + System.out.println("===================================================="); + System.out.println("📈 数据源查询统计:"); + System.out.println("🏢 企业数据源: " + enterpriseCount + " 条记录"); + System.out.println("📱 Wechat数据源: " + (wechatCount + updateCount) + " 条记录 (" + wechatCount + " 新增, " + updateCount + " 更新)"); + System.out.println("📊 合并后总客户数: " + resultMap.size() + " 条记录"); + System.out.println("===================================================="); + + // 打印详细样例(限制数量避免日志过多) + int sampleCount = 0; + System.out.println("📋 最终数据样例:"); + for (Map.Entry entry : resultMap.entrySet()) { + if (sampleCount >= 1) break; + + String key = entry.getKey(); + UnifiedCustomerDTO dto = entry.getValue(); + + System.out.println("--- 客户 " + (sampleCount + 1) + " ---"); + System.out.println(" 键: " + key); + System.out.println(" 数据源: " + dto.getDataSource()); + System.out.println(" ID: " + dto.getId()); + System.out.println(" 公司: " + dto.getCompany()); + System.out.println(" 联系人: " + dto.getNickName()); + System.out.println(" 手机: " + dto.getPhoneNumber()); + System.out.println(" 微信号: " + dto.getWechat()); + System.out.println(" 类型: " + dto.getType()); + System.out.println(" 等级: " + dto.getLevel()); + System.out.println(" 地区: " + dto.getRegion()); + System.out.println(" 需求: " + dto.getDemand()); + System.out.println(" 规格: " + dto.getSpec()); + + // 产品信息 + System.out.println(" 产品名称: " + dto.getProductName()); + System.out.println(" 品种: " + dto.getVariety()); + System.out.println(" 规格说明: " + dto.getSpecification()); + System.out.println(" 数量: " + dto.getQuantity()); + System.out.println(" 毛重: " + dto.getGrossWeight()); + System.out.println(" 蛋黄: " + dto.getYolk()); + + // 联系人详细信息 + System.out.println(" 账户: " + dto.getAccount()); + System.out.println(" 账号: " + dto.getAccountNumber()); + System.out.println(" 银行: " + dto.getBank()); + System.out.println(" 地址: " + dto.getAddress()); + + // 负责人信息 + System.out.println(" 负责公司: " + dto.getManagercompany()); + System.out.println(" 负责部门: " + dto.getManagerdepartment()); + System.out.println(" 组织: " + dto.getOrganization()); + System.out.println(" 角色: " + dto.getRole()); + System.out.println(" 负责人: " + dto.getUserName()); + System.out.println(" 协助人: " + dto.getAssistant()); + + // 时间信息 + System.out.println(" 创建时间: " + dto.getCreated_at()); + System.out.println(" 更新时间: " + dto.getUpdated_at()); + + // 购物车信息 + if (dto.getCartItems() != null && !dto.getCartItems().isEmpty()) { + System.out.println(" 购物车项数量: " + dto.getCartItems().size()); + for (int i = 0; i < Math.min(dto.getCartItems().size(), 3); i++) { + UnifiedCustomerDTO.CartItem item = dto.getCartItems().get(i); + System.out.println(" 购物车项 " + (i + 1) + ":"); + System.out.println(" ID: " + item.getCartItemId()); + System.out.println(" 产品ID: " + item.getProductId()); + System.out.println(" 产品名称: " + item.getProductName()); + System.out.println(" 规格: " + item.getSpecification()); + System.out.println(" 数量: " + item.getQuantity()); + System.out.println(" 毛重: " + item.getGrossWeight()); + System.out.println(" 蛋黄: " + item.getYolk()); + } + } else { + System.out.println(" 购物车项: 无"); + } + + // 联系人列表信息 + if (dto.getContacts() != null && !dto.getContacts().isEmpty()) { + System.out.println(" 联系人数量: " + dto.getContacts().size()); + for (int i = 0; i < Math.min(dto.getContacts().size(), 3); i++) { + UnifiedCustomerDTO.ContactInfo contact = dto.getContacts().get(i); + System.out.println(" 联系人 " + (i + 1) + ":"); + System.out.println(" ID: " + contact.getContactId()); + System.out.println(" 微信: " + contact.getWechat()); + System.out.println(" 账户: " + contact.getAccount()); + System.out.println(" 账号: " + contact.getAccountNumber()); + System.out.println(" 银行: " + contact.getBank()); + System.out.println(" 地址: " + contact.getAddress()); + } + } else { + System.out.println(" 联系人列表: 无"); + } + + // 自定义详情 + if (dto.getCustomDetails() != null && dto.getCustomDetails().length > 0) { + System.out.println(" 自定义详情数量: " + dto.getCustomDetails().length); + } else { + System.out.println(" 自定义详情: 无"); + } + + sampleCount++; + System.out.println(); // 空行分隔 + } + System.out.println("===================================================="); + } + + // 保留原有的辅助方法 + 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()) { + List validContacts = userInfo.getUsersContacts().stream() + .filter(contact -> contact != null) + .collect(Collectors.toList()); + + if (!validContacts.isEmpty()) { + UserProductCartDTO.UsersContacts contact = validContacts.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); + } + } else { + setDefaultContactValues(dto); + } + } + + private void setDefaultContactValues(UnifiedCustomerDTO dto) { + dto.setWechat(""); + dto.setAccount(""); + dto.setAccountNumber(""); + dto.setBank(""); + dto.setAddress(""); + } + + /** + * 根据ID获取客户详细信息 + * GET /pool/customers/{id} + */ + @GetMapping("/{id}") + public ResponseEntity> getCustomerById(@PathVariable String id) { + Map response = new HashMap<>(); + try { + EnterpriseInfoDTO customer = enterpriseService.getEnterpriseInfoById(id); + if (customer != null) { + response.put("success", true); + response.put("data", customer); + response.put("message", "获取客户信息成功"); + return ResponseEntity.ok(response); + } else { + response.put("success", false); + response.put("message", "未找到对应的客户信息"); + return ResponseEntity.notFound().build(); + } + } catch (Exception e) { + response.put("success", false); + response.put("message", "获取客户信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 搜索客户信息 + * GET /pool/customers/search?keyword=xxx + */ + @GetMapping("/search") + public ResponseEntity> searchCustomers(@RequestParam(required = false) String keyword) { + Map response = new HashMap<>(); + try { + List customers = enterpriseService.searchEnterprises(keyword); + // 类型转换:将每个企业类型从数据库类型转换为前端类型 + for (EnterpriseInfoDTO customer : customers) { + if (customer.getEnterprise() != null) { + String frontendType = convertDatabaseTypeToFrontend(customer.getEnterprise().getType()); + customer.getEnterprise().setType(frontendType); + } + } + response.put("success", true); + response.put("data", customers); + response.put("message", "搜索客户信息成功"); + response.put("total", customers.size()); + return ResponseEntity.ok(response); + } catch (Exception e) { + response.put("success", false); + response.put("message", "搜索客户信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 获取所有企业基本信息 + * GET /pool/customers/enterprises + */ + @GetMapping("/enterprises") + public ResponseEntity> getAllEnterprises() { + Map response = new HashMap<>(); + try { + List enterprises = enterpriseService.getAllEnterprises(); + // 类型转换:将每个企业类型从数据库类型转换为前端类型 + for (Enterprise enterprise : enterprises) { + String frontendType = convertDatabaseTypeToFrontend(enterprise.getType()); + enterprise.setType(frontendType); + } + response.put("success", true); + response.put("data", enterprises); + response.put("message", "获取企业信息成功"); + response.put("total", enterprises.size()); + return ResponseEntity.ok(response); + } catch (Exception e) { + response.put("success", false); + response.put("message", "获取企业信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 获取指定企业的联系人信息 + * GET /pool/customers/{id}/contacts + */ + @GetMapping("/{id}/contacts") + public ResponseEntity> getContactsByEnterpriseId(@PathVariable String id) { + Map response = new HashMap<>(); + try { + List contacts = enterpriseService.getContactsByEnterpriseId(id); + response.put("success", true); + response.put("data", contacts); + response.put("message", "获取联系人信息成功"); + response.put("total", contacts.size()); + return ResponseEntity.ok(response); + } catch (Exception e) { + response.put("success", false); + response.put("message", "获取联系人信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 获取指定企业的负责人信息 + * GET /pool/customers/{id}/managers + */ + @GetMapping("/{id}/managers") + public ResponseEntity> getManagersByEnterpriseId(@PathVariable String id) { + Map response = new HashMap<>(); + try { + List managers = enterpriseService.getManagersByEnterpriseId(id); + response.put("success", true); + response.put("data", managers); + response.put("message", "获取负责人信息成功"); + response.put("total", managers.size()); + return ResponseEntity.ok(response); + } catch (Exception e) { + response.put("success", false); + response.put("message", "获取负责人信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 健康检查接口 + * GET /pool/customers/health + */ + @GetMapping("/health") + public ResponseEntity> healthCheck() { + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "客户信息服务运行正常"); + response.put("timestamp", System.currentTimeMillis()); + return ResponseEntity.ok(response); + } + + /** + * 获取所有客户信息 - 不使用前端参数,直接查询双数据源所有数据 + * GET /pool/all-customers + */ + @ResponseBody + @GetMapping("/all-customers") + public ResponseEntity> getAllCustomersWithoutFilter() { + System.out.println("===================================================="); + System.out.println("🔄 开始获取所有客户信息 - 双数据源查询(无负责人过滤)"); + System.out.println("===================================================="); + + Map response = new HashMap<>(); + try { + // 1. 创建结果存储容器,优先使用手机号作为键 + Map resultMap = new HashMap<>(); + + // 2. 处理primary数据源(企业信息)- 不进行负责人过滤 + System.out.println("🏢 开始获取企业数据..."); + List enterpriseData = enterpriseService.getAllEnterpriseInfo(); + + System.out.println("✅ 企业数据查询完成"); + System.out.println("📊 企业数据条数: " + enterpriseData.size()); + + int enterpriseCount = 0; + if (enterpriseData != null) { + for (EnterpriseInfoDTO enterpriseInfo : enterpriseData) { + try { + UnifiedCustomerDTO dto = convertToUnifiedDTOFromEnterprise(enterpriseInfo); + String key = dto.getPhoneNumber() != null && !dto.getPhoneNumber().isEmpty() + ? dto.getPhoneNumber() + : dto.getId(); + + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无有效标识的企业数据"); + continue; + } + + resultMap.put(key, dto); + enterpriseCount++; + + // 打印前几条数据的详细信息 + if (enterpriseCount <= 3) { + System.out.println("📝 企业客户样例 " + enterpriseCount + ": " + + "公司=" + dto.getCompany() + + ", 联系人=" + dto.getNickName() + + ", 手机=" + dto.getPhoneNumber() + + ", 类型=" + dto.getType()); + } + } catch (Exception e) { + System.err.println("❌ 处理企业数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ 企业数据源成功处理: " + enterpriseCount + " 条记录"); + } + + // 3. 处理wechat数据源(用户信息)- 不进行负责人过滤 + System.out.println("📱 开始获取wechat数据..."); + List userData = poolCustomerService.getAllCustomersWithoutFilter(); + +// 🔥 新增:批量查询负责人信息(与getAllCustomers方法相同) + List allUserIds = new ArrayList<>(); + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + if (userInfo.getUserId() != null && !userInfo.getUserId().isEmpty()) { + allUserIds.add(userInfo.getUserId()); + } + } + } + + Map managerMap = new HashMap<>(); + if (!allUserIds.isEmpty()) { + try { + List allManagers = usersManagementsMapper.findByUserIds(allUserIds); + 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()); + } + } + + System.out.println("✅ Wechat数据查询完成"); + System.out.println("📊 Wechat数据条数: " + userData.size()); + + int wechatCount = 0; + int updateCount = 0; + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + try { + String key = userInfo.getPhoneNumber() != null ? userInfo.getPhoneNumber() : ""; + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无手机号的用户数据"); + continue; + } + + // 检查是否已存在该手机号的记录 + if (resultMap.containsKey(key)) { + UnifiedCustomerDTO existingDto = resultMap.get(key); + updateUnifiedDTOWithWechatData(existingDto, userInfo); + updateCount++; + } else { + // 🔥 修改:传入managerMap + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo, managerMap); + resultMap.put(key, dto); + wechatCount++; + } + } catch (Exception e) { + System.err.println("❌ 处理用户数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ Wechat数据源成功处理: " + wechatCount + " 条新记录, " + updateCount + " 条更新记录"); + } + + // 4. 打印最终统计信息和详细信息 + printFinalStatistics(resultMap, enterpriseCount, wechatCount, updateCount); + + // 5. 构建响应 + response.put("success", true); + response.put("data", resultMap); + response.put("message", "获取客户信息成功"); + response.put("total", resultMap.size()); + response.put("enterpriseCount", enterpriseCount); + response.put("wechatCount", wechatCount); + response.put("updateCount", updateCount); + + return ResponseEntity.ok(convertTypesForFrontend(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); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/FollowupController.java b/src/main/java/com/example/web/controller/FollowupController.java new file mode 100644 index 0000000..6dcd3a9 --- /dev/null +++ b/src/main/java/com/example/web/controller/FollowupController.java @@ -0,0 +1,53 @@ +package com.example.web.controller; + +import com.example.web.service.FollowUpService; +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; + + /** + * 根据电话号码获取跟进信息 + * @param phoneNumber 电话号码 + * @param dataSource 数据源(primary或wechat) + * @return 跟进信息 + */ + @GetMapping("/get") + public ResponseEntity getFollowUp( + @RequestParam String phoneNumber) { + try { + String followup = followUpService.getFollowUpByPhone(phoneNumber); + return ResponseEntity.ok(followup); + } catch (Exception e) { + return ResponseEntity.internalServerError().body("获取跟进信息失败:" + e.getMessage()); + } + } + + /** + * 保存跟进信息 + * @param phoneNumber 电话号码 + * @param followup 跟进信息 + * @return 保存结果 + */ + @PostMapping("/save") + public ResponseEntity saveFollowUp( + @RequestParam String phoneNumber, + @RequestParam String followup) { + try { + boolean result = followUpService.saveFollowUp(phoneNumber, followup); + if (result) { + return ResponseEntity.ok("跟进信息保存成功"); + } else { + return ResponseEntity.badRequest().body("跟进信息保存失败"); + } + } catch (Exception e) { + return ResponseEntity.internalServerError().body("保存跟进信息失败:" + e.getMessage()); + } + } +} diff --git a/src/main/java/com/example/web/controller/LoginController.java b/src/main/java/com/example/web/controller/LoginController.java new file mode 100644 index 0000000..c27aaed --- /dev/null +++ b/src/main/java/com/example/web/controller/LoginController.java @@ -0,0 +1,39 @@ +package com.example.web.controller; + +import com.example.web.service.LoginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("/") +public class LoginController { + + @Autowired + private LoginService loginService; + + @PostMapping("/logins") + public Map login(@RequestParam String projectName, + @RequestParam String userName, + @RequestParam String password) { + + System.out.println("=== Controller收到登录请求 ==="); + System.out.println("工位名: " + projectName + ", 用户名: " + userName + ", 密码: " + password); + + Map 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 user = (Map) result.get("user"); + System.out.println("用户信息: " + user); + } + System.out.println(result.get("user")); + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/PoolCustomerController.java b/src/main/java/com/example/web/controller/PoolCustomerController.java new file mode 100644 index 0000000..011fe18 --- /dev/null +++ b/src/main/java/com/example/web/controller/PoolCustomerController.java @@ -0,0 +1,72 @@ +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 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 createErrorResponse(String error, String message) { + Map errorResponse = new HashMap<>(); + errorResponse.put("error", error); + errorResponse.put("message", message); + errorResponse.put("timestamp", System.currentTimeMillis()); + return errorResponse; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/SupplyCustomerController.java b/src/main/java/com/example/web/controller/SupplyCustomerController.java new file mode 100644 index 0000000..a78d5d7 --- /dev/null +++ b/src/main/java/com/example/web/controller/SupplyCustomerController.java @@ -0,0 +1,895 @@ +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.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; + + /** + * 根据公司ID查询客户详情 - 优先处理wechat数据源 + * GET /supply/pool/customers/{id} + */ + @GetMapping("/customers/{id}") + public ResponseEntity> getById(@PathVariable String id, HttpServletRequest request) { + System.out.println("===================================================="); + System.out.println("🔍 查询客户: " + id); + System.out.println("===================================================="); + + Map 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); + 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); + 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); + 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()); + + // 关键:设置数据源标识为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 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> getByPhone(@RequestParam String phoneNumber, HttpServletRequest request) { + System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + System.out.println("getByPhone: ---------------------------------------------------" + phoneNumber); + Map 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> updatePhoneCustomer(@RequestBody UnifiedCustomerDTO updatedDTO, HttpServletRequest request) { + Map 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", "客户信息更新成功"); + 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> updateCustomer( + @RequestBody UnifiedCustomerDTO updatedDTO, HttpServletRequest request) { + + Map 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); + } + + // 🎯 关键修复:返回修正后的数据源信息给前端 + Map 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/SupplyCustomerRecycleController.java b/src/main/java/com/example/web/controller/SupplyCustomerRecycleController.java new file mode 100644 index 0000000..f96eaf5 --- /dev/null +++ b/src/main/java/com/example/web/controller/SupplyCustomerRecycleController.java @@ -0,0 +1,63 @@ +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> manualRecycle() { + Map 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> getRecycleConfig() { + Map 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/SupplyEnterpriseController.java b/src/main/java/com/example/web/controller/SupplyEnterpriseController.java new file mode 100644 index 0000000..00d8f58 --- /dev/null +++ b/src/main/java/com/example/web/controller/SupplyEnterpriseController.java @@ -0,0 +1,1105 @@ +package com.example.web.controller; + +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.entity.UsersManagements; +import com.example.web.mapper.SupplyUsersManagementsMapper; +import com.example.web.service.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + + +import java.util.ArrayList; +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 SupplyEnterpriseController { + + @Autowired + private SupplyEnterpriseService supplyenterpriseService; + @Autowired + private SupplyPoolCustomerService supplypoolCustomerService; + @Autowired + private SupplyCustomerService supplycustomerService; + @Autowired + private SupplyUsersManagementsMapper supplyUsersManagementsMapper; + + // 修改类型转换方法 - 前端类型转数据库类型 + 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; + } + } + + // 添加完整的类型转换方法 + private Map convertTypesForFrontend(Map response) { + if (response.containsKey("data")) { + Object data = response.get("data"); + if (data instanceof Map) { + Map dataMap = (Map) data; + for (UnifiedCustomerDTO dto : dataMap.values()) { + String frontendType = convertDatabaseTypeToFrontend(dto.getType()); + dto.setType(frontendType); + // 不再设置固定的数据源,数据源由实际来源决定 + } + } else if (data instanceof List) { + List dataList = (List) data; + for (Object item : dataList) { + if (item instanceof UnifiedCustomerDTO) { + UnifiedCustomerDTO dto = (UnifiedCustomerDTO) item; + String frontendType = convertDatabaseTypeToFrontend(dto.getType()); + dto.setType(frontendType); + // 不再设置固定的数据源,数据源由实际来源决定 + } else if (item instanceof EnterpriseInfoDTO) { + EnterpriseInfoDTO enterpriseInfo = (EnterpriseInfoDTO) item; + if (enterpriseInfo.getEnterprise() != null) { + String frontendType = convertDatabaseTypeToFrontend(enterpriseInfo.getEnterprise().getType()); + enterpriseInfo.getEnterprise().setType(frontendType); + } + } + } + } else if (data instanceof UnifiedCustomerDTO) { + UnifiedCustomerDTO dto = (UnifiedCustomerDTO) data; + String frontendType = convertDatabaseTypeToFrontend(dto.getType()); + dto.setType(frontendType); + // 不再设置固定的数据源,数据源由实际来源决定 + } else if (data instanceof EnterpriseInfoDTO) { + EnterpriseInfoDTO enterpriseInfo = (EnterpriseInfoDTO) data; + if (enterpriseInfo.getEnterprise() != null) { + String frontendType = convertDatabaseTypeToFrontend(enterpriseInfo.getEnterprise().getType()); + enterpriseInfo.getEnterprise().setType(frontendType); + } + } + } + return response; + } + + // 新增:电话号码重复检查接口(供前端预校验) + @GetMapping("/customers/check-phone") + public ResponseEntity> checkPhoneNumber(@RequestParam String phoneNumber) { + System.out.println("电话号码重复检查接口(双数据源):------------"+phoneNumber); + Map response = new HashMap<>(); + + // 1. 非空校验 + if (!StringUtils.hasText(phoneNumber)) { + response.put("success", false); + response.put("message", "电话号码不能为空"); + response.put("isDuplicate", false); + return ResponseEntity.badRequest().body(response); + } + + // 2. 格式校验(简单示例:11位数字) + if (!phoneNumber.matches("^1[3-9]\\d{9}$")) { + response.put("success", false); + response.put("message", "请输入有效的11位手机号码"); + response.put("isDuplicate", false); + return ResponseEntity.badRequest().body(response); + } + + // 3. 重复校验 - 现在检查双数据源 + boolean isDuplicate = supplyenterpriseService.isPhoneNumberDuplicate(phoneNumber); + response.put("success", true); + response.put("isDuplicate", isDuplicate); + response.put("message", isDuplicate ? "电话号码已存在" : "电话号码可用"); + return ResponseEntity.ok(response); + } + + // 新增:处理前端提交的客户数据(保存到默认数据源) + @PostMapping("/inputcustomers") + public ResponseEntity> addCustomer(@Valid UnifiedCustomerDTO dto, BindingResult result) { + System.out.println("新增客户信息:------------"); + System.out.println("原始类型: " + dto.getType()); + System.out.println(dto.getPhoneNumber()+"----"+dto.getCreated_at()+"----"+dto.getNickName()+"----"+dto.getUserName()); + Map response = new HashMap<>(); + + // 1. 参数校验(包括手机号非空) + if (result.hasErrors()) { + response.put("success", false); + response.put("message", "参数错误:" + result.getFieldError().getDefaultMessage()); + return ResponseEntity.badRequest().body(response); + } + + // 在检查重复之前转换类型 + String databaseType = convertFrontendTypeToDatabase(dto.getType()); + dto.setType(databaseType); + System.out.println("转换后类型: " + dto.getType()); + + // 采购端权限:校验客户类型,只允许seller和both + System.out.println("权限校验 - 当前类型: " + dto.getType() + ", 是否seller: " + "seller".equals(dto.getType()) + ", 是否both: " + "both".equals(dto.getType())); + + if (!"seller".equals(dto.getType()) && !"both".equals(dto.getType())) { + response.put("success", false); + response.put("message", "采购端权限只能新增供应端类型客户和BOTH类型客户"); + return ResponseEntity.badRequest().body(response); + } + + // 2. 检查电话号码是否重复(同时检查双数据源) + if (supplyenterpriseService.isPhoneNumberDuplicate(dto.getPhoneNumber())) { + response.put("success", false); + response.put("message", "电话号码已存在,添加失败"); + response.put("isDuplicate", true); + return ResponseEntity.badRequest().body(response); + } + + // 3. 执行新增逻辑 + try { + boolean success = supplyenterpriseService.addCustomer(dto); + if (success) { + response.put("success", true); + response.put("message", "新增客户成功"); + return ResponseEntity.ok(response); + } else { + response.put("success", false); + response.put("message", "新增客户失败"); + return ResponseEntity.internalServerError().body(response); + } + } catch (Exception e) { + response.put("success", false); + response.put("message", "服务器错误:" + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 获取所有客户完整信息 - 融合双数据查询 + 负责人过滤 + * GET /supply/pool/customers + */ + @ResponseBody + @GetMapping("/customers") + public ResponseEntity> getAllCustomers(HttpServletRequest request) { + System.out.println("===================================================="); + System.out.println("🔄 获取所有客户信息 - 双数据源查询 + 负责人过滤"); + System.out.println("===================================================="); + + Map response = new HashMap<>(); + try { + // 从URL参数中获取用户类型,默认为采购端 + String isSupplySideParam = request.getParameter("isSupplySide"); + boolean isSupplySide = !"false".equalsIgnoreCase(isSupplySideParam); + System.out.println("🔍 根据登录信息判断用户类型: " + (isSupplySide ? "采购端" : "销售端")); + + // 获取认证信息,基于登录信息判断用户类型 + 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()); + System.out.println("🔐 负责人过滤条件: " + + "公司=" + authInfo.getManagercompany() + + ", 部门=" + authInfo.getManagerdepartment()); + + // 1. 创建结果存储容器,优先使用手机号作为键 + Map resultMap = new HashMap<>(); + + // 2. 处理primary数据源(企业信息)- 添加负责人过滤 + System.out.println("🏢 开始获取企业数据..."); + List enterpriseData = supplyenterpriseService.getAllEnterpriseInfo(); + + // 应用负责人过滤 + List filteredEnterpriseData = enterpriseData.stream() + .filter(enterpriseInfo -> hasManagerPermission(enterpriseInfo, authInfo)) + .collect(Collectors.toList()); + + long enterpriseEndTime = System.currentTimeMillis(); + + System.out.println("✅ 企业数据查询完成"); + System.out.println("📊 企业数据条数: " + enterpriseData.size() + " → 过滤后: " + filteredEnterpriseData.size()); + + int enterpriseCount = 0; + if (filteredEnterpriseData != null) { + for (EnterpriseInfoDTO enterpriseInfo : filteredEnterpriseData) { + try { + UnifiedCustomerDTO dto = convertToUnifiedDTOFromEnterprise(enterpriseInfo); + String key = dto.getPhoneNumber() != null && !dto.getPhoneNumber().isEmpty() + ? dto.getPhoneNumber() + : dto.getId(); + + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无有效标识的企业数据"); + continue; + } + + resultMap.put(key, dto); + enterpriseCount++; + + // 打印前几条数据的详细信息 + if (enterpriseCount <= 3) { + System.out.println("📝 企业客户样例 " + enterpriseCount + ": " + + "公司=" + dto.getCompany() + + ", 联系人=" + dto.getNickName() + + ", 手机=" + dto.getPhoneNumber() + + ", 类型=" + dto.getType()); + } + } catch (Exception e) { + System.err.println("❌ 处理企业数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ 企业数据源成功处理: " + enterpriseCount + " 条记录"); + } + + // 3. 处理wechat数据源(用户信息)- 添加负责人过滤 + System.out.println("📱 开始获取wechat数据..."); + long wechatStartTime = System.currentTimeMillis(); + + List userData = supplypoolCustomerService.getAllCustomers(authInfo); + + + // 🔥 新增:在Controller层进行负责人过滤之前,先收集所有用户ID用于批量查询 + List allUserIds = new ArrayList<>(); + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + if (userInfo.getUserId() != null && !userInfo.getUserId().isEmpty()) { + allUserIds.add(userInfo.getUserId()); + } + } + } + +// 🔥 新增:批量查询所有用户的负责人信息 + Map managerMap = new HashMap<>(); + if (!allUserIds.isEmpty()) { + try { + List allManagers = supplyUsersManagementsMapper.findByUserIds(allUserIds); + 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()); + // 即使批量查询失败,也不影响后续逻辑,只是会使用默认值 + } + } + + // 在Controller层进行负责人过滤 + List filteredUserData = new ArrayList<>(); + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + try { + if (hasPermissionToViewWechatCustomer(userInfo, authInfo)) { + filteredUserData.add(userInfo); + } + } catch (Exception e) { + System.err.println("❌ 检查用户权限时出错: " + e.getMessage()); + // 单个用户权限检查失败不影响其他用户 + } + } + } + + long wechatEndTime = System.currentTimeMillis(); + + System.out.println("✅ Wechat数据查询完成"); + System.out.println("📊 Wechat数据条数: " + userData.size() + " → 过滤后: " + filteredUserData.size()); + System.out.println("⏱️ Wechat查询耗时: " + (wechatEndTime - wechatStartTime) + "ms"); + + int wechatCount = 0; + int updateCount = 0; + if (filteredUserData != null) { + for (UserProductCartDTO userInfo : filteredUserData) { + try { + String key = userInfo.getPhoneNumber() != null ? userInfo.getPhoneNumber() : ""; + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无手机号的用户数据"); + continue; + } +// 检查是否已存在该手机号的记录 + if (resultMap.containsKey(key)) { + // 更新现有记录(补充wechat数据源的信息) + UnifiedCustomerDTO existingDto = resultMap.get(key); + updateUnifiedDTOWithWechatData(existingDto, userInfo); + updateCount++; + } else { + // 🔥 修改:传入managerMap,避免N+1查询 + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo, managerMap); + resultMap.put(key, dto); + wechatCount++; + } + } catch (Exception e) { + System.err.println("❌ 处理用户数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ Wechat数据源成功处理: " + wechatCount + " 条新记录, " + updateCount + " 条更新记录"); + } + + // 4. 打印最终统计信息和详细信息 + printFinalStatistics(resultMap, enterpriseCount, wechatCount, updateCount); + + // 5. 构建响应 + response.put("success", true); + response.put("data", resultMap); + response.put("message", "获取客户信息成功"); + response.put("total", resultMap.size()); + response.put("enterpriseCount", enterpriseCount); + response.put("wechatCount", wechatCount); + response.put("updateCount", updateCount); + + return ResponseEntity.ok(convertTypesForFrontend(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); + } + } + + /** + * 从请求中获取ManagerAuthInfo - 基于登录信息判断用户类型 + */ + private ManagerAuthInfo getManagerAuthInfoFromRequest(HttpServletRequest request, boolean isSupplySide) { + System.out.println("🔍 获取认证信息,用户类型: " + (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 = ""; + } + + // 记录用户类型,但不进行严格的部门名称验证 + System.out.println("✅ 用户认证信息获取成功: " + (isSupplySide ? "采购端" : "销售端")); + + // 验证公司信息一致性 + 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 : "" + ); + + System.out.println("🎯 认证信息详情: " + + "系统类型=" + (isSupplySide ? "采购端" : "销售端") + + ", 公司=" + authInfo.getManagercompany() + + ", 部门=" + authInfo.getManagerdepartment() + + ", 组织=" + authInfo.getOrganization() + + ", 角色=" + authInfo.getRole() + + ", 负责人=" + authInfo.getUserName() + + ", 协助人=" + authInfo.getAssistant()); + + return authInfo; + } + + /** + * 检查企业负责人权限 + */ + private boolean hasManagerPermission(EnterpriseInfoDTO enterpriseInfo, ManagerAuthInfo authInfo) { + if (enterpriseInfo.getManagers() == null) { + System.out.println("✅ 企业客户无负责人信息,允许查看"); + return true; + } + + return isManagerMatch(enterpriseInfo.getManagers(), authInfo); + } + + /** + * 检查负责人信息是否匹配(Managers) + */ + private boolean isManagerMatch(Managers manager, ManagerAuthInfo authInfo) { + boolean match = + (authInfo.getManagercompany() == null || authInfo.getManagercompany().equals(manager.getManagercompany())) && + (authInfo.getManagerdepartment() == null || authInfo.getManagerdepartment().equals(manager.getManagerdepartment())) && + (authInfo.getOrganization() == null || authInfo.getOrganization().equals(manager.getOrganization())) && + (authInfo.getRole() == null || authInfo.getRole().equals(manager.getRole())) && + (authInfo.getUserName() == null || authInfo.getUserName().equals(manager.getUserName())); + + System.out.println("🔐 企业负责人权限检查: " + (match ? "✅ 匹配" : "❌ 不匹配")); + return match; + } + + /** + * 检查是否有权限查看微信客户 - 修复版本 + */ + private boolean hasPermissionToViewWechatCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + try { + System.out.println("🔐 开始检查微信客户权限,用户ID: " + userInfo.getUserId()); + + // 公海池客户对所有人可见 + if (isPublicSeaCustomer(userInfo)) { + System.out.println("✅ 公海池客户,无需负责人权限验证"); + return true; + } + + // 非公海池客户需要检查负责人权限 + System.out.println("🔍 非公海池客户,检查负责人权限..."); + boolean hasPermission = supplycustomerService.hasPermissionToViewWechatCustomer(userInfo, authInfo); + System.out.println("🔐 微信客户权限检查结果: " + (hasPermission ? "✅ 有权限" : "❌ 无权限")); + return hasPermission; + } catch (Exception e) { + System.err.println("❌ 检查微信客户权限失败: " + e.getMessage()); + // 发生异常时,出于安全考虑返回false + return false; + } + } + + /** + * 判断是否为公海池客户 + */ + private boolean isPublicSeaCustomer(UserProductCartDTO userInfo) { + // 定义公海池等级 + java.util.Set publicSeaLevels = java.util.Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池"); + + // 检查等级是否为公海池 + boolean isPublicSeaLevel = publicSeaLevels.contains(userInfo.getLevel()); + + System.out.println("🔍 客户等级检查: " + userInfo.getLevel() + " → 是否公海池: " + isPublicSeaLevel); + + // 公海池客户:等级是公海池 + return isPublicSeaLevel; + } + + /** + * 从企业数据源转换DTO + */ + private UnifiedCustomerDTO convertToUnifiedDTOFromEnterprise(EnterpriseInfoDTO enterpriseInfo) { + UnifiedCustomerDTO dto = new UnifiedCustomerDTO(); + + // 设置企业信息 + if (enterpriseInfo.getEnterprise() != null) { + dto.setId(enterpriseInfo.getEnterprise().getId() != null ? enterpriseInfo.getEnterprise().getId() : ""); + dto.setCompany(enterpriseInfo.getEnterprise().getCompany() != null ? enterpriseInfo.getEnterprise().getCompany() : ""); + dto.setRegion(enterpriseInfo.getEnterprise().getRegion() != null ? enterpriseInfo.getEnterprise().getRegion() : ""); + dto.setLevel(enterpriseInfo.getEnterprise().getLevel() != null ? enterpriseInfo.getEnterprise().getLevel() : ""); + String frontendType = convertDatabaseTypeToFrontend(enterpriseInfo.getEnterprise().getType()); + dto.setType(frontendType != null ? frontendType : ""); + dto.setDemand(enterpriseInfo.getEnterprise().getDemand() != null ? enterpriseInfo.getEnterprise().getDemand() : ""); + dto.setSpec(enterpriseInfo.getEnterprise().getSpec() != null ? enterpriseInfo.getEnterprise().getSpec() : ""); + } + + // 设置联系人信息 + if (enterpriseInfo.getContacts() != null) { + dto.setNickName(enterpriseInfo.getContacts().getNickName() != null ? enterpriseInfo.getContacts().getNickName() : ""); + dto.setPhoneNumber(enterpriseInfo.getContacts().getPhoneNumber() != null ? enterpriseInfo.getContacts().getPhoneNumber() : ""); + dto.setWechat(enterpriseInfo.getContacts().getWechat() != null ? enterpriseInfo.getContacts().getWechat() : ""); + dto.setAccount(enterpriseInfo.getContacts().getAccount() != null ? enterpriseInfo.getContacts().getAccount() : ""); + dto.setAccountNumber(enterpriseInfo.getContacts().getAccountNumber() != null ? enterpriseInfo.getContacts().getAccountNumber() : ""); + dto.setBank(enterpriseInfo.getContacts().getBank() != null ? enterpriseInfo.getContacts().getBank() : ""); + dto.setAddress(enterpriseInfo.getContacts().getAddress() != null ? enterpriseInfo.getContacts().getAddress() : ""); + dto.setCreated_at(enterpriseInfo.getContacts().getCreated_at()); + dto.setUpdated_at(enterpriseInfo.getContacts().getUpdated_at()); + } + + // 设置负责人信息(根据新的表结构) + if (enterpriseInfo.getManagers() != null) { + // 使用新的字段名 + dto.setManagercompany(enterpriseInfo.getManagers().getManagercompany() != null ? enterpriseInfo.getManagers().getManagercompany() : ""); + dto.setManagerdepartment(enterpriseInfo.getManagers().getManagerdepartment() != null ? enterpriseInfo.getManagers().getManagerdepartment() : ""); + dto.setOrganization(enterpriseInfo.getManagers().getOrganization() != null ? enterpriseInfo.getManagers().getOrganization() : ""); + dto.setRole(enterpriseInfo.getManagers().getRole() != null ? enterpriseInfo.getManagers().getRole() : ""); + dto.setUserName(enterpriseInfo.getManagers().getUserName() != null ? enterpriseInfo.getManagers().getUserName() : ""); + dto.setAssistant(enterpriseInfo.getManagers().getAssistant() != null ? enterpriseInfo.getManagers().getAssistant() : ""); + } + + // 设置数据源标识 + dto.setDataSource("enterprise"); + + return dto; + } + + /** + * 专门为公海池客户转换DTO的方法 - 补充负责人信息(优化版本) + */ + private UnifiedCustomerDTO convertToUnifiedDTOForPublicSea( + UserProductCartDTO userInfo, + Map managerMap) { // 🔥 新增参数:批量查询的负责人信息 + + 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()); + + // 关键:设置数据源标识为wechat + dto.setDataSource("wechat"); + + // 设置联系人信息 - 从usersContacts获取 + setContactInfoSafely(dto, userInfo); + + // 🔥 关键修改:从批量查询的managerMap中获取负责人信息,避免N+1查询 + setManagerInfoFromMap(dto, userInfo.getUserId(), managerMap); + + // 设置公海需求信息 - 从产品表获取(采购端特有逻辑) + if (userInfo.getProducts() != null && !userInfo.getProducts().isEmpty()) { + UserProductCartDTO.ProductInfo selectedProduct = userInfo.getProducts().get(0); + + 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()); + } else { + // 如果没有产品信息,设置默认值 + setDefaultProductValues(dto); + } + + return dto; + } + + /** + * 从批量查询的Map中设置负责人信息 - 避免N+1查询(与销售端保持一致) + */ + private void setManagerInfoFromMap(UnifiedCustomerDTO dto, String userId, Map managerMap) { + try { + System.out.println("🔍 从批量Map中查询用户负责人信息,用户ID: " + userId); + UsersManagements userManager = managerMap.get(userId); + + if (userManager != null) { + System.out.println("✅ 从批量Map中找到负责人信息,设置到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("⚠️ 未在批量Map中找到负责人信息,用户可能为公海池客户"); + // 设置默认值 + setDefaultManagerValues(dto); + } + } catch (Exception e) { + System.err.println("❌ 从Map设置负责人信息失败: " + e.getMessage()); + // 发生异常时设置默认值 + setDefaultManagerValues(dto); + } + } + + /** + * 设置默认负责人信息(与销售端保持一致) + */ + private void setDefaultManagerValues(UnifiedCustomerDTO dto) { + dto.setManagercompany(""); + dto.setManagerdepartment(""); + dto.setOrganization(""); + dto.setRole(""); + dto.setUserName(""); + dto.setAssistant(""); + } + + /** + * 用wechat数据更新现有的DTO(用于数据合并)- 采购端使用产品信息 + */ + private void updateUnifiedDTOWithWechatData(UnifiedCustomerDTO existingDto, UserProductCartDTO userInfo) { + // 补充产品信息(如果企业数据中缺少) + if ((existingDto.getProductName() == null || existingDto.getProductName().isEmpty()) && + userInfo.getProducts() != null && !userInfo.getProducts().isEmpty()) { + + UserProductCartDTO.ProductInfo product = userInfo.getProducts().get(0); + existingDto.setProductName(product.getProductName() != null ? product.getProductName() : ""); + existingDto.setVariety(product.getVariety() != null ? product.getVariety() : ""); + existingDto.setSpecification(product.getSpecification() != null ? product.getSpecification() : ""); + existingDto.setQuantity(product.getQuantity() != null ? product.getQuantity() : 0); + existingDto.setGrossWeight(product.getGrossWeight() != null ? product.getGrossWeight() : ""); + existingDto.setYolk(product.getYolk() != null ? product.getYolk() : ""); + + // 设置customDetails + existingDto.setCustomDetails(userInfo.getProducts().toArray()); + } + + // 补充其他可能缺失的字段 + if (existingDto.getLevel() == null || existingDto.getLevel().isEmpty()) { + existingDto.setLevel(userInfo.getLevel() != null ? userInfo.getLevel() : "公海池"); + } + + // 标记为混合数据源 + existingDto.setDataSource("mixed"); + } + + /** + * 打印最终统计信息 - 采购端版本 + */ + private void printFinalStatistics(Map resultMap, int enterpriseCount, int wechatCount, int updateCount) { + System.out.println("===================================================="); + System.out.println("📈 [采购端] 数据源查询统计:"); + System.out.println("🏢 企业数据源: " + enterpriseCount + " 条记录"); + System.out.println("📱 Wechat数据源: " + (wechatCount + updateCount) + " 条记录 (" + wechatCount + " 新增, " + updateCount + " 更新)"); + System.out.println("📊 合并后总客户数: " + resultMap.size() + " 条记录"); + System.out.println("===================================================="); + + // 打印详细样例(限制数量避免日志过多) + int sampleCount = 0; + System.out.println("📋 [采购端] 最终数据样例:"); + for (Map.Entry entry : resultMap.entrySet()) { + if (sampleCount >= 5) break; + + String key = entry.getKey(); + UnifiedCustomerDTO dto = entry.getValue(); + + System.out.println("--- 客户 " + (sampleCount + 1) + " ---"); + System.out.println(" 键: " + key); + System.out.println(" 数据源: " + dto.getDataSource()); + System.out.println(" ID: " + dto.getId()); + System.out.println(" 公司: " + dto.getCompany()); + System.out.println(" 联系人: " + dto.getNickName()); + System.out.println(" 手机: " + dto.getPhoneNumber()); + System.out.println(" 微信号: " + dto.getWechat()); + System.out.println(" 类型: " + dto.getType()); + System.out.println(" 等级: " + dto.getLevel()); + System.out.println(" 地区: " + dto.getRegion()); + System.out.println(" 需求: " + dto.getDemand()); + System.out.println(" 规格: " + dto.getSpec()); + + // 产品信息 + System.out.println(" 产品名称: " + dto.getProductName()); + System.out.println(" 品种: " + dto.getVariety()); + System.out.println(" 规格说明: " + dto.getSpecification()); + System.out.println(" 数量: " + dto.getQuantity()); + System.out.println(" 毛重: " + dto.getGrossWeight()); + System.out.println(" 蛋黄: " + dto.getYolk()); + + // 联系人详细信息 + System.out.println(" 账户: " + dto.getAccount()); + System.out.println(" 账号: " + dto.getAccountNumber()); + System.out.println(" 银行: " + dto.getBank()); + System.out.println(" 地址: " + dto.getAddress()); + + // 负责人信息 + System.out.println(" 负责公司: " + dto.getManagercompany()); + System.out.println(" 负责部门: " + dto.getManagerdepartment()); + System.out.println(" 组织: " + dto.getOrganization()); + System.out.println(" 角色: " + dto.getRole()); + System.out.println(" 负责人: " + dto.getUserName()); + System.out.println(" 协助人: " + dto.getAssistant()); + + // 时间信息 + System.out.println(" 创建时间: " + dto.getCreated_at()); + System.out.println(" 更新时间: " + dto.getUpdated_at()); + + sampleCount++; + System.out.println(); // 空行分隔 + } + System.out.println("===================================================="); + } + + // 保留原有的辅助方法 - 采购端版本 + 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()) { + List validContacts = userInfo.getUsersContacts().stream() + .filter(contact -> contact != null) + .collect(Collectors.toList()); + + if (!validContacts.isEmpty()) { + UserProductCartDTO.UsersContacts contact = validContacts.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); + } + } else { + setDefaultContactValues(dto); + } + } + + private void setDefaultContactValues(UnifiedCustomerDTO dto) { + dto.setWechat(""); + dto.setAccount(""); + dto.setAccountNumber(""); + dto.setBank(""); + dto.setAddress(""); + } + + // 其他方法保持不变... + /** + * 根据ID获取客户详细信息 + * GET /supply/pool/customers/{id} + */ + @GetMapping("/{id}") + public ResponseEntity> getCustomerById(@PathVariable String id) { + Map response = new HashMap<>(); + try { + EnterpriseInfoDTO customer = supplyenterpriseService.getEnterpriseInfoById(id); + if (customer != null) { + response.put("success", true); + response.put("data", customer); + response.put("message", "获取客户信息成功"); + return ResponseEntity.ok(response); + } else { + response.put("success", false); + response.put("message", "未找到对应的客户信息"); + return ResponseEntity.notFound().build(); + } + } catch (Exception e) { + response.put("success", false); + response.put("message", "获取客户信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(response); + } + } + + /** + * 搜索客户信息 + * GET /supply/pool/customers/search?keyword=xxx + */ + @GetMapping("/search") + public ResponseEntity> searchCustomers(@RequestParam(required = false) String keyword) { + Map response = new HashMap<>(); + try { + List customers = supplyenterpriseService.searchEnterprises(keyword); + // 类型转换:将每个企业类型从数据库类型转换为前端类型 + for (EnterpriseInfoDTO customer : customers) { + if (customer.getEnterprise() != null) { + String frontendType = convertDatabaseTypeToFrontend(customer.getEnterprise().getType()); + customer.getEnterprise().setType(frontendType); + } + } + response.put("success", true); + response.put("data", customers); + response.put("message", "搜索客户信息成功"); + response.put("total", customers.size()); + 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/enterprises + */ + @GetMapping("/enterprises") + public ResponseEntity> getAllEnterprises() { + Map response = new HashMap<>(); + try { + List enterprises = supplyenterpriseService.getAllEnterprises(); + // 类型转换:将每个企业类型从数据库类型转换为前端类型 + for (Enterprise enterprise : enterprises) { + String frontendType = convertDatabaseTypeToFrontend(enterprise.getType()); + enterprise.setType(frontendType); + } + response.put("success", true); + response.put("data", enterprises); + response.put("message", "获取企业信息成功"); + response.put("total", enterprises.size()); + 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/{id}/contacts + */ + @GetMapping("/{id}/contacts") + public ResponseEntity> getContactsByEnterpriseId(@PathVariable String id) { + Map response = new HashMap<>(); + try { + List contacts = supplyenterpriseService.getContactsByEnterpriseId(id); + response.put("success", true); + response.put("data", contacts); + response.put("message", "获取联系人信息成功"); + response.put("total", contacts.size()); + 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/{id}/managers + */ + @GetMapping("/{id}/managers") + public ResponseEntity> getManagersByEnterpriseId(@PathVariable String id) { + Map response = new HashMap<>(); + try { + List managers = supplyenterpriseService.getManagersByEnterpriseId(id); + response.put("success", true); + response.put("data", managers); + response.put("message", "获取负责人信息成功"); + response.put("total", managers.size()); + 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/health + */ + @GetMapping("/health") + public ResponseEntity> healthCheck() { + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "客户信息服务运行正常"); + response.put("timestamp", System.currentTimeMillis()); + return ResponseEntity.ok(response); + } + + /** + * 获取所有客户信息 - 不使用前端参数,直接查询双数据源所有数据 + * GET /pool/all-customers + */ + @ResponseBody + @GetMapping("/all-customers") + public ResponseEntity> getAllCustomersWithoutFilter() { + System.out.println("===================================================="); + System.out.println("🔄 开始获取所有客户信息 - 双数据源查询(无负责人过滤)"); + System.out.println("===================================================="); + + Map response = new HashMap<>(); + try { + // 1. 创建结果存储容器,优先使用手机号作为键 + Map resultMap = new HashMap<>(); + + // 2. 处理primary数据源(企业信息)- 不进行负责人过滤 + System.out.println("🏢 开始获取企业数据..."); + List enterpriseData = supplyenterpriseService.getAllEnterpriseInfo(); + + System.out.println("✅ 企业数据查询完成"); + System.out.println("📊 企业数据条数: " + enterpriseData.size()); + + int enterpriseCount = 0; + if (enterpriseData != null) { + for (EnterpriseInfoDTO enterpriseInfo : enterpriseData) { + try { + UnifiedCustomerDTO dto = convertToUnifiedDTOFromEnterprise(enterpriseInfo); + String key = dto.getPhoneNumber() != null && !dto.getPhoneNumber().isEmpty() + ? dto.getPhoneNumber() + : dto.getId(); + + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无有效标识的企业数据"); + continue; + } + + resultMap.put(key, dto); + enterpriseCount++; + + // 打印前几条数据的详细信息 + if (enterpriseCount <= 3) { + System.out.println("📝 企业客户样例 " + enterpriseCount + ": " + + "公司=" + dto.getCompany() + + ", 联系人=" + dto.getNickName() + + ", 手机=" + dto.getPhoneNumber() + + ", 类型=" + dto.getType()); + } + } catch (Exception e) { + System.err.println("❌ 处理企业数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ 企业数据源成功处理: " + enterpriseCount + " 条记录"); + } + + // 3. 处理wechat数据源(用户信息)- 不进行负责人过滤 + System.out.println("📱 开始获取wechat数据..."); + List userData = supplypoolCustomerService.getAllCustomersWithoutFilter(); + +// 🔥 新增:批量查询负责人信息(与getAllCustomers方法相同) + List allUserIds = new ArrayList<>(); + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + if (userInfo.getUserId() != null && !userInfo.getUserId().isEmpty()) { + allUserIds.add(userInfo.getUserId()); + } + } + } + + Map managerMap = new HashMap<>(); + if (!allUserIds.isEmpty()) { + try { + List allManagers = supplyUsersManagementsMapper.findByUserIds(allUserIds); + 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()); + } + } + + System.out.println("✅ Wechat数据查询完成"); + System.out.println("📊 Wechat数据条数: " + userData.size()); + + int wechatCount = 0; + int updateCount = 0; + if (userData != null) { + for (UserProductCartDTO userInfo : userData) { + try { + String key = userInfo.getPhoneNumber() != null ? userInfo.getPhoneNumber() : ""; + if (!StringUtils.hasText(key)) { + System.out.println("⚠️ 跳过无手机号的用户数据"); + continue; + } + + // 检查是否已存在该手机号的记录 + if (resultMap.containsKey(key)) { + UnifiedCustomerDTO existingDto = resultMap.get(key); + updateUnifiedDTOWithWechatData(existingDto, userInfo); + updateCount++; + } else { + // 🔥 修改:传入managerMap + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo, managerMap); + resultMap.put(key, dto); + wechatCount++; + } + } catch (Exception e) { + System.err.println("❌ 处理用户数据时出错: " + e.getMessage()); + e.printStackTrace(); + } + } + System.out.println("✅ Wechat数据源成功处理: " + wechatCount + " 条新记录, " + updateCount + " 条更新记录"); + } + + // 4. 打印最终统计信息和详细信息 + printFinalStatistics(resultMap, enterpriseCount, wechatCount, updateCount); + + // 5. 构建响应 + response.put("success", true); + response.put("data", resultMap); + response.put("message", "获取客户信息成功"); + response.put("total", resultMap.size()); + response.put("enterpriseCount", enterpriseCount); + response.put("wechatCount", wechatCount); + response.put("updateCount", updateCount); + + return ResponseEntity.ok(convertTypesForFrontend(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); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/controller/SupplyPoolCustomerController.java b/src/main/java/com/example/web/controller/SupplyPoolCustomerController.java new file mode 100644 index 0000000..cebfb3d --- /dev/null +++ b/src/main/java/com/example/web/controller/SupplyPoolCustomerController.java @@ -0,0 +1,73 @@ +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 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 createErrorResponse(String error, String message) { + Map errorResponse = new HashMap<>(); + errorResponse.put("error", error); + errorResponse.put("message", message); + errorResponse.put("timestamp", System.currentTimeMillis()); + return errorResponse; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/dto/EnterpriseInfoDTO.java b/src/main/java/com/example/web/dto/EnterpriseInfoDTO.java new file mode 100644 index 0000000..410e8c4 --- /dev/null +++ b/src/main/java/com/example/web/dto/EnterpriseInfoDTO.java @@ -0,0 +1,47 @@ +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:提供属性的获取和设置方法,用于在各层之间传递数据 +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; + } + } diff --git a/src/main/java/com/example/web/dto/ManagerAuthInfo.java b/src/main/java/com/example/web/dto/ManagerAuthInfo.java new file mode 100644 index 0000000..9f25ac9 --- /dev/null +++ b/src/main/java/com/example/web/dto/ManagerAuthInfo.java @@ -0,0 +1,80 @@ +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; + } +} diff --git a/src/main/java/com/example/web/dto/UnifiedCustomerDTO.java b/src/main/java/com/example/web/dto/UnifiedCustomerDTO.java new file mode 100644 index 0000000..e795ae2 --- /dev/null +++ b/src/main/java/com/example/web/dto/UnifiedCustomerDTO.java @@ -0,0 +1,321 @@ +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 LocalDateTime created_at; + private LocalDateTime updated_at; + + // 扩展信息 + private Object[] customDetails = new Object[0]; + private String dataSource; // "wechat" 或 "default" 或 "mixed" + + // ==================== 一对多字段 ==================== + private List contacts = new ArrayList<>(); + private List cartItems = new ArrayList<>(); // 销售端使用 + private List 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 getContacts() { return contacts; } + public void setContacts(List contacts) { this.contacts = contacts; } + + public List getCartItems() { return cartItems; } + public void setCartItems(List cartItems) { this.cartItems = cartItems; } + + public List getProductItems() { return productItems; } + public void setProductItems(List 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 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; } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/dto/UserProductCartDTO.java b/src/main/java/com/example/web/dto/UserProductCartDTO.java new file mode 100644 index 0000000..baab599 --- /dev/null +++ b/src/main/java/com/example/web/dto/UserProductCartDTO.java @@ -0,0 +1,423 @@ +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 List products= new ArrayList<>();;//产品信息 + private List cartItems= new ArrayList<>();;//购物车信息 + private List 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 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 getProducts() { + return products; + } + + public void setProducts(List products) { + this.products = products; + } + + public List getCartItems() { + return cartItems; + } + + public void setCartItems(List cartItems) { + this.cartItems = cartItems; + } + + public List getUsersContacts() { + return usersContacts; + } + + public void setUsersContacts(List 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; + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/web/entity/Cart_items.java b/src/main/java/com/example/web/entity/Cart_items.java new file mode 100644 index 0000000..527f849 --- /dev/null +++ b/src/main/java/com/example/web/entity/Cart_items.java @@ -0,0 +1,128 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + + +/** + * 购物车表 + */ +@Data +@NoArgsConstructor +public class Cart_items { + private Integer id;//购物车id + private String userId;//用户id + private String productId;//产品id + private String productName;//产品名称 + private String specification;//规格 + private Integer quantity;//数量 + private String grossWeight;//毛重 + private String price;//单价 + private Integer selected;//是否选中 + private String added_at;//添加时间 + private String yolk;//蛋黄 + + public Cart_items(Integer id, String userId, String productId, String productName, String specification, Integer quantity, String grossWeight, String price, Integer selected, String added_at, String yolk) { + this.id = id; + this.userId = userId; + this.productId = productId; + this.productName = productName; + this.specification = specification; + this.quantity = quantity; + this.grossWeight = grossWeight; + this.price = price; + this.selected = selected; + this.added_at = added_at; + this.yolk = yolk; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + 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 getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } + + public Integer getSelected() { + return selected; + } + + public void setSelected(Integer selected) { + this.selected = selected; + } + + public String getAdded_at() { + return added_at; + } + + public void setAdded_at(String added_at) { + this.added_at = added_at; + } + + public String getYolk() { + return yolk; + } + + public void setYolk(String yolk) { + this.yolk = yolk; + } +} diff --git a/src/main/java/com/example/web/entity/Contacts.java b/src/main/java/com/example/web/entity/Contacts.java new file mode 100644 index 0000000..d175e95 --- /dev/null +++ b/src/main/java/com/example/web/entity/Contacts.java @@ -0,0 +1,138 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @Description: 联系人实体类 + */ +@Data +public class Contacts { + private String contact_id;//联系人id + 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 LocalDateTime created_at;//创建时间 + private LocalDateTime updated_at;//更新时间 + + 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 Contacts() { + + } + public Contacts(String contact_id, String id, String nickName, String phoneNumber, String wechat, String account, String accountNumber, String bank, String address, String followup) { + this.contact_id = contact_id; + this.id = id; + this.nickName = nickName; + this.phoneNumber = phoneNumber; + this.wechat = wechat; + this.account = account; + this.accountNumber = accountNumber; + this.bank = bank; + this.address = address; + this.followup = followup; + } + + public String getContact_id() { + return contact_id; + } + + public void setContact_id(String contact_id) { + this.contact_id = contact_id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + 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; + } + + 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 getFollowup() { + return followup; + } + + public void setFollowup(String followup) { + this.followup = followup; + } +} diff --git a/src/main/java/com/example/web/entity/Enterprise.java b/src/main/java/com/example/web/entity/Enterprise.java new file mode 100644 index 0000000..119dd63 --- /dev/null +++ b/src/main/java/com/example/web/entity/Enterprise.java @@ -0,0 +1,88 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description: 企业实体类 + */ +@Data +public class Enterprise { + private String id;//企业id + private String company;//企业名称 + private String region;//地区 + private String level;//企业等级 + private String type;//企业类型 + private String demand;//企业需求 + private String spec;//规格 + + // 无参构造器 + public Enterprise() { + } + public Enterprise(String id, String company, String region, String level, String type, String demand, String spec) { + this.id = id; + this.company = company; + this.region = region; + this.level = level; + this.type = type; + this.demand = demand; + this.spec = spec; + } + + 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; + } +} diff --git a/src/main/java/com/example/web/entity/Login.java b/src/main/java/com/example/web/entity/Login.java new file mode 100644 index 0000000..36a0dcf --- /dev/null +++ b/src/main/java/com/example/web/entity/Login.java @@ -0,0 +1,51 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description: 登录实体类 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Login { + private String projectName;//职位名称 + private String userName;//用户名 + private String password;//密码 + private Integer id;//用户id + + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } +} diff --git a/src/main/java/com/example/web/entity/Managers.java b/src/main/java/com/example/web/entity/Managers.java new file mode 100644 index 0000000..c1030a3 --- /dev/null +++ b/src/main/java/com/example/web/entity/Managers.java @@ -0,0 +1,124 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @Description: 管理员实体类 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Managers { + private Integer manager_id;//管理员id + private String id;//企业id + private String managerId; // 负责人id + private String managercompany; // 负责公司 + private String managerdepartment; // 负责部门 + private String organization; // 负责小组 + private String role; // 角色 + private String root; // 权限 + private LocalDateTime created_at; // 创建时间 + private LocalDateTime updated_at; // 更新时间 + private String userName;//负责人姓名 + private String assistant;//协助人 + + public Integer getManager_id() { + return manager_id; + } + + public void setManager_id(Integer manager_id) { + this.manager_id = manager_id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + 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 getRoot() { + return root; + } + + public void setRoot(String root) { + this.root = root; + } + + 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 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; + } +} diff --git a/src/main/java/com/example/web/entity/Products.java b/src/main/java/com/example/web/entity/Products.java new file mode 100644 index 0000000..8a5e96a --- /dev/null +++ b/src/main/java/com/example/web/entity/Products.java @@ -0,0 +1,161 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +// 数据库中grossWeight和price已改为varchar类型 +import java.time.LocalDateTime; + + +/** + * 产品实体类 + */ +@Data +@NoArgsConstructor + +public class Products { + private Integer id;//产品id + private String productId;//产品编号 + private String sellerId;//卖家id + private String productName;//产品名称 + private String price;//单价 (数据库中为varchar类型) + private Integer quantity;//数量 + private String variety;//品种 + private String grossWeight;//毛重 (数据库中为varchar类型) + private String specification;//规格 + private String status;//状态 + private LocalDateTime created_at;//创建时间 + private LocalDateTime updated_at;//更新时间 + private String yolk;//蛋黄 + private String rejectReason;//驳回原因 + + public Products(Integer id, String productId, String sellerId, String productName, String price, Integer quantity, String variety, String grossWeight, String specification, String status, LocalDateTime created_at, LocalDateTime updated_at, String yolk, String rejectReason) { + this.id = id; + this.productId = productId; + this.sellerId = sellerId; + this.productName = productName; + this.price = price; + this.quantity = quantity; + this.variety = variety; + this.grossWeight = grossWeight; + this.specification = specification; + this.status = status; + this.created_at = created_at; + this.updated_at = updated_at; + this.yolk = yolk; + this.rejectReason = rejectReason; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + + public String getSellerId() { + return sellerId; + } + + public void setSellerId(String sellerId) { + this.sellerId = sellerId; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public String getVariety() { + return variety; + } + + public void setVariety(String variety) { + this.variety = variety; + } + + public String getGrossWeight() { + return grossWeight; + } + + public void setGrossWeight(String grossWeight) { + this.grossWeight = grossWeight; + } + + public String getSpecification() { + return specification; + } + + public void setSpecification(String specification) { + this.specification = specification; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + 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 String getYolk() { + return yolk; + } + + public void setYolk(String yolk) { + this.yolk = yolk; + } + + public String getRejectReason() { + return rejectReason; + } + + public void setRejectReason(String rejectReason) { + this.rejectReason = rejectReason; + } +} diff --git a/src/main/java/com/example/web/entity/Rootdb.java b/src/main/java/com/example/web/entity/Rootdb.java new file mode 100644 index 0000000..eea20ba --- /dev/null +++ b/src/main/java/com/example/web/entity/Rootdb.java @@ -0,0 +1,34 @@ +package com.example.web.entity; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description: 权限划分实体类 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Rootdb { + private String projectName;//职位名称 + private Integer root;//权限划分 + + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public Integer getRoot() { + return root; + } + + public void setRoot(Integer root) { + this.root = root; + } +} diff --git a/src/main/java/com/example/web/entity/Users.java b/src/main/java/com/example/web/entity/Users.java new file mode 100644 index 0000000..7b66187 --- /dev/null +++ b/src/main/java/com/example/web/entity/Users.java @@ -0,0 +1,228 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 用户实体类 + */ +@Data +@NoArgsConstructor +public class Users { + private Integer id;//用户id + private String openid;//用户唯一标识 + private String userId;//用户id + private String nickName;//用户昵称 + private String avatarUrl;//用户头像 + private String phoneNumber;//用户手机号 + private String type;//用户类型 + private Integer gender;//用户性别 + private String country;//国家 + private String province;//省份 + private String city;//城市 + private String language;//语言 + private String session_key;//会话密钥 + private LocalDateTime created_at;//创建时间 + private LocalDateTime updated_at;//更新时间 + private String company;//客户公司 + private String region;//客户地区 + private String level;//客户等级 + private String demand;//客户需求 + private String spec;//规格 + private String followup;//跟进信息 + + 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) { + this.id = id; + this.openid = openid; + this.userId = userId; + this.nickName = nickName; + this.avatarUrl = avatarUrl; + this.phoneNumber = phoneNumber; + this.type = type; + this.gender = gender; + this.country = country; + this.province = province; + this.city = city; + this.language = language; + this.session_key = session_key; + this.created_at = created_at; + this.updated_at = updated_at; + this.company = company; + this.region = region; + this.level = level; + this.demand = demand; + this.spec = spec; + this.followup = followup; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getAvatarUrl() { + return avatarUrl; + } + + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } + + 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 Integer getGender() { + return gender; + } + + public void setGender(Integer gender) { + this.gender = gender; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getSession_key() { + return session_key; + } + + public void setSession_key(String session_key) { + this.session_key = session_key; + } + + 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 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 getFollowup() { + return followup; + } + + public void setFollowup(String followup) { + this.followup = followup; + } +} diff --git a/src/main/java/com/example/web/entity/UsersContacts.java b/src/main/java/com/example/web/entity/UsersContacts.java new file mode 100644 index 0000000..936c747 --- /dev/null +++ b/src/main/java/com/example/web/entity/UsersContacts.java @@ -0,0 +1,122 @@ +package com.example.web.entity; + +import lombok.Data; + +import java.time.LocalDateTime; +@Data +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;//联系人账号 + private String bank;//联系人银行 + private String address;//联系人地址 + private LocalDateTime created_at;//创建时间 + 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) { + this.userId = userId; + this.id = id; + this.nickName = nickName; + this.phoneNumber = phoneNumber; + this.wechat = wechat; + this.account = account; + this.accountNumber = accountNumber; + this.bank = bank; + this.address = address; + this.created_at = created_at; + this.updated_at = updated_at; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getId() { + return id; + } + + public void setId(String id) { + 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; + } + + 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 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; + } +} diff --git a/src/main/java/com/example/web/entity/UsersManagements.java b/src/main/java/com/example/web/entity/UsersManagements.java new file mode 100644 index 0000000..53f6ec4 --- /dev/null +++ b/src/main/java/com/example/web/entity/UsersManagements.java @@ -0,0 +1,127 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 负责人信息实体类 + * 对应wechat_app数据源中的用户管理表 + * 存储微信小程序用户的负责人信息 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UsersManagements { + + private Integer id; // 自增主键id + private String userId; // 用户id + private String managerId; // 负责人id + private String managercompany; // 负责公司 + private String managerdepartment; // 负责部门 + private String organization; // 负责小组 + private String role; // 角色 + private String root; // 权限 + private LocalDateTime created_at; // 创建时间 + private LocalDateTime updated_at; // 更新时间 + private String userName;//负责人姓名 + private String assistant;//协助人 + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + 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 getRoot() { + return root; + } + + public void setRoot(String root) { + this.root = root; + } + + 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 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; + } +} diff --git a/src/main/java/com/example/web/mapper/Cart_itemsMapper.java b/src/main/java/com/example/web/mapper/Cart_itemsMapper.java new file mode 100644 index 0000000..c16e300 --- /dev/null +++ b/src/main/java/com/example/web/mapper/Cart_itemsMapper.java @@ -0,0 +1,9 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +@DataSource("wechat") +public interface Cart_itemsMapper { +} diff --git a/src/main/java/com/example/web/mapper/ContactsMapper.java b/src/main/java/com/example/web/mapper/ContactsMapper.java new file mode 100644 index 0000000..e560bc6 --- /dev/null +++ b/src/main/java/com/example/web/mapper/ContactsMapper.java @@ -0,0 +1,35 @@ +package com.example.web.mapper; + +import com.example.web.entity.Contacts; +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.util.List; + +@Mapper +public interface ContactsMapper { + + // 根据企业ID查询联系人信息 + List selectContactsByEnterpriseId(String id); + + // 查询所有联系人信息 + List selectAllContacts(); + // 新增联系人 + int insertContacts(Contacts contacts); + // 新增:根据电话号码查询记录数(用于判断是否重复) + int countByPhoneNumber(String phoneNumber); + // 新增:根据手机号查询联系人(关联企业信息) + Contacts selectByPhoneNumber(String phoneNumber); + // 修改编辑功能 + int updateContacts(Contacts contacts); + + // 查询跟进信息 + @Select("SELECT followup FROM contacts WHERE phoneNumber = #{phoneNumber}") + 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); +} diff --git a/src/main/java/com/example/web/mapper/EnterpriseMapper.java b/src/main/java/com/example/web/mapper/EnterpriseMapper.java new file mode 100644 index 0000000..e61c412 --- /dev/null +++ b/src/main/java/com/example/web/mapper/EnterpriseMapper.java @@ -0,0 +1,24 @@ +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 selectAllEnterpriseInfo(); + + // 根据企业ID查询详细信息 + EnterpriseInfoDTO selectEnterpriseInfoById(String id); + + // 查询所有企业基本信息 + List selectAllEnterprises(); + // 新增企业 + int insertEnterprise(Enterprise enterprise); + //编辑修改 + int updateEnterprise(Enterprise enterprise); +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/LoginMapper.java b/src/main/java/com/example/web/mapper/LoginMapper.java new file mode 100644 index 0000000..59a7ef9 --- /dev/null +++ b/src/main/java/com/example/web/mapper/LoginMapper.java @@ -0,0 +1,22 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import com.example.web.entity.Login; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +@DataSource("primary") +public interface LoginMapper { + + + // 移除 @Select 注解,使用 XML 配置 + Login findByProjectNameAndUserName(@Param("projectName") String projectName, + @Param("userName") String userName); + + // 新增方法 - 支持动态查询 + List findByConditions(@Param("projectName") String projectName, + @Param("userName") String userName); +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/ManagersMapper.java b/src/main/java/com/example/web/mapper/ManagersMapper.java new file mode 100644 index 0000000..c0534f1 --- /dev/null +++ b/src/main/java/com/example/web/mapper/ManagersMapper.java @@ -0,0 +1,35 @@ +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; +import java.util.Map; + +@Mapper +public interface ManagersMapper { + List selectManagersByEnterpriseId(String id); + + // 查询所有负责人信息 + List selectAllManagers(); + + // 分页查询负责人信息 + List selectAllManagersWithPagination(@Param("limit") int limit, @Param("offset") int offset); + + // 获取负责人总数 + int getManagersCount(); + + // 新增负责人 + int insertManagers(Managers managers); + + //编辑修改 + int updateManagers(Managers managers); + + // 根据负责人姓名查询 + List selectByUserName(@Param("userName") String userName); + + // 🔥 新增:根据用户名和managerId查询 + List selectByUserNameAndManagerId(@Param("userName") String userName, + @Param("managerId") String managerId); +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/ProductsMapper.java b/src/main/java/com/example/web/mapper/ProductsMapper.java new file mode 100644 index 0000000..5ef6371 --- /dev/null +++ b/src/main/java/com/example/web/mapper/ProductsMapper.java @@ -0,0 +1,12 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +@DataSource("wechat") +public interface ProductsMapper { + + + +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/RootdbMapper.java b/src/main/java/com/example/web/mapper/RootdbMapper.java new file mode 100644 index 0000000..3900949 --- /dev/null +++ b/src/main/java/com/example/web/mapper/RootdbMapper.java @@ -0,0 +1,28 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import com.example.web.entity.Rootdb; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 权限信息Mapper接口 + * 对应userlogin数据源中的权限表操作 + * 使用@DataSource("primary")注解指定数据源 + */ +@Mapper +@DataSource("primary") +public interface RootdbMapper { + // 权限信息相关的数据库操作方法将在这里定义 + // 移除 @Select 注解,使用 XML 配置 + Rootdb findByProjectName(@Param("projectName") String projectName); + + // 新增方法 + List findAll(); + + List findByRootLevel(@Param("root") Integer root); + + List findByProjectNames(@Param("projectNames") List projectNames); +} diff --git a/src/main/java/com/example/web/mapper/SupplyCart_itemsMapper.java b/src/main/java/com/example/web/mapper/SupplyCart_itemsMapper.java new file mode 100644 index 0000000..5441a23 --- /dev/null +++ b/src/main/java/com/example/web/mapper/SupplyCart_itemsMapper.java @@ -0,0 +1,10 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +@DataSource("wechat") +public interface SupplyCart_itemsMapper { + +} diff --git a/src/main/java/com/example/web/mapper/SupplyContactsMapper.java b/src/main/java/com/example/web/mapper/SupplyContactsMapper.java new file mode 100644 index 0000000..e672326 --- /dev/null +++ b/src/main/java/com/example/web/mapper/SupplyContactsMapper.java @@ -0,0 +1,24 @@ +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 selectContactsByEnterpriseId(String id); + + // 查询所有联系人信息 + List selectAllContacts(); + // 新增联系人 + int insertContacts(Contacts contacts); + // 新增:根据电话号码查询记录数(用于判断是否重复) + int countByPhoneNumber(String phoneNumber); + // 新增:根据手机号查询联系人(关联企业信息) + Contacts selectByPhoneNumber(String phoneNumber); + // 修改编辑功能 + int updateContacts(Contacts contacts); +} diff --git a/src/main/java/com/example/web/mapper/SupplyEnterpriseMapper.java b/src/main/java/com/example/web/mapper/SupplyEnterpriseMapper.java new file mode 100644 index 0000000..47f7eb9 --- /dev/null +++ b/src/main/java/com/example/web/mapper/SupplyEnterpriseMapper.java @@ -0,0 +1,24 @@ +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 selectAllEnterpriseInfo(); + + // 根据企业ID查询详细信息 + EnterpriseInfoDTO selectEnterpriseInfoById(String id); + + // 查询所有企业基本信息 + List selectAllEnterprises(); + // 新增企业 + int insertEnterprise(Enterprise enterprise); + //编辑修改 + int updateEnterprise(Enterprise enterprise); +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/SupplyManagersMapper.java b/src/main/java/com/example/web/mapper/SupplyManagersMapper.java new file mode 100644 index 0000000..19cd8d0 --- /dev/null +++ b/src/main/java/com/example/web/mapper/SupplyManagersMapper.java @@ -0,0 +1,28 @@ +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 selectManagersByEnterpriseId(String id); + + // 查询所有负责人信息 + List selectAllManagers(); + + // 新增负责人 + int insertManagers(Managers managers); + + //编辑修改 + int updateManagers(Managers managers); + + // 根据负责人姓名查询 + List selectByUserName(@Param("userName") String userName); + + // 🔥 新增:根据用户名和managerId查询 + List selectByUserNameAndManagerId(@Param("userName") String userName, + @Param("managerId") String managerId); +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/SupplyProductsMapper.java b/src/main/java/com/example/web/mapper/SupplyProductsMapper.java new file mode 100644 index 0000000..91768a5 --- /dev/null +++ b/src/main/java/com/example/web/mapper/SupplyProductsMapper.java @@ -0,0 +1,12 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +@DataSource("wechat") +public interface SupplyProductsMapper { + + + +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/SupplyUsersManagementsMapper.java b/src/main/java/com/example/web/mapper/SupplyUsersManagementsMapper.java new file mode 100644 index 0000000..3044876 --- /dev/null +++ b/src/main/java/com/example/web/mapper/SupplyUsersManagementsMapper.java @@ -0,0 +1,64 @@ +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 findByUserNameAndManagerId(@Param("userName") String userName, + @Param("managerId") String managerId); + + // 新增方法 + List findByDepartment(@Param("department") String department); + + List 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 findAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo); + + // 🔥 新增:批量查询用户负责人信息 + List findByUserIds(@Param("userIds") List userIds); + +} diff --git a/src/main/java/com/example/web/mapper/SupplyUsersMapper.java b/src/main/java/com/example/web/mapper/SupplyUsersMapper.java new file mode 100644 index 0000000..3289a22 --- /dev/null +++ b/src/main/java/com/example/web/mapper/SupplyUsersMapper.java @@ -0,0 +1,266 @@ +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 getSellerProducts(@Param("sellerId") String sellerId); + + + /** + * 获取所有用户的基本信息 + */ + List 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 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 getUserAllContacts(@Param("userId") String userId); + + + /** + * 采购端:获取用户所有产品信息 - 一对多逻辑新增 + */ + List 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 findUnclassifiedCustomersOlderThan(LocalDateTime thresholdTime); + + /** + * 查询超过指定时间的组织公海池客户 + */ + List 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 findRecentlyRecycledCustomers(@Param("sinceTime") LocalDateTime sinceTime); + + // 新增:直接查询有权限的客户列表(支持分页) + List getAuthorizedCustomers(@Param("authInfo") ManagerAuthInfo authInfo, @Param("limit") int limit, @Param("offset") int offset); + + /** + * 根据认证信息获取有权限的用户ID列表 + */ + List getAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo); + + // 获取授权客户总数 + int getAuthorizedCustomersCount(@Param("authInfo") ManagerAuthInfo authInfo); + + /** + * 根据手机号和权限查询用户 + */ + UserProductCartDTO selectByPhoneWithAuth(@Param("phoneNumber") String phoneNumber, + @Param("authInfo") ManagerAuthInfo authInfo); + + // 🔥 新增:批量查询用户联系人信息 + List getUserContactsByUserIds(@Param("userIds") List userIds); + + // 🔥 新增:批量查询用户产品信息 + List getSellerProductsByUserIds(@Param("userIds") List userIds); + + // 🔥 新增:批量查询用户基本信息 + List getUserBasicInfoByUserIds(@Param("userIds") List 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); +} \ No newline at end of file diff --git a/src/main/java/com/example/web/mapper/UsersManagementsMapper.java b/src/main/java/com/example/web/mapper/UsersManagementsMapper.java new file mode 100644 index 0000000..09a248c --- /dev/null +++ b/src/main/java/com/example/web/mapper/UsersManagementsMapper.java @@ -0,0 +1,63 @@ +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 findByUserNameAndManagerId(@Param("userName") String userName, + @Param("managerId") String managerId); + + // 新增方法 + List findByDepartment(@Param("department") String department); + + List 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 findAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo); + + // 🔥 新增:批量查询用户负责人信息 + List findByUserIds(@Param("userIds") List userIds); +} diff --git a/src/main/java/com/example/web/mapper/UsersMapper.java b/src/main/java/com/example/web/mapper/UsersMapper.java new file mode 100644 index 0000000..7bf4f74 --- /dev/null +++ b/src/main/java/com/example/web/mapper/UsersMapper.java @@ -0,0 +1,269 @@ +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 getSellerProducts(@Param("sellerId") String sellerId); + + // 销售端:获取买家的购物车信息(公海需求) + List getBuyerCartItems(@Param("userId") String userId); + + /** + * 销售端:获取所有买家用户的基本信息(类型为buyer和both) + */ + List 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 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 getUserAllContacts(@Param("userId") String userId); + + /** + * 获取用户所有购物车项 - 一对多逻辑新增 + */ + List 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 findUnclassifiedCustomersOlderThan(LocalDateTime thresholdTime); + + /** + * 查询超过指定时间的组织公海池客户 + */ + List 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 findRecentlyRecycledCustomers(@Param("sinceTime") LocalDateTime sinceTime); + + // 新增:直接查询有权限的客户列表(支持分页) + List getAuthorizedCustomers(@Param("authInfo") ManagerAuthInfo authInfo, @Param("limit") int limit, @Param("offset") int offset); + + /** + * 根据认证信息获取有权限的用户ID列表 + */ + List getAuthorizedUserIds(@Param("authInfo") ManagerAuthInfo authInfo); + + // 获取授权客户总数 + int getAuthorizedCustomersCount(@Param("authInfo") ManagerAuthInfo authInfo); + + /** + * 根据手机号和权限查询用户 + */ + UserProductCartDTO selectByPhoneWithAuth(@Param("phoneNumber") String phoneNumber, + @Param("authInfo") ManagerAuthInfo authInfo); + + // 🔥 新增:批量查询用户联系人信息 + List getUserContactsByUserIds(@Param("userIds") List userIds); + + // 🔥 新增:批量查询用户购物车信息 + List getCartItemsByUserIds(@Param("userIds") List userIds); + + // 🔥 新增:批量查询用户基本信息 + List getUserBasicInfoByUserIds(@Param("userIds") List userIds); +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/CustomerService.java b/src/main/java/com/example/web/service/CustomerService.java new file mode 100644 index 0000000..d550ed9 --- /dev/null +++ b/src/main/java/com/example/web/service/CustomerService.java @@ -0,0 +1,1328 @@ +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.Managers; +import com.example.web.entity.UsersManagements; +import com.example.web.mapper.*; +import lombok.RequiredArgsConstructor; +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.Arrays; +import java.util.List; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class CustomerService { + + @Autowired + private ContactsMapper contactsMapper; + + @Autowired + private UsersMapper usersMapper; + + @Autowired + private EnterpriseMapper enterpriseMapper; + + @Autowired + private UsersManagementsMapper usersManagementsMapper; + + @Autowired + private EnterpriseService enterpriseService; + + @Autowired + private PoolCustomerService poolCustomerService; + + + // ==================== 精确更新方法 ==================== + + /** + * 精确更新基于电话号码的客户信息 - 支持精确修改特定联系人或购物车项 + * 专门用于公海池客户更新,处理客户等级变化时的负责人信息更新 + */ + public boolean updatePhoneBasedCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo) { + validatePhoneNumber(dto.getPhoneNumber()); + + System.out.println("🚀 开始精确更新公海池客户信息,手机号: " + dto.getPhoneNumber()); + + try { + // 1. 验证客户存在性 + UserProductCartDTO existingUser = validateAndGetUser(dto.getPhoneNumber()); + String userId = existingUser.getUserId(); + + // 记录原始等级用于比较 + String originalLevel = existingUser.getLevel(); + String newLevel = dto.getLevel(); + + System.out.println("📊 客户等级变化检查 - 原始: " + originalLevel + ", 新: " + newLevel); + + // 🔥 修复:添加详细的类型转换日志 + System.out.println("🔄 开始类型转换,原始类型: " + dto.getType()); + + // 类型转换逻辑 + String originalType = dto.getType(); + String convertedType = convertCustomerType(originalType); + dto.setType(convertedType); + + System.out.println("🔄 类型转换完成: " + originalType + " → " + convertedType); + + // 2. 权限校验 + System.out.println("🔐 开始权限校验,当前类型: " + dto.getType()); + validateSalesPermission(dto.getType()); + + // 3. 更新用户基本信息 + updateUserBasicInfo(dto, existingUser); + + // 4. 精确更新联系人信息 + updateContactInfoPrecise(dto, userId); + + // 5. 精确更新购物车信息 + updateCartItemInfoPrecise(dto, userId); + + // 6. 检查是否需要生成/更新负责人信息(从公海池变为非公海池)- 传递authInfo + checkAndUpdateManagerInfo(originalLevel, newLevel, dto, userId, authInfo); + + System.out.println("✅ 公海池客户信息精确更新成功"); + return true; + + } catch (Exception e) { + System.err.println("❌ 精确更新公海池客户信息失败: " + e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("更新公海池客户信息失败: " + e.getMessage(), e); + } + } + + /** + * 统一的客户类型转换方法 + */ + private String convertCustomerType(String originalType) { + if (originalType == null) { + System.out.println("⚠️ 客户类型为空,使用默认值 buyer"); + return "buyer"; + } + + switch (originalType.toUpperCase()) { + case "BOTH": + case "供应端&BOTH": + System.out.println("🔄 转换 BOTH 类型"); + return "both"; + case "客户端": + case "BUYER": + System.out.println("🔄 转换 buyer 类型"); + return "buyer"; + case "供应端": + case "SELLER": + System.out.println("🔄 转换 seller 类型"); + return "seller"; + default: + System.out.println("⚠️ 未知类型: " + originalType + ",使用原值"); + return originalType; + } + } + + /** + * 检查并更新负责人信息(当客户从公海池变为非公海池时,或者前端修改了负责人信息时) + */ + private void checkAndUpdateManagerInfo(String originalLevel, String newLevel, + UnifiedCustomerDTO dto, String userId, ManagerAuthInfo authInfo) { + // 定义公海池等级 + Set publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池"); + + // 定义非公海池等级 + Set nonPublicSeaLevels = Set.of("important", "normal", "low-value", "logistics", "unclassified"); + + // 检查是否从公海池变为非公海池 + boolean isFromPublicSeaToNonPublic = publicSeaLevels.contains(originalLevel) && + nonPublicSeaLevels.contains(newLevel); + + // 🔥 新增:检查前端是否修改了负责人信息 + boolean hasFrontendManagerInfo = hasFrontendManagerInfo(dto); + boolean isNonPublicSeaCustomer = nonPublicSeaLevels.contains(newLevel); + + System.out.println("🎯 等级变化分析: " + + "原始=" + originalLevel + + ", 新=" + newLevel + + ", 是否公海池变为非公海池=" + isFromPublicSeaToNonPublic + + ", 是否非公海池客户=" + isNonPublicSeaCustomer + + ", 前端是否提交负责人信息=" + hasFrontendManagerInfo); + + // 🔥 修改条件:公海池变为非公海池 OR (已经是非公海池客户且前端提交了负责人信息) + boolean shouldUpdateManager = isFromPublicSeaToNonPublic || + (isNonPublicSeaCustomer && hasFrontendManagerInfo); + + if (shouldUpdateManager) { + System.out.println("🎯 检测到需要更新负责人信息,开始处理"); + + if (hasFrontendManagerInfo) { + System.out.println("✅ 前端提交了负责人信息,使用前端数据"); + System.out.println("👤 前端负责人信息:"); + System.out.println(" 公司: " + dto.getManagercompany()); + System.out.println(" 部门: " + dto.getManagerdepartment()); + System.out.println(" 组织: " + dto.getOrganization()); + System.out.println(" 角色: " + dto.getRole()); + System.out.println(" 负责人: " + dto.getUserName()); + System.out.println(" 协助人: " + dto.getAssistant()); + } else { + System.out.println("🔄 前端未提交负责人信息,使用当前登录用户信息"); + System.out.println("👤 使用当前登录用户信息填充负责人记录:"); + System.out.println(" 公司: " + authInfo.getManagercompany()); + System.out.println(" 部门: " + authInfo.getManagerdepartment()); + System.out.println(" 组织: " + authInfo.getOrganization()); + System.out.println(" 角色: " + authInfo.getRole()); + System.out.println(" 负责人: " + authInfo.getUserName()); + System.out.println(" 协助人: " + authInfo.getAssistant()); + } + + try { + // 处理负责人信息记录 - 根据是否有前端信息选择数据源 + handleManagerRecord(userId, dto, authInfo, hasFrontendManagerInfo); + System.out.println("✅ 负责人信息处理成功"); + } catch (Exception e) { + System.err.println("❌ 处理负责人信息失败: " + e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("处理负责人信息失败: " + e.getMessage(), e); + } + } else { + System.out.println("ℹ️ 无需处理负责人信息 - " + + (isFromPublicSeaToNonPublic ? "" : "客户等级未从公海池变为非公海池") + + (hasFrontendManagerInfo && !isNonPublicSeaCustomer ? "且客户不是非公海池客户" : "")); + System.out.println(" 原始等级: " + originalLevel + ", 新等级: " + newLevel); + } + } + + /** + * 检查前端是否提交了负责人信息 + */ + private boolean hasFrontendManagerInfo(UnifiedCustomerDTO dto) { + return StringUtils.hasText(dto.getManagercompany()) || + StringUtils.hasText(dto.getManagerdepartment()) || + StringUtils.hasText(dto.getOrganization()) || + StringUtils.hasText(dto.getRole()) || + StringUtils.hasText(dto.getUserName()) || + StringUtils.hasText(dto.getAssistant()); + } + + + /** + * 处理负责人信息记录 - 更新或创建(支持前端数据和登录数据) + */ + private void handleManagerRecord(String userId, UnifiedCustomerDTO dto, ManagerAuthInfo authInfo, boolean useFrontendData) { + try { + System.out.println("🔍 开始处理负责人记录,用户ID: " + userId); + System.out.println("📝 数据来源: " + (useFrontendData ? "前端提交" : "登录信息")); + + // 检查是否已存在负责人记录 + UsersManagements existingManager = usersManagementsMapper.findByUserId(userId); + + if (existingManager != null) { + System.out.println("🔄 负责人记录已存在,进行更新"); + // 更新现有记录 - 根据数据来源选择 + updateExistingManagerRecord(existingManager, dto, authInfo, useFrontendData); + } else { + System.out.println("➕ 创建新的负责人记录"); + // 创建新记录 - 根据数据来源选择 + createNewManagerRecord(userId, dto, authInfo, useFrontendData); + } + + // 验证负责人信息是否成功处理 + UsersManagements verifiedManager = usersManagementsMapper.findByUserId(userId); + System.out.println("✅ 负责人信息验证: " + (verifiedManager != null ? "成功" : "失败")); + if (verifiedManager != null) { + System.out.println("📝 验证后的负责人信息: " + + "公司=" + verifiedManager.getManagercompany() + + ", 部门=" + verifiedManager.getManagerdepartment() + + ", 组织=" + verifiedManager.getOrganization() + + ", 角色=" + verifiedManager.getRole() + + ", 负责人=" + verifiedManager.getUserName() + + ", 协助人=" + verifiedManager.getAssistant()); + } + + } catch (Exception e) { + System.err.println("❌ 处理负责人记录失败: " + e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("处理负责人记录失败: " + e.getMessage(), e); + } + } + + /** + * 更新现有的负责人记录 - 支持前端数据和登录数据 + */ + private void updateExistingManagerRecord(UsersManagements existingManager, UnifiedCustomerDTO dto, + ManagerAuthInfo authInfo, boolean useFrontendData) { + if (useFrontendData) { + // 使用前端提交的数据 + existingManager.setManagercompany(getSafeString(dto.getManagercompany())); + existingManager.setManagerdepartment(getSafeString(dto.getManagerdepartment())); + existingManager.setOrganization(getSafeString(dto.getOrganization())); + existingManager.setRole(getSafeString(dto.getRole())); + existingManager.setUserName(getSafeString(dto.getUserName())); + existingManager.setAssistant(getSafeString(dto.getAssistant())); + } else { + // 使用当前登录用户信息 + existingManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + existingManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + existingManager.setOrganization(getSafeString(authInfo.getOrganization())); + existingManager.setRole(getSafeString(authInfo.getRole())); + existingManager.setUserName(getSafeString(authInfo.getUserName())); + existingManager.setAssistant(getSafeString(authInfo.getAssistant())); + } + + // 确保managerId不为空 + if (existingManager.getManagerId() == null || existingManager.getManagerId().isEmpty()) { + existingManager.setManagerId(authInfo.getManagerId()); + } + + // 确保root不为空 + if (existingManager.getRoot() == null || existingManager.getRoot().isEmpty()) { + existingManager.setRoot("3"); + } + + existingManager.setUpdated_at(LocalDateTime.now()); + + int rows = usersManagementsMapper.updateUsersManagements(existingManager); + System.out.println("✅ 更新负责人记录影响行数: " + rows); + } + + + /** + * 创建新的负责人记录 - 支持前端数据和登录数据 + */ + private void createNewManagerRecord(String userId, UnifiedCustomerDTO dto, + ManagerAuthInfo authInfo, boolean useFrontendData) { + UsersManagements newManager = new UsersManagements(); + newManager.setUserId(userId); + + if (useFrontendData) { + // 使用前端提交的数据 + newManager.setManagercompany(getSafeString(dto.getManagercompany())); + newManager.setManagerdepartment(getSafeString(dto.getManagerdepartment())); + newManager.setOrganization(getSafeString(dto.getOrganization())); + newManager.setRole(getSafeString(dto.getRole())); + newManager.setUserName(getSafeString(dto.getUserName())); + newManager.setAssistant(getSafeString(dto.getAssistant())); + } else { + // 使用当前登录用户信息 + newManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + newManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + newManager.setOrganization(getSafeString(authInfo.getOrganization())); + newManager.setRole(getSafeString(authInfo.getRole())); + newManager.setUserName(getSafeString(authInfo.getUserName())); + newManager.setAssistant(getSafeString(authInfo.getAssistant())); + } + + newManager.setManagerId(getSafeString(authInfo.getManagerId())); + newManager.setRoot("3"); // 默认权限 + + newManager.setCreated_at(LocalDateTime.now()); + newManager.setUpdated_at(LocalDateTime.now()); + + int rows = usersManagementsMapper.insertUsersManagements(newManager); + System.out.println("✅ 插入负责人记录影响行数: " + rows); + } + + /** + * 处理负责人信息记录 - 更新或创建 + */ + private void handleManagerRecord(String userId, ManagerAuthInfo authInfo) { + try { + System.out.println("🔍 开始处理负责人记录,用户ID: " + userId); + + // 检查是否已存在负责人记录 + UsersManagements existingManager = usersManagementsMapper.findByUserId(userId); + + if (existingManager != null) { + System.out.println("🔄 负责人记录已存在,进行更新"); + // 更新现有记录 - 使用当前登录用户信息 + updateExistingManagerRecord(existingManager, authInfo); + } else { + System.out.println("➕ 创建新的负责人记录"); + // 创建新记录 - 使用当前登录用户信息 + createNewManagerRecord(userId, authInfo); + } + + // 验证负责人信息是否成功处理 + UsersManagements verifiedManager = usersManagementsMapper.findByUserId(userId); + System.out.println("✅ 负责人信息验证: " + (verifiedManager != null ? "成功" : "失败")); + if (verifiedManager != null) { + System.out.println("📝 验证后的负责人信息: " + + "公司=" + verifiedManager.getManagercompany() + + ", 部门=" + verifiedManager.getManagerdepartment() + + ", 组织=" + verifiedManager.getOrganization() + + ", 角色=" + verifiedManager.getRole() + + ", 负责人=" + verifiedManager.getUserName() + + ", 协助人=" + verifiedManager.getAssistant()); + } + + } catch (Exception e) { + System.err.println("❌ 处理负责人记录失败: " + e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("处理负责人记录失败: " + e.getMessage(), e); + } + } + + + + /** + * 更新wechat数据源客户信息 - 重载方法,支持控制是否处理负责人信息 + */ + public boolean updateWechatCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo, boolean handleManagerInfo) { + validatePhoneNumber(dto.getPhoneNumber()); + + System.out.println("开始更新微信用户(wechat数据源),手机号: " + dto.getPhoneNumber()); + System.out.println("处理负责人信息: " + handleManagerInfo); + + UserProductCartDTO existingUser; + String userId = dto.getId(); // 优先使用DTO中的ID + + // 关键修复:优先使用用户ID进行精确查询,避免多结果问题 + if (userId != null && !userId.trim().isEmpty()) { + System.out.println("优先通过用户ID查询:" + userId); + try { + existingUser = usersMapper.selectByUserId(userId); + if (existingUser == null) { + throw new RuntimeException("未找到ID对应的客户:" + userId); + } + } catch (Exception e) { + System.out.println("通过ID查询失败,尝试通过手机号查询: " + e.getMessage()); + existingUser = validateAndGetUser(dto.getPhoneNumber()); + userId = existingUser.getUserId(); + } + } else { + // 如果没有提供ID,回退到通过手机号查询 + existingUser = validateAndGetUser(dto.getPhoneNumber()); + userId = existingUser.getUserId(); + } + + // 记录原始等级用于比较 + String originalLevel = existingUser.getLevel(); + String newLevel = dto.getLevel(); + + System.out.println("📊 客户等级变化检查 - 原始: " + originalLevel + ", 新: " + newLevel); + + // 🔥 修复:完善类型转换逻辑 + String type = dto.getType(); + if (type != null) { + switch (type) { + case "BOTH": + case "供应端&BOTH": + type = "both"; + break; + case "客户端": + case "buyer": + type = "buyer"; + break; + case "供应端": + case "seller": + type = "seller"; + break; + default: + // 保持原值 + break; + } + dto.setType(type); + System.out.println("🔄 类型转换结果: " + dto.getType() + " → " + type); + } else { + System.out.println("⚠️ 客户类型为空,使用默认值"); + dto.setType("buyer"); // 默认设为buyer + } + + validateSalesPermission(type); + + // 更新用户基本信息 + boolean success = updateWechatDataSource(dto, existingUser); + + // 根据参数决定是否处理负责人信息 + if (success && handleManagerInfo) { + checkAndUpdateManagerInfo(originalLevel, newLevel, dto, userId, authInfo); + } + + // 对于非公海池客户更新,不处理负责人信息 + return success; + } + + + + + /** + * 更新现有的负责人记录 - 使用当前登录用户信息 + */ + private void updateExistingManagerRecord(UsersManagements existingManager, ManagerAuthInfo authInfo) { + // 使用当前登录用户的认证信息填充 + existingManager.setManagerId(getSafeString(authInfo.getManagerId())); + existingManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + existingManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + existingManager.setOrganization(getSafeString(authInfo.getOrganization())); + existingManager.setRole(getSafeString(authInfo.getRole())); + existingManager.setUserName(getSafeString(authInfo.getUserName())); + existingManager.setAssistant(getSafeString(authInfo.getAssistant())); + + // 确保managerId不为空 + if (existingManager.getManagerId() == null || existingManager.getManagerId().isEmpty()) { + existingManager.setManagerId(authInfo.getManagerId()); + } + + // 确保root不为空 + if (existingManager.getRoot() == null || existingManager.getRoot().isEmpty()) { + existingManager.setRoot("3"); + } + + existingManager.setUpdated_at(LocalDateTime.now()); + + int rows = usersManagementsMapper.updateUsersManagements(existingManager); + System.out.println("✅ 更新负责人记录影响行数: " + rows); + } + + /** + * 创建新的负责人记录 - 使用当前登录用户信息 + */ + private void createNewManagerRecord(String userId, ManagerAuthInfo authInfo) { + UsersManagements newManager = new UsersManagements(); + newManager.setUserId(userId); + + // 使用当前登录用户的认证信息填充 + newManager.setManagerId(getSafeString(authInfo.getManagerId())); + newManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + newManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + newManager.setOrganization(getSafeString(authInfo.getOrganization())); + newManager.setRole(getSafeString(authInfo.getRole())); + newManager.setRoot("3"); // 默认权限 + newManager.setUserName(getSafeString(authInfo.getUserName())); + newManager.setAssistant(getSafeString(authInfo.getAssistant())); + + newManager.setCreated_at(LocalDateTime.now()); + newManager.setUpdated_at(LocalDateTime.now()); + + int rows = usersManagementsMapper.insertUsersManagements(newManager); + System.out.println("✅ 插入负责人记录影响行数: " + rows); + } + + + + /** + * 更新默认数据源客户信息 + */ + public boolean updateDefaultCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo) { + validateCompanyId(dto.getId()); + validateSalesPermission(dto.getType()); + + return enterpriseService.updateDefaultCustomer(dto, authInfo); + } + + + // ==================== 查询方法 ==================== + + /** + * 根据手机号查询客户 - 使用数据库权限过滤 + */ + public UnifiedCustomerDTO getCustomerByPhone(String phoneNumber, ManagerAuthInfo authInfo) { + validatePhoneNumber(phoneNumber); + + System.out.println("🔍 开始根据手机号查询授权客户: " + phoneNumber); + + try { + // 🎯 优化:使用带权限检查的查询方法 + UserProductCartDTO userInfo = usersMapper.selectByPhoneWithAuth(phoneNumber, authInfo); + if (userInfo == null) { + throw new RuntimeException("未找到手机号对应的客户:" + phoneNumber); + } + + // 🎯 优化:不再需要手动权限检查 + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo); + dto.setDataSource("wechat"); + return dto; + + } catch (Exception e) { + System.err.println("❌ 查询授权客户失败: " + e.getMessage()); + throw new RuntimeException("查询客户信息失败: " + e.getMessage()); + } + } + /** + * 检查是否有权限查看客户(统一权限检查方法) + */ + private boolean hasPermissionToViewCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + // 使用新的公海池判断逻辑 + if (isPublicSeaCustomer(userInfo, authInfo)) { + System.out.println("✅ 公海池客户,无需负责人权限验证"); + return true; + } + + // 非公海池客户需要检查负责人权限 + return hasUserManagerPermission(userInfo.getUserId(), authInfo); + } + /** + * 检查是否有权限查看微信客户 + */ + public boolean hasPermissionToViewWechatCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + // 公海池客户对所有人可见 + if (isPublicSeaCustomer(userInfo,authInfo)) { + System.out.println("✅ 公海池客户,无需负责人权限验证"); + return true; + } + + // 非公海池客户需要检查负责人权限 + return hasUserManagerPermission(userInfo.getUserId(), authInfo); + } + + /** + * 判断是否为公海池客户 - 根据认证字段是否有值判断 + */ + private boolean isPublicSeaCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + // 定义公海池等级 + Set publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池"); + + // 检查等级是否为公海池 + boolean isPublicSeaLevel = publicSeaLevels.contains(userInfo.getLevel()); + System.out.println("🔍 CustomerService - 客户等级检查: " + 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 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; + } + } + + /** + * 检查企业负责人权限 + */ + private boolean hasManagerPermission(EnterpriseInfoDTO enterpriseInfo, ManagerAuthInfo authInfo) { + if (enterpriseInfo.getManagers() == null) { + System.out.println("✅ 企业客户无负责人信息,允许查看"); + return true; + } + + return isManagerMatch(enterpriseInfo.getManagers(), authInfo); + } + + /** + * 检查负责人信息是否匹配(Managers)- 使用新字段 + */ + private boolean isManagerMatch(Managers manager, ManagerAuthInfo authInfo) { + boolean match = + (authInfo.getManagercompany() == null || authInfo.getManagercompany().equals(manager.getManagercompany())) && + (authInfo.getManagerdepartment() == null || authInfo.getManagerdepartment().equals(manager.getManagerdepartment())) && + (authInfo.getOrganization() == null || authInfo.getOrganization().equals(manager.getOrganization())) && + (authInfo.getRole() == null || authInfo.getRole().equals(manager.getRole())) && + (authInfo.getUserName() == null || authInfo.getUserName().equals(manager.getUserName())); + + System.out.println("🔐 负责人权限检查: " + (match ? "✅ 匹配" : "❌ 不匹配")); + return match; + } + + /** + * 检查负责人信息是否匹配(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; + } + + /** + * 根据企业ID查询客户信息(默认数据源)- 添加负责人过滤 + */ + public UnifiedCustomerDTO getCustomerById(String id, ManagerAuthInfo authInfo) { + EnterpriseInfoDTO enterpriseInfo = enterpriseService.getEnterpriseInfoById(id); + if (enterpriseInfo == null) { + return null; + } + + // 检查负责人权限 + if (!hasManagerPermission(enterpriseInfo, authInfo)) { + System.out.println("❌ 负责人权限不足,无法查看该客户"); + return null; + } + + return convertToUnifiedDTO(enterpriseInfo); + } + + // ==================== 私有方法 - 精确更新逻辑 ==================== + + /** + * 精确更新联系人信息 + */ + private void updateContactInfoPrecise(UnifiedCustomerDTO dto, String userId) { + if (dto.getTargetContactId() != null && !dto.getTargetContactId().trim().isEmpty()) { + // 精确更新特定联系人 + updateSpecificContact(dto, userId); + } else if (dto.getContacts() != null && !dto.getContacts().isEmpty()) { + // 更新第一个联系人(向后兼容) + updateFirstContact(dto, userId); + } else { + // 使用单个字段更新第一个联系人(向后兼容) + updateContactFromSingleFields(dto, userId); + } + } + + /** + * 精确更新购物车信息 + */ + private void updateCartItemInfoPrecise(UnifiedCustomerDTO dto, String userId) { + if (!"buyer".equals(dto.getType()) && !"both".equals(dto.getType())) { + return; + } + + if (dto.getTargetCartItemId() != null && !dto.getTargetCartItemId().trim().isEmpty()) { + // 精确更新特定购物车项 + updateSpecificCartItem(dto, userId); + } else if (dto.getCartItems() != null && !dto.getCartItems().isEmpty()) { + // 更新第一个购物车项(向后兼容) + updateFirstCartItem(dto, userId); + } else { + // 使用单个字段更新第一个购物车项(向后兼容) + updateCartItemFromSingleFields(dto, userId); + } + } + + /** + * 更新用户基本信息 + */ + private void updateUserBasicInfo(UnifiedCustomerDTO dto, UserProductCartDTO existingUser) { + UserProductCartDTO updateUser = new UserProductCartDTO(); + updateUser.setUserId(existingUser.getUserId()); + updateUser.setPhoneNumber(dto.getPhoneNumber()); + + // 只更新非空字段 + updateUser.setNickName(getUpdateValue(dto.getNickName(), existingUser.getNickName())); + updateUser.setType(dto.getType()); // 类型必须更新 + updateUser.setCompany(getUpdateValue(dto.getCompany(), existingUser.getCompany())); + updateUser.setRegion(getUpdateValue(dto.getRegion(), existingUser.getRegion())); + updateUser.setLevel(getUpdateValue(dto.getLevel(), existingUser.getLevel())); + updateUser.setDemand(getUpdateValue(dto.getDemand(), existingUser.getDemand())); + updateUser.setSpec(getUpdateValue(dto.getSpec(), existingUser.getSpec())); + // 优先使用前端传递的更新时间,如果没有则使用当前时间 + updateUser.setUpdated_at(dto.getUpdated_at() != null ? dto.getUpdated_at() : LocalDateTime.now()); + + int rows = usersMapper.updateByPhone(updateUser); + System.out.println("更新用户基本信息影响行数: " + rows); + + if (rows == 0) { + throw new RuntimeException("用户基本信息更新失败"); + } + } + + /** + * 精确更新特定联系人 + */ + private void updateSpecificContact(UnifiedCustomerDTO dto, String userId) { + System.out.println("📞 精确更新特定联系人,contactId: " + dto.getTargetContactId()); + + UnifiedCustomerDTO.ContactInfo contactData = dto.getUpdateContactData(); + if (contactData == null) { + System.out.println("未提供联系人更新数据,跳过联系人更新"); + return; + } + + int contactsRows = usersMapper.updateSpecificContact( + userId, + dto.getTargetContactId(), + contactData.getWechat(), + contactData.getAccount(), + contactData.getAccountNumber(), + contactData.getBank(), + contactData.getAddress() + ); + System.out.println("更新特定联系人影响行数: " + contactsRows); + + if (contactsRows == 0) { + System.out.println("⚠️ 特定联系人更新失败,可能ID不存在"); + } + } + + /** + * 更新第一个联系人(向后兼容) + */ + private void updateFirstContact(UnifiedCustomerDTO dto, String userId) { + System.out.println("📞 更新第一个联系人(向后兼容)"); + UnifiedCustomerDTO.ContactInfo contact = dto.getContacts().get(0); + + int contactsRows = usersMapper.updateContactsByUserId( + userId, + contact.getWechat(), + contact.getAccount(), + contact.getAccountNumber(), + contact.getBank(), + contact.getAddress() + ); + System.out.println("更新第一个联系人影响行数: " + contactsRows); + } + + /** + * 从单个字段更新联系人(向后兼容) + */ + private void updateContactFromSingleFields(UnifiedCustomerDTO dto, String userId) { + System.out.println("📞 从单个字段更新联系人(向后兼容)"); + + int contactsRows = usersMapper.updateContactsByUserId( + userId, + dto.getWechat(), + dto.getAccount(), + dto.getAccountNumber(), + dto.getBank(), + dto.getAddress() + ); + System.out.println("更新联系人信息影响行数: " + contactsRows); + } + + /** + * 精确更新特定购物车项 + */ + private void updateSpecificCartItem(UnifiedCustomerDTO dto, String userId) { + System.out.println("🛒 精确更新特定购物车项,cartItemId: " + dto.getTargetCartItemId()); + + UnifiedCustomerDTO.CartItem cartItemData = dto.getUpdateCartItemData(); + if (cartItemData == null) { + System.out.println("未提供购物车项更新数据,跳过购物车项更新"); + return; + } + + int cartRows = usersMapper.updateSpecificCartItem( + userId, + dto.getTargetCartItemId(), + cartItemData.getProductId(), + cartItemData.getProductName(), + cartItemData.getSpecification(), + cartItemData.getQuantity(), + cartItemData.getGrossWeight(), + cartItemData.getYolk() + ); + System.out.println("更新特定购物车项影响行数: " + cartRows); + + if (cartRows == 0) { + System.out.println("⚠️ 特定购物车项更新失败,可能ID不存在"); + } + } + + /** + * 更新第一个购物车项(向后兼容) + */ + private void updateFirstCartItem(UnifiedCustomerDTO dto, String userId) { + System.out.println("🛒 更新第一个购物车项(向后兼容)"); + UnifiedCustomerDTO.CartItem cartItem = dto.getCartItems().get(0); + + String productId = getExistingProductId(userId); + int cartRows = usersMapper.updateCartItemByBuyerId( + userId, + productId, + cartItem.getProductName(), + cartItem.getSpecification(), + cartItem.getQuantity(), + cartItem.getGrossWeight(), + cartItem.getYolk() + ); + System.out.println("更新公海需求信息(购物车)影响行数: " + cartRows); + } + + /** + * 从单个字段更新购物车项(向后兼容) + */ + private void updateCartItemFromSingleFields(UnifiedCustomerDTO dto, String userId) { + if (!StringUtils.hasText(dto.getProductName())) { + return; + } + + System.out.println("🛒 从单个字段更新购物车信息"); + + String productId = getExistingProductId(userId); + int cartRows = usersMapper.updateCartItemByBuyerId( + userId, + productId, + dto.getProductName(), + dto.getSpecification(), + dto.getQuantity(), + dto.getGrossWeight(), + dto.getYolk() + ); + System.out.println("更新公海需求信息(购物车)影响行数: " + cartRows); + + if (cartRows == 0) { + System.out.println("⚠️ 公海需求信息(购物车)更新影响0行,可能需要插入新记录"); + } + } + + // ==================== 私有方法 - 辅助方法 ==================== + + /** + * 验证手机号 + */ + private void validatePhoneNumber(String phoneNumber) { + if (!StringUtils.hasText(phoneNumber)) { + throw new IllegalArgumentException("手机号不能为空"); + } + } + + /** + * 验证公司ID + */ + private void validateCompanyId(String companyId) { + if (!StringUtils.hasText(companyId)) { + throw new IllegalArgumentException("公司ID不能为空"); + } + } + + /** + * 验证销售员权限 + */ + private void validateSalesPermission(String type) { + System.out.println("🔐 ====== 开始权限校验 ======"); + System.out.println("🔐 当前类型: " + type); + System.out.println("🔐 类型比较 - buyer: " + "buyer".equals(type)); + System.out.println("🔐 类型比较 - both: " + "both".equals(type)); + System.out.println("🔐 类型类名: " + (type != null ? type.getClass().getName() : "null")); + + if (!"buyer".equals(type) && !"both".equals(type)) { + System.out.println("❌ 权限校验失败:销售员只能更新为buyer或both类型,当前类型: " + type); + System.out.println("❌ 类型长度: " + (type != null ? type.length() : "null")); + System.out.println("❌ 类型字符: " + (type != null ? Arrays.toString(type.toCharArray()) : "null")); + throw new IllegalArgumentException("销售员权限只能操作客户端类型客户和BOTH类型客户"); + } + System.out.println("✅ 权限校验通过"); + System.out.println("🔐 ====== 权限校验结束 ======"); + } + + /** + * 验证并获取用户信息 + */ + private UserProductCartDTO validateAndGetUser(String phoneNumber) { + try { + // 尝试通过手机号精确查询 + UserProductCartDTO user = usersMapper.selectByPhone(phoneNumber); + if (user == null) { + throw new RuntimeException("未找到手机号对应的客户:" + phoneNumber); + } + System.out.println("找到现有客户,用户ID: " + user.getUserId()); + return user; + } catch (org.apache.ibatis.exceptions.TooManyResultsException e) { + // 处理多个结果的情况 - 这是一个关键修复 + System.out.println("警告:通过手机号" + phoneNumber + "查询到多个客户,使用userId进行精确查询"); + // 由于在updateWechatCustomer方法中我们已经有userId,这里需要一个备用方案 + // 从更新的DTO中获取userId会更合适,但为了保持方法签名不变,我们做一个临时修复 + // 实际解决方案应该是修改updateWechatCustomer方法,传入userId进行精确查询 + throw new RuntimeException("查询到多个客户记录,请提供更精确的查询条件"); + } + } + + /** + * 获取更新值(优先使用新值,如果新值为空则使用原值) + */ + private String getUpdateValue(String newValue, String originalValue) { + return StringUtils.hasText(newValue) ? newValue : originalValue; + } + + /** + * 获取现有的产品ID + */ + private String getExistingProductId(String userId) { + List existingCartItems = usersMapper.getBuyerCartItems(userId); + return existingCartItems != null && !existingCartItems.isEmpty() ? + existingCartItems.get(0).getProductId() : "default_product_id"; + } + + /** + * 只更新wechat数据源(包含负责人信息生成) + */ + private boolean updateWechatDataSource(UnifiedCustomerDTO dto, UserProductCartDTO existingUser) { + UserProductCartDTO updateUser = new UserProductCartDTO(); + updateUser.setUserId(existingUser.getUserId()); + updateUser.setPhoneNumber(dto.getPhoneNumber()); + + // 只更新非空字段 + updateUser.setNickName(getUpdateValue(dto.getNickName(), existingUser.getNickName())); + updateUser.setType(dto.getType()); // 类型必须更新 + updateUser.setCompany(getUpdateValue(dto.getCompany(), existingUser.getCompany())); + updateUser.setRegion(getUpdateValue(dto.getRegion(), existingUser.getRegion())); + updateUser.setLevel(getUpdateValue(dto.getLevel(), existingUser.getLevel())); + updateUser.setDemand(getUpdateValue(dto.getDemand(), existingUser.getDemand())); + updateUser.setSpec(getUpdateValue(dto.getSpec(), existingUser.getSpec())); + // 优先使用前端传递的更新时间,如果没有则使用当前时间 + updateUser.setUpdated_at(dto.getUpdated_at() != null ? dto.getUpdated_at() : LocalDateTime.now()); + + int rows = usersMapper.updateByPhone(updateUser); + System.out.println("更新用户基本信息影响行数: " + rows); + + if (rows == 0) { + throw new RuntimeException("用户基本信息更新失败"); + } + + // 更新联系人信息 + updateContactInfoPrecise(dto, existingUser.getUserId()); + + // 更新购物车信息 + updateCartItemInfoPrecise(dto, existingUser.getUserId()); + + return rows > 0; + } + + // ==================== 转换方法 ==================== + + /** + * 为公海池客户转换DTO的方法 - 支持多个联系人和购物车项 + */ + private UnifiedCustomerDTO convertToUnifiedDTOForPublicSea(UserProductCartDTO userInfo) { + UnifiedCustomerDTO dto = new UnifiedCustomerDTO(); + dto.setId(userInfo.getUserId()); + dto.setPhoneNumber(userInfo.getPhoneNumber()); + dto.setNickName(userInfo.getNickName()); + dto.setType(userInfo.getType()); + dto.setCompany(getSafeString(userInfo.getCompany())); + dto.setRegion(getSafeString(userInfo.getRegion())); + dto.setLevel(getSafeString(userInfo.getLevel(), "公海池")); + dto.setDemand(getSafeString(userInfo.getDemand())); + dto.setSpec(getSafeString(userInfo.getSpec())); + dto.setCreated_at(userInfo.getCreated_at()); + dto.setUpdated_at(userInfo.getUpdated_at()); + + // 设置多个联系人信息 + if (userInfo.getUsersContacts() != null && !userInfo.getUsersContacts().isEmpty()) { + List contacts = new ArrayList<>(); + for (UserProductCartDTO.UsersContacts userContact : userInfo.getUsersContacts()) { + UnifiedCustomerDTO.ContactInfo contact = new UnifiedCustomerDTO.ContactInfo(); + contact.setContactId(getSafeString(userContact.getContactId())); + contact.setWechat(getSafeString(userContact.getWechat())); + contact.setAccount(getSafeString(userContact.getAccount())); + contact.setAccountNumber(getSafeString(userContact.getAccountNumber())); + contact.setBank(getSafeString(userContact.getBank())); + contact.setAddress(getSafeString(userContact.getAddress())); + contacts.add(contact); + } + dto.setContacts(contacts); + + // 向后兼容:设置第一个联系人到单个字段 + UserProductCartDTO.UsersContacts firstContact = userInfo.getUsersContacts().get(0); + dto.setWechat(firstContact.getWechat()); + dto.setAccount(firstContact.getAccount()); + dto.setAccountNumber(firstContact.getAccountNumber()); + dto.setBank(firstContact.getBank()); + dto.setAddress(firstContact.getAddress()); + } + + // 设置多个购物车项 + if (userInfo.getCartItems() != null && !userInfo.getCartItems().isEmpty()) { + List cartItems = new ArrayList<>(); + for (UserProductCartDTO.CartItem userCartItem : userInfo.getCartItems()) { + UnifiedCustomerDTO.CartItem cartItem = new UnifiedCustomerDTO.CartItem(); + cartItem.setCartItemId(getSafeString(userCartItem.getCartItemId())); + cartItem.setProductId(getSafeString(userCartItem.getProductId())); + cartItem.setProductName(getSafeString(userCartItem.getProductName())); + cartItem.setSpecification(getSafeString(userCartItem.getSpecification())); + cartItem.setQuantity(userCartItem.getQuantity() != null ? userCartItem.getQuantity() : 0); + cartItem.setGrossWeight(userCartItem.getGrossWeight() != null ? userCartItem.getGrossWeight() : ""); + cartItem.setYolk(getSafeString(userCartItem.getYolk())); + cartItems.add(cartItem); + } + dto.setCartItems(cartItems); + + // 向后兼容:设置第一个购物车项到单个字段 + UserProductCartDTO.CartItem firstCartItem = userInfo.getCartItems().get(0); + dto.setProductName(firstCartItem.getProductName()); + dto.setSpecification(firstCartItem.getSpecification()); + dto.setQuantity(firstCartItem.getQuantity()); + dto.setGrossWeight(firstCartItem.getGrossWeight()); + dto.setYolk(firstCartItem.getYolk()); + dto.setVariety(""); // 购物车没有品种字段 + } else { + setDefaultProductValues(dto); + } + + return dto; + } + + /** + * 将企业信息转换为UnifiedCustomerDTO - 使用新字段 + */ + private UnifiedCustomerDTO convertToUnifiedDTO(EnterpriseInfoDTO enterpriseInfo) { + UnifiedCustomerDTO dto = new UnifiedCustomerDTO(); + + if (enterpriseInfo.getEnterprise() != null) { + dto.setId(enterpriseInfo.getEnterprise().getId()); + dto.setCompany(getSafeString(enterpriseInfo.getEnterprise().getCompany())); + dto.setRegion(getSafeString(enterpriseInfo.getEnterprise().getRegion())); + dto.setLevel(getSafeString(enterpriseInfo.getEnterprise().getLevel())); + dto.setType(getSafeString(enterpriseInfo.getEnterprise().getType())); + dto.setDemand(getSafeString(enterpriseInfo.getEnterprise().getDemand())); + dto.setSpec(getSafeString(enterpriseInfo.getEnterprise().getSpec())); + } + + if (enterpriseInfo.getContacts() != null) { + dto.setNickName(getSafeString(enterpriseInfo.getContacts().getNickName())); + dto.setPhoneNumber(getSafeString(enterpriseInfo.getContacts().getPhoneNumber())); + dto.setWechat(getSafeString(enterpriseInfo.getContacts().getWechat())); + dto.setAccount(getSafeString(enterpriseInfo.getContacts().getAccount())); + dto.setAccountNumber(getSafeString(enterpriseInfo.getContacts().getAccountNumber())); + dto.setBank(getSafeString(enterpriseInfo.getContacts().getBank())); + dto.setAddress(getSafeString(enterpriseInfo.getContacts().getAddress())); + dto.setCreated_at(enterpriseInfo.getContacts().getCreated_at()); + dto.setUpdated_at(enterpriseInfo.getContacts().getUpdated_at()); + } + + if (enterpriseInfo.getManagers() != null) { + // 使用新的负责人字段 + dto.setManagerId(getSafeString(enterpriseInfo.getManagers().getManagerId())); + dto.setManagercompany(getSafeString(enterpriseInfo.getManagers().getManagercompany())); + dto.setManagerdepartment(getSafeString(enterpriseInfo.getManagers().getManagerdepartment())); + dto.setOrganization(getSafeString(enterpriseInfo.getManagers().getOrganization())); + dto.setRole(getSafeString(enterpriseInfo.getManagers().getRole())); + dto.setUserName(getSafeString(enterpriseInfo.getManagers().getUserName())); + dto.setAssistant(getSafeString(enterpriseInfo.getManagers().getAssistant())); + } + + return dto; + } + + /** + * 设置默认产品值 + */ + private void setDefaultProductValues(UnifiedCustomerDTO dto) { + dto.setProductName(""); + dto.setVariety(""); + dto.setSpecification(""); + dto.setQuantity(0); + dto.setGrossWeight(""); + dto.setYolk(""); + dto.setCustomDetails(new Object[0]); + } + + /** + * 安全获取字符串(带默认值) + */ + private String getSafeString(String value) { + return value != null ? value : ""; + } + + /** + * 安全获取字符串(带自定义默认值) + */ + private String getSafeString(String value, String defaultValue) { + return value != null ? value : defaultValue; + } + + /** + * 根据ID查询wechat数据源客户信息 + */ + public UnifiedCustomerDTO getWechatCustomerById(String userId, ManagerAuthInfo authInfo) { + try { + System.out.println("🔍 开始根据用户ID查询wechat客户: " + userId); + + UserProductCartDTO userInfo = poolCustomerService.getPublicSeaCustomerInfo(userId); + if (userInfo == null) { + System.out.println("❌ 未找到用户ID对应的客户:" + userId); + return null; + } + + // 销售员权限校验 + if (!"buyer".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) { + System.out.println("❌ 该客户不在销售员权限范围内,类型: " + userInfo.getType()); + return null; + } + + // 负责人权限校验 + if (!hasPermissionToViewCustomer(userInfo, authInfo)) { + System.out.println("❌ 负责人权限不足,无法查看该客户"); + return null; + } + + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo); + dto.setDataSource("wechat"); + return dto; + + } catch (Exception e) { + System.err.println("❌ 查询wechat客户信息失败: " + e.getMessage()); + return null; + } + } + /** + * 检查默认数据源中是否存在指定ID的客户 + */ + public boolean checkDefaultCustomerExists(String customerId) { + try { + System.out.println("🔍 检查默认数据源客户是否存在,客户ID: " + customerId); + + if (customerId == null || customerId.trim().isEmpty()) { + System.out.println("❌ 客户ID为空,无法检查"); + return false; + } + + // 使用现有的selectEnterpriseInfoById方法检查存在性 + EnterpriseInfoDTO enterpriseInfo = enterpriseMapper.selectEnterpriseInfoById(customerId); + + boolean exists = enterpriseInfo != null && enterpriseInfo.getEnterprise() != null; + System.out.println("📊 默认数据源客户检查结果: " + (exists ? "✅ 存在" : "❌ 不存在")); + + if (exists) { + System.out.println("📝 客户信息: 公司=" + enterpriseInfo.getEnterprise().getCompany() + + ", 区域=" + enterpriseInfo.getEnterprise().getRegion()); + } + + return exists; + + } catch (Exception e) { + System.err.println("❌ 检查默认数据源客户存在性失败: " + e.getMessage()); + e.printStackTrace(); + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/EnterpriseService.java b/src/main/java/com/example/web/service/EnterpriseService.java new file mode 100644 index 0000000..6ce6b92 --- /dev/null +++ b/src/main/java/com/example/web/service/EnterpriseService.java @@ -0,0 +1,501 @@ +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 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 getAllEnterprises() { + try { + return enterpriseMapper.selectAllEnterprises(); + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + /** + * 根据企业ID获取联系人信息 + */ + public List getContactsByEnterpriseId(String id) { + try { + return contactsMapper.selectContactsByEnterpriseId(id); + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + /** + * 根据企业ID获取负责人信息 + */ + public List getManagersByEnterpriseId(String id) { + try { + return managersMapper.selectManagersByEnterpriseId(id); + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + + /** + * 搜索企业信息(根据企业名称、地区、类型等) + */ + public List searchEnterprises(String keyword) { + try { + // 这里可以扩展更复杂的搜索逻辑 + List allInfo = enterpriseMapper.selectAllEnterpriseInfo(); + if (keyword == null || keyword.trim().isEmpty()) { + return allInfo; + } + + List 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 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 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; + } +} diff --git a/src/main/java/com/example/web/service/FollowUpService.java b/src/main/java/com/example/web/service/FollowUpService.java new file mode 100644 index 0000000..9727376 --- /dev/null +++ b/src/main/java/com/example/web/service/FollowUpService.java @@ -0,0 +1,59 @@ +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 +@RequiredArgsConstructor +public class FollowUpService { + + private final ContactsMapper contactsMapper; + private final UsersMapper usersMapper; + private final SupplyContactsMapper supplyContactsMapper; + private final 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; + } + } +} diff --git a/src/main/java/com/example/web/service/LoginService.java b/src/main/java/com/example/web/service/LoginService.java new file mode 100644 index 0000000..59c7592 --- /dev/null +++ b/src/main/java/com/example/web/service/LoginService.java @@ -0,0 +1,259 @@ +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 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 authenticate(String projectName, String userName, String password) { + Map 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 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 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 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; + } + + /** + * 将 Managers 实体转换为 UsersManagements 实体 + */ + 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; + } + + /** + * 判断是否为采购端工位 + */ + 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 buildUserResponse(UsersManagements userManagement) { + Map 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/PoolCustomerService.java b/src/main/java/com/example/web/service/PoolCustomerService.java new file mode 100644 index 0000000..441cbf7 --- /dev/null +++ b/src/main/java/com/example/web/service/PoolCustomerService.java @@ -0,0 +1,805 @@ +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 contacts = usersMapper.getUserAllContacts(userInfo.getUserId()); + // 转换为UserProductCartDTO.UsersContacts格式以保持兼容 + if (contacts != null && !contacts.isEmpty()) { + List 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 cartItems = usersMapper.getUserAllCartItems(userInfo.getUserId()); + // 转换为UserProductCartDTO.CartItem格式以保持兼容 + if (cartItems != null && !cartItems.isEmpty()) { + List 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 products = usersMapper.getSellerProducts(userId); + userInfo.setProducts(products != null ? products : Collections.emptyList()); + userInfo.setCartItems(Collections.emptyList()); + } else { + // 买家:查询购物车信息 + List cartItems = usersMapper.getBuyerCartItems(userId); + userInfo.setCartItems(cartItems != null ? cartItems : Collections.emptyList()); + userInfo.setProducts(Collections.emptyList()); + } + + // 3. 获取联系人信息 - 安全处理 + try { + List 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 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 authorizedUsers = usersMapper.getAuthorizedCustomers(authInfo, limit, offset); + + System.out.println("✅ 授权客户数据查询完成"); + System.out.println("📊 当前页获取到授权客户数据条数: " + (authorizedUsers != null ? authorizedUsers.size() : "null")); + + List result = new ArrayList<>(); + + if (authorizedUsers != null && !authorizedUsers.isEmpty()) { + // 🔥 修改:收集所有用户ID用于批量查询 + List userIds = authorizedUsers.stream() + .map(UserProductCartDTO::getUserId) + .filter(Objects::nonNull) + .filter(userId -> !userId.trim().isEmpty()) + .distinct() + .collect(Collectors.toList()); + + System.out.println("🔍 需要批量查询的用户数量: " + userIds.size()); + + // 🔥 修改:批量查询所有相关数据 + Map managerMap = batchQueryManagers(userIds); + Map> contactsMap = batchQueryContacts(userIds); + Map> 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 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 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; + } + + // 销售端权限校验 + 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 contacts = usersMapper.getUserAllContacts(userId); + if (contacts != null && !contacts.isEmpty()) { + List 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 cartItems = usersMapper.getUserAllCartItems(userId); + System.out.println("🔍 查询到的购物车数据条数: " + (cartItems != null ? cartItems.size() : 0)); + + if (cartItems != null && !cartItems.isEmpty()) { + List 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 getAllCustomersWithoutFilter() { + try { + System.out.println("===================================================="); + System.out.println("🚀 开始获取所有微信用户数据(无过滤,销售端)..."); + + // 1. 获取所有用户的基本信息 + System.out.println("📋 查询用户基础信息..."); + List allUsers = usersMapper.getAllUserBasicInfo(); + + System.out.println("✅ 基础用户数据查询完成"); + System.out.println("📊 获取到基础用户数据条数: " + (allUsers != null ? allUsers.size() : "null")); + + List result = new ArrayList<>(); + + if (allUsers != null && !allUsers.isEmpty()) { + // 🔥 修改:收集所有用户ID用于批量查询 + List userIds = allUsers.stream() + .map(UserProductCartDTO::getUserId) + .filter(Objects::nonNull) + .filter(userId -> !userId.trim().isEmpty()) + .distinct() + .collect(Collectors.toList()); + + System.out.println("🔍 需要批量查询的用户数量: " + userIds.size()); + + // 🔥 修改:批量查询所有相关数据 + Map managerMap = batchQueryManagers(userIds); + Map> contactsMap = batchQueryContacts(userIds); + Map> 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 batchQueryManagers(List userIds) { + Map managerMap = new HashMap<>(); + if (userIds != null && !userIds.isEmpty()) { + try { + List 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> batchQueryContacts(List userIds) { + Map> contactsMap = new HashMap<>(); + if (userIds != null && !userIds.isEmpty()) { + try { + List 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> batchQueryCartItems(List userIds) { + Map> cartItemsMap = new HashMap<>(); + if (userIds != null && !userIds.isEmpty()) { + try { + List 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 managerMap, + Map> contactsMap, + Map> 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 contacts = contactsMap.get(userId); + if (contacts != null && !contacts.isEmpty()) { + List 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 cartItems = cartItemsMap.get(userId); + if (cartItems != null && !cartItems.isEmpty()) { + List 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 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 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<>(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/RootService.java b/src/main/java/com/example/web/service/RootService.java new file mode 100644 index 0000000..830c76b --- /dev/null +++ b/src/main/java/com/example/web/service/RootService.java @@ -0,0 +1,10 @@ +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 { + +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/SupplyCustomerRecycleService.java b/src/main/java/com/example/web/service/SupplyCustomerRecycleService.java new file mode 100644 index 0000000..abc93cb --- /dev/null +++ b/src/main/java/com/example/web/service/SupplyCustomerRecycleService.java @@ -0,0 +1,204 @@ +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 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 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 getRecycledCustomersWithManagerInfo() { + log.info("🔍 获取回流客户完整信息(包含负责人信息)"); + + // 获取最近回流的客户(例如最近1天内回流的) + LocalDateTime sinceTime = LocalDateTime.now().minusDays(1); + List 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/SupplyCustomerService.java b/src/main/java/com/example/web/service/SupplyCustomerService.java new file mode 100644 index 0000000..f20a085 --- /dev/null +++ b/src/main/java/com/example/web/service/SupplyCustomerService.java @@ -0,0 +1,1330 @@ +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.Managers; +import com.example.web.entity.UsersManagements; +import com.example.web.mapper.*; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class SupplyCustomerService { + + @Autowired + private SupplyContactsMapper supplyContactsMapper; + + @Autowired + private SupplyUsersMapper supplyUsersMapper; + + @Autowired + private SupplyEnterpriseMapper supplyEnterpriseMapper; + + @Autowired + private SupplyUsersManagementsMapper supplyUsersManagementsMapper; + + @Autowired + private SupplyEnterpriseService supplyEnterpriseService; + + @Autowired + private SupplyPoolCustomerService supplyPoolCustomerService; + + // ==================== 精确更新方法 ==================== + + /** + * 精确更新基于电话号码的客户信息 - 支持精确修改特定联系人或产品项 + * 专门用于公海池客户更新,处理客户等级变化时的负责人信息更新 + */ + public boolean updatePhoneBasedCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo) { + validatePhoneNumber(dto.getPhoneNumber()); + + System.out.println("🚀 [采购端] 开始精确更新公海池客户信息,手机号: " + dto.getPhoneNumber()); + + try { + // 1. 验证客户存在性 + UserProductCartDTO existingUser = validateAndGetUser(dto.getPhoneNumber()); + String userId = existingUser.getUserId(); + + // 🔥 关键修复:规范化类型 + String normalizedType = normalizeCustomerType(dto.getType()); + dto.setType(normalizedType); + + // 记录原始等级用于比较 + String originalLevel = existingUser.getLevel(); + String newLevel = dto.getLevel(); + + System.out.println("📊 客户等级变化检查 - 原始: " + originalLevel + ", 新: " + newLevel); + + // 🔥 修复:添加详细的类型转换日志 + System.out.println("🔄 开始类型转换,原始类型: " + dto.getType()); + + // 类型转换逻辑 + String originalType = dto.getType(); + String convertedType = convertCustomerType(originalType); + dto.setType(convertedType); + + System.out.println("🔄 类型转换完成: " + originalType + " → " + convertedType); + + // 2. 权限校验 + System.out.println("🔐 开始权限校验,当前类型: " + dto.getType()); + validatePurchasePermission(dto.getType()); + + // 3. 更新用户基本信息 + updateUserBasicInfo(dto, existingUser); + + // 4. 精确更新联系人信息 + updateContactInfoPrecise(dto, userId); + + // 5. 精确更新产品信息 + updateProductInfoPrecise(dto, userId); + + // 6. 检查是否需要生成/更新负责人信息(从公海池变为非公海池)- 传递authInfo + checkAndUpdateManagerInfo(originalLevel, newLevel, dto, userId, authInfo); + + System.out.println("✅ 公海池客户信息精确更新成功"); + return true; + + } catch (Exception e) { + System.err.println("❌ 精确更新公海池客户信息失败: " + e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("更新公海池客户信息失败: " + e.getMessage(), e); + } + } + + /** + * 统一的客户类型转换方法 - 采购端 + */ + private String convertCustomerType(String originalType) { + if (originalType == null) { + System.out.println("⚠️ 客户类型为空,使用默认值 seller"); + return "seller"; + } + + switch (originalType.toUpperCase()) { + case "BOTH": + case "供应端&BOTH": + System.out.println("🔄 转换 BOTH 类型"); + return "both"; + case "客户端": + case "BUYER": + System.out.println("🔄 转换 buyer 类型"); + return "buyer"; + case "供应端": + case "SELLER": + System.out.println("🔄 转换 seller 类型"); + return "seller"; + default: + System.out.println("⚠️ 未知类型: " + originalType + ",使用原值"); + return originalType; + } + } + + private void checkAndUpdateManagerInfo(String originalLevel, String newLevel, + UnifiedCustomerDTO dto, String userId, ManagerAuthInfo authInfo) { + // 定义公海池和非公海池等级 + Set publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池"); + Set nonPublicSeaLevels = Set.of("important", "normal", "low-value", "logistics", "unclassified"); + + // 情况1:从公海池变为非公海池 + boolean isFromPublicSeaToNonPublic = publicSeaLevels.contains(originalLevel) && + nonPublicSeaLevels.contains(newLevel); + + // 情况2:已经是非公海池且前端提交了负责人信息 + boolean isNonPublicSeaWithManagerUpdate = nonPublicSeaLevels.contains(originalLevel) && + nonPublicSeaLevels.contains(newLevel) && + hasFrontendManagerInfo(dto); + + System.out.println("🎯 负责人信息更新检查: " + + "公海池→非公海池=" + isFromPublicSeaToNonPublic + + ", 非公海池更新负责人=" + isNonPublicSeaWithManagerUpdate); + + if (isFromPublicSeaToNonPublic || isNonPublicSeaWithManagerUpdate) { + System.out.println("🎯 需要处理负责人信息,数据来源: " + + (hasFrontendManagerInfo(dto) ? "前端提交" : "登录信息")); + + handleManagerRecord(userId, dto, authInfo, hasFrontendManagerInfo(dto)); + System.out.println("✅ 负责人信息处理成功"); + } else { + System.out.println("ℹ️ 无需处理负责人信息"); + System.out.println(" 原始等级: " + originalLevel + ", 新等级: " + newLevel); + System.out.println(" 前端提交负责人信息: " + hasFrontendManagerInfo(dto)); + } + } + + /** + * 检查前端是否提交了负责人信息 + */ + private boolean hasFrontendManagerInfo(UnifiedCustomerDTO dto) { + return StringUtils.hasText(dto.getManagercompany()) || + StringUtils.hasText(dto.getManagerdepartment()) || + StringUtils.hasText(dto.getOrganization()) || + StringUtils.hasText(dto.getRole()) || + StringUtils.hasText(dto.getUserName()) || + StringUtils.hasText(dto.getAssistant()); + } + + /** + * 处理负责人信息记录 - 更新或创建(支持前端数据和登录数据) + */ + private void handleManagerRecord(String userId, UnifiedCustomerDTO dto, ManagerAuthInfo authInfo, boolean useFrontendData) { + try { + System.out.println("🔍 开始处理负责人记录,用户ID: " + userId); + System.out.println("📝 数据来源: " + (useFrontendData ? "前端提交" : "登录信息")); + + // 检查是否已存在负责人记录 + UsersManagements existingManager = supplyUsersManagementsMapper.findByUserId(userId); + + if (existingManager != null) { + System.out.println("🔄 负责人记录已存在,进行更新"); + // 更新现有记录 - 根据数据来源选择 + updateExistingManagerRecord(existingManager, dto, authInfo, useFrontendData); + } else { + System.out.println("➕ 创建新的负责人记录"); + // 创建新记录 - 根据数据来源选择 + createNewManagerRecord(userId, dto, authInfo, useFrontendData); + } + + // 验证负责人信息是否成功处理 + UsersManagements verifiedManager = supplyUsersManagementsMapper.findByUserId(userId); + System.out.println("✅ 负责人信息验证: " + (verifiedManager != null ? "成功" : "失败")); + if (verifiedManager != null) { + System.out.println("📝 验证后的负责人信息: " + + "公司=" + verifiedManager.getManagercompany() + + ", 部门=" + verifiedManager.getManagerdepartment() + + ", 组织=" + verifiedManager.getOrganization() + + ", 角色=" + verifiedManager.getRole() + + ", 负责人=" + verifiedManager.getUserName() + + ", 协助人=" + verifiedManager.getAssistant()); + } + + } catch (Exception e) { + System.err.println("❌ 处理负责人记录失败: " + e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("处理负责人记录失败: " + e.getMessage(), e); + } + } + + /** + * 处理负责人信息记录 - 更新或创建(仅使用登录用户信息) + */ + private void handleManagerRecord(String userId, ManagerAuthInfo authInfo) { + try { + System.out.println("🔍 [采购端] 开始处理负责人记录,用户ID: " + userId); + + // 检查是否已存在负责人记录 + UsersManagements existingManager = supplyUsersManagementsMapper.findByUserId(userId); + + if (existingManager != null) { + System.out.println("🔄 负责人记录已存在,进行更新"); + // 更新现有记录 - 使用当前登录用户信息 + updateExistingManagerRecord(existingManager, authInfo); + } else { + System.out.println("➕ 创建新的负责人记录"); + // 创建新记录 - 使用当前登录用户信息 + createNewManagerRecord(userId, authInfo); + } + + // 验证负责人信息是否成功处理 + UsersManagements verifiedManager = supplyUsersManagementsMapper.findByUserId(userId); + System.out.println("✅ 负责人信息验证: " + (verifiedManager != null ? "成功" : "失败")); + if (verifiedManager != null) { + System.out.println("📝 验证后的负责人信息: " + + "公司=" + verifiedManager.getManagercompany() + + ", 部门=" + verifiedManager.getManagerdepartment() + + ", 组织=" + verifiedManager.getOrganization() + + ", 角色=" + verifiedManager.getRole() + + ", 负责人=" + verifiedManager.getUserName() + + ", 协助人=" + verifiedManager.getAssistant()); + } + + } catch (Exception e) { + System.err.println("❌ 处理负责人记录失败: " + e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("处理负责人记录失败: " + e.getMessage(), e); + } + } + + /** + * 更新现有的负责人记录 - 支持前端数据和登录数据 + */ + private void updateExistingManagerRecord(UsersManagements existingManager, UnifiedCustomerDTO dto, + ManagerAuthInfo authInfo, boolean useFrontendData) { + if (useFrontendData) { + // 使用前端提交的数据 + existingManager.setManagercompany(getSafeString(dto.getManagercompany())); + existingManager.setManagerdepartment(getSafeString(dto.getManagerdepartment())); + existingManager.setOrganization(getSafeString(dto.getOrganization())); + existingManager.setRole(getSafeString(dto.getRole())); + existingManager.setUserName(getSafeString(dto.getUserName())); + existingManager.setAssistant(getSafeString(dto.getAssistant())); + } else { + // 使用当前登录用户信息 + existingManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + existingManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + existingManager.setOrganization(getSafeString(authInfo.getOrganization())); + existingManager.setRole(getSafeString(authInfo.getRole())); + existingManager.setUserName(getSafeString(authInfo.getUserName())); + existingManager.setAssistant(getSafeString(authInfo.getAssistant())); + } + + // 确保managerId不为空 + if (existingManager.getManagerId() == null || existingManager.getManagerId().isEmpty()) { + existingManager.setManagerId(authInfo.getManagerId()); + } + + // 确保root不为空(采购端默认权限2) + if (existingManager.getRoot() == null || existingManager.getRoot().isEmpty()) { + existingManager.setRoot("2"); + } + + existingManager.setUpdated_at(LocalDateTime.now()); + + int rows = supplyUsersManagementsMapper.updateUsersManagements(existingManager); + System.out.println("✅ 更新负责人记录影响行数: " + rows); + } + + /** + * 创建新的负责人记录 - 支持前端数据和登录数据 + */ + private void createNewManagerRecord(String userId, UnifiedCustomerDTO dto, + ManagerAuthInfo authInfo, boolean useFrontendData) { + UsersManagements newManager = new UsersManagements(); + newManager.setUserId(userId); + + if (useFrontendData) { + // 使用前端提交的数据 + newManager.setManagercompany(getSafeString(dto.getManagercompany())); + newManager.setManagerdepartment(getSafeString(dto.getManagerdepartment())); + newManager.setOrganization(getSafeString(dto.getOrganization())); + newManager.setRole(getSafeString(dto.getRole())); + newManager.setUserName(getSafeString(dto.getUserName())); + newManager.setAssistant(getSafeString(dto.getAssistant())); + } else { + // 使用当前登录用户信息 + newManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + newManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + newManager.setOrganization(getSafeString(authInfo.getOrganization())); + newManager.setRole(getSafeString(authInfo.getRole())); + newManager.setUserName(getSafeString(authInfo.getUserName())); + newManager.setAssistant(getSafeString(authInfo.getAssistant())); + } + + newManager.setManagerId(getSafeString(authInfo.getManagerId())); + newManager.setRoot("2"); // 采购端默认权限 + + newManager.setCreated_at(LocalDateTime.now()); + newManager.setUpdated_at(LocalDateTime.now()); + + int rows = supplyUsersManagementsMapper.insertUsersManagements(newManager); + System.out.println("✅ 插入负责人记录影响行数: " + rows); + } + + /** + * 更新wechat数据源客户信息 - 重载方法,支持控制是否处理负责人信息 + */ + public boolean updateWechatCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo, boolean handleManagerInfo) { + validatePhoneNumber(dto.getPhoneNumber()); + + System.out.println("[采购端] 开始更新微信用户(wechat数据源),手机号: " + dto.getPhoneNumber()); + System.out.println("处理负责人信息: " + handleManagerInfo); + + UserProductCartDTO existingUser; + String userId = dto.getId(); // 优先使用DTO中的ID + + // 关键修复:优先使用用户ID进行精确查询,避免多结果问题 + if (userId != null && !userId.trim().isEmpty()) { + System.out.println("[采购端] 优先通过用户ID查询:" + userId); + try { + existingUser = supplyUsersMapper.selectByUserId(userId); + if (existingUser == null) { + throw new RuntimeException("未找到ID对应的客户:" + userId); + } + } catch (Exception e) { + System.out.println("[采购端] 通过ID查询失败,尝试通过手机号查询: " + e.getMessage()); + existingUser = validateAndGetUser(dto.getPhoneNumber()); + userId = existingUser.getUserId(); + } + } else { + // 如果没有提供ID,回退到通过手机号查询 + existingUser = validateAndGetUser(dto.getPhoneNumber()); + userId = existingUser.getUserId(); + } + + // 记录原始等级用于比较 + String originalLevel = existingUser.getLevel(); + String newLevel = dto.getLevel(); + + System.out.println("📊 客户等级变化检查 - 原始: " + originalLevel + ", 新: " + newLevel); + + // 🔥 修复:完善类型转换逻辑(增加规范化步骤) + String type = dto.getType(); + if (type != null) { + // 先规范化类型 + type = normalizeCustomerType(type); + switch (type) { + case "BOTH": + case "供应端&BOTH": + type = "both"; + break; + case "客户端": + case "buyer": + type = "buyer"; + break; + case "供应端": + case "seller": + type = "seller"; + break; + default: + // 保持原值 + break; + } + dto.setType(type); + System.out.println("🔄 类型转换结果: " + dto.getType() + " → " + type); + } else { + System.out.println("⚠️ 客户类型为空,使用默认值"); + dto.setType("seller"); // 采购端默认设为seller + } + + validatePurchasePermission(type); + + // 更新用户基本信息 + boolean success = updateWechatDataSource(dto, existingUser); + + // 根据参数决定是否处理负责人信息 + if (success && handleManagerInfo) { + checkAndUpdateManagerInfo(originalLevel, newLevel, dto, userId, authInfo); + } + + // 对于非公海池客户更新,不处理负责人信息 + return success; + } + + /** + * 更新现有的负责人记录 - 使用当前登录用户信息(采购端) + */ + private void updateExistingManagerRecord(UsersManagements existingManager, ManagerAuthInfo authInfo) { + // 使用当前登录用户的认证信息填充 + existingManager.setManagerId(getSafeString(authInfo.getManagerId())); + existingManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + existingManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + existingManager.setOrganization(getSafeString(authInfo.getOrganization())); + existingManager.setRole(getSafeString(authInfo.getRole())); + existingManager.setUserName(getSafeString(authInfo.getUserName())); + existingManager.setAssistant(getSafeString(authInfo.getAssistant())); + + // 确保managerId不为空 + if (existingManager.getManagerId() == null || existingManager.getManagerId().isEmpty()) { + existingManager.setManagerId(authInfo.getManagerId()); + } + + // 确保root不为空(采购端默认权限2) + if (existingManager.getRoot() == null || existingManager.getRoot().isEmpty()) { + existingManager.setRoot("2"); + } + + existingManager.setUpdated_at(LocalDateTime.now()); + + int rows = supplyUsersManagementsMapper.updateUsersManagements(existingManager); + System.out.println("✅ 更新负责人记录影响行数: " + rows); + } + + /** + * 创建新的负责人记录 - 使用当前登录用户信息(采购端) + */ + private void createNewManagerRecord(String userId, ManagerAuthInfo authInfo) { + UsersManagements newManager = new UsersManagements(); + newManager.setUserId(userId); + + // 使用当前登录用户的认证信息填充 + newManager.setManagerId(getSafeString(authInfo.getManagerId())); + newManager.setManagercompany(getSafeString(authInfo.getManagercompany())); + newManager.setManagerdepartment(getSafeString(authInfo.getManagerdepartment())); + newManager.setOrganization(getSafeString(authInfo.getOrganization())); + newManager.setRole(getSafeString(authInfo.getRole())); + newManager.setRoot("2"); // 采购端默认权限 + newManager.setUserName(getSafeString(authInfo.getUserName())); + newManager.setAssistant(getSafeString(authInfo.getAssistant())); + + newManager.setCreated_at(LocalDateTime.now()); + newManager.setUpdated_at(LocalDateTime.now()); + + int rows = supplyUsersManagementsMapper.insertUsersManagements(newManager); + System.out.println("✅ 插入负责人记录影响行数: " + rows); + } + + /** + * 更新默认数据源客户信息 - 修复版本(增加类型规范化) + */ + public boolean updateDefaultCustomer(UnifiedCustomerDTO dto, ManagerAuthInfo authInfo) { + validateCompanyId(dto.getId()); + + // 🔥 关键修复:规范化类型(适配采购端) + String normalizedType = normalizeCustomerType(dto.getType()); + dto.setType(normalizedType); + + validatePurchasePermission(dto.getType()); + + return supplyEnterpriseService.updateDefaultCustomer(dto, authInfo); + } + + // ==================== 查询方法 ==================== + + /** + * 根据手机号查询客户 - 采购员权限 + 负责人过滤 + */ + public UnifiedCustomerDTO getCustomerByPhone(String phoneNumber, ManagerAuthInfo authInfo) { + validatePhoneNumber(phoneNumber); + + System.out.println("🔍 [采购端] 开始根据手机号查询客户: " + phoneNumber); + + UserProductCartDTO userInfo = supplyPoolCustomerService.getWechatCustomerByPhone(phoneNumber); + if (userInfo == null) { + throw new RuntimeException("未找到手机号对应的客户:" + phoneNumber); + } + + // 采购员权限校验 + if (!"seller".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) { + throw new RuntimeException("该客户不在采购员权限范围内,类型: " + userInfo.getType()); + } + + // 负责人权限校验 - 添加对公海池客户的特殊处理 + if (!hasPermissionToViewCustomer(userInfo, authInfo)) { + System.out.println("❌ 负责人权限不足,无法查看该客户"); + throw new RuntimeException("您没有权限查看该客户信息"); + } + + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo); + dto.setDataSource("wechat"); + return dto; + } + + /** + * 检查是否有权限查看客户(统一权限检查方法) + */ + private boolean hasPermissionToViewCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + // 使用新的公海池判断逻辑 + if (isPublicSeaCustomer(userInfo, authInfo)) { + System.out.println("✅ 公海池客户,无需负责人权限验证"); + return true; + } + + // 非公海池客户需要检查负责人权限 + return hasUserManagerPermission(userInfo.getUserId(), authInfo); + } + + /** + * 检查是否有权限查看微信客户 + */ + public boolean hasPermissionToViewWechatCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + // 公海池客户对所有人可见 + if (isPublicSeaCustomer(userInfo, authInfo)) { + System.out.println("✅ 公海池客户,无需负责人权限验证"); + return true; + } + + // 非公海池客户需要检查负责人权限 + return hasUserManagerPermission(userInfo.getUserId(), authInfo); + } + + /** + * 判断是否为公海池客户 - 根据认证字段是否有值判断 + */ + private boolean isPublicSeaCustomer(UserProductCartDTO userInfo, ManagerAuthInfo authInfo) { + // 定义公海池等级 + Set publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池"); + + // 检查等级是否为公海池 + boolean isPublicSeaLevel = publicSeaLevels.contains(userInfo.getLevel()); + System.out.println("🔍 SupplyCustomerService - 客户等级检查: " + 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 = 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 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; + } + } + + /** + * 检查企业负责人权限 + */ + private boolean hasManagerPermission(EnterpriseInfoDTO enterpriseInfo, ManagerAuthInfo authInfo) { + if (enterpriseInfo.getManagers() == null) { + System.out.println("✅ 企业客户无负责人信息,允许查看"); + return true; + } + + return isManagerMatch(enterpriseInfo.getManagers(), authInfo); + } + + /** + * 检查负责人信息是否匹配(Managers)- 使用新字段 + */ + private boolean isManagerMatch(Managers manager, ManagerAuthInfo authInfo) { + boolean match = + (authInfo.getManagercompany() == null || authInfo.getManagercompany().equals(manager.getManagercompany())) && + (authInfo.getManagerdepartment() == null || authInfo.getManagerdepartment().equals(manager.getManagerdepartment())) && + (authInfo.getOrganization() == null || authInfo.getOrganization().equals(manager.getOrganization())) && + (authInfo.getRole() == null || authInfo.getRole().equals(manager.getRole())) && + (authInfo.getUserName() == null || authInfo.getUserName().equals(manager.getUserName())); + + System.out.println("🔐 负责人权限检查: " + (match ? "✅ 匹配" : "❌ 不匹配")); + return match; + } + + /** + * 检查负责人信息是否匹配(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; + } + + /** + * 根据企业ID查询客户信息(默认数据源)- 添加负责人过滤 + */ + public UnifiedCustomerDTO getCustomerById(String id, ManagerAuthInfo authInfo) { + System.out.println("🔍 [采购端-Service] 根据ID查询客户: " + id); + + EnterpriseInfoDTO enterpriseInfo = supplyEnterpriseService.getEnterpriseInfoById(id); + if (enterpriseInfo == null) { + System.out.println("❌ [采购端-Service] 未找到企业信息,ID: " + id); + return null; + } + + // 检查负责人权限 + if (!hasManagerPermission(enterpriseInfo, authInfo)) { + System.out.println("❌ 负责人权限不足,无法查看该客户"); + return null; + } + + return convertToUnifiedDTO(enterpriseInfo); + } + + // ==================== 私有方法 - 精确更新逻辑 ==================== + + /** + * 精确更新联系人信息 + */ + private void updateContactInfoPrecise(UnifiedCustomerDTO dto, String userId) { + if (dto.getTargetContactId() != null && !dto.getTargetContactId().trim().isEmpty()) { + // 精确更新特定联系人 + updateSpecificContact(dto, userId); + } else if (dto.getContacts() != null && !dto.getContacts().isEmpty()) { + // 更新第一个联系人(向后兼容) + updateFirstContact(dto, userId); + } else { + // 使用单个字段更新第一个联系人(向后兼容) + updateContactFromSingleFields(dto, userId); + } + } + + /** + * 精确更新产品信息 + */ + private void updateProductInfoPrecise(UnifiedCustomerDTO dto, String userId) { + if (!"seller".equals(dto.getType()) && !"both".equals(dto.getType())) { + return; + } + + if (dto.getTargetProductItemId() != null && !dto.getTargetProductItemId().trim().isEmpty()) { + // 精确更新特定产品项 + updateSpecificProductItem(dto, userId); + } else if (dto.getProductItems() != null && !dto.getProductItems().isEmpty()) { + // 更新第一个产品项(向后兼容) + updateFirstProductItem(dto, userId); + } else { + // 使用单个字段更新第一个产品项(向后兼容) + updateProductItemFromSingleFields(dto, userId); + } + } + + /** + * 更新用户基本信息 + */ + private void updateUserBasicInfo(UnifiedCustomerDTO dto, UserProductCartDTO existingUser) { + UserProductCartDTO updateUser = new UserProductCartDTO(); + updateUser.setUserId(existingUser.getUserId()); + updateUser.setPhoneNumber(dto.getPhoneNumber()); + + // 只更新非空字段 + updateUser.setNickName(getUpdateValue(dto.getNickName(), existingUser.getNickName())); + updateUser.setType(dto.getType()); // 类型必须更新 + updateUser.setCompany(getUpdateValue(dto.getCompany(), existingUser.getCompany())); + updateUser.setRegion(getUpdateValue(dto.getRegion(), existingUser.getRegion())); + updateUser.setLevel(getUpdateValue(dto.getLevel(), existingUser.getLevel())); + updateUser.setDemand(getUpdateValue(dto.getDemand(), existingUser.getDemand())); + updateUser.setSpec(getUpdateValue(dto.getSpec(), existingUser.getSpec())); + // 优先使用前端传递的更新时间,如果没有则使用当前时间 + updateUser.setUpdated_at(dto.getUpdated_at() != null ? dto.getUpdated_at() : LocalDateTime.now()); + + int rows = supplyUsersMapper.updateByPhone(updateUser); + System.out.println("更新用户基本信息影响行数: " + rows); + + if (rows == 0) { + throw new RuntimeException("用户基本信息更新失败"); + } + } + + /** + * 精确更新特定联系人 + */ + private void updateSpecificContact(UnifiedCustomerDTO dto, String userId) { + System.out.println("📞 [采购端] 精确更新特定联系人,contactId: " + dto.getTargetContactId()); + + UnifiedCustomerDTO.ContactInfo contactData = dto.getUpdateContactData(); + if (contactData == null) { + System.out.println("未提供联系人更新数据,跳过联系人更新"); + return; + } + + int contactsRows = supplyUsersMapper.updateSpecificContact( + userId, + dto.getTargetContactId(), + contactData.getWechat(), + contactData.getAccount(), + contactData.getAccountNumber(), + contactData.getBank(), + contactData.getAddress() + ); + System.out.println("更新特定联系人影响行数: " + contactsRows); + + if (contactsRows == 0) { + System.out.println("⚠️ 特定联系人更新失败,可能ID不存在"); + } + } + + /** + * 更新第一个联系人(向后兼容) + */ + private void updateFirstContact(UnifiedCustomerDTO dto, String userId) { + System.out.println("📞 [采购端] 更新第一个联系人(向后兼容)"); + UnifiedCustomerDTO.ContactInfo contact = dto.getContacts().get(0); + + int contactsRows = supplyUsersMapper.updateContactsByUserId( + userId, + contact.getWechat(), + contact.getAccount(), + contact.getAccountNumber(), + contact.getBank(), + contact.getAddress() + ); + System.out.println("更新第一个联系人影响行数: " + contactsRows); + } + + /** + * 从单个字段更新联系人(向后兼容) + */ + private void updateContactFromSingleFields(UnifiedCustomerDTO dto, String userId) { + System.out.println("📞 [采购端] 从单个字段更新联系人(向后兼容)"); + + int contactsRows = supplyUsersMapper.updateContactsByUserId( + userId, + dto.getWechat(), + dto.getAccount(), + dto.getAccountNumber(), + dto.getBank(), + dto.getAddress() + ); + System.out.println("更新联系人信息影响行数: " + contactsRows); + } + + /** + * 精确更新特定产品项 + */ + private void updateSpecificProductItem(UnifiedCustomerDTO dto, String userId) { + System.out.println("📦 [采购端] 精确更新特定产品项,targetProductItemId: " + dto.getTargetProductItemId()); + + UnifiedCustomerDTO.ProductItem productItemData = dto.getUpdateProductItemData(); + if (productItemData == null) { + System.out.println("❌ 未提供产品项更新数据,跳过产品项更新"); + return; + } + + // 确保使用targetProductItemId + String productId = dto.getTargetProductItemId(); + if (productId == null || productId.trim().isEmpty()) { + System.out.println("❌ targetProductItemId为空,无法更新"); + return; + } + + int productRows = supplyUsersMapper.updateSpecificProduct( + userId, + productId, + productItemData.getProductName(), + productItemData.getVariety(), + productItemData.getSpecification(), + productItemData.getQuantity(), + productItemData.getGrossWeight(), + productItemData.getYolk() + ); + System.out.println("✅ 更新特定产品项影响行数: " + productRows); + + if (productRows == 0) { + System.out.println("⚠️ 特定产品项更新失败,可能ID不存在"); + } + } + + /** + * 更新第一个产品项(向后兼容) + */ + private void updateFirstProductItem(UnifiedCustomerDTO dto, String userId) { + System.out.println("📦 [采购端] 更新第一个产品项(向后兼容)"); + UnifiedCustomerDTO.ProductItem productItem = dto.getProductItems().get(0); + + String productId = getExistingProductId(userId); + int productRows = supplyUsersMapper.updateProductBySellerId( + userId, + productId, + productItem.getProductName(), + productItem.getSpecification(), + productItem.getQuantity(), + productItem.getGrossWeight(), + productItem.getYolk() + ); + System.out.println("更新产品信息影响行数: " + productRows); + } + + /** + * 从单个字段更新产品项(向后兼容) + */ + private void updateProductItemFromSingleFields(UnifiedCustomerDTO dto, String userId) { + if (!StringUtils.hasText(dto.getProductName())) { + return; + } + + System.out.println("📦 [采购端] 从单个字段更新产品信息"); + + String productId = getExistingProductId(userId); + int productRows = supplyUsersMapper.updateProductBySellerId( + userId, + productId, + dto.getProductName(), + dto.getSpecification(), + dto.getQuantity(), + dto.getGrossWeight(), + dto.getYolk() + ); + System.out.println("更新产品信息影响行数: " + productRows); + + if (productRows == 0) { + System.out.println("⚠️ 产品信息更新影响0行,可能需要插入新记录"); + } + } + + // ==================== 私有方法 - 辅助方法 ==================== + + /** + * 验证手机号 + */ + private void validatePhoneNumber(String phoneNumber) { + if (!StringUtils.hasText(phoneNumber)) { + throw new IllegalArgumentException("手机号不能为空"); + } + } + + /** + * 验证公司ID + */ + private void validateCompanyId(String companyId) { + if (!StringUtils.hasText(companyId)) { + throw new IllegalArgumentException("公司ID不能为空"); + } + } + + /** + * 验证采购员权限 - 修复版本(支持大小写不敏感) + */ + private void validatePurchasePermission(String type) { + System.out.println("🔐 ====== [采购端] 开始权限校验 ======"); + + // 🔥 关键修复:规范化类型(转换为小写) + String normalizedType = normalizeCustomerType(type); + + System.out.println("🔐 原始类型: " + type); + System.out.println("🔐 规范化后类型: " + normalizedType); + System.out.println("🔐 类型比较 - seller: " + "seller".equals(normalizedType)); + System.out.println("🔐 类型比较 - both: " + "both".equals(normalizedType)); + System.out.println("🔐 类型类名: " + (type != null ? type.getClass().getName() : "null")); + + if (!"seller".equals(normalizedType) && !"both".equals(normalizedType)) { + System.out.println("❌ 权限校验失败:采购员只能更新为seller或both类型,当前类型: " + type + " -> " + normalizedType); + System.out.println("❌ 类型长度: " + (type != null ? type.length() : "null")); + System.out.println("❌ 类型字符: " + (type != null ? Arrays.toString(type.toCharArray()) : "null")); + throw new IllegalArgumentException("采购员权限只能操作供应端类型客户和BOTH类型客户"); + } + System.out.println("✅ 权限校验通过"); + System.out.println("🔐 ====== 权限校验结束 ======"); + } + + /** + * 规范化客户类型(支持大小写不敏感)- 采购端适配 + */ + private String normalizeCustomerType(String type) { + if (type == null) { + return null; + } + + // 转换为小写进行比较 + String lowerType = type.toLowerCase(); + + switch (lowerType) { + case "both": + case "seller": + case "buyer": + return lowerType; + case "供应端": + return "seller"; + case "客户端": + return "buyer"; + case "供应端&both": + return "both"; + default: + return type; // 返回原值 + } + } + + /** + * 验证并获取用户信息 + */ + private UserProductCartDTO validateAndGetUser(String phoneNumber) { + try { + // 尝试通过手机号精确查询 + UserProductCartDTO user = supplyUsersMapper.selectByPhone(phoneNumber); + if (user == null) { + throw new RuntimeException("未找到手机号对应的客户:" + phoneNumber); + } + System.out.println("找到现有客户,用户ID: " + user.getUserId()); + return user; + } catch (org.apache.ibatis.exceptions.TooManyResultsException e) { + // 处理多个结果的情况 + System.out.println("[采购端] 警告:通过手机号" + phoneNumber + "查询到多个客户"); + throw new RuntimeException("查询到多个客户记录,请提供更精确的查询条件"); + } + } + + /** + * 获取更新值(优先使用新值,如果新值为空则使用原值) + */ + private String getUpdateValue(String newValue, String originalValue) { + return StringUtils.hasText(newValue) ? newValue : originalValue; + } + + /** + * 获取现有的产品ID + */ + private String getExistingProductId(String userId) { + List existingProducts = supplyUsersMapper.getSellerProducts(userId); + return existingProducts != null && !existingProducts.isEmpty() ? + existingProducts.get(0).getProductId() : "default_product_id"; + } + + /** + * 只更新wechat数据源(包含负责人信息生成) + */ + private boolean updateWechatDataSource(UnifiedCustomerDTO dto, UserProductCartDTO existingUser) { + UserProductCartDTO updateUser = new UserProductCartDTO(); + updateUser.setUserId(existingUser.getUserId()); + updateUser.setPhoneNumber(dto.getPhoneNumber()); + + // 只更新非空字段 + updateUser.setNickName(getUpdateValue(dto.getNickName(), existingUser.getNickName())); + updateUser.setType(dto.getType()); // 类型必须更新 + updateUser.setCompany(getUpdateValue(dto.getCompany(), existingUser.getCompany())); + updateUser.setRegion(getUpdateValue(dto.getRegion(), existingUser.getRegion())); + updateUser.setLevel(getUpdateValue(dto.getLevel(), existingUser.getLevel())); + updateUser.setDemand(getUpdateValue(dto.getDemand(), existingUser.getDemand())); + updateUser.setSpec(getUpdateValue(dto.getSpec(), existingUser.getSpec())); + // 优先使用前端传递的更新时间,如果没有则使用当前时间 + updateUser.setUpdated_at(dto.getUpdated_at() != null ? dto.getUpdated_at() : LocalDateTime.now()); + + int rows = supplyUsersMapper.updateByPhone(updateUser); + System.out.println("更新用户基本信息影响行数: " + rows); + + if (rows == 0) { + throw new RuntimeException("用户基本信息更新失败"); + } + + // 更新联系人信息 + updateContactInfoPrecise(dto, existingUser.getUserId()); + + // 更新产品信息 + updateProductInfoPrecise(dto, existingUser.getUserId()); + + return rows > 0; + } + + // ==================== 转换方法 ==================== + + /** + * 为公海池客户转换DTO的方法 - 支持多个联系人和产品项 + */ + private UnifiedCustomerDTO convertToUnifiedDTOForPublicSea(UserProductCartDTO userInfo) { + UnifiedCustomerDTO dto = new UnifiedCustomerDTO(); + dto.setId(userInfo.getUserId()); + dto.setPhoneNumber(userInfo.getPhoneNumber()); + dto.setNickName(userInfo.getNickName()); + dto.setType(userInfo.getType()); + dto.setCompany(getSafeString(userInfo.getCompany())); + dto.setRegion(getSafeString(userInfo.getRegion())); + dto.setLevel(getSafeString(userInfo.getLevel(), "公海池")); + dto.setDemand(getSafeString(userInfo.getDemand())); + dto.setSpec(getSafeString(userInfo.getSpec())); + dto.setCreated_at(userInfo.getCreated_at()); + dto.setUpdated_at(userInfo.getUpdated_at()); + + // 设置多个联系人信息 + if (userInfo.getUsersContacts() != null && !userInfo.getUsersContacts().isEmpty()) { + List contacts = new ArrayList<>(); + for (UserProductCartDTO.UsersContacts userContact : userInfo.getUsersContacts()) { + UnifiedCustomerDTO.ContactInfo contact = new UnifiedCustomerDTO.ContactInfo(); + contact.setContactId(getSafeString(userContact.getContactId())); + contact.setWechat(getSafeString(userContact.getWechat())); + contact.setAccount(getSafeString(userContact.getAccount())); + contact.setAccountNumber(getSafeString(userContact.getAccountNumber())); + contact.setBank(getSafeString(userContact.getBank())); + contact.setAddress(getSafeString(userContact.getAddress())); + contacts.add(contact); + } + dto.setContacts(contacts); + + // 向后兼容:设置第一个联系人到单个字段 + UserProductCartDTO.UsersContacts firstContact = userInfo.getUsersContacts().get(0); + dto.setWechat(firstContact.getWechat()); + dto.setAccount(firstContact.getAccount()); + dto.setAccountNumber(firstContact.getAccountNumber()); + dto.setBank(firstContact.getBank()); + dto.setAddress(firstContact.getAddress()); + } + + // 设置多个产品项 + if (userInfo.getProducts() != null && !userInfo.getProducts().isEmpty()) { + List productItems = new ArrayList<>(); + for (UserProductCartDTO.ProductInfo userProduct : userInfo.getProducts()) { + UnifiedCustomerDTO.ProductItem productItem = new UnifiedCustomerDTO.ProductItem(); + productItem.setProductId(getSafeString(userProduct.getProductId())); + productItem.setProductName(getSafeString(userProduct.getProductName())); + productItem.setVariety(getSafeString(userProduct.getVariety())); + productItem.setSpecification(getSafeString(userProduct.getSpecification())); + productItem.setQuantity(userProduct.getQuantity() != null ? userProduct.getQuantity() : 0); + productItem.setGrossWeight(userProduct.getGrossWeight() != null ? userProduct.getGrossWeight() : ""); + productItem.setYolk(getSafeString(userProduct.getYolk())); + productItems.add(productItem); + } + dto.setProductItems(productItems); + + // 向后兼容:设置第一个产品项到单个字段 + UserProductCartDTO.ProductInfo firstProduct = userInfo.getProducts().get(0); + dto.setProductName(firstProduct.getProductName()); + dto.setVariety(firstProduct.getVariety()); + dto.setSpecification(firstProduct.getSpecification()); + dto.setQuantity(firstProduct.getQuantity()); + dto.setGrossWeight(firstProduct.getGrossWeight()); + dto.setYolk(firstProduct.getYolk()); + } else { + setDefaultProductValues(dto); + } + + return dto; + } + + /** + * 将企业信息转换为UnifiedCustomerDTO - 使用新字段 + */ + private UnifiedCustomerDTO convertToUnifiedDTO(EnterpriseInfoDTO enterpriseInfo) { + UnifiedCustomerDTO dto = new UnifiedCustomerDTO(); + + if (enterpriseInfo.getEnterprise() != null) { + dto.setId(enterpriseInfo.getEnterprise().getId()); + dto.setCompany(getSafeString(enterpriseInfo.getEnterprise().getCompany())); + dto.setRegion(getSafeString(enterpriseInfo.getEnterprise().getRegion())); + dto.setLevel(getSafeString(enterpriseInfo.getEnterprise().getLevel())); + dto.setType(getSafeString(enterpriseInfo.getEnterprise().getType())); + dto.setDemand(getSafeString(enterpriseInfo.getEnterprise().getDemand())); + dto.setSpec(getSafeString(enterpriseInfo.getEnterprise().getSpec())); + } + + if (enterpriseInfo.getContacts() != null) { + dto.setNickName(getSafeString(enterpriseInfo.getContacts().getNickName())); + dto.setPhoneNumber(getSafeString(enterpriseInfo.getContacts().getPhoneNumber())); + dto.setWechat(getSafeString(enterpriseInfo.getContacts().getWechat())); + dto.setAccount(getSafeString(enterpriseInfo.getContacts().getAccount())); + dto.setAccountNumber(getSafeString(enterpriseInfo.getContacts().getAccountNumber())); + dto.setBank(getSafeString(enterpriseInfo.getContacts().getBank())); + dto.setAddress(getSafeString(enterpriseInfo.getContacts().getAddress())); + dto.setCreated_at(enterpriseInfo.getContacts().getCreated_at()); + dto.setUpdated_at(enterpriseInfo.getContacts().getUpdated_at()); + } + + if (enterpriseInfo.getManagers() != null) { + // 使用新的负责人字段 + dto.setManagerId(getSafeString(enterpriseInfo.getManagers().getManagerId())); + dto.setManagercompany(getSafeString(enterpriseInfo.getManagers().getManagercompany())); + dto.setManagerdepartment(getSafeString(enterpriseInfo.getManagers().getManagerdepartment())); + dto.setOrganization(getSafeString(enterpriseInfo.getManagers().getOrganization())); + dto.setRole(getSafeString(enterpriseInfo.getManagers().getRole())); + dto.setUserName(getSafeString(enterpriseInfo.getManagers().getUserName())); + dto.setAssistant(getSafeString(enterpriseInfo.getManagers().getAssistant())); + } + + return dto; + } + + /** + * 设置默认产品值 + */ + private void setDefaultProductValues(UnifiedCustomerDTO dto) { + dto.setProductName(""); + dto.setVariety(""); + dto.setSpecification(""); + dto.setQuantity(0); + dto.setGrossWeight(""); + dto.setYolk(""); + dto.setCustomDetails(new Object[0]); + } + + /** + * 安全获取字符串(带默认值) + */ + private String getSafeString(String value) { + return value != null ? value : ""; + } + + /** + * 安全获取字符串(带自定义默认值) + */ + private String getSafeString(String value, String defaultValue) { + return value != null ? value : defaultValue; + } + + /** + * 根据ID查询wechat数据源客户信息 + */ + public UnifiedCustomerDTO getWechatCustomerById(String userId, ManagerAuthInfo authInfo) { + try { + System.out.println("🔍 [采购端] 开始根据用户ID查询wechat客户: " + userId); + + UserProductCartDTO userInfo = supplyPoolCustomerService.getPublicSeaCustomerInfo(userId); + if (userInfo == null) { + System.out.println("❌ 未找到用户ID对应的客户:" + userId); + return null; + } + + // 采购员权限校验 + if (!"seller".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) { + System.out.println("❌ 该客户不在采购员权限范围内,类型: " + userInfo.getType()); + return null; + } + + // 负责人权限校验 + if (!hasPermissionToViewCustomer(userInfo, authInfo)) { + System.out.println("❌ 负责人权限不足,无法查看该客户"); + return null; + } + + UnifiedCustomerDTO dto = convertToUnifiedDTOForPublicSea(userInfo); + dto.setDataSource("wechat"); + return dto; + + } catch (Exception e) { + System.err.println("❌ 查询wechat客户信息失败: " + e.getMessage()); + return null; + } + } + /** + * 检查默认数据源中是否存在指定ID的客户 + */ + public boolean checkDefaultCustomerExists(String customerId) { + try { + System.out.println("🔍 检查默认数据源客户是否存在,客户ID: " + customerId); + + if (customerId == null || customerId.trim().isEmpty()) { + System.out.println("❌ 客户ID为空,无法检查"); + return false; + } + + // 使用现有的selectEnterpriseInfoById方法检查存在性 + EnterpriseInfoDTO enterpriseInfo = supplyEnterpriseMapper.selectEnterpriseInfoById(customerId); + + boolean exists = enterpriseInfo != null && enterpriseInfo.getEnterprise() != null; + System.out.println("📊 默认数据源客户检查结果: " + (exists ? "✅ 存在" : "❌ 不存在")); + + if (exists) { + System.out.println("📝 客户信息: 公司=" + enterpriseInfo.getEnterprise().getCompany() + + ", 区域=" + enterpriseInfo.getEnterprise().getRegion()); + } + + return exists; + + } catch (Exception e) { + System.err.println("❌ 检查默认数据源客户存在性失败: " + e.getMessage()); + e.printStackTrace(); + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/SupplyEnterpriseService.java b/src/main/java/com/example/web/service/SupplyEnterpriseService.java new file mode 100644 index 0000000..f63c3a3 --- /dev/null +++ b/src/main/java/com/example/web/service/SupplyEnterpriseService.java @@ -0,0 +1,515 @@ +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 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 getAllEnterprises() { + try { + return supplyenterpriseMapper.selectAllEnterprises(); + } catch (Exception e) { + System.err.println("[采购端] 获取所有企业基本信息失败: " + e.getMessage()); + e.printStackTrace(); + return new ArrayList<>(); + } + } + + /** + * 根据企业ID获取联系人信息 + */ + public List 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 getManagersByEnterpriseId(String id) { + try { + return supplymanagersMapper.selectManagersByEnterpriseId(id); + } catch (Exception e) { + System.err.println("[采购端] 根据企业ID获取负责人信息失败: " + e.getMessage()); + e.printStackTrace(); + return new ArrayList<>(); + } + } + + /** + * 搜索企业信息(根据企业名称、地区、类型等) + */ + public List searchEnterprises(String keyword) { + try { + System.out.println("🔍 [采购端] 搜索企业信息,关键词: " + keyword); + // 这里可以扩展更复杂的搜索逻辑 + List allInfo = supplyenterpriseMapper.selectAllEnterpriseInfo(); + if (keyword == null || keyword.trim().isEmpty()) { + return allInfo; + } + + List 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 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 publicSeaLevels = Set.of("company-sea-pools", "organization-sea-pools", "department-sea-pools", "公海池"); + Set 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/SupplyPoolCustomerService.java b/src/main/java/com/example/web/service/SupplyPoolCustomerService.java new file mode 100644 index 0000000..0778ac4 --- /dev/null +++ b/src/main/java/com/example/web/service/SupplyPoolCustomerService.java @@ -0,0 +1,799 @@ +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 contacts = supplyusersMapper.getUserAllContacts(userInfo.getUserId()); + // 转换为UserProductCartDTO.UsersContacts格式以保持兼容 + if (contacts != null && !contacts.isEmpty()) { + List 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 products = supplyusersMapper.getUserAllProducts(userInfo.getUserId()); + // 转换为UserProductCartDTO.ProductInfo格式以保持兼容 + if (products != null && !products.isEmpty()) { + List 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 products = supplyusersMapper.getSellerProducts(userId); + userInfo.setProducts(products != null ? products : Collections.emptyList()); + userInfo.setCartItems(Collections.emptyList()); // 采购端不使用购物车 + + // 3. 获取联系人信息 - 安全处理 + try { + List 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 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 authorizedUsers = supplyusersMapper.getAuthorizedCustomers(authInfo, limit, offset); + + System.out.println("✅ 授权客户数据查询完成"); + System.out.println("📊 当前页获取到授权客户数据条数: " + (authorizedUsers != null ? authorizedUsers.size() : "null")); + + List result = new ArrayList<>(); + + if (authorizedUsers != null && !authorizedUsers.isEmpty()) { + // 🔥 新增:收集所有用户ID用于批量查询 + List userIds = authorizedUsers.stream() + .map(UserProductCartDTO::getUserId) + .filter(Objects::nonNull) + .filter(userId -> !userId.trim().isEmpty()) + .distinct() + .collect(Collectors.toList()); + + System.out.println("🔍 需要批量查询的授权用户数量: " + userIds.size()); + + // 🔥 新增:批量查询所有相关数据 + Map managerMap = batchQueryManagers(userIds); + Map> contactsMap = batchQueryContacts(userIds); + Map> 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 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 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而不是抛出异常 + } +// 🔥 新增:查询负责人信息 + 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 contacts = supplyusersMapper.getUserAllContacts(userId); + if (contacts != null && !contacts.isEmpty()) { + List 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 products = supplyusersMapper.getUserAllProducts(userId); + System.out.println("🔍 查询到的产品数据条数: " + (products != null ? products.size() : 0)); + + if (products != null && !products.isEmpty()) { + List 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 getAllCustomersWithoutFilter() { + try { + System.out.println("===================================================="); + System.out.println("🚀 开始获取所有微信用户数据(无过滤,采购端)..."); + + // 1. 获取所有用户的基本信息(采购端:只获取seller和both类型) + System.out.println("📋 查询用户基础信息..."); + List allUsers = supplyusersMapper.getAllUserBasicInfo(); + + System.out.println("✅ 基础用户数据查询完成"); + System.out.println("📊 获取到基础用户数据条数: " + (allUsers != null ? allUsers.size() : "null")); + + List result = new ArrayList<>(); + + if (allUsers != null && !allUsers.isEmpty()) { + // 🔥 修改:收集所有用户ID用于批量查询 + List userIds = allUsers.stream() + .map(UserProductCartDTO::getUserId) + .filter(Objects::nonNull) + .filter(userId -> !userId.trim().isEmpty()) + .distinct() + .collect(Collectors.toList()); + + System.out.println("🔍 需要批量查询的用户数量: " + userIds.size()); + + // 🔥 修改:批量查询所有相关数据(采购端需要产品信息) + Map managerMap = batchQueryManagers(userIds); + Map> contactsMap = batchQueryContacts(userIds); + Map> 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 batchQueryManagers(List userIds) { + Map managerMap = new HashMap<>(); + if (userIds != null && !userIds.isEmpty()) { + try { + // 🔥 注意:采购端可能需要使用不同的Mapper + List 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> batchQueryContacts(List userIds) { + Map> contactsMap = new HashMap<>(); + if (userIds != null && !userIds.isEmpty()) { + try { + // 🔥 注意:采购端需要使用采购端的Mapper + List 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> batchQueryProducts(List userIds) { + Map> productsMap = new HashMap<>(); + if (userIds != null && !userIds.isEmpty()) { + try { + List 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 managerMap, + Map> contactsMap, + Map> 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 contacts = contactsMap.get(userId); + if (contacts != null && !contacts.isEmpty()) { + List 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 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 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 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<>(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/service/SupplyUserService.java b/src/main/java/com/example/web/service/SupplyUserService.java new file mode 100644 index 0000000..7ef59a1 --- /dev/null +++ b/src/main/java/com/example/web/service/SupplyUserService.java @@ -0,0 +1,8 @@ +package com.example.web.service; + +import org.springframework.stereotype.Service; + +@Service +public class SupplyUserService { + +} diff --git a/src/main/java/com/example/web/service/UserService.java b/src/main/java/com/example/web/service/UserService.java new file mode 100644 index 0000000..4545a6d --- /dev/null +++ b/src/main/java/com/example/web/service/UserService.java @@ -0,0 +1,13 @@ +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 { + +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..fab977f --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,44 @@ +spring: + 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 + username: root + password: schl@2025 + driver-class-name: com.mysql.cj.jdbc.Driver + # 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 + username: root + password: schl@2025 + driver-class-name: com.mysql.cj.jdbc.Driver + +# 应用配置 +app: + recycle: + # 未分级客户回流到组织公海池的天数阈值 + unclassified-to-organization-days: 30 + # 组织公海池客户回流到部门公海池的天数阈值 + organization-to-department-days: 30 + +server: + servlet: + context-path: /DL + # 在Tomcat中部署时,端口由Tomcat配置决定,这里不需要指定 + # address属性在Tomcat部署中不生效,由容器控制 + +mybatis: + type-aliases-package: com.example.web.entity + mapper-locations: classpath:mapper/*.xml + configuration: + # 确保这些配置存在 + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + call-setters-on-nulls: true + jdbc-type-for-null: null + +logging: + level: + com.example.web.mapper: DEBUG + com.example.web.config: DEBUG + com.example.web.aspect: DEBUG + diff --git a/src/main/resources/mapper/Cart_itemsMapper.xml b/src/main/resources/mapper/Cart_itemsMapper.xml new file mode 100644 index 0000000..9d1b5c0 --- /dev/null +++ b/src/main/resources/mapper/Cart_itemsMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/ContactsMapper.xml b/src/main/resources/mapper/ContactsMapper.xml new file mode 100644 index 0000000..9642a87 --- /dev/null +++ b/src/main/resources/mapper/ContactsMapper.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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} + ) + + + + + + + + + + UPDATE contacts + SET + id = #{id}, + nickName = #{nickName}, + wechat = #{wechat}, + account = #{account}, + accountNumber = #{accountNumber}, + bank = #{bank}, + address = #{address} + WHERE contact_id = #{contact_id} + + \ No newline at end of file diff --git a/src/main/resources/mapper/EnterpriseMapper.xml b/src/main/resources/mapper/EnterpriseMapper.xml new file mode 100644 index 0000000..a9184ac --- /dev/null +++ b/src/main/resources/mapper/EnterpriseMapper.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO enterprise ( + id, company, region, level, type, demand, spec + ) VALUES ( + #{id}, #{company}, #{region}, #{level}, #{type}, #{demand}, #{spec} + ) + + + + UPDATE enterprise + SET + company = #{company}, + region = #{region}, + level = #{level}, + type = #{type}, + demand = #{demand}, + spec = #{spec} + WHERE id = #{id} + + \ No newline at end of file diff --git a/src/main/resources/mapper/LoginMapper.xml b/src/main/resources/mapper/LoginMapper.xml new file mode 100644 index 0000000..7831443 --- /dev/null +++ b/src/main/resources/mapper/LoginMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/ManagersMapper.xml b/src/main/resources/mapper/ManagersMapper.xml new file mode 100644 index 0000000..c15fad0 --- /dev/null +++ b/src/main/resources/mapper/ManagersMapper.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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} + ) + + + + + UPDATE managers + SET + managercompany = #{managercompany}, + managerdepartment = #{managerdepartment}, + organization = #{organization}, + role = #{role}, + userName = #{userName}, + assistant = #{assistant}, + updated_at = #{updated_at} + WHERE id = #{id} + + \ No newline at end of file diff --git a/src/main/resources/mapper/ProductsMapper.xml b/src/main/resources/mapper/ProductsMapper.xml new file mode 100644 index 0000000..5fba03e --- /dev/null +++ b/src/main/resources/mapper/ProductsMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/RootdbMapper.xml b/src/main/resources/mapper/RootdbMapper.xml new file mode 100644 index 0000000..208ff92 --- /dev/null +++ b/src/main/resources/mapper/RootdbMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/SupplyCart_itemsMapper.xml b/src/main/resources/mapper/SupplyCart_itemsMapper.xml new file mode 100644 index 0000000..8cff072 --- /dev/null +++ b/src/main/resources/mapper/SupplyCart_itemsMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/SupplyContactsMapper.xml b/src/main/resources/mapper/SupplyContactsMapper.xml new file mode 100644 index 0000000..e65578a --- /dev/null +++ b/src/main/resources/mapper/SupplyContactsMapper.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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} + ) + + + + + + + + + + UPDATE contacts + SET + id = #{id}, + nickName = #{nickName}, + wechat = #{wechat}, + account = #{account}, + accountNumber = #{accountNumber}, + bank = #{bank}, + address = #{address} + WHERE contact_id = #{contact_id} + + \ No newline at end of file diff --git a/src/main/resources/mapper/SupplyEnterpriseMapper.xml b/src/main/resources/mapper/SupplyEnterpriseMapper.xml new file mode 100644 index 0000000..5a37588 --- /dev/null +++ b/src/main/resources/mapper/SupplyEnterpriseMapper.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO enterprise ( + id, company, region, level, type, demand, spec + ) VALUES ( + #{id}, #{company}, #{region}, #{level}, #{type}, #{demand}, #{spec} + ) + + + + UPDATE enterprise + SET + company = #{company}, + region = #{region}, + level = #{level}, + type = #{type}, + demand = #{demand}, + spec = #{spec} + WHERE id = #{id} + + \ No newline at end of file diff --git a/src/main/resources/mapper/SupplyManagersMapper.xml b/src/main/resources/mapper/SupplyManagersMapper.xml new file mode 100644 index 0000000..50a586f --- /dev/null +++ b/src/main/resources/mapper/SupplyManagersMapper.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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} + ) + + + + + UPDATE managers + SET + managercompany = #{managercompany}, + managerdepartment = #{managerdepartment}, + organization = #{organization}, + role = #{role}, + userName = #{userName}, + assistant = #{assistant}, + updated_at = #{updated_at} + WHERE id = #{id} + + \ No newline at end of file diff --git a/src/main/resources/mapper/SupplyProductsMapper.xml b/src/main/resources/mapper/SupplyProductsMapper.xml new file mode 100644 index 0000000..81cafd6 --- /dev/null +++ b/src/main/resources/mapper/SupplyProductsMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/SupplyUsersManagementsMapper.xml b/src/main/resources/mapper/SupplyUsersManagementsMapper.xml new file mode 100644 index 0000000..5ee40b1 --- /dev/null +++ b/src/main/resources/mapper/SupplyUsersManagementsMapper.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO usermanagements ( + userId, managerId, managercompany, managerdepartment, + organization, role, root, created_at, updated_at, + userName, assistant + ) VALUES ( + #{userId}, #{managerId}, #{managercompany}, #{managerdepartment}, + #{organization}, #{role}, #{root}, #{created_at}, #{updated_at}, + #{userName}, #{assistant} + ) + + + + + UPDATE usermanagements + SET managerId = #{managerId}, + managercompany = #{managercompany}, + managerdepartment = #{managerdepartment}, + organization = #{organization}, + role = #{role}, + root = #{root}, + updated_at = #{updated_at}, + userName = #{userName}, + assistant = #{assistant} + WHERE id = #{id} + + + + + UPDATE users_managements + SET updated_at = #{updatedAt} + WHERE user_id = #{userId} + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/SupplyUsersMapper.xml b/src/main/resources/mapper/SupplyUsersMapper.xml new file mode 100644 index 0000000..e84f58f --- /dev/null +++ b/src/main/resources/mapper/SupplyUsersMapper.xml @@ -0,0 +1,700 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE products + SET + productName = #{product.productName}, + specification = #{product.specification}, + yolk = #{product.yolk}, + quantity = #{product.quantity}, + variety = #{product.variety}, + grossWeight = #{product.grossWeight}, + price = #{product.price}, + updated_at = NOW() + WHERE + sellerId = #{sellerId} + AND productId = #{productId} + + + + + UPDATE users + SET + + nickName = #{nickName}, + + + type = #{type}, + + + company = #{company}, + + + region = #{region}, + + + level = #{level}, + + + demand = #{demand}, + + + spec = #{spec}, + + updated_at = #{updated_at} + WHERE phoneNumber = #{phoneNumber} + + + + + UPDATE products + SET + + productName = #{productName}, + + + variety = #{variety}, + + + specification = #{specification}, + + + quantity = #{quantity}, + + + grossWeight = #{grossWeight}, + + + yolk = #{yolk}, + + updated_at = NOW() + WHERE sellerId = #{sellerId} + + + + + INSERT INTO products ( + productId, + sellerId, + productName, + variety, + specification, + quantity, + grossWeight, + yolk, + price, + created_at, + updated_at + ) VALUES ( + COALESCE(#{productId}, UUID()), + #{sellerId}, + #{productName}, + #{variety}, + #{specification}, + #{quantity}, + #{grossWeight}, + #{yolk}, + #{price}, + NOW(), + NOW() + ) + ON DUPLICATE KEY UPDATE + productName = VALUES(productName), + variety = VALUES(variety), + specification = VALUES(specification), + quantity = VALUES(quantity), + grossWeight = VALUES(grossWeight), + yolk = VALUES(yolk), + price = VALUES(price), + updated_at = NOW() + + + + + + + + UPDATE contacts + SET + + wechat = #{wechat}, + + + account = #{account}, + + + accountNumber = #{accountNumber}, + + + bank = #{bank}, + + + address = #{address}, + + updated_at = NOW() + WHERE userId = #{userId} + LIMIT 1 + + + + + INSERT INTO contacts (id, userId, wechat, account, accountNumber, bank, address, updated_at) + VALUES ( + COALESCE(#{contactId}, UUID()), + #{userId}, + #{wechat}, + #{account}, + #{accountNumber}, + #{bank}, + #{address}, + NOW() + ) + ON DUPLICATE KEY UPDATE + wechat = VALUES(wechat), + account = VALUES(account), + accountNumber = VALUES(accountNumber), + bank = VALUES(bank), + address = VALUES(address), + updated_at = NOW() + + + + + UPDATE contacts + SET + + wechat = #{wechat}, + + + account = #{account}, + + + accountNumber = #{accountNumber}, + + + bank = #{bank}, + + + address = #{address}, + + updated_at = NOW() + WHERE userId = #{userId} + AND id = #{contactId} + + + + + UPDATE products + SET + + productName = #{productName}, + + + variety = #{variety}, + + + specification = #{specification}, + + + quantity = #{quantity}, + + + grossWeight = #{grossWeight}, + + + yolk = #{yolk}, + + updated_at = NOW() + WHERE sellerId = #{sellerId} + AND productId = #{productId} + + + + + + + + + + + + + + + + + + + + + UPDATE products + SET + productName = #{productName}, + variety = #{variety}, + specification = #{specification}, + quantity = #{quantity}, + grossWeight = #{grossWeight}, + yolk = #{yolk}, + updated_at = NOW() + WHERE productId = #{productId} + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/UsersManagementsMapper.xml b/src/main/resources/mapper/UsersManagementsMapper.xml new file mode 100644 index 0000000..4a1fa29 --- /dev/null +++ b/src/main/resources/mapper/UsersManagementsMapper.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO usermanagements ( + userId, managerId, managercompany, managerdepartment, + organization, role, root, created_at, updated_at, + userName, assistant + ) VALUES ( + #{userId}, #{managerId}, #{managercompany}, #{managerdepartment}, + #{organization}, #{role}, #{root}, #{created_at}, #{updated_at}, + #{userName}, #{assistant} + ) + + + + + UPDATE usermanagements + SET managerId = #{managerId}, + managercompany = #{managercompany}, + managerdepartment = #{managerdepartment}, + organization = #{organization}, + role = #{role}, + root = #{root}, + updated_at = #{updated_at}, + userName = #{userName}, + assistant = #{assistant} + WHERE id = #{id} + + + + + UPDATE users_managements + SET updated_at = #{updatedAt} + WHERE user_id = #{userId} + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/UsersMapper.xml b/src/main/resources/mapper/UsersMapper.xml new file mode 100644 index 0000000..0e567be --- /dev/null +++ b/src/main/resources/mapper/UsersMapper.xml @@ -0,0 +1,715 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE products + SET + productName = #{product.productName}, + specification = #{product.specification}, + yolk = #{product.yolk}, + quantity = #{product.quantity}, + variety = #{product.variety}, + grossWeight = #{product.grossWeight}, + updated_at = NOW() + WHERE + sellerId = #{sellerId} + AND productId = #{productId} + + + + + UPDATE users + SET + + nickName = #{nickName}, + + + type = #{type}, + + + company = #{company}, + + + region = #{region}, + + + level = #{level}, + + + demand = #{demand}, + + + spec = #{spec}, + + updated_at = #{updated_at} + WHERE phoneNumber = #{phoneNumber} + + + + + UPDATE products + SET + + productName = #{productName}, + + + variety = #{variety}, + + + specification = #{specification}, + + + quantity = #{quantity}, + + + grossWeight = #{grossWeight}, + + + yolk = #{yolk}, + + updated_at = NOW() + WHERE sellerId = #{sellerId} + + + + + UPDATE cart_items + SET + productName = #{productName}, + specification = #{specification}, + quantity = #{quantity}, + grossWeight = #{grossWeight}, + yolk = #{yolk}, + added_at = NOW() + WHERE userId = #{buyerId} + AND productId = #{productId} + + + + + + + + UPDATE contacts + SET + + wechat = #{wechat}, + + + account = #{account}, + + + accountNumber = #{accountNumber}, + + + bank = #{bank}, + + + address = #{address}, + + updated_at = NOW() + WHERE userId = #{userId} + LIMIT 1 + + + + + INSERT INTO contacts (id, userId, wechat, account, accountNumber, bank, address, updated_at) + VALUES ( + COALESCE(#{contactId}, UUID()), + #{userId}, + #{wechat}, + #{account}, + #{accountNumber}, + #{bank}, + #{address}, + NOW() + ) + ON DUPLICATE KEY UPDATE + wechat = VALUES(wechat), + account = VALUES(account), + accountNumber = VALUES(accountNumber), + bank = VALUES(bank), + address = VALUES(address), + updated_at = NOW() + + + + + INSERT INTO cart_items (id, userId, productId, productName, specification, quantity, grossWeight, yolk, added_at) + VALUES ( + COALESCE(#{cartItemId}, UUID()), + #{userId}, + #{productId}, + #{productName}, + #{specification}, + #{quantity}, + #{grossWeight}, + #{yolk}, + NOW() + ) + ON DUPLICATE KEY UPDATE + productName = VALUES(productName), + specification = VALUES(specification), + quantity = VALUES(quantity), + grossWeight = VALUES(grossWeight), + yolk = VALUES(yolk), + added_at = NOW() + + + + + + + + + + + UPDATE contacts + SET + + wechat = #{wechat}, + + + account = #{account}, + + + accountNumber = #{accountNumber}, + + + bank = #{bank}, + + + address = #{address}, + + updated_at = NOW() + WHERE userId = #{userId} + AND id = #{contactId} + + + + + UPDATE cart_items + SET + + productId = #{productId}, + + + productName = #{productName}, + + + specification = #{specification}, + + + quantity = #{quantity}, + + + grossWeight = #{grossWeight}, + + + yolk = #{yolk}, + + added_at = NOW() + WHERE userId = #{userId} + AND id = #{cartItemId} + + + + + + + + + + + + + + + + + + + + + + UPDATE products + SET + productName = #{productName}, + variety = #{variety}, + specification = #{specification}, + quantity = #{quantity}, + grossWeight = #{grossWeight}, + yolk = #{yolk}, + updated_at = NOW() + WHERE productId = #{productId} + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/loginmm.html b/src/main/resources/static/loginmm.html new file mode 100644 index 0000000..4576522 --- /dev/null +++ b/src/main/resources/static/loginmm.html @@ -0,0 +1,986 @@ + + + + + + 客户关系管理系统 - 登录 + + + + + + + + + +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/mainapp-sells.html b/src/main/resources/static/mainapp-sells.html new file mode 100644 index 0000000..4136926 --- /dev/null +++ b/src/main/resources/static/mainapp-sells.html @@ -0,0 +1,13833 @@ + + + + + + + + 客户关系管理系统 + + + + + + + + +
+
+
+ +
加载中...
+
+
欢迎,祝您有个愉快的一天
+
Welcome, hope you have a nice day
+
+
+ +
+ + + + +
+
+ + +
+
+ +
0
+
总客户数
+
+
+ +
0
+
活跃客户
+
+
+ +
0%
+
客户留存率
+
+
+ +
¥0
+
本月销售额
+
+
+ +
+ +
+
+

+ + 最近客户 +

+ + + + + + + + + + + + + + + + + + +
客户名称联系人联系方式最后订单状态操作
暂无客户数据
+
+
+
+ + +
+
+
+ + +
+ + + + + + + + + +
+
+ + +
+
时间筛选:
+
+
+ 动态筛选 +
+
动态筛选
+
自定义筛选
+
+
+ +
+ 今天 +
+
今天
+
昨天
+
前天
+
大前天
+
一周内
+
一个月内
+
+
+ + + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
客户公司客户地区基本需求规格联系人电话创建时间修改时间操作
暂无重要客户数据
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+

客户跟进管理

+ + + +
+ + + + + + + + + + + + + + + + +
跟进详情联系人电话日期操作
暂无跟进记录
+
+ + +
+
+ + +
+
+

数据分析

+

数据分析功能即将上线,敬请期待。

+
+
+ +
+
+

日程安排

+

日程安排功能即将上线,敬请期待。

+
+
+ +
+
+

系统设置

+

系统设置功能即将上线,敬请期待。

+
+
+
+ + + + + +
+
+

添加附加联系人信息

+ + + + + + + +
+
+ + + + + +
+ 数据已刷新 +
+ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/mainapp-supplys.html b/src/main/resources/static/mainapp-supplys.html new file mode 100644 index 0000000..fd51b96 --- /dev/null +++ b/src/main/resources/static/mainapp-supplys.html @@ -0,0 +1,13691 @@ + + + + + + + + 供应关系管理系统 + + + + + + + + +
+
+
+ +
加载中...
+
+
欢迎,祝您有个愉快的一天
+
Welcome, hope you have a nice day
+
+
+ +
+ + + + +
+
+ + +
+
+ +
0
+
总客户数
+
+
+ +
0
+
活跃客户
+
+
+ +
0%
+
客户留存率
+
+
+ +
¥0
+
本月销售额
+
+
+ +
+ +
+
+

+ + 最近客户 +

+ + + + + + + + + + + + + + + + + + +
客户名称联系人联系方式最后订单状态操作
暂无客户数据
+
+
+
+ + +
+
+
+ + +
+ + + + + + + + + +
+
+ + +
+
时间筛选:
+
+
+ 动态筛选 +
+
动态筛选
+
自定义筛选
+
+
+ +
+ 今天 +
+
今天
+
昨天
+
前天
+
大前天
+
一周内
+
一个月内
+
+
+ + + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
客户公司客户地区基本需求规格联系人电话创建时间修改时间操作
暂无重要客户数据
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+

客户跟进管理

+ + + +
+ + + + + + + + + + + + + + + + +
跟进详情联系人电话日期操作
暂无跟进记录
+
+ + +
+
+ + +
+
+

数据分析

+

数据分析功能即将上线,敬请期待。

+
+
+ +
+
+

日程安排

+

日程安排功能即将上线,敬请期待。

+
+
+ +
+
+

系统设置

+

系统设置功能即将上线,敬请期待。

+
+
+
+ + + + + +
+
+

添加附加联系人信息

+ + + + + + + +
+
+ + + + + +
+ 数据已刷新 +
+ + + + + + + + + \ No newline at end of file diff --git a/src/test/java/com/example/WebApplicationTests.java b/src/test/java/com/example/WebApplicationTests.java new file mode 100644 index 0000000..6a70368 --- /dev/null +++ b/src/test/java/com/example/WebApplicationTests.java @@ -0,0 +1,13 @@ +package com.example; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class WebApplicationTests { + + @Test + void contextLoads() { + } + +}