0318-海宁菜单调整+北仑客群部分开发
This commit is contained in:
187
CLAUDE.md
Normal file
187
CLAUDE.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a full-stack banking management system (数字支行辅助管理系统) built on the RuoYi framework v3.8.8. It provides grid-based customer relationship management, customer grouping, visit tracking, and performance statistics for banking institutions.
|
||||
|
||||
- **Backend**: Spring Boot 2.5.14 + MyBatis + MySQL + Redis + JWT
|
||||
- **Frontend**: Vue 2.6.12 + Element UI 2.15.14
|
||||
- **Java Version**: 1.8
|
||||
|
||||
## Architecture
|
||||
|
||||
### Backend Module Structure
|
||||
|
||||
The backend is a multi-module Maven project with the following modules:
|
||||
|
||||
```
|
||||
ruoyi/
|
||||
├── ruoyi-admin/ # Web entry point, main application (RuoYiApplication)
|
||||
├── ruoyi-framework/ # Core framework (security, config, interceptors)
|
||||
├── ruoyi-system/ # System management (users, roles, menus, depts)
|
||||
├── ruoyi-common/ # Common utilities, annotations, domain classes
|
||||
├── ruoyi-quartz/ # Scheduled task management
|
||||
├── ruoyi-generator/ # Code generation tools
|
||||
├── ibs/ # Main business module: grid customer management (网格营销)
|
||||
└── ibs-group/ # Customer group management module (客户分组)
|
||||
```
|
||||
|
||||
### Business Domain Structure
|
||||
|
||||
Each business module (e.g., `ibs`, `ibs-group`) follows this package structure:
|
||||
|
||||
```
|
||||
com.ruoyi.<module>/
|
||||
├── controller/ # REST controllers (handle HTTP requests)
|
||||
├── service/ # Business logic layer
|
||||
├── mapper/ # MyBatis mappers (database access)
|
||||
├── domain/
|
||||
│ ├── entity/ # Database entities
|
||||
│ ├── dto/ # Data Transfer Objects (request)
|
||||
│ └── vo/ # View Objects (response)
|
||||
└── handler/ # Custom MyBatis type handlers
|
||||
```
|
||||
|
||||
### Frontend Structure
|
||||
|
||||
```
|
||||
ruoyi-ui/
|
||||
├── src/
|
||||
│ ├── api/ # API request modules (organized by feature)
|
||||
│ ├── views/ # Page components
|
||||
│ ├── components/ # Reusable components
|
||||
│ ├── store/ # Vuex state management
|
||||
│ ├── router/ # Vue Router configuration
|
||||
│ ├── utils/ # Utility functions
|
||||
│ └── directive/ # Custom Vue directives
|
||||
```
|
||||
|
||||
## Build and Run Commands
|
||||
|
||||
### Backend (Maven)
|
||||
|
||||
```bash
|
||||
# Clean build artifacts
|
||||
cd bin && clean.bat
|
||||
|
||||
# Package the project (creates JAR in ruoyi-admin/target/)
|
||||
cd bin && package.bat
|
||||
|
||||
# Run the backend server (requires packaged JAR)
|
||||
cd bin && run.bat
|
||||
|
||||
# Or run directly with Maven from ruoyi-admin/
|
||||
mvn spring-boot:run
|
||||
|
||||
# The main class is: com.ruoyi.RuoYiApplication
|
||||
# Default port: 8080
|
||||
```
|
||||
|
||||
### Frontend (npm/Vue CLI)
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
|
||||
# Development server (runs on port 80)
|
||||
npm run dev
|
||||
|
||||
# For older Node.js versions with OpenSSL issues
|
||||
npm run dev_t
|
||||
|
||||
# Build for production
|
||||
npm run build:prod
|
||||
|
||||
# Build for staging
|
||||
npm run build:stage
|
||||
|
||||
# Build for pre-production
|
||||
npm run build:pre
|
||||
|
||||
# Lint code
|
||||
npm run lint
|
||||
```
|
||||
|
||||
## Development Configuration
|
||||
|
||||
### Application Profiles
|
||||
|
||||
Backend uses Spring profiles located in `ruoyi-admin/src/main/resources/`:
|
||||
- `application.yml` - Base configuration
|
||||
- `application-dev.yml` - Development environment
|
||||
- `application-uat.yml` - UAT environment
|
||||
- `application-pre.yml` - Pre-production
|
||||
- `application-pro.yml` - Production
|
||||
|
||||
Active profile is set in `application.yml` (default: `dev`).
|
||||
|
||||
### Frontend API Proxy
|
||||
|
||||
The Vue dev server proxies API requests to `http://localhost:8080`:
|
||||
- Frontend dev server: `http://localhost:80`
|
||||
- API requests: `/dev-api/*` → `http://localhost:8080/*`
|
||||
|
||||
## Key Patterns and Conventions
|
||||
|
||||
### Backend Code Patterns
|
||||
|
||||
1. **Controller-Service-Mapper Layering**
|
||||
- Controllers handle HTTP requests/responses, use `@RestController`
|
||||
- Services contain business logic, use `@Service`
|
||||
- Mappers use MyBatis annotations or XML files in `resources/mapper/`
|
||||
|
||||
2. **DTO/VO Pattern**
|
||||
- DTOs (Data Transfer Objects) for incoming requests
|
||||
- VOs (View Objects) for outgoing responses
|
||||
- Entities map directly to database tables
|
||||
|
||||
3. **Security & Authentication**
|
||||
- JWT-based authentication via `TokenService`
|
||||
- Role-based access: `head`, `branch`, `outlet`, `manager`
|
||||
- Use `SecurityUtils` for current user context: `SecurityUtils.getUsername()`, `SecurityUtils.getDeptId()`, `SecurityUtils.userRole()`
|
||||
|
||||
4. **Pagination**
|
||||
- Uses `PageHelper` for database pagination
|
||||
- Controllers return `TableDataInfo` with `total` and `rows`
|
||||
|
||||
5. **Caching**
|
||||
- Uses `RedisCache` for Redis operations
|
||||
- Cache keys often follow pattern: `{module}:{feature}:{key}`
|
||||
|
||||
### Frontend Code Patterns
|
||||
|
||||
1. **API Modules**
|
||||
- Each feature has a dedicated API file in `src/api/`
|
||||
- Uses `request()` wrapper around axios
|
||||
- API base URL is configured in `src/utils/request.js`
|
||||
|
||||
2. **Vuex Store**
|
||||
- Modules in `src/store/modules/`: user, app, permission, settings, tagsView
|
||||
- State persists via `vuex-persistedstate`
|
||||
|
||||
3. **Permission Directives**
|
||||
- `v-hasPermi` for button-level permissions
|
||||
- `v-hasRole` for role-based display
|
||||
|
||||
## Common Business Concepts
|
||||
|
||||
- **网格**: Grid-based territory management for customer assignment
|
||||
- **客户经理**: Relationship managers assigned to customers
|
||||
- **客户星级**: Customer rating levels (5星, 4星, 3星, 2星, 1星, 基础, 长尾)
|
||||
- **AUM (Asset Under Management)**: Customer assets, tracked with monthly averages
|
||||
- **PAD走访**: Mobile visit records for customer interactions
|
||||
|
||||
## MyBatis Configuration
|
||||
|
||||
- Mapper XML files: `classpath*:mapper/**/*Mapper.xml`
|
||||
- Type aliases: `com.ruoyi.**.domain`
|
||||
- Custom type handlers: `com.ruoyi.ibs.handler`
|
||||
- Pagination: `pagehelper` with MySQL dialect
|
||||
|
||||
## Notes
|
||||
|
||||
- The project uses Chinese comments and variable names in many places
|
||||
- Scheduled tasks use Quartz via `ruoyi-quartz` module
|
||||
- File upload path configured in `application.yml`: `ruoyi.profile`
|
||||
- Swagger/Knife4j API docs available when running
|
||||
@@ -50,14 +50,14 @@ public class CustGroupController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客群详情
|
||||
* 根据ID查询客群详情
|
||||
*/
|
||||
@ApiOperation("获取客群详情")
|
||||
@Log(title = "客群管理-获取客群详情")
|
||||
@ApiOperation("根据ID查询客群详情")
|
||||
@Log(title = "客群管理-查询客群详情")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getCustGroup(@PathVariable Long id) {
|
||||
CustGroupVO vo = custGroupService.getCustGroup(id);
|
||||
return AjaxResult.success(vo);
|
||||
CustGroupVO custGroup = custGroupService.getCustGroup(id);
|
||||
return AjaxResult.success(custGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,17 +83,6 @@ public class CustGroupController extends BaseController {
|
||||
return AjaxResult.success(custGroupService.createCustGroupByTemplate(custGroup, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新客群
|
||||
*/
|
||||
@ApiOperation("更新客群")
|
||||
@Log(title = "客群管理-更新客群", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
public AjaxResult updateCustGroup(@RequestBody @Valid CustGroup custGroup) {
|
||||
String result = custGroupService.updateCustGroup(custGroup);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新客群(网格导入)
|
||||
*/
|
||||
@@ -150,15 +139,4 @@ public class CustGroupController extends BaseController {
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动移除客群客户
|
||||
*/
|
||||
@ApiOperation("手动移除客群客户")
|
||||
@Log(title = "客群管理-手动移除客户", businessType = BusinessType.DELETE)
|
||||
@PostMapping("/removeMembers")
|
||||
public AjaxResult removeMembers(@RequestParam Long groupId, @RequestBody List<Long> memberIds) {
|
||||
String result = custGroupService.removeMembers(groupId, memberIds);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.ruoyi.group.controller;
|
||||
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||
import com.ruoyi.group.service.ICustGroupMemberService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客群客户Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Api(tags = "客群客户接口")
|
||||
@RestController
|
||||
@RequestMapping("/group/member")
|
||||
public class CustGroupMemberController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private ICustGroupMemberService custGroupMemberService;
|
||||
|
||||
/**
|
||||
* 分页查询客群客户列表
|
||||
*/
|
||||
@ApiOperation("分页查询客群客户列表")
|
||||
@Log(title = "客群客户-查询客户列表")
|
||||
@GetMapping("/list/{groupId}")
|
||||
public TableDataInfo listCustGroupMembers(@PathVariable Long groupId,
|
||||
CustGroupMemberQueryDTO dto) {
|
||||
startPage();
|
||||
List<CustGroupMemberVO> list = custGroupMemberService.listCustGroupMembers(groupId, dto);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动移除客群客户
|
||||
*/
|
||||
@ApiOperation("手动移除客群客户")
|
||||
@Log(title = "客群客户-手动移除客户", businessType = BusinessType.DELETE)
|
||||
@PostMapping("/remove")
|
||||
public AjaxResult removeMembers(@RequestParam Long groupId, @RequestBody List<Long> memberIds) {
|
||||
String result = custGroupMemberService.removeMembers(groupId, memberIds);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.ruoyi.group.domain.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 客群客户查询DTO
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(description = "客群客户查询条件")
|
||||
public class CustGroupMemberQueryDTO {
|
||||
|
||||
/**
|
||||
* 客户类型:0=个人, 1=商户, 2=企业
|
||||
*/
|
||||
@ApiModelProperty(value = "客户类型")
|
||||
private String custType;
|
||||
|
||||
/**
|
||||
* 客户姓名
|
||||
*/
|
||||
@ApiModelProperty(value = "客户姓名")
|
||||
private String custName;
|
||||
}
|
||||
@@ -59,7 +59,6 @@ public class CustGroup {
|
||||
* 所属机构ID
|
||||
*/
|
||||
@ApiModelProperty(value = "所属机构ID", name = "deptId")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
@@ -74,13 +73,6 @@ public class CustGroup {
|
||||
@ApiModelProperty(value = "可见部门ID列表(逗号分隔)", name = "shareDeptIds")
|
||||
private String shareDeptIds;
|
||||
|
||||
/**
|
||||
* 共享部门ID列表(非表字段,用于接收前端传参)
|
||||
*/
|
||||
@ApiModelProperty(value = "共享部门ID列表", name = "shareDeptIdList")
|
||||
@TableField(exist = false)
|
||||
private List<Long> shareDeptIdList;
|
||||
|
||||
/**
|
||||
* 客群状态:0=正常, 1=已禁用
|
||||
*/
|
||||
@@ -109,14 +101,12 @@ public class CustGroup {
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,14 +63,12 @@ public class CustGroupMember {
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,10 +65,10 @@ public class CustGroupVO {
|
||||
private Integer shareEnabled;
|
||||
|
||||
/**
|
||||
* 可见部门ID列表
|
||||
* 可见部门ID列表(逗号分隔)
|
||||
*/
|
||||
@ApiModelProperty(value = "可见部门ID列表", name = "shareDeptIds")
|
||||
private List<Long> shareDeptIds;
|
||||
@ApiModelProperty(value = "可见部门ID列表(逗号分隔)", name = "shareDeptIds")
|
||||
private String shareDeptIds;
|
||||
|
||||
/**
|
||||
* 客群状态:0=正常, 1=已禁用
|
||||
@@ -114,6 +114,49 @@ public class CustGroupVO {
|
||||
@ApiModelProperty(value = "备注", name = "remark")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 有效期截止时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty(value = "有效期截止时间", name = "validTime")
|
||||
private Date validTime;
|
||||
|
||||
/**
|
||||
* 创建状态:0=创建中, 1=创建成功, 2=创建失败
|
||||
*/
|
||||
@ApiModelProperty(value = "创建状态:0=创建中, 1=创建成功, 2=创建失败", name = "createStatus")
|
||||
private String createStatus;
|
||||
|
||||
/**
|
||||
* 网格类型:0=绩效网格, 1=地理网格, 2=绘制网格
|
||||
*/
|
||||
@ApiModelProperty(value = "网格类型", name = "gridType")
|
||||
private String gridType;
|
||||
|
||||
/**
|
||||
* 绩效业务类型:retail=零售, corporate=公司
|
||||
*/
|
||||
@ApiModelProperty(value = "绩效业务类型", name = "cmpmBizType")
|
||||
private String cmpmBizType;
|
||||
|
||||
/**
|
||||
* 客户经理列表(逗号分隔)
|
||||
*/
|
||||
@ApiModelProperty(value = "客户经理列表", name = "gridUserNames")
|
||||
private String gridUserNames;
|
||||
|
||||
/**
|
||||
* 地理网格ID列表(逗号分隔)
|
||||
*/
|
||||
@ApiModelProperty(value = "地理网格ID列表", name = "regionGridIds")
|
||||
private String regionGridIds;
|
||||
|
||||
/**
|
||||
* 绘制网格ID列表(逗号分隔)
|
||||
*/
|
||||
@ApiModelProperty(value = "绘制网格ID列表", name = "drawGridIds")
|
||||
private String drawGridIds;
|
||||
|
||||
/**
|
||||
* 客户列表
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
|
||||
import com.ruoyi.group.domain.entity.CustGroup;
|
||||
import com.ruoyi.group.domain.vo.CustGroupVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
@@ -13,6 +14,7 @@ import java.util.List;
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Mapper
|
||||
public interface CustGroupMapper extends BaseMapper<CustGroup> {
|
||||
|
||||
/**
|
||||
@@ -31,4 +33,12 @@ public interface CustGroupMapper extends BaseMapper<CustGroup> {
|
||||
* @return 客群VO列表
|
||||
*/
|
||||
List<CustGroupVO> selectCustGroupList(@Param("dto") CustGroupQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 根据ID查询客群详情
|
||||
*
|
||||
* @param id 客群ID
|
||||
* @return 客群VO
|
||||
*/
|
||||
CustGroupVO selectCustGroupById(@Param("id") Long id);
|
||||
}
|
||||
@@ -1,21 +1,36 @@
|
||||
package com.ruoyi.group.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||
import com.ruoyi.group.domain.entity.CustGroupMember;
|
||||
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客群客户关联Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Mapper
|
||||
public interface CustGroupMemberMapper extends BaseMapper<CustGroupMember> {
|
||||
|
||||
/**
|
||||
* 查询客群客户数量
|
||||
* 分页查询客群客户列表
|
||||
*
|
||||
* @param groupId 客群ID
|
||||
* @return 数量
|
||||
* @param dto 查询条件
|
||||
* @return 客户列表
|
||||
*/
|
||||
Long countByGroupId(@Param("groupId") Long groupId);
|
||||
List<CustGroupMemberVO> selectCustGroupMemberList(@Param("groupId") Long groupId,
|
||||
@Param("dto") CustGroupMemberQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 批量插入客群客户(INSERT IGNORE,遇到重复键自动跳过)
|
||||
*
|
||||
* @param memberList 客户列表
|
||||
*/
|
||||
void batchInsertMembers(@Param("list") List<CustGroupMember> memberList);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ruoyi.group.service;
|
||||
|
||||
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客群客户服务接口
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface ICustGroupMemberService {
|
||||
|
||||
/**
|
||||
* 分页查询客群客户列表
|
||||
*
|
||||
* @param groupId 客群ID
|
||||
* @param dto 查询条件
|
||||
* @return 客户列表
|
||||
*/
|
||||
List<CustGroupMemberVO> listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 手动移除客群客户
|
||||
*
|
||||
* @param groupId 客群ID
|
||||
* @param memberIds 客户ID列表
|
||||
* @return 结果
|
||||
*/
|
||||
String removeMembers(Long groupId, List<Long> memberIds);
|
||||
}
|
||||
@@ -23,6 +23,14 @@ public interface ICustGroupService {
|
||||
*/
|
||||
List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto);
|
||||
|
||||
/**
|
||||
* 根据ID查询客群详情
|
||||
*
|
||||
* @param id 客群ID
|
||||
* @return 客群VO
|
||||
*/
|
||||
CustGroupVO getCustGroup(Long id);
|
||||
|
||||
/**
|
||||
* 异步创建客群(模板导入)
|
||||
*
|
||||
@@ -57,14 +65,6 @@ public interface ICustGroupService {
|
||||
*/
|
||||
String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file);
|
||||
|
||||
/**
|
||||
* 更新客群
|
||||
*
|
||||
* @param custGroup 客群实体
|
||||
* @return 结果消息
|
||||
*/
|
||||
String updateCustGroup(CustGroup custGroup);
|
||||
|
||||
/**
|
||||
* 删除客群
|
||||
*
|
||||
@@ -73,14 +73,6 @@ public interface ICustGroupService {
|
||||
*/
|
||||
String deleteCustGroup(List<Long> idList);
|
||||
|
||||
/**
|
||||
* 获取客群详情
|
||||
*
|
||||
* @param id 客群ID
|
||||
* @return 客群VO
|
||||
*/
|
||||
CustGroupVO getCustGroup(Long id);
|
||||
|
||||
/**
|
||||
* 检查客群名称是否存在
|
||||
*
|
||||
@@ -97,15 +89,6 @@ public interface ICustGroupService {
|
||||
*/
|
||||
String getCreateStatus(Long id);
|
||||
|
||||
/**
|
||||
* 手动移除客群客户
|
||||
*
|
||||
* @param groupId 客群ID
|
||||
* @param memberIds 客群成员ID列表
|
||||
* @return 结果消息
|
||||
*/
|
||||
String removeMembers(Long groupId, List<Long> memberIds);
|
||||
|
||||
/**
|
||||
* 更新动态客群(定时任务调用)
|
||||
* 根据原始导入条件重新查询并更新客户列表
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.ruoyi.group.service.impl;
|
||||
|
||||
import com.ruoyi.group.domain.dto.CustGroupMemberQueryDTO;
|
||||
import com.ruoyi.group.domain.entity.CustGroup;
|
||||
import com.ruoyi.group.domain.entity.CustGroupMember;
|
||||
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||
import com.ruoyi.group.mapper.CustGroupMapper;
|
||||
import com.ruoyi.group.mapper.CustGroupMemberMapper;
|
||||
import com.ruoyi.group.service.ICustGroupMemberService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客群客户服务实现类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service
|
||||
public class CustGroupMemberServiceImpl implements ICustGroupMemberService {
|
||||
|
||||
@Resource
|
||||
private CustGroupMemberMapper custGroupMemberMapper;
|
||||
|
||||
@Resource
|
||||
private CustGroupMapper custGroupMapper;
|
||||
|
||||
@Override
|
||||
public List<CustGroupMemberVO> listCustGroupMembers(Long groupId, CustGroupMemberQueryDTO dto) {
|
||||
return custGroupMemberMapper.selectCustGroupMemberList(groupId, dto);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String removeMembers(Long groupId, List<Long> memberIds) {
|
||||
// 检查客群是否存在
|
||||
CustGroup custGroup = custGroupMapper.selectById(groupId);
|
||||
if (custGroup == null) {
|
||||
return "客群不存在";
|
||||
}
|
||||
|
||||
// 删除客户关联
|
||||
memberIds.forEach(memberId -> {
|
||||
CustGroupMember member = custGroupMemberMapper.selectById(memberId);
|
||||
if (member != null && member.getGroupId().equals(groupId)) {
|
||||
// 设置手动移除标识
|
||||
member.setManualRemove(1);
|
||||
// 逻辑删除
|
||||
custGroupMemberMapper.deleteById(memberId);
|
||||
}
|
||||
});
|
||||
|
||||
return "移除成功";
|
||||
}
|
||||
}
|
||||
@@ -2,36 +2,28 @@ package com.ruoyi.group.service.impl;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.mapper.SysDeptMapper;
|
||||
import com.ruoyi.ibs.cmpm.domain.entity.GridCmpm;
|
||||
import com.ruoyi.ibs.cmpm.mapper.GridCmpmMapper;
|
||||
import com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate;
|
||||
import com.ruoyi.ibs.draw.domain.entity.DrawShapeCust;
|
||||
import com.ruoyi.ibs.draw.mapper.DrawGridShapeRelateMapper;
|
||||
import com.ruoyi.ibs.draw.mapper.DrawShapeCustMapper;
|
||||
import com.ruoyi.ibs.grid.domain.vo.CustVO;
|
||||
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO;
|
||||
import com.ruoyi.ibs.cmpm.service.GridCmpmService;
|
||||
import com.ruoyi.ibs.draw.mapper.DrawGridCustUserUnbindMapper;
|
||||
import com.ruoyi.ibs.grid.service.RegionGridListService;
|
||||
import com.ruoyi.group.domain.dto.CustGroupMemberTemplate;
|
||||
import com.ruoyi.group.domain.dto.CustGroupQueryDTO;
|
||||
import com.ruoyi.group.domain.dto.GridImportDTO;
|
||||
import com.ruoyi.group.domain.entity.CustGroup;
|
||||
import com.ruoyi.group.domain.entity.CustGroupMember;
|
||||
import com.ruoyi.group.domain.vo.CustGroupMemberVO;
|
||||
import com.ruoyi.group.domain.vo.CustGroupVO;
|
||||
import com.ruoyi.group.mapper.CustGroupMapper;
|
||||
import com.ruoyi.group.mapper.CustGroupMemberMapper;
|
||||
import com.ruoyi.group.service.ICustGroupService;
|
||||
import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
|
||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
||||
import com.ruoyi.ibs.grid.mapper.RegionCustUserMapper;
|
||||
import com.ruoyi.ibs.grid.mapper.RegionGridMapper;
|
||||
import com.ruoyi.ibs.handler.DynamicTableNameHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -61,31 +53,31 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
private ExecutorService executorService;
|
||||
|
||||
@Resource
|
||||
private GridCmpmMapper gridCmpmMapper;
|
||||
|
||||
@Resource
|
||||
private RegionCustUserMapper regionCustUserMapper;
|
||||
|
||||
@Resource
|
||||
private RegionGridMapper regionGridMapper;
|
||||
private GridCmpmService gridCmpmService;
|
||||
|
||||
@Resource
|
||||
private RegionGridListService regionGridListService;
|
||||
|
||||
@Resource
|
||||
private DrawShapeCustMapper drawShapeCustMapper;
|
||||
private DrawGridCustUserUnbindMapper drawGridCustUserUnbindMapper;
|
||||
|
||||
@Resource
|
||||
private DrawGridShapeRelateMapper drawGridShapeRelateMapper;
|
||||
|
||||
@Resource
|
||||
private SysDeptMapper sysDeptMapper;
|
||||
private TransactionTemplate transactionTemplate;
|
||||
|
||||
@Override
|
||||
public List<CustGroupVO> listCustGroup(CustGroupQueryDTO dto) {
|
||||
return custGroupMapper.selectCustGroupList(dto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustGroupVO getCustGroup(Long id) {
|
||||
CustGroupVO custGroup = custGroupMapper.selectCustGroupById(id);
|
||||
if (custGroup == null) {
|
||||
throw new ServiceException("客群不存在");
|
||||
}
|
||||
return custGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createCustGroupByTemplate(CustGroup custGroup, MultipartFile file) {
|
||||
@@ -176,21 +168,6 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
return String.valueOf(custGroup.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String updateCustGroup(CustGroup custGroup) {
|
||||
CustGroup existGroup = custGroupMapper.selectById(custGroup.getId());
|
||||
if (existGroup == null) {
|
||||
throw new ServiceException("客群不存在");
|
||||
}
|
||||
// 检查客群名称是否存在(排除自己)
|
||||
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
||||
throw new ServiceException("客群名称已存在");
|
||||
}
|
||||
custGroupMapper.updateById(custGroup);
|
||||
return "客群更新成功";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String updateCustGroupByGrid(GridImportDTO gridImportDTO) {
|
||||
@@ -200,10 +177,17 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
if (existGroup == null) {
|
||||
throw new ServiceException("客群不存在");
|
||||
}
|
||||
// 检查客群是否正在创建或更新
|
||||
if ("0".equals(existGroup.getCreateStatus())) {
|
||||
throw new ServiceException("客群正在处理中,请稍后再试");
|
||||
}
|
||||
// 更新客群基本信息
|
||||
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
||||
throw new ServiceException("客群名称已存在");
|
||||
}
|
||||
// 检查网格条件是否发生变化
|
||||
boolean gridConditionChanged = isGridConditionChanged(existGroup, gridImportDTO);
|
||||
|
||||
// 重新查询数据库,获取最新状态
|
||||
CustGroup latestGroup = custGroupMapper.selectById(custGroup.getId());
|
||||
latestGroup.setGroupName(custGroup.getGroupName());
|
||||
@@ -212,7 +196,48 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
latestGroup.setValidTime(custGroup.getValidTime());
|
||||
latestGroup.setShareEnabled(custGroup.getShareEnabled());
|
||||
latestGroup.setShareDeptIds(custGroup.getShareDeptIds());
|
||||
|
||||
// 保存新的网格导入条件
|
||||
String gridType = gridImportDTO.getGridType();
|
||||
latestGroup.setGridType(gridType);
|
||||
if ("0".equals(gridType)) {
|
||||
latestGroup.setCmpmBizType(gridImportDTO.getCmpmBizType());
|
||||
if (gridImportDTO.getUserNames() != null && !gridImportDTO.getUserNames().isEmpty()) {
|
||||
latestGroup.setGridUserNames(String.join(",", gridImportDTO.getUserNames()));
|
||||
} else {
|
||||
latestGroup.setGridUserNames(null);
|
||||
}
|
||||
} else if ("1".equals(gridType)) {
|
||||
if (gridImportDTO.getRegionGridIds() != null && !gridImportDTO.getRegionGridIds().isEmpty()) {
|
||||
latestGroup.setRegionGridIds(gridImportDTO.getRegionGridIds().stream()
|
||||
.map(String::valueOf).collect(Collectors.joining(",")));
|
||||
} else {
|
||||
latestGroup.setRegionGridIds(null);
|
||||
}
|
||||
} else if ("2".equals(gridType)) {
|
||||
if (gridImportDTO.getDrawGridIds() != null && !gridImportDTO.getDrawGridIds().isEmpty()) {
|
||||
latestGroup.setDrawGridIds(gridImportDTO.getDrawGridIds().stream()
|
||||
.map(String::valueOf).collect(Collectors.joining(",")));
|
||||
} else {
|
||||
latestGroup.setDrawGridIds(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
latestGroup.setUpdateBy(SecurityUtils.getUsername());
|
||||
latestGroup.setUpdateTime(new Date());
|
||||
custGroupMapper.updateById(latestGroup);
|
||||
|
||||
// 如果网格条件没有变化,直接返回成功
|
||||
if (!gridConditionChanged) {
|
||||
log.info("客群网格条件未变化,跳过客户重新导入,客群ID:{}", custGroup.getId());
|
||||
return "客群更新成功(客户列表无需变更)";
|
||||
}
|
||||
|
||||
// 网格条件发生变化,需要重新导入客户
|
||||
log.info("客群网格条件已变化,开始重新导入客户,客群ID:{}", custGroup.getId());
|
||||
// 设置更新状态为"更新中"
|
||||
latestGroup.setCreateStatus("0");
|
||||
custGroupMapper.updateById(latestGroup);
|
||||
// 重新设置回DTO(确保异步线程能获取到正确的ID和状态)
|
||||
gridImportDTO.setCustGroup(latestGroup);
|
||||
@@ -223,6 +248,80 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
return "客群更新中";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查网格条件是否发生变化
|
||||
*/
|
||||
private boolean isGridConditionChanged(CustGroup existGroup, GridImportDTO gridImportDTO) {
|
||||
// 首先检查网格类型是否变化
|
||||
String newGridType = gridImportDTO.getGridType();
|
||||
String oldGridType = existGroup.getGridType();
|
||||
|
||||
// 如果旧记录没有网格类型信息,说明需要导入
|
||||
if (oldGridType == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 网格类型变化
|
||||
if (!newGridType.equals(oldGridType)) {
|
||||
log.info("网格类型变化:旧={}, 新={}", oldGridType, newGridType);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据网格类型检查具体条件
|
||||
if ("0".equals(newGridType)) {
|
||||
// 绩效网格:比较业务类型和客户经理列表
|
||||
String oldBizType = existGroup.getCmpmBizType();
|
||||
String newBizType = gridImportDTO.getCmpmBizType();
|
||||
List<String> newUserNames = gridImportDTO.getUserNames();
|
||||
|
||||
if (!Objects.equals(oldBizType, newBizType)) {
|
||||
log.info("绩效业务类型变化:旧={}, 新={}", oldBizType, newBizType);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 比较客户经理列表
|
||||
String oldUserNames = existGroup.getGridUserNames();
|
||||
String newUserNamesStr = newUserNames == null || newUserNames.isEmpty()
|
||||
? null : String.join(",", newUserNames);
|
||||
if (!Objects.equals(oldUserNames, newUserNamesStr)) {
|
||||
log.info("客户经理列表变化:旧={}, 新={}", oldUserNames, newUserNamesStr);
|
||||
return true;
|
||||
}
|
||||
} else if ("1".equals(newGridType)) {
|
||||
// 地理网格:比较网格ID列表
|
||||
String oldRegionIds = existGroup.getRegionGridIds();
|
||||
List<Long> newRegionIds = gridImportDTO.getRegionGridIds();
|
||||
String newRegionIdsStr = newRegionIds == null || newRegionIds.isEmpty()
|
||||
? null : newRegionIds.stream().map(String::valueOf).sorted().collect(Collectors.joining(","));
|
||||
|
||||
// 归一化比较(排序后比较)
|
||||
String oldRegionIdsSorted = oldRegionIds == null ? null :
|
||||
Arrays.stream(oldRegionIds.split(",")).sorted().collect(Collectors.joining(","));
|
||||
|
||||
if (!Objects.equals(oldRegionIdsSorted, newRegionIdsStr)) {
|
||||
log.info("地理网格ID列表变化:旧={}, 新={}", oldRegionIdsSorted, newRegionIdsStr);
|
||||
return true;
|
||||
}
|
||||
} else if ("2".equals(newGridType)) {
|
||||
// 绘制网格:比较网格ID列表
|
||||
String oldDrawIds = existGroup.getDrawGridIds();
|
||||
List<Long> newDrawIds = gridImportDTO.getDrawGridIds();
|
||||
String newDrawIdsStr = newDrawIds == null || newDrawIds.isEmpty()
|
||||
? null : newDrawIds.stream().map(String::valueOf).sorted().collect(Collectors.joining(","));
|
||||
|
||||
// 归一化比较(排序后比较)
|
||||
String oldDrawIdsSorted = oldDrawIds == null ? null :
|
||||
Arrays.stream(oldDrawIds.split(",")).sorted().collect(Collectors.joining(","));
|
||||
|
||||
if (!Objects.equals(oldDrawIdsSorted, newDrawIdsStr)) {
|
||||
log.info("绘制网格ID列表变化:旧={}, 新={}", oldDrawIdsSorted, newDrawIdsStr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String updateCustGroupByTemplate(CustGroup custGroup, MultipartFile file) {
|
||||
@@ -231,6 +330,10 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
if (existGroup == null) {
|
||||
throw new ServiceException("客群不存在");
|
||||
}
|
||||
// 检查客群是否正在创建或更新
|
||||
if ("0".equals(existGroup.getCreateStatus())) {
|
||||
throw new ServiceException("客群正在处理中,请稍后再试");
|
||||
}
|
||||
// 更新客群基本信息
|
||||
if (!existGroup.getGroupName().equals(custGroup.getGroupName()) && checkGroupNameExist(custGroup.getGroupName())) {
|
||||
throw new ServiceException("客群名称已存在");
|
||||
@@ -243,7 +346,11 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
latestGroup.setValidTime(custGroup.getValidTime());
|
||||
latestGroup.setShareEnabled(custGroup.getShareEnabled());
|
||||
latestGroup.setShareDeptIds(custGroup.getShareDeptIds());
|
||||
// 设置更新状态为"更新中"
|
||||
latestGroup.setCreateStatus("0");
|
||||
// 更新数据库
|
||||
latestGroup.setUpdateBy(SecurityUtils.getUsername());
|
||||
latestGroup.setUpdateTime(new Date());
|
||||
custGroupMapper.updateById(latestGroup);
|
||||
// 获取当前用户部门编码(异步线程中无法获取)
|
||||
String headId = SecurityUtils.getHeadId();
|
||||
@@ -269,82 +376,6 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
return "客群删除成功";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String removeMembers(Long groupId, List<Long> memberIds) {
|
||||
if (memberIds == null || memberIds.isEmpty()) {
|
||||
throw new ServiceException("请选择要移除的客户");
|
||||
}
|
||||
// 检查客群是否存在
|
||||
CustGroup custGroup = custGroupMapper.selectById(groupId);
|
||||
if (custGroup == null) {
|
||||
throw new ServiceException("客群不存在");
|
||||
}
|
||||
// 标记为手动移除(软删除)
|
||||
for (Long memberId : memberIds) {
|
||||
CustGroupMember member = custGroupMemberMapper.selectById(memberId);
|
||||
if (member != null && member.getGroupId().equals(groupId)) {
|
||||
member.setManualRemove(1);
|
||||
member.setDelFlag(1);
|
||||
custGroupMemberMapper.updateById(member);
|
||||
}
|
||||
}
|
||||
return "成功移除 " + memberIds.size() + " 个客户";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustGroupVO getCustGroup(Long id) {
|
||||
CustGroup custGroup = custGroupMapper.selectById(id);
|
||||
if (custGroup == null) {
|
||||
throw new ServiceException("客群不存在");
|
||||
}
|
||||
CustGroupVO vo = new CustGroupVO();
|
||||
vo.setId(custGroup.getId());
|
||||
vo.setGroupName(custGroup.getGroupName());
|
||||
vo.setGroupMode(custGroup.getGroupMode());
|
||||
vo.setCreateMode(custGroup.getCreateMode());
|
||||
vo.setUserName(custGroup.getUserName());
|
||||
vo.setNickName(custGroup.getNickName());
|
||||
vo.setDeptId(custGroup.getDeptId());
|
||||
vo.setShareEnabled(custGroup.getShareEnabled());
|
||||
vo.setGroupStatus(custGroup.getGroupStatus());
|
||||
vo.setRemark(custGroup.getRemark());
|
||||
vo.setCreateBy(custGroup.getCreateBy());
|
||||
vo.setCreateTime(custGroup.getCreateTime());
|
||||
vo.setUpdateBy(custGroup.getUpdateBy());
|
||||
vo.setUpdateTime(custGroup.getUpdateTime());
|
||||
// 处理共享部门ID列表
|
||||
if (StringUtils.isNotEmpty(custGroup.getShareDeptIds())) {
|
||||
List<Long> deptIds = new ArrayList<>();
|
||||
for (String idStr : custGroup.getShareDeptIds().split(",")) {
|
||||
if (StringUtils.isNotEmpty(idStr)) {
|
||||
deptIds.add(Long.valueOf(idStr));
|
||||
}
|
||||
}
|
||||
vo.setShareDeptIds(deptIds);
|
||||
}
|
||||
// 查询客户列表
|
||||
LambdaQueryWrapper<CustGroupMember> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CustGroupMember::getGroupId, id);
|
||||
List<CustGroupMember> memberList = custGroupMemberMapper.selectList(wrapper);
|
||||
List<CustGroupMemberVO> memberVOList = new ArrayList<>();
|
||||
for (CustGroupMember member : memberList) {
|
||||
CustGroupMemberVO memberVO = new CustGroupMemberVO();
|
||||
memberVO.setId(member.getId());
|
||||
memberVO.setGroupId(member.getGroupId());
|
||||
memberVO.setCustType(member.getCustType());
|
||||
memberVO.setCustId(member.getCustId());
|
||||
memberVO.setCustName(member.getCustName());
|
||||
memberVO.setCustIdc(member.getCustIdc());
|
||||
memberVO.setSocialCreditCode(member.getSocialCreditCode());
|
||||
memberVO.setCreateTime(member.getCreateTime());
|
||||
memberVOList.add(memberVO);
|
||||
}
|
||||
vo.setCustList(memberVOList);
|
||||
vo.setCustCount(memberVOList.size());
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkGroupNameExist(String groupName) {
|
||||
LambdaQueryWrapper<CustGroup> wrapper = new LambdaQueryWrapper<>();
|
||||
@@ -486,14 +517,14 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
gridImportDTO.setRegionGridIds(Arrays.stream(custGroup.getRegionGridIds().split(","))
|
||||
.map(Long::valueOf).collect(Collectors.toList()));
|
||||
}
|
||||
newMemberList.addAll(importFromRegionGrid(custGroup, gridImportDTO));
|
||||
newMemberList.addAll(importFromRegionGrid(custGroup, gridImportDTO, headId));
|
||||
} else if ("2".equals(gridType)) {
|
||||
// 绘制网格
|
||||
if (StringUtils.isNotEmpty(custGroup.getDrawGridIds())) {
|
||||
gridImportDTO.setDrawGridIds(Arrays.stream(custGroup.getDrawGridIds().split(","))
|
||||
.map(Long::valueOf).collect(Collectors.toList()));
|
||||
}
|
||||
newMemberList.addAll(importFromDrawGrid(custGroup, gridImportDTO));
|
||||
newMemberList.addAll(importFromDrawGrid(custGroup, gridImportDTO, headId));
|
||||
}
|
||||
|
||||
// 计算差异
|
||||
@@ -601,51 +632,71 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
}
|
||||
memberList.add(member);
|
||||
}
|
||||
// 批量插入
|
||||
int batchSize = 1000;
|
||||
int successCount = 0;
|
||||
int skippedCount = 0;
|
||||
int restoredCount = 0;
|
||||
for (int i = 0; i < memberList.size(); i += batchSize) {
|
||||
int endIndex = Math.min(i + batchSize, memberList.size());
|
||||
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
||||
for (CustGroupMember member : batchList) {
|
||||
try {
|
||||
custGroupMemberMapper.insert(member);
|
||||
successCount++;
|
||||
} catch (DuplicateKeyException e) {
|
||||
// 客户已存在,检查是否是被手动移除的
|
||||
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(CustGroupMember::getGroupId, member.getGroupId())
|
||||
.eq(CustGroupMember::getCustId, member.getCustId())
|
||||
.eq(CustGroupMember::getCustType, member.getCustType());
|
||||
CustGroupMember existMember = custGroupMemberMapper.selectOne(queryWrapper);
|
||||
if (existMember != null && existMember.getManualRemove() != null && existMember.getManualRemove() == 1) {
|
||||
// 是被手动移除的客户,清除标记并恢复
|
||||
existMember.setManualRemove(0);
|
||||
existMember.setDelFlag(0);
|
||||
existMember.setCustName(member.getCustName());
|
||||
custGroupMemberMapper.updateById(existMember);
|
||||
restoredCount++;
|
||||
log.debug("恢复手动移除的客户:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||
} else {
|
||||
// 正常存在的客户,跳过
|
||||
skippedCount++;
|
||||
log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||
|
||||
// 使用编程式事务:先删除旧客户,再插入新客户
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
// 删除该客群的所有旧客户
|
||||
log.info("开始删除客群旧客户(模板导入),客群ID:{}", custGroup.getId());
|
||||
LambdaQueryWrapper<CustGroupMember> memberWrapper = new LambdaQueryWrapper<>();
|
||||
memberWrapper.eq(CustGroupMember::getGroupId, custGroup.getId());
|
||||
custGroupMemberMapper.delete(memberWrapper);
|
||||
log.info("客群旧客户删除完成(模板导入),客群ID:{}", custGroup.getId());
|
||||
|
||||
// 批量插入新客户
|
||||
log.info("开始批量插入客户(模板导入),客群ID:{},客户总数:{}", custGroup.getId(), memberList.size());
|
||||
int batchSize = 1000;
|
||||
int successCount = 0;
|
||||
int skippedCount = 0;
|
||||
int restoredCount = 0;
|
||||
for (int i = 0; i < memberList.size(); i += batchSize) {
|
||||
int endIndex = Math.min(i + batchSize, memberList.size());
|
||||
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
||||
log.info("处理批次 [{}/{}],本批大小:{}", i / batchSize + 1, (memberList.size() + batchSize - 1) / batchSize, batchList.size());
|
||||
for (CustGroupMember member : batchList) {
|
||||
try {
|
||||
custGroupMemberMapper.insert(member);
|
||||
successCount++;
|
||||
} catch (DuplicateKeyException e) {
|
||||
// 客户已存在,检查是否是被手动移除的
|
||||
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(CustGroupMember::getGroupId, member.getGroupId())
|
||||
.eq(CustGroupMember::getCustId, member.getCustId())
|
||||
.eq(CustGroupMember::getCustType, member.getCustType());
|
||||
CustGroupMember existMember = custGroupMemberMapper.selectOne(queryWrapper);
|
||||
if (existMember != null && existMember.getManualRemove() != null && existMember.getManualRemove() == 1) {
|
||||
// 是被手动移除的客户,清除标记并恢复
|
||||
existMember.setManualRemove(0);
|
||||
existMember.setDelFlag(0);
|
||||
existMember.setCustName(member.getCustName());
|
||||
custGroupMemberMapper.updateById(existMember);
|
||||
restoredCount++;
|
||||
log.debug("恢复手动移除的客户:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||
} else {
|
||||
// 正常存在的客户,跳过
|
||||
skippedCount++;
|
||||
log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("客群客户导入完成(模板),客群ID:{},成功:{},跳过重复:{},恢复:{}",
|
||||
custGroup.getId(), successCount, skippedCount, restoredCount);
|
||||
// 更新创建状态为成功
|
||||
custGroup.setCreateStatus("1");
|
||||
custGroupMapper.updateById(custGroup);
|
||||
log.info("客群客户导入完成(模板),客群ID:{},成功:{},跳过重复:{},恢复:{}",
|
||||
custGroup.getId(), successCount, skippedCount, restoredCount);
|
||||
// 更新创建状态为成功
|
||||
custGroup.setCreateStatus("1");
|
||||
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||
custGroup.setUpdateTime(new Date());
|
||||
custGroupMapper.updateById(custGroup);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("客群客户导入失败,客群ID:{},异常:{}", custGroup.getId(), e.getMessage(), e);
|
||||
|
||||
// 注意:由于删除和插入在同一事务中,插入失败会自动回滚删除操作,无需手动清理
|
||||
|
||||
// 更新创建状态为失败
|
||||
custGroup.setCreateStatus("2");
|
||||
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||
custGroup.setUpdateTime(new Date());
|
||||
custGroupMapper.updateById(custGroup);
|
||||
log.error("客群客户导入失败,客群ID:{}", custGroup.getId(), e);
|
||||
throw new ServiceException("客群客户导入失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -664,63 +715,122 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
memberList.addAll(importFromCmpmGrid(custGroup, gridImportDTO, headId));
|
||||
} else if ("1".equals(gridType)) {
|
||||
// 地理网格
|
||||
memberList.addAll(importFromRegionGrid(custGroup, gridImportDTO));
|
||||
memberList.addAll(importFromRegionGrid(custGroup, gridImportDTO, headId));
|
||||
} else if ("2".equals(gridType)) {
|
||||
// 绘制网格
|
||||
memberList.addAll(importFromDrawGrid(custGroup, gridImportDTO));
|
||||
memberList.addAll(importFromDrawGrid(custGroup, gridImportDTO, headId));
|
||||
}
|
||||
if (memberList.isEmpty()) {
|
||||
throw new ServiceException("未查询到任何客户");
|
||||
}
|
||||
// 批量插入
|
||||
int batchSize = 1000;
|
||||
int successCount = 0;
|
||||
int skippedCount = 0;
|
||||
int restoredCount = 0;
|
||||
for (int i = 0; i < memberList.size(); i += batchSize) {
|
||||
int endIndex = Math.min(i + batchSize, memberList.size());
|
||||
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
||||
for (CustGroupMember member : batchList) {
|
||||
try {
|
||||
custGroupMemberMapper.insert(member);
|
||||
successCount++;
|
||||
} catch (DuplicateKeyException e) {
|
||||
// 客户已存在,检查是否是被手动移除的
|
||||
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(CustGroupMember::getGroupId, member.getGroupId())
|
||||
.eq(CustGroupMember::getCustId, member.getCustId())
|
||||
.eq(CustGroupMember::getCustType, member.getCustType());
|
||||
CustGroupMember existMember = custGroupMemberMapper.selectOne(queryWrapper);
|
||||
if (existMember != null && existMember.getManualRemove() != null && existMember.getManualRemove() == 1) {
|
||||
// 是被手动移除的客户,清除标记并恢复
|
||||
existMember.setManualRemove(0);
|
||||
existMember.setDelFlag(0);
|
||||
existMember.setCustName(member.getCustName());
|
||||
custGroupMemberMapper.updateById(existMember);
|
||||
restoredCount++;
|
||||
log.debug("恢复手动移除的客户:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||
} else {
|
||||
// 正常存在的客户,跳过
|
||||
skippedCount++;
|
||||
log.debug("客户已存在,跳过:groupId={}, custId={}", member.getGroupId(), member.getCustId());
|
||||
|
||||
// 使用编程式事务:先删除旧客户,再插入新客户
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
// 删除该客群的所有旧客户
|
||||
log.info("开始删除客群旧客户,客群ID:{}", custGroup.getId());
|
||||
LambdaQueryWrapper<CustGroupMember> memberWrapper = new LambdaQueryWrapper<>();
|
||||
memberWrapper.eq(CustGroupMember::getGroupId, custGroup.getId());
|
||||
custGroupMemberMapper.delete(memberWrapper);
|
||||
log.info("客群旧客户删除完成,客群ID:{}", custGroup.getId());
|
||||
|
||||
// 批量插入新客户
|
||||
log.info("开始批量插入客户,客群ID:{},客户总数:{}", custGroup.getId(), memberList.size());
|
||||
|
||||
// 分批批量插入(每批1000条)
|
||||
int batchSize = 1000;
|
||||
int totalInserted = 0;
|
||||
int totalRestored = 0;
|
||||
int totalSkipped = 0;
|
||||
|
||||
for (int i = 0; i < memberList.size(); i += batchSize) {
|
||||
int endIndex = Math.min(i + batchSize, memberList.size());
|
||||
List<CustGroupMember> batchList = memberList.subList(i, endIndex);
|
||||
log.info("处理批次 [{}/{}],本批大小:{}", i / batchSize + 1,
|
||||
(memberList.size() + batchSize - 1) / batchSize, batchList.size());
|
||||
|
||||
// SQL层面的批量插入
|
||||
custGroupMemberMapper.batchInsertMembers(batchList);
|
||||
|
||||
// 查询本批中被手动移除的客户,需要恢复
|
||||
List<CustGroupMember> toRestore = findManualRemovedToRestore(custGroup.getId(), batchList);
|
||||
if (!toRestore.isEmpty()) {
|
||||
// 恢复被手动移除的客户
|
||||
for (CustGroupMember m : toRestore) {
|
||||
batchList.stream()
|
||||
.filter(b -> b.getCustId().equals(m.getCustId()) && b.getCustType().equals(m.getCustType()))
|
||||
.findFirst()
|
||||
.ifPresent(origin -> m.setCustName(origin.getCustName()));
|
||||
m.setManualRemove(0);
|
||||
custGroupMemberMapper.updateById(m);
|
||||
}
|
||||
log.info("本批恢复被手动移除的客户:{} 条", toRestore.size());
|
||||
totalRestored += toRestore.size();
|
||||
}
|
||||
|
||||
totalInserted += batchList.size() - toRestore.size();
|
||||
totalSkipped += toRestore.size();
|
||||
}
|
||||
}
|
||||
log.info("客群客户导入完成(网格),客群ID:{},成功:{},跳过重复:{},恢复:{}",
|
||||
custGroup.getId(), successCount, skippedCount, restoredCount);
|
||||
// 更新创建状态为成功
|
||||
custGroup.setCreateStatus("1");
|
||||
custGroupMapper.updateById(custGroup);
|
||||
|
||||
log.info("客群客户导入完成(网格),客群ID:{},插入:{},恢复:{},跳过:{}",
|
||||
custGroup.getId(), totalInserted, totalRestored, totalSkipped);
|
||||
|
||||
// 更新创建状态为成功
|
||||
custGroup.setCreateStatus("1");
|
||||
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||
custGroup.setUpdateTime(new Date());
|
||||
log.info("准备更新客群状态为成功,客群ID:{}", custGroup.getId());
|
||||
custGroupMapper.updateById(custGroup);
|
||||
log.info("客群状态更新成功完成,客群ID:{}", custGroup.getId());
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// 先记录原始异常(必须第一时间记录,避免后续异常覆盖)
|
||||
log.error("==========客群客户导入异常========== 客群ID:{},异常类型:{},异常消息:{}",
|
||||
custGroup.getId(), e.getClass().getName(), e.getMessage(), e);
|
||||
|
||||
// 注意:由于删除和插入在同一事务中,插入失败会自动回滚删除操作,无需手动清理
|
||||
|
||||
// 更新创建状态为失败
|
||||
custGroup.setCreateStatus("2");
|
||||
custGroupMapper.updateById(custGroup);
|
||||
log.error("客群客户导入失败,客群ID:{}", custGroup.getId(), e);
|
||||
try {
|
||||
custGroup.setCreateStatus("2");
|
||||
custGroup.setUpdateBy(custGroup.getCreateBy());
|
||||
custGroup.setUpdateTime(new Date());
|
||||
log.info("准备更新客群状态为失败,客群ID:{}", custGroup.getId());
|
||||
custGroupMapper.updateById(custGroup);
|
||||
log.info("客群状态更新为失败完成,客群ID:{}", custGroup.getId());
|
||||
} catch (Exception updateException) {
|
||||
log.error("==========更新客群状态为失败也异常了========== 客群ID:{},异常类型:{},异常消息:{}",
|
||||
custGroup.getId(), updateException.getClass().getName(), updateException.getMessage(), updateException);
|
||||
}
|
||||
throw new ServiceException("客群客户导入失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找需要恢复的被手动移除的客户
|
||||
*
|
||||
* @param groupId 客群ID
|
||||
* @param batchList 本批导入的客户列表
|
||||
* @return 需要恢复的客户列表
|
||||
*/
|
||||
private List<CustGroupMember> findManualRemovedToRestore(Long groupId, List<CustGroupMember> batchList) {
|
||||
// 构建本批客户的 (custId, custType) 集合
|
||||
Set<String> batchKeys = batchList.stream()
|
||||
.map(m -> m.getCustId() + "|" + m.getCustType())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 查询该客群所有被手动移除的客户
|
||||
LambdaQueryWrapper<CustGroupMember> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(CustGroupMember::getGroupId, groupId)
|
||||
.eq(CustGroupMember::getManualRemove, 1)
|
||||
.eq(CustGroupMember::getDelFlag, 0);
|
||||
List<CustGroupMember> manualRemovedList = custGroupMemberMapper.selectList(queryWrapper);
|
||||
|
||||
// 筛选出本批中需要恢复的
|
||||
return manualRemovedList.stream()
|
||||
.filter(m -> batchKeys.contains(m.getCustId() + "|" + m.getCustType()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从绩效网格导入客户
|
||||
*/
|
||||
@@ -735,13 +845,13 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
gridTypes.add("corporate");
|
||||
gridTypes.add("corporate_account");
|
||||
} else {
|
||||
throw new ServiceException("请选择绩效网格业务类型(零售/公司)");
|
||||
throw new ServiceException("请选择绩效网格业务类型(零售/对公/对公账户)");
|
||||
}
|
||||
// 查询客户
|
||||
for (String userName : gridImportDTO.getUserNames()) {
|
||||
for (String gridType : gridTypes) {
|
||||
List<GridCmpm> cmpmList = gridCmpmMapper.getGridCmpmByUserName(userName, headId, gridType);
|
||||
for (GridCmpm cmpm : cmpmList) {
|
||||
List<GridCmpmVO> cmpmList = gridCmpmService.selectManageListForImport(gridType, userName, headId);
|
||||
for (GridCmpmVO cmpm : cmpmList) {
|
||||
CustGroupMember member = new CustGroupMember();
|
||||
member.setGroupId(custGroup.getId());
|
||||
member.setCustId(cmpm.getCustId());
|
||||
@@ -760,25 +870,21 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
/**
|
||||
* 从地理网格导入客户
|
||||
*/
|
||||
private List<CustGroupMember> importFromRegionGrid(CustGroup custGroup, GridImportDTO gridImportDTO) {
|
||||
private List<CustGroupMember> importFromRegionGrid(CustGroup custGroup, GridImportDTO gridImportDTO, String headId) {
|
||||
List<CustGroupMember> memberList = new ArrayList<>();
|
||||
// 查询地理网格获取编码
|
||||
if (gridImportDTO.getRegionGridIds() == null || gridImportDTO.getRegionGridIds().isEmpty()) {
|
||||
throw new ServiceException("请选择地理网格");
|
||||
}
|
||||
List<RegionGrid> regionGrids = regionGridMapper.selectBatchIds(gridImportDTO.getRegionGridIds());
|
||||
// 使用 selectAllCustFromGrid 方法查询所有客户(不限制客户类型)
|
||||
for (RegionGrid regionGrid : regionGrids) {
|
||||
List<RegionCustUser> custUsers = regionGridListService.selectAllCustFromGrid(regionGrid);
|
||||
for (RegionCustUser custUser : custUsers) {
|
||||
CustGroupMember member = new CustGroupMember();
|
||||
member.setGroupId(custGroup.getId());
|
||||
member.setCustId(custUser.getCustId());
|
||||
member.setCustName(custUser.getCustName());
|
||||
member.setCustType(custUser.getCustType());
|
||||
member.setCreateTime(new Date());
|
||||
memberList.add(member);
|
||||
}
|
||||
// 直接根据网格ID列表批量查询所有客户(SQL中直接拼接headId,绕过MyBatis-Plus拦截器)
|
||||
List<RegionCustUser> custUsers = regionGridListService.selectAllCustByGridIds(gridImportDTO.getRegionGridIds(), headId);
|
||||
for (RegionCustUser custUser : custUsers) {
|
||||
CustGroupMember member = new CustGroupMember();
|
||||
member.setGroupId(custGroup.getId());
|
||||
member.setCustId(custUser.getCustId());
|
||||
member.setCustName(custUser.getCustName());
|
||||
member.setCustType(custUser.getCustType());
|
||||
member.setCreateTime(new Date());
|
||||
memberList.add(member);
|
||||
}
|
||||
return memberList;
|
||||
}
|
||||
@@ -786,27 +892,21 @@ public class CustGroupServiceImpl implements ICustGroupService {
|
||||
/**
|
||||
* 从绘制网格导入客户
|
||||
*/
|
||||
private List<CustGroupMember> importFromDrawGrid(CustGroup custGroup, GridImportDTO gridImportDTO) {
|
||||
private List<CustGroupMember> importFromDrawGrid(CustGroup custGroup, GridImportDTO gridImportDTO, String headId) {
|
||||
List<CustGroupMember> memberList = new ArrayList<>();
|
||||
if (gridImportDTO.getDrawGridIds() == null || gridImportDTO.getDrawGridIds().isEmpty()) {
|
||||
throw new ServiceException("请选择绘制网格");
|
||||
}
|
||||
// 查询绘制网格关联的图形ID
|
||||
// 使用 selectCustByDrawGridId 方法(直接在SQL中拼接headId,绕过拦截器)
|
||||
for (Long gridId : gridImportDTO.getDrawGridIds()) {
|
||||
LambdaQueryWrapper<DrawGridShapeRelate> relateWrapper = new LambdaQueryWrapper<>();
|
||||
relateWrapper.eq(DrawGridShapeRelate::getGridId, gridId);
|
||||
List<DrawGridShapeRelate> relates = drawGridShapeRelateMapper.selectList(relateWrapper);
|
||||
for (DrawGridShapeRelate relate : relates) {
|
||||
// 根据图形ID查询客户
|
||||
LambdaQueryWrapper<DrawShapeCust> custWrapper = new LambdaQueryWrapper<>();
|
||||
custWrapper.eq(DrawShapeCust::getShapeId, relate.getShapeId());
|
||||
List<DrawShapeCust> shapeCusts = drawShapeCustMapper.selectList(custWrapper);
|
||||
for (DrawShapeCust shapeCust : shapeCusts) {
|
||||
List<RegionCustUser> custUsers = drawGridCustUserUnbindMapper.selectCustByDrawGridId(gridId, headId);
|
||||
if (custUsers != null && !custUsers.isEmpty()) {
|
||||
for (RegionCustUser custUser : custUsers) {
|
||||
CustGroupMember member = new CustGroupMember();
|
||||
member.setGroupId(custGroup.getId());
|
||||
member.setCustId(shapeCust.getCustId());
|
||||
member.setCustName(shapeCust.getCustName());
|
||||
member.setCustType(shapeCust.getCustType());
|
||||
member.setCustId(custUser.getCustId());
|
||||
member.setCustName(custUser.getCustName());
|
||||
member.setCustType(custUser.getCustType());
|
||||
member.setCreateTime(new Date());
|
||||
memberList.add(member);
|
||||
}
|
||||
|
||||
@@ -43,4 +43,33 @@
|
||||
ORDER BY cg.create_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectCustGroupById" resultType="CustGroupVO">
|
||||
SELECT
|
||||
cg.id,
|
||||
cg.group_name,
|
||||
cg.group_mode,
|
||||
cg.create_mode,
|
||||
cg.user_name,
|
||||
cg.nick_name,
|
||||
cg.dept_id,
|
||||
cg.share_enabled,
|
||||
cg.share_dept_ids,
|
||||
cg.group_status,
|
||||
cg.valid_time,
|
||||
cg.create_by,
|
||||
cg.create_time,
|
||||
cg.update_by,
|
||||
cg.update_time,
|
||||
cg.remark,
|
||||
cg.create_status,
|
||||
cg.grid_type,
|
||||
cg.cmpm_biz_type,
|
||||
cg.grid_user_names,
|
||||
cg.region_grid_ids,
|
||||
cg.draw_grid_ids,
|
||||
(SELECT COUNT(*) FROM ibs_cust_group_member cgm WHERE cgm.group_id = cg.id AND cgm.del_flag = '0') AS cust_count
|
||||
FROM ibs_cust_group cg
|
||||
WHERE cg.id = #{id} AND cg.del_flag = '0'
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.group.mapper.CustGroupMemberMapper">
|
||||
|
||||
<select id="selectCustGroupMemberList" resultType="CustGroupMemberVO">
|
||||
SELECT
|
||||
cgm.id,
|
||||
cgm.group_id,
|
||||
cgm.cust_type,
|
||||
cgm.cust_id,
|
||||
cgm.cust_name,
|
||||
cgm.cust_idc,
|
||||
cgm.social_credit_code,
|
||||
cgm.create_time
|
||||
FROM ibs_cust_group_member cgm
|
||||
<where>
|
||||
cgm.group_id = #{groupId}
|
||||
AND cgm.del_flag = '0'
|
||||
<if test="dto != null and dto.custType != null and dto.custType != ''">
|
||||
AND cgm.cust_type = #{dto.custType}
|
||||
</if>
|
||||
<if test="dto != null and dto.custName != null and dto.custName != ''">
|
||||
AND cgm.cust_name LIKE CONCAT('%', #{dto.custName}, '%')
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY cgm.create_time ASC
|
||||
</select>
|
||||
|
||||
<!-- 批量插入客群客户(INSERT IGNORE,遇到重复键自动跳过) -->
|
||||
<insert id="batchInsertMembers">
|
||||
INSERT IGNORE INTO ibs_cust_group_member
|
||||
(group_id, cust_type, cust_id, cust_name, cust_idc, social_credit_code, create_by, create_time, del_flag, manual_remove)
|
||||
VALUES
|
||||
<foreach collection="list" item="item" index="index" separator=",">
|
||||
(#{item.groupId}, #{item.custType}, #{item.custId}, #{item.custName}, #{item.custIdc}, #{item.socialCreditCode}, #{item.createBy}, NOW(), '0', '0')
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@@ -117,4 +117,14 @@ public class GridCmpmController extends BaseController {
|
||||
public AjaxResult selectCustBaseInfoList(@RequestBody CustBaseInfo custBaseInfo) {
|
||||
return AjaxResult.success( gridCmpmCustService.selectCustInfoList (custBaseInfo)) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据网格类型获取客户经理列表
|
||||
*/
|
||||
@GetMapping("/managerList")
|
||||
@Log(title = "绩效网格-获取客户经理列表")
|
||||
@ApiOperation("获取客户经理列表")
|
||||
public AjaxResult getManagerListByGridType(@RequestParam String gridType) {
|
||||
return AjaxResult.success(gridCmpmService.getManagerListByGridType(gridType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,15 @@ import com.ruoyi.ibs.cmpm.domain.entity.GridCmpm;
|
||||
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailCustLevelManagerDetailVO;
|
||||
import com.ruoyi.ibs.cmpm.domain.vo.DwbRetailResultVO;
|
||||
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmClaimVO;
|
||||
import com.ruoyi.ibs.cmpm.domain.vo.GridCmpmVO;
|
||||
import com.ruoyi.ibs.customerselect.domain.CustBaseInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
@@ -60,6 +64,32 @@ public interface GridCmpmMapper {
|
||||
|
||||
List<String> selectManagerList();
|
||||
|
||||
/**
|
||||
* 根据网格类型和总行ID查询客户经理列表
|
||||
* @param gridType 网格类型(零售retail、对公corporate、对公账户corporate_account)
|
||||
* @param headId 总行ID
|
||||
* @return 客户经理列表(user_name, nick_name)
|
||||
*/
|
||||
List<Map<String, String>> getManagerListByGridType(@Param("gridType") String gridType, @Param("headId") String headId);
|
||||
|
||||
/**
|
||||
* 根据网格类型、总行ID和客户经理查询客户列表(分表查询,适用于客群导入)
|
||||
* @param gridType 网格类型
|
||||
* @param userName 客户经理柜员号
|
||||
* @param headId 总行ID
|
||||
* @return 客户列表
|
||||
*/
|
||||
List<GridCmpmVO> getCustomerListForImport(@Param("gridType") String gridType, @Param("userName") String userName, @Param("headId") String headId);
|
||||
|
||||
/**
|
||||
* 根据网格类型、总行ID和客户经理流式查询客户列表(使用Cursor,适用于大数据量场景)
|
||||
* @param gridType 网格类型
|
||||
* @param userName 客户经理柜员号
|
||||
* @param headId 总行ID
|
||||
* @return 客户列表游标
|
||||
*/
|
||||
Cursor<GridCmpmVO> getCustomerListForImportCursor(@Param("gridType") String gridType, @Param("userName") String userName, @Param("headId") String headId);
|
||||
|
||||
|
||||
// List<CustBaseInfo> selectCustInfoRetailFromGridCmpm(CustBaseInfo custBaseInfo);
|
||||
//
|
||||
|
||||
@@ -302,4 +302,26 @@ public class GridCmpmService {
|
||||
return changesMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据网格类型和总行ID查询客户经理列表
|
||||
* @param gridType 网格类型(零售retail、对公corporate、对公账户corporate_account)
|
||||
* @return 客户经理列表
|
||||
*/
|
||||
public List<Map<String, String>> getManagerListByGridType(String gridType) {
|
||||
String headId = SecurityUtils.getHeadId();
|
||||
return gridCmpmMapper.getManagerListByGridType(gridType, headId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数查询绩效网格客户列表(不依赖SecurityUtils,适用于异步线程)
|
||||
* 所有参数由调用者传入,不在方法内部获取用户上下文
|
||||
* @param gridType 网格类型
|
||||
* @param userName 客户经理柜员号
|
||||
* @param headId 总行ID
|
||||
* @return 客户列表
|
||||
*/
|
||||
public List<GridCmpmVO> selectManageListForImport(String gridType, String userName, String headId) {
|
||||
return gridCmpmMapper.getCustomerListForImport(gridType, userName, headId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -80,6 +80,16 @@ public class DrawGridController extends BaseController {
|
||||
return getDataTable(gridList, page);
|
||||
}
|
||||
|
||||
@ApiOperation("获取网格列表(用于客群创建,简化查询)")
|
||||
@Log(title = "自定义绘制网格--获取网格列表(客群创建用)")
|
||||
@GetMapping("/simpleList")
|
||||
public R<List<DrawGridListVO>> getSimpleGridList(DrawGridListDTO drawGridListDTO) {
|
||||
drawGridListDTO.setUserName(SecurityUtils.getUsername());
|
||||
drawGridListDTO.setDeptId(SecurityUtils.getDeptId());
|
||||
List<DrawGridListVO> gridList = drawGridService.getSimpleGridList(drawGridListDTO);
|
||||
return R.ok(gridList);
|
||||
}
|
||||
|
||||
@ApiOperation("分页获取网格内客户列表")
|
||||
@Log(title = "自定义绘制网格--分页获取网格内客户列表")
|
||||
@GetMapping("/cust/list")
|
||||
|
||||
@@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.ibs.draw.domain.dto.grid.DrawGridCustListDTO;
|
||||
import com.ruoyi.ibs.draw.domain.entity.DrawGridCustUserUnbind;
|
||||
import com.ruoyi.ibs.draw.domain.vo.DrawGridCustVO;
|
||||
import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,5 +20,13 @@ public interface DrawGridCustUserUnbindMapper extends BaseMapper<DrawGridCustUse
|
||||
|
||||
List<DrawGridCustVO> getCustListByManager(DrawGridCustListDTO drawGridCustListDTO);
|
||||
|
||||
/**
|
||||
* 根据绘制网格ID查询所有客户(用于客群导入,拼接headId绕过拦截器)
|
||||
* @param gridId 绘制网格ID
|
||||
* @param headId 总行机构号前三位(用于拼接动态表名)
|
||||
* @return 客户列表
|
||||
*/
|
||||
List<RegionCustUser> selectCustByDrawGridId(@Param("gridId") Long gridId, @Param("headId") String headId);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ package com.ruoyi.ibs.draw.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.ibs.draw.domain.entity.DrawGridShapeRelate;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author 吴凯程
|
||||
@@ -10,4 +14,19 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
**/
|
||||
@Mapper
|
||||
public interface DrawGridShapeRelateMapper extends BaseMapper<DrawGridShapeRelate> {
|
||||
|
||||
/**
|
||||
* 根据网格ID查询关联的图形(用于异步导入客群,原生SQL绕过拦截器)
|
||||
* @param gridId 网格ID
|
||||
* @return 图形关联列表
|
||||
*/
|
||||
List<DrawGridShapeRelate> selectByGridId(@Param("gridId") Long gridId);
|
||||
|
||||
/**
|
||||
* 根据网格ID查询所有客户(用于客群导入,直接拼接headId绕过拦截器)
|
||||
* @param gridId 网格ID
|
||||
* @param headId 部门代码(用于拼接动态表名)
|
||||
* @return 客户列表,包含 custId, custName, custType
|
||||
*/
|
||||
List<Map<String, Object>> selectCustListByGridId(@Param("gridId") Long gridId, @Param("headId") String headId);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@ public interface DrawGridService {
|
||||
|
||||
List<DrawGridListVO> getGridList(DrawGridListDTO drawGridListDTO);
|
||||
|
||||
/**
|
||||
* 获取网格列表(用于客群创建,简化查询不统计客户数量)
|
||||
* @param drawGridListDTO 查询条件
|
||||
* @return 网格列表(不含客户数量)
|
||||
*/
|
||||
List<DrawGridListVO> getSimpleGridList(DrawGridListDTO drawGridListDTO);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -334,6 +334,15 @@ public class DrawGridServiceImpl implements DrawGridService {
|
||||
return gridList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DrawGridListVO> getSimpleGridList(DrawGridListDTO drawGridListDTO) {
|
||||
if (SecurityUtils.userRole().equals("manager")) {
|
||||
return drawGridMapper.getGridListByManager(drawGridListDTO);
|
||||
} else {
|
||||
return drawGridMapper.getGridList(drawGridListDTO);
|
||||
}
|
||||
}
|
||||
|
||||
private CustCountDTO getCustCountByGrid(Long gridId) {
|
||||
CustCountDTO custCountDTO = new CustCountDTO();
|
||||
LambdaQueryWrapper<DrawGridShapeRelate> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.ruoyi.ibs.grid.domain.dto.GridCustListDTO;
|
||||
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
|
||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||
import com.ruoyi.ibs.grid.mapper.RegionGridMapper;
|
||||
import com.ruoyi.ibs.grid.service.RegionGridListService;
|
||||
@@ -108,6 +109,14 @@ public class RegionGridListController extends BaseController {
|
||||
return AjaxResult.success("开始更新行社所有网格");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询地理网格列表(专用于客群创建)
|
||||
*/
|
||||
@ApiOperation("查询地理网格列表(专用于客群创建)")
|
||||
@Log(title = "地理网格-查询地理网格列表(客群创建用)")
|
||||
@GetMapping("/groupList")
|
||||
public AjaxResult getRegionGridListForGroup(RegionGridListDTO dto) {
|
||||
return AjaxResult.success(regionGridListService.getRegionGridListForGroup(dto));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.ibs.grid.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 地理网格选择VO - 专用于客群创建时的网格选择
|
||||
* 只包含必要字段,提升查询效率
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
public class RegionGridGroupVO implements Serializable {
|
||||
|
||||
/**
|
||||
* 网格主键
|
||||
*/
|
||||
@ApiModelProperty(value = "网格主键")
|
||||
private Long gridId;
|
||||
|
||||
/**
|
||||
* 网格名称
|
||||
*/
|
||||
@ApiModelProperty(value = "网格名称")
|
||||
private String gridName;
|
||||
}
|
||||
@@ -40,6 +40,13 @@ public interface RegionCustUserMapper extends BaseMapper<RegionCustUser> {
|
||||
|
||||
Long countCustInSecGrid(@Param("gridId") Long gridId, @Param("custType") String custType);
|
||||
|
||||
/**
|
||||
* 根据网格ID列表查询所有客户(用于异步导入客群,直接根据gridIds查询无需区分等级)
|
||||
* @param gridIds 网格ID列表
|
||||
* @param headId 总行机构号前三位(用于拼接动态表名)
|
||||
*/
|
||||
List<RegionCustUser> selectAllCustByGridIds(@Param("gridIds") List<Long> gridIds, @Param("headId") String headId);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.ruoyi.ibs.grid.domain.dto.GridCustListDTO;
|
||||
import com.ruoyi.ibs.grid.domain.dto.RegionGridListDTO;
|
||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
@@ -69,4 +70,10 @@ public interface RegionGridMapper extends BaseMapper<RegionGrid> {
|
||||
|
||||
List<Long> getSecGridIdByTopGridId(@Param("gridId") Long gridId);
|
||||
|
||||
/**
|
||||
* 查询地理网格列表(简化版)- 专用于客群创建
|
||||
* 只返回gridId和gridName,避免N+1查询问题
|
||||
*/
|
||||
List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.ruoyi.ibs.grid.domain.entity.RegionCustUser;
|
||||
import com.ruoyi.ibs.grid.domain.entity.RegionGrid;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
||||
|
||||
@@ -36,11 +37,20 @@ public interface RegionGridListService {
|
||||
List<RegionGridListVO> getSecGridListByManager(RegionGridListDTO regionGridListDTO);
|
||||
|
||||
/**
|
||||
* 查询网格内所有客户(不限制客户类型,用于导入客群)
|
||||
* @param regionGrid 地理网格对象
|
||||
* @return 网格内所有客户列表
|
||||
* 根据网格ID列表批量查询所有客户(用于导入客群,优化版)
|
||||
* @param gridIds 网格ID列表
|
||||
* @param headId 总行机构号前三位(用于拼接动态表名)
|
||||
* @return 客户列表(去重)
|
||||
*/
|
||||
List<RegionCustUser> selectAllCustFromGrid(RegionGrid regionGrid);
|
||||
List<RegionCustUser> selectAllCustByGridIds(List<Long> gridIds, String headId);
|
||||
|
||||
/**
|
||||
* 查询地理网格列表(简化版)- 专用于客群创建
|
||||
* 只返回gridId和gridName,避免N+1查询问题
|
||||
* @param regionGridListDTO 查询条件
|
||||
* @return 网格列表(仅包含ID和名称)
|
||||
*/
|
||||
List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.ruoyi.ibs.grid.domain.dto.UnbindDTO;
|
||||
import com.ruoyi.ibs.grid.domain.entity.*;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionCustUserVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridCustVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridGroupVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionGridListVO;
|
||||
import com.ruoyi.ibs.grid.domain.vo.RegionUnbindVo;
|
||||
import com.ruoyi.ibs.grid.mapper.*;
|
||||
@@ -432,23 +433,33 @@ public class RegionGridListServiceImpl implements RegionGridListService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询网格内所有客户(不限制客户类型,用于导入客群)
|
||||
* @param regionGrid 地理网格对象
|
||||
* @return 网格内所有客户列表
|
||||
* 根据网格ID列表批量查询所有客户(用于导入客群,优化版)
|
||||
* @param gridIds 网格ID列表
|
||||
* @return 客户列表(去重)
|
||||
*/
|
||||
@Override
|
||||
public List<RegionCustUser> selectAllCustFromGrid(RegionGrid regionGrid) {
|
||||
LambdaQueryWrapper<RegionCustUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
// 根据网格等级判断使用一级还是二级网格ID查询
|
||||
if (regionGrid.getGridLevel().equals("1")) {
|
||||
queryWrapper.eq(RegionCustUser::getTopGridId, regionGrid.getGridId());
|
||||
} else if (regionGrid.getGridLevel().equals("2")) {
|
||||
queryWrapper.eq(RegionCustUser::getSecGridId, regionGrid.getGridId());
|
||||
} else {
|
||||
throw new ServiceException("无效的网格等级: " + regionGrid.getGridLevel());
|
||||
public List<RegionCustUser> selectAllCustByGridIds(List<Long> gridIds, String headId) {
|
||||
return regionCustUserMapper.selectAllCustByGridIds(gridIds, headId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询地理网格列表(简化版)- 专用于客群创建
|
||||
* 只返回gridId和gridName,避免N+1查询问题
|
||||
* @param regionGridListDTO 查询条件
|
||||
* @return 网格列表(仅包含ID和名称)
|
||||
*/
|
||||
@Override
|
||||
public List<RegionGridGroupVO> getRegionGridListForGroup(RegionGridListDTO regionGridListDTO) {
|
||||
// 设置当前用户部门ID用于权限过滤
|
||||
regionGridListDTO.setDeptId(SecurityUtils.getDeptId());
|
||||
|
||||
// 如果是总行用户,设置归属部室
|
||||
if (SecurityUtils.isHead()) {
|
||||
regionGridListDTO.setOpsDept(SecurityUtils.getOpsDept());
|
||||
}
|
||||
// 不限制客户类型,查询所有类型的客户
|
||||
return regionCustUserMapper.selectList(queryWrapper);
|
||||
|
||||
// 直接查询,一次SQL完成,无N+1问题
|
||||
return regionGridMapper.getRegionGridListForGroup(regionGridListDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,6 +41,14 @@ public class CustInfoBusinessVo {
|
||||
@ApiModelProperty(value = "手动打标")
|
||||
private List<TreeNode> tagManual;
|
||||
|
||||
/** 九维画像分数 */
|
||||
@ApiModelProperty(value = "九维画像分数")
|
||||
private Ent9vPortraitOrc ent9vPortrait;
|
||||
|
||||
/** 九维画像详细信息 */
|
||||
@ApiModelProperty(value = "九维画像详细信息")
|
||||
private NineVFinalInfoOrc nineVFinalInfo;
|
||||
|
||||
public List<TreeNode> getTagManual() {
|
||||
return tagManual;
|
||||
}
|
||||
@@ -187,4 +195,20 @@ public class CustInfoBusinessVo {
|
||||
public void setTabEnmuVos(List<TreeNode> tabEnmuVos) {
|
||||
this.tabEnmuVos = tabEnmuVos;
|
||||
}
|
||||
|
||||
public Ent9vPortraitOrc getEnt9vPortrait() {
|
||||
return ent9vPortrait;
|
||||
}
|
||||
|
||||
public void setEnt9vPortrait(Ent9vPortraitOrc ent9vPortrait) {
|
||||
this.ent9vPortrait = ent9vPortrait;
|
||||
}
|
||||
|
||||
public NineVFinalInfoOrc getNineVFinalInfo() {
|
||||
return nineVFinalInfo;
|
||||
}
|
||||
|
||||
public void setNineVFinalInfo(NineVFinalInfoOrc nineVFinalInfo) {
|
||||
this.nineVFinalInfo = nineVFinalInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.ruoyi.ibs.list.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 企业九维画像分数对象 ent_9v_portrait_orc_825
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-03-18
|
||||
*/
|
||||
@Data
|
||||
@TableName("ent_9v_portrait_orc_825")
|
||||
public class Ent9vPortraitOrc implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 统一社会信用代码 */
|
||||
private String uniscid;
|
||||
|
||||
/** 客户内码 */
|
||||
private String cstId;
|
||||
|
||||
/** 机构号 */
|
||||
private String orgNo;
|
||||
|
||||
/** score_1合规经营 */
|
||||
private BigDecimal score1;
|
||||
|
||||
/** score_2风险准入 */
|
||||
private BigDecimal score2;
|
||||
|
||||
/** score_3高管信用评价 */
|
||||
private String score3;
|
||||
|
||||
/** score_4股东信用评价 */
|
||||
private BigDecimal score4;
|
||||
|
||||
/** score_5社会贡献度 */
|
||||
private BigDecimal score5;
|
||||
|
||||
/** score_6稳定经营 */
|
||||
private BigDecimal score6;
|
||||
|
||||
/** score_7经营能力 */
|
||||
private BigDecimal score7;
|
||||
|
||||
/** score_8偿债能力 */
|
||||
private BigDecimal score8;
|
||||
|
||||
/** score_9潜在代偿资源 */
|
||||
private BigDecimal score9;
|
||||
|
||||
/** 九维总分 */
|
||||
private BigDecimal scoreAll;
|
||||
|
||||
/** 九维总分排名 */
|
||||
private BigDecimal scoreAllRank;
|
||||
|
||||
/** 会计日期 */
|
||||
private String datDt;
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package com.ruoyi.ibs.list.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业九维画像详细信息对象 9v_final_info_orc_825
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-03-18
|
||||
*/
|
||||
@Data
|
||||
@TableName("9v_final_info_orc_825")
|
||||
public class NineVFinalInfoOrc implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 统一社会信用代码 */
|
||||
private String uniscid;
|
||||
|
||||
/** 机构号 */
|
||||
private String orgNo;
|
||||
|
||||
/** 是否存在经营异常名录信息 */
|
||||
private String score11;
|
||||
|
||||
/** 是否存在严重违法失信企业名单信息 */
|
||||
private String score12;
|
||||
|
||||
/** 企业环境行为信仰登记是否为"E"或D */
|
||||
private String score13;
|
||||
|
||||
/** 是否存在税务重大税收违法黑名单信息 */
|
||||
private String score14;
|
||||
|
||||
/** 是否存在拖欠工资黑名单 */
|
||||
private String score15;
|
||||
|
||||
/** 是否存在工商吊销企业信息 */
|
||||
private String score16;
|
||||
|
||||
/** 是否存在注销企业信息 */
|
||||
private String score21;
|
||||
|
||||
/** 是否存在执行案件信息 */
|
||||
private String score22;
|
||||
|
||||
/** 是否存在查封信息 */
|
||||
private String score23;
|
||||
|
||||
/** 是否存在单位未履行生效裁判信息 */
|
||||
private String score24;
|
||||
|
||||
/** 是否存在企业破产清算信息 */
|
||||
private String score25;
|
||||
|
||||
/** 是否失信被执行人 */
|
||||
private String score26;
|
||||
|
||||
/** 是否为诉讼案件被告 */
|
||||
private String score27;
|
||||
|
||||
/** 是否存在查封信息(score_3) */
|
||||
private String score31;
|
||||
|
||||
/** 是否存在执行案件信息(score_3) */
|
||||
private String score32;
|
||||
|
||||
/** 是否存在个人未履行生效裁判信息 */
|
||||
private String score33;
|
||||
|
||||
/** 是否存在拖欠工资黑名单(score_3) */
|
||||
private String score34;
|
||||
|
||||
/** 是否失信被执行人(score_3) */
|
||||
private String score35;
|
||||
|
||||
/** 是否存在刑事案件被告人生效判决信息 */
|
||||
private String score36;
|
||||
|
||||
/** 是否为诉讼案件被告(score_3) */
|
||||
private String score37;
|
||||
|
||||
/** 是否存在查封信息(score_4) */
|
||||
private String score41;
|
||||
|
||||
/** 是否存在执行案件信息(score_4) */
|
||||
private String score42;
|
||||
|
||||
/** 是否存在个人未履行生效裁判信息(score_4) */
|
||||
private String score43;
|
||||
|
||||
/** 是否存在拖欠工资黑名单(score_4) */
|
||||
private String score44;
|
||||
|
||||
/** 是否失信被执行人(score_4) */
|
||||
private String score45;
|
||||
|
||||
/** 是否存在刑事案件被告人生效判决信息(score_4) */
|
||||
private String score46;
|
||||
|
||||
/** 是否为诉讼案件被告(score_4) */
|
||||
private String score47;
|
||||
|
||||
/** 是否存在企业未履行生效裁判信息 */
|
||||
private String score48;
|
||||
|
||||
/** 前12个月纳税金额 */
|
||||
private String score51;
|
||||
|
||||
/** 纳税等级 */
|
||||
private String score52;
|
||||
|
||||
/** 缴纳社保人数 */
|
||||
private String score53;
|
||||
|
||||
/** 公积金缴纳人数 */
|
||||
private String score54;
|
||||
|
||||
/** 是否为出口退税生产清单企业 */
|
||||
private String score55;
|
||||
|
||||
/** 市场主体经营年限 */
|
||||
private String score61;
|
||||
|
||||
/** 股东(或发起人)或投资人信息认缴出资人数 */
|
||||
private String score62;
|
||||
|
||||
/** 最大股东持股占比 */
|
||||
private String score63;
|
||||
|
||||
/** 近三年法定代表人变更次数 */
|
||||
private String score64;
|
||||
|
||||
/** 近三年股东变更次数 */
|
||||
private String score65;
|
||||
|
||||
/** 近三年经营范围变更次数 */
|
||||
private String score66;
|
||||
|
||||
/** 近三年经营地址变更次数 */
|
||||
private String score67;
|
||||
|
||||
/** 近三年缴税年数 */
|
||||
private String score68;
|
||||
|
||||
/** 法人户籍 */
|
||||
private String score69;
|
||||
|
||||
/** 法人婚姻状况 */
|
||||
private String score610;
|
||||
|
||||
/** 上年增值税金额 */
|
||||
private String score71;
|
||||
|
||||
/** 今年增值税同比变动 */
|
||||
private String score72;
|
||||
|
||||
/** 上年企业所得税金额 */
|
||||
private String score73;
|
||||
|
||||
/** 今年所得税同比变动 */
|
||||
private String score74;
|
||||
|
||||
/** 缴纳社保人数同比变动 */
|
||||
private String score75;
|
||||
|
||||
/** 公积金缴纳人数同比变动 */
|
||||
private String score76;
|
||||
|
||||
/** 当年纳税金额同比变动 */
|
||||
private String score77;
|
||||
|
||||
/** 上年出口退税金额 */
|
||||
private String score78;
|
||||
|
||||
/** 房产套数 */
|
||||
private String score81;
|
||||
|
||||
/** 房产面积 */
|
||||
private String score82;
|
||||
|
||||
/** 未抵押房产套数 */
|
||||
private String score83;
|
||||
|
||||
/** 未抵押房产面积 */
|
||||
private String score84;
|
||||
|
||||
/** 已抵押房产被担保债权数额 */
|
||||
private String score85;
|
||||
|
||||
/** 企业车产金额 */
|
||||
private String score86;
|
||||
|
||||
/** 法人及股东房产面积合计 */
|
||||
private String score91;
|
||||
|
||||
/** 法人及股东房产套数合计 */
|
||||
private String score92;
|
||||
|
||||
/** 法人及股东未抵押房产套数合计 */
|
||||
private String score93;
|
||||
|
||||
/** 法人及股东未抵押房产面积 */
|
||||
private String score94;
|
||||
|
||||
/** 法人及股东已抵押房产被担保债权数额合计 */
|
||||
private String score95;
|
||||
|
||||
/** 法人代表车产金额 */
|
||||
private String score96;
|
||||
|
||||
/** 生态信用扣分 */
|
||||
private String scoreB015;
|
||||
|
||||
/** 列入环境失信黑名单时间 */
|
||||
private String scoreB016Time;
|
||||
|
||||
/** 列入环境失信黑名单原因 */
|
||||
private String scoreB016Reason;
|
||||
|
||||
/** 环境行为信用评价等级 */
|
||||
private String scoreA020;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.ibs.list.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.ibs.list.domain.Ent9vPortraitOrc;
|
||||
|
||||
/**
|
||||
* 企业九维画像分数Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-03-18
|
||||
*/
|
||||
public interface Ent9vPortraitOrcMapper extends BaseMapper<Ent9vPortraitOrc> {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.ibs.list.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.ibs.list.domain.NineVFinalInfoOrc;
|
||||
|
||||
/**
|
||||
* 企业九维画像详细信息Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-03-18
|
||||
*/
|
||||
public interface NineVFinalInfoOrcMapper extends BaseMapper<NineVFinalInfoOrc> {
|
||||
}
|
||||
@@ -24,7 +24,9 @@ import com.ruoyi.ibs.grid.util.CustExcelUtil;
|
||||
import com.ruoyi.ibs.list.domain.*;
|
||||
import com.ruoyi.ibs.list.mapper.CorporateShareholderMapper;
|
||||
import com.ruoyi.ibs.list.mapper.CustInfoRetailMapper;
|
||||
import com.ruoyi.ibs.list.mapper.Ent9vPortraitOrcMapper;
|
||||
import com.ruoyi.ibs.list.mapper.FamilyMembersMapper;
|
||||
import com.ruoyi.ibs.list.mapper.NineVFinalInfoOrcMapper;
|
||||
import com.ruoyi.ibs.list.mapper.SignedProductsMapper;
|
||||
import com.ruoyi.ibs.list.service.ICustInfoBusinessService;
|
||||
import com.ruoyi.system.mapper.SysIndustryMapper;
|
||||
@@ -75,6 +77,12 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService
|
||||
@Autowired
|
||||
private FamilyMembersMapper familyMembersMapper;
|
||||
|
||||
@Autowired
|
||||
private Ent9vPortraitOrcMapper ent9vPortraitOrcMapper;
|
||||
|
||||
@Autowired
|
||||
private NineVFinalInfoOrcMapper nineVFinalInfoOrcMapper;
|
||||
|
||||
@Autowired
|
||||
private CustMapMapper custMapMapper;
|
||||
|
||||
@@ -138,6 +146,17 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService
|
||||
//查询手动标签列表
|
||||
List<CustManualTagVO> tagCreateVos = familyMembersMapper.selectManualTag(params);
|
||||
custInfoBusinessVo.setTagManual(TreeNode.convertToTreeByParentId(tagCreateVos));
|
||||
|
||||
// 仅当登录机构为825时,查询九维画像数据
|
||||
// if ("825".equals(getHeadId())) {
|
||||
// LambdaQueryWrapper<Ent9vPortraitOrc> portraitWrapper = new LambdaQueryWrapper<>();
|
||||
// portraitWrapper.eq(Ent9vPortraitOrc::getUniscid, custInfoBusiness.getSocialCreditCode());
|
||||
// custInfoBusinessVo.setEnt9vPortrait(ent9vPortraitOrcMapper.selectOne(portraitWrapper));
|
||||
//
|
||||
// LambdaQueryWrapper<NineVFinalInfoOrc> finalInfoWrapper = new LambdaQueryWrapper<>();
|
||||
// finalInfoWrapper.eq(NineVFinalInfoOrc::getUniscid, custInfoBusiness.getSocialCreditCode());
|
||||
// custInfoBusinessVo.setNineVFinalInfo(nineVFinalInfoOrcMapper.selectOne(finalInfoWrapper));
|
||||
// }
|
||||
}
|
||||
return custInfoBusinessVo;
|
||||
}
|
||||
|
||||
@@ -131,4 +131,15 @@ public class WorkRecordController extends BaseController {
|
||||
return toAjax(workRecordService.updateReadTime(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有预警类型
|
||||
*/
|
||||
@GetMapping("/alter/types")
|
||||
@Log(title = "工作台-查询预警类型")
|
||||
@ApiOperation("查询所有预警类型")
|
||||
public AjaxResult getAlterTypes() {
|
||||
List<String> types = workRecordService.getAlterTypes();
|
||||
return AjaxResult.success(types);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -112,4 +112,12 @@ public interface WorkRecordMapper extends BaseMapper<WorkRecord> {
|
||||
List<AlterConfigVO> selectAlterConfigList(String alterType);
|
||||
|
||||
int updateAlterConfig(AlterConfig alterConfig);
|
||||
|
||||
/**
|
||||
* 查询所有预警类型
|
||||
*
|
||||
* @param headId 总部ID(部门ID前三位)
|
||||
* @return 预警类型列表
|
||||
*/
|
||||
List<String> selectAlterTypes(@Param("headId") String headId);
|
||||
}
|
||||
|
||||
@@ -85,4 +85,11 @@ public interface WorkRecordService{
|
||||
int updateReadTime(Long id);
|
||||
|
||||
AlterCountVO getAlterCount(Date reportTime);
|
||||
|
||||
/**
|
||||
* 查询所有预警类型
|
||||
*
|
||||
* @return 预警类型列表
|
||||
*/
|
||||
List<String> getAlterTypes();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.stream.IntStream;
|
||||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.ruoyi.common.core.page.TableDataPageInfo;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.utils.PageUtils;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
|
||||
@@ -39,6 +40,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Service
|
||||
public class WorkRecordServiceImpl implements WorkRecordService {
|
||||
|
||||
private final static String alterTypesRedisKey = "work:record:alter:types";
|
||||
|
||||
@Autowired
|
||||
private WorkRecordMapper workRecordMapper;
|
||||
|
||||
@@ -48,6 +51,9 @@ public class WorkRecordServiceImpl implements WorkRecordService {
|
||||
@Autowired
|
||||
private ISysDeptService sysDeptService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
/**
|
||||
* 查询我的工作清单
|
||||
*
|
||||
@@ -253,6 +259,19 @@ public class WorkRecordServiceImpl implements WorkRecordService {
|
||||
return alterCountVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAlterTypes() {
|
||||
String headId = SecurityUtils.getHeadId();
|
||||
String cacheKey = alterTypesRedisKey + ":" + headId;
|
||||
List<String> cachedTypes = redisCache.getCacheObject(cacheKey);
|
||||
if (cachedTypes != null) {
|
||||
return cachedTypes;
|
||||
}
|
||||
List<String> types = workRecordMapper.selectAlterTypes(headId);
|
||||
redisCache.setCacheObjectToEndDay(cacheKey, types);
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户权限范围内所有post_ids
|
||||
* @return
|
||||
|
||||
@@ -193,7 +193,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
and status = #{status}
|
||||
</if>
|
||||
<if test="alterType != null and alterType != ''">
|
||||
and alter_type LIKE CONCAT('%', #{alterType}, '%')
|
||||
and alter_type = #{alterType}
|
||||
</if>
|
||||
</where>
|
||||
order by create_time desc, status asc
|
||||
@@ -232,7 +232,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
and wr.status = #{status}
|
||||
</if>
|
||||
<if test="alterType != null and alterType != ''">
|
||||
and wr.alter_type LIKE CONCAT('%', #{alterType}, '%')
|
||||
and wr.alter_type = #{alterType}
|
||||
</if>
|
||||
<!-- "走访异常提醒"类型直接通过用户名匹配,其他类型按角色权限处理 -->
|
||||
and ((wr.alter_type = '走访异常提醒' and wr.user_name = #{username}) or
|
||||
@@ -268,4 +268,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<select id="selectAlterTypes" resultType="string">
|
||||
select distinct alter_type
|
||||
from work_record
|
||||
where is_alter = 1
|
||||
and alter_type is not null
|
||||
and alter_type != ''
|
||||
and left(user_name, 3) = #{headId}
|
||||
order by alter_type
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -505,4 +505,26 @@
|
||||
where manager_id is not null
|
||||
</select>
|
||||
|
||||
<!-- 根据网格类型和总行ID查询客户经理列表 -->
|
||||
<select id="getManagerListByGridType" resultType="java.util.HashMap">
|
||||
select distinct user_name as userName, nick_name as nickName
|
||||
from grid_cmpm_${gridType}_${headId}
|
||||
where user_name is not null
|
||||
order by user_name
|
||||
</select>
|
||||
|
||||
<!-- 根据网格类型、总行ID和客户经理查询客户列表(分表查询,适用于客群导入)-->
|
||||
<select id="getCustomerListForImport" resultType="GridCmpmVO">
|
||||
select cust_id, cust_name, cust_type, cust_idc, usci
|
||||
from grid_cmpm_${gridType}_${headId}
|
||||
where user_name = #{userName}
|
||||
</select>
|
||||
|
||||
<!-- 根据网格类型、总行ID和客户经理流式查询客户列表(使用Cursor,适用于大数据量场景)-->
|
||||
<select id="getCustomerListForImportCursor" resultType="GridCmpmVO" fetchSize="1000">
|
||||
select cust_id, cust_name, cust_type, cust_idc, usci
|
||||
from grid_cmpm_${gridType}_${headId}
|
||||
where user_name = #{userName}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -55,8 +55,17 @@
|
||||
<if test="custName != null and custName != ''">AND b.cust_name like concat('%', #{custName}, '%')</if>
|
||||
<if test="custType != null and custType != ''">AND b.cust_type = #{custType}</if>
|
||||
</select>
|
||||
<select id="">
|
||||
|
||||
<!-- 根据绘制网格ID查询所有客户(用于客群导入,直接拼接headId绕过拦截器) -->
|
||||
<select id="selectCustByDrawGridId" resultType="com.ruoyi.ibs.grid.domain.entity.RegionCustUser">
|
||||
SELECT DISTINCT
|
||||
sc.cust_id,
|
||||
sc.cust_name,
|
||||
sc.cust_type
|
||||
FROM grid_draw_shape_relate sr
|
||||
INNER JOIN draw_shape_cust_${headId} sc ON sc.shape_id = sr.shape_id
|
||||
WHERE sr.grid_id = #{gridId}
|
||||
AND sr.delete_flag = '0'
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -121,4 +121,30 @@
|
||||
WHERE sec_grid_id = #{gridId}
|
||||
AND cust_type = #{custType}
|
||||
</select>
|
||||
|
||||
<!-- 根据网格ID列表查询所有客户(一级或二级网格) -->
|
||||
<select id="selectAllCustByGridIds" resultType="RegionCustUser">
|
||||
SELECT DISTINCT cust_id, cust_name, cust_type
|
||||
FROM grid_region_cust_user_${headId}
|
||||
WHERE (top_grid_id IN
|
||||
<foreach collection="gridIds" item="gridId" index="index" open="(" separator="," close=")">
|
||||
#{gridId}
|
||||
</foreach>
|
||||
OR sec_grid_id IN
|
||||
<foreach collection="gridIds" item="gridId" index="index" open="(" separator="," close=")">
|
||||
#{gridId}
|
||||
</foreach>)
|
||||
</select>
|
||||
|
||||
<!-- 根据绘制网格ID查询所有客户(用于客群导入,直接拼接headId绕过拦截器) -->
|
||||
<select id="selectCustByDrawGridId" resultType="RegionCustUser">
|
||||
SELECT DISTINCT
|
||||
sc.cust_id,
|
||||
sc.cust_name,
|
||||
sc.cust_type
|
||||
FROM grid_draw_shape_relate sr
|
||||
LEFT JOIN draw_shape_cust_${headId} sc ON sc.shape_id = sr.shape_id
|
||||
WHERE sr.grid_id = #{gridId}
|
||||
AND sr.delete_flag = '0'
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -339,4 +339,26 @@
|
||||
<select id="getSecGridIdByTopGridId" resultType="Long">
|
||||
select grid_id from grid_region_grid where parent_grid_id = #{gridId} and grid_level = '2' and delete_flag = '0'
|
||||
</select>
|
||||
|
||||
<!-- 查询地理网格列表(简化版)- 专用于客群创建 -->
|
||||
<select id="getRegionGridListForGroup" parameterType="RegionGridListDTO" resultType="RegionGridGroupVO">
|
||||
select distinct a.grid_id, a.grid_name
|
||||
from grid_region_grid a
|
||||
left join grid_region_user_relate c on c.grid_id = a.grid_id
|
||||
where a.delete_flag = '0'
|
||||
and c.delete_flag = '0'
|
||||
and a.grid_level = #{gridLevel}
|
||||
and (a.dept_id in (select dept_id from sys_dept where dept_id = #{deptId} or find_in_set(#{deptId}, ancestors))
|
||||
or c.relate_dept_id in (select dept_id from sys_dept where dept_id = #{deptId} or find_in_set(#{deptId}, ancestors)))
|
||||
<if test="opsDept != null and opsDept != ''">
|
||||
and a.ops_dept = #{opsDept}
|
||||
</if>
|
||||
<if test="gridDutyType != null and gridDutyType != ''">
|
||||
and a.grid_duty_type = #{gridDutyType}
|
||||
</if>
|
||||
<if test="gridName != null and gridName != ''">
|
||||
and a.grid_name like concat('%', #{gridName}, '%')
|
||||
</if>
|
||||
ORDER BY a.grid_name
|
||||
</select>
|
||||
</mapper>
|
||||
168
ruoyi-ui/CLAUDE.md
Normal file
168
ruoyi-ui/CLAUDE.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## 项目概述
|
||||
|
||||
**数字支行辅助管理系统** - 基于 RuoYi-Vue 框架的银行客户网格化管理平台。
|
||||
|
||||
这是一个全栈项目:
|
||||
- **前端**: `ruoyi-ui/` - Vue 2.6 + Element UI
|
||||
- **后端**: `../ibs/` - Java Spring Boot (若依框架)
|
||||
|
||||
## 常用命令
|
||||
|
||||
### 开发
|
||||
```bash
|
||||
# 安装依赖(建议使用国内镜像)
|
||||
npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
# 启动开发服务器 (默认端口80)
|
||||
npm run dev
|
||||
|
||||
# Node.js 版本兼容性问题的启动方式
|
||||
npm run dev_t
|
||||
```
|
||||
|
||||
### 构建
|
||||
```bash
|
||||
# 构建生产环境
|
||||
npm run build:prod
|
||||
|
||||
# 构建测试环境
|
||||
npm run build:stage
|
||||
|
||||
# 构建预发布环境
|
||||
npm run build:pre
|
||||
```
|
||||
|
||||
### 代码质量
|
||||
```bash
|
||||
# ESLint 检查
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Mock 服务
|
||||
```bash
|
||||
# 启动 Mock 服务器
|
||||
npm run mock
|
||||
```
|
||||
|
||||
## 项目架构
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
ruoyi-ui/
|
||||
├── src/
|
||||
│ ├── api/ # API 接口层,按业务模块分组
|
||||
│ ├── assets/ # 静态资源(图片、样式、图标SVG)
|
||||
│ ├── components/ # 全局通用组件
|
||||
│ ├── directive/ # Vue 自定义指令
|
||||
│ ├── layout/ # 布局组件(侧边栏、头部等)
|
||||
│ ├── map/ # 地图相关工具/配置
|
||||
│ ├── plugins/ # 插件配置
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── store/ # Vuex 状态管理
|
||||
│ ├── utils/ # 工具函数
|
||||
│ ├── views/ # 页面组件
|
||||
│ ├── App.vue # 根组件
|
||||
│ ├── main.js # 入口文件
|
||||
│ └── permission.js # 路由权限控制
|
||||
├── public/
|
||||
│ ├── baidu/ # 百度地图静态资源
|
||||
│ └── index.html
|
||||
├── mock/ # Mock 数据
|
||||
├── .env.development # 开发环境配置
|
||||
├── .env.production # 生产环境配置
|
||||
└── .env.staging # 测试环境配置
|
||||
```
|
||||
|
||||
### 核心业务模块
|
||||
|
||||
| 目录 | 功能 |
|
||||
|------|------|
|
||||
| `views/grid/` | 网格管理(创建、列表、地图、走访、绩效) |
|
||||
| `views/customer/` | 客户管理(360视图、建档、客群、标签) |
|
||||
| `views/system/` | 系统管理(用户、角色、菜单、字典) |
|
||||
| `views/monitor/` | 系统监控(日志、在线用户、服务监控) |
|
||||
| `views/configure/` | 配置管理(级别配置、参数设置、模板) |
|
||||
| `views/approveCenter/` | 审批中心 |
|
||||
| `views/gridSearch/` | 网格搜索(管户、绩效、公海池) |
|
||||
|
||||
### API 层组织
|
||||
|
||||
API 请求按业务模块组织在 `src/api/` 目录:
|
||||
- 每个模块导出具体的请求函数
|
||||
- 使用 `src/utils/request.js` 中的 axios 实例
|
||||
- 默认添加 Bearer Token 认证
|
||||
- 统一错误处理(401/500/601等状态码)
|
||||
|
||||
### 状态管理 (Vuex)
|
||||
|
||||
Store 模块位于 `src/store/modules/`:
|
||||
- `app` - 应用状态(侧边栏、设备类型)
|
||||
- `user` - 用户信息
|
||||
- `permission` - 权限路由
|
||||
- `settings` - 系统设置
|
||||
- `dict` - 字典数据
|
||||
- `tagsView` - 标签页视图
|
||||
- `featuredAreas` - 特色区域
|
||||
- `rate` - 汇率相关
|
||||
|
||||
使用 `vuex-persistedstate` 将 settings 持久化到 localStorage。
|
||||
|
||||
### 路由系统
|
||||
|
||||
- 路由分为 `constantRoutes`(静态路由)和 `dynamicRoutes`(动态路由)
|
||||
- 动态路由基于用户权限(permissions)动态加载
|
||||
- 使用 `src/permission.js` 进行路由守卫和权限控制
|
||||
- 支持嵌套路由和路由元信息配置
|
||||
|
||||
### 百度地图集成
|
||||
|
||||
项目深度集成百度地图 API:
|
||||
- 静态资源位于 `public/baidu/`
|
||||
- 地图相关 API 在 `src/api/grid/` 下
|
||||
- 支持 WebGL 渲染、绘制管理器、热力图等
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `VUE_APP_BASE_API` | 后端 API 基础路径 |
|
||||
| `VUE_APP_MOCK_API` | Mock API 路径 |
|
||||
| `VUE_APP_MOCK` | 是否启用 Mock |
|
||||
| `VUE_APP_STAGE_URL` | 测试环境地址 |
|
||||
| `VUE_APP_BAIDU_URL` | 百度地图服务地址 |
|
||||
| `VUE_APP_SHARP_URL` | SHARP 服务地址 |
|
||||
|
||||
## 代码规范
|
||||
|
||||
- ESLint 配置基于 `plugin:vue/recommended`
|
||||
- 使用单引号、2空格缩进
|
||||
- 组件名使用 PascalCase
|
||||
- pre-commit 钩子自动执行 lint-staged
|
||||
|
||||
## 全局组件
|
||||
|
||||
以下组件已全局注册,可直接使用:
|
||||
- `Pagination` - 分页组件
|
||||
- `RightToolbar` - 表格工具栏
|
||||
- `Editor` - 富文本编辑器
|
||||
- `FileUpload` - 文件上传
|
||||
- `ImageUpload` - 图片上传
|
||||
- `ImagePreview` - 图片预览
|
||||
- `DictTag` - 字典标签
|
||||
- `treeselect` - 树形选择器
|
||||
- `DownloadBtn` - 下载按钮
|
||||
|
||||
## 全局方法
|
||||
|
||||
通过 `Vue.prototype` 挂载的全局方法:
|
||||
- `getDicts()` - 获取字典数据
|
||||
- `getConfigKey()` - 获取配置项
|
||||
- `parseTime()` - 时间格式化
|
||||
- `resetForm()` - 重置表单
|
||||
- `download()` - 文件下载
|
||||
- `handleTree()` - 处理树形数据
|
||||
@@ -287,6 +287,15 @@ export function getFeaturegrid(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取网格列表(用于客群创建,简化查询)
|
||||
export function getSimpleGridList(data) {
|
||||
return request({
|
||||
url: `/draw/grid/simpleList`,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除网格
|
||||
export function featureGridDelete(data) {
|
||||
return request({
|
||||
|
||||
137
ruoyi-ui/src/api/group/custGroup.js
Normal file
137
ruoyi-ui/src/api/group/custGroup.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询客群列表
|
||||
export function listCustGroup(query) {
|
||||
return request({
|
||||
url: '/group/cust/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取客群详情
|
||||
export function getCustGroup(id) {
|
||||
return request({
|
||||
url: '/group/cust/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 异步创建客群(网格导入)
|
||||
export function createCustGroupByGrid(data) {
|
||||
return request({
|
||||
url: '/group/cust/createByGrid',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 异步创建客群(模板导入)
|
||||
export function createCustGroupByTemplate(data) {
|
||||
return request({
|
||||
url: '/group/cust/createByTemplate',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}
|
||||
|
||||
// 更新客群
|
||||
export function updateCustGroup(data) {
|
||||
return request({
|
||||
url: '/group/cust/update',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新客群(网格导入)
|
||||
export function updateCustGroupByGrid(data) {
|
||||
return request({
|
||||
url: '/group/cust/updateByGrid',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新客群(模板导入)
|
||||
export function updateCustGroupByTemplate(data) {
|
||||
return request({
|
||||
url: '/group/cust/updateByTemplate',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}
|
||||
|
||||
// 轮询客群创建状态
|
||||
export function getCreateStatus(id) {
|
||||
return request({
|
||||
url: '/group/cust/createStatus/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 客户信息模板下载
|
||||
export function downloadTemplate() {
|
||||
return request({
|
||||
url: '/group/cust/download',
|
||||
method: 'post',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除客群
|
||||
export function deleteCustGroup(idList) {
|
||||
return request({
|
||||
url: '/group/cust/delete',
|
||||
method: 'post',
|
||||
data: idList
|
||||
})
|
||||
}
|
||||
|
||||
// 手动移除客群客户
|
||||
export function removeMembers(groupId, memberIds) {
|
||||
return request({
|
||||
url: '/group/cust/removeMembers',
|
||||
method: 'post',
|
||||
params: { groupId: groupId },
|
||||
data: memberIds
|
||||
})
|
||||
}
|
||||
|
||||
// 检查客群名称是否存在
|
||||
export function checkGroupNameExist(groupName) {
|
||||
return request({
|
||||
url: '/group/cust/checkName',
|
||||
method: 'get',
|
||||
params: { groupName: groupName }
|
||||
})
|
||||
}
|
||||
|
||||
// 根据网格类型获取客户经理列表
|
||||
export function getManagerList(gridType) {
|
||||
return request({
|
||||
url: '/grid/cmpm/managerList',
|
||||
method: 'get',
|
||||
params: { gridType: gridType }
|
||||
})
|
||||
}
|
||||
|
||||
// 分页查询客群成员列表
|
||||
export function listCustGroupMembers(groupId, query) {
|
||||
return request({
|
||||
url: '/group/member/list/' + groupId,
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询地理网格列表(专用于客群创建)
|
||||
export function getRegionGridListForGroup(query) {
|
||||
return request({
|
||||
url: '/grid/region/groupList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -258,3 +258,11 @@ export function warningCardNum(query) {
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询所有预警类型
|
||||
export function getAlterTypes() {
|
||||
return request({
|
||||
url: '/work/record/alter/types',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -227,6 +227,17 @@ export const constantRoutes = [
|
||||
}]
|
||||
|
||||
},
|
||||
{
|
||||
path: '/group/custGroup/detail',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [{
|
||||
path: '/group/custGroup/detail',
|
||||
name: 'CustGroupDetail',
|
||||
meta: { title: '客群详情', activeMenu: '/group/custGroup' },
|
||||
component: () => import('@/views/group/custGroup/detail')
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/checklist/customerlist',
|
||||
component: Layout,
|
||||
|
||||
@@ -11,6 +11,7 @@ const user = {
|
||||
permissions: [],
|
||||
nickName: '',
|
||||
userName: '',
|
||||
deptId: '',
|
||||
groupErr:{},
|
||||
},
|
||||
|
||||
|
||||
@@ -2,9 +2,16 @@
|
||||
<div class="customer-wrap">
|
||||
<div>
|
||||
<el-radio-group class="header-radio" v-model="selectedTab" @change="handleTabChange">
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
<div class="customerMain">
|
||||
<div :class="iscollapsed ? 'customerMain_left_sq' : 'customerMain_left_zk'">
|
||||
@@ -317,6 +324,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['roles', 'userName']),
|
||||
deptId() {
|
||||
return this.$store.state.user.deptId
|
||||
},
|
||||
filtereDate() {
|
||||
if (this.searchQuery) {
|
||||
return this.tableData.filter((item) => item.companyName.includes(this.searchQuery) || item.legalName.includes(this.searchQuery))
|
||||
@@ -776,6 +786,12 @@ export default {
|
||||
if (query.backUrl) {
|
||||
this.selectedTab = query.selectedTab
|
||||
this.queryParams.custPattern = query.selectedTab
|
||||
} else {
|
||||
// 默认选中第一个tab
|
||||
const deptId = this.$store.state.user.deptId
|
||||
const deptIdStr = deptId ? String(deptId) : ''
|
||||
this.selectedTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||
this.queryParams.custPattern = this.selectedTab
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
const commonCol = function (isCom, type) {
|
||||
return [
|
||||
// 当headId=875时需要隐藏的字段列表
|
||||
const HIDDEN_PROPS_FOR_875 = [
|
||||
'regionTopGridName', // 总行行政网格名称
|
||||
'regionSecGridName', // 支行行政网格名称
|
||||
'belongBranchName', // 行政网格归属支行
|
||||
'belongOutletName', // 行政网格归属网点
|
||||
'belongUserNameList', // 行政网格客户经理
|
||||
'drawGridName', // 自绘地图网格名称
|
||||
'drawBranchNames', // 自绘地图网格归属支行
|
||||
'drawOutletNames', // 自绘地图网格归属网点
|
||||
'drawUserNames', // 自绘地图网格客户经理
|
||||
'virtualGridName', // 自建名单网格名称
|
||||
'virtualBranchNames', // 自建名单网格归属支行
|
||||
'virtualOutletNames', // 自建名单网格归属网点
|
||||
'virtualUserNames', // 自建名单网格客户经理
|
||||
]
|
||||
|
||||
const commonCol = function (isCom, type, headId) {
|
||||
const allColumns = [
|
||||
{
|
||||
prop: 'regionTopGridName',
|
||||
label: '总行行政网格名称',
|
||||
@@ -101,9 +118,16 @@ const commonCol = function (isCom, type) {
|
||||
showOverflowTooltip: true,
|
||||
},
|
||||
]
|
||||
|
||||
// 当headId=875时,过滤掉指定的列
|
||||
if (String(headId).startsWith('875')) {
|
||||
return allColumns.filter(col => !HIDDEN_PROPS_FOR_875.includes(col.prop))
|
||||
}
|
||||
|
||||
return allColumns
|
||||
}
|
||||
|
||||
export const placeholderMap = (type) => ({
|
||||
export const placeholderMap = (type, headId) => ({
|
||||
'2': {
|
||||
placeholder: '搜索企业名称/法人名字',
|
||||
custPattern: '2',
|
||||
@@ -138,7 +162,7 @@ export const placeholderMap = (type) => ({
|
||||
type: 'myCustLevel',
|
||||
// desc: '建档输入/取新华社数据',
|
||||
},
|
||||
...commonCol('2', type),
|
||||
...commonCol('2', type, headId),
|
||||
{
|
||||
prop: 'lpName',
|
||||
label: '法人姓名',
|
||||
@@ -393,7 +417,7 @@ export const placeholderMap = (type) => ({
|
||||
type: 'myCustIdsn',
|
||||
// desc: '建档输入/取新华社数据',
|
||||
},
|
||||
...commonCol('1', type),
|
||||
...commonCol('1', type, headId),
|
||||
{
|
||||
prop: 'lpName',
|
||||
label: '经营者姓名',
|
||||
@@ -634,7 +658,7 @@ export const placeholderMap = (type) => ({
|
||||
type: 'myCustLevel',
|
||||
// desc: '取大信贷数据',
|
||||
},
|
||||
...commonCol('0', type),
|
||||
...commonCol('0', type, headId),
|
||||
{
|
||||
prop: 'custPhone',
|
||||
label: '联系方式',
|
||||
|
||||
@@ -2,9 +2,16 @@
|
||||
<div class="customer-wrap">
|
||||
<div>
|
||||
<el-radio-group class="header-radio" v-model="selectedTab" @change="handleTabChange">
|
||||
<el-radio-button label="2" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button> // :disabled="isPrivate"
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
<div class="customerMain">
|
||||
<div :class="iscollapsed ? 'customerMain_left_sq' : 'customerMain_left_zk'">
|
||||
@@ -283,8 +290,9 @@ export default {
|
||||
const type = this.roles.includes('headPublic') ? 'isPublic' :
|
||||
this.roles.includes('headPrivate') ? 'isPrivate' :
|
||||
this.roles.includes('headOps') ? 'isOps' : ""
|
||||
this.tableColoumns = placeholderMap(type)[val].tableColoumns
|
||||
this.placeholder = placeholderMap(type)[val].placeholder
|
||||
const headId = this.deptId
|
||||
this.tableColoumns = placeholderMap(type, headId)[val].tableColoumns
|
||||
this.placeholder = placeholderMap(type, headId)[val].placeholder
|
||||
this.queryParams.custPattern = val
|
||||
this.searchColoumns = this.getSearchColoumns()
|
||||
}
|
||||
@@ -310,6 +318,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['roles']),
|
||||
deptId() {
|
||||
return this.$store.state.user.deptId
|
||||
},
|
||||
filtereDate() {
|
||||
if (this.searchQuery) {
|
||||
return this.tableData.filter((item) => item.companyName.includes(this.searchQuery) || item.legalName.includes(this.searchQuery))
|
||||
@@ -777,6 +788,12 @@ export default {
|
||||
if (query.backUrl) {
|
||||
this.selectedTab = query.selectedTab
|
||||
this.queryParams.custPattern = query.selectedTab
|
||||
} else {
|
||||
// 默认选中第一个tab
|
||||
const deptId = this.$store.state.user.deptId
|
||||
const deptIdStr = deptId ? String(deptId) : ''
|
||||
this.selectedTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||
this.queryParams.custPattern = this.selectedTab
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
680
ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue
Normal file
680
ruoyi-ui/src/views/group/custGroup/components/create-dialog.vue
Normal file
@@ -0,0 +1,680 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="isEdit ? '编辑客群' : '创建客群'"
|
||||
:visible.sync="visible"
|
||||
width="700px"
|
||||
:before-close="handleClose"
|
||||
append-to-body
|
||||
>
|
||||
<el-alert
|
||||
v-if="isEdit && isProcessing"
|
||||
title="客群正在处理中,请等待处理完成后再进行编辑"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
style="margin-bottom: 15px"
|
||||
/>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px" v-loading="submitting">
|
||||
<el-form-item label="客群名称" prop="groupName">
|
||||
<el-input v-model="form.groupName" placeholder="请输入客群名称" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客群模式" prop="groupMode">
|
||||
<el-radio-group v-model="form.groupMode" :disabled="isEdit && form.createStatus === '1'">
|
||||
<el-radio label="0">静态客群</el-radio>
|
||||
<el-radio label="1">动态客群</el-radio>
|
||||
</el-radio-group>
|
||||
<div class="form-tip">
|
||||
静态客群:创建后客户列表固定,除非手动更新
|
||||
<br />
|
||||
动态客群:系统定期根据原始条件自动刷新客户列表
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期" prop="validTime">
|
||||
<el-date-picker
|
||||
v-model="form.validTime"
|
||||
type="datetime"
|
||||
placeholder="选择有效期截止时间"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="form-tip">留空表示永久有效</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" :rows="2" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开启共享">
|
||||
<el-switch v-model="shareEnabled" />
|
||||
</el-form-item>
|
||||
<el-form-item label="可见部门" v-if="shareEnabled">
|
||||
<el-select
|
||||
v-model="form.shareDeptIdList"
|
||||
multiple
|
||||
placeholder="请选择可见部门"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="dept in deptOptions"
|
||||
:key="dept.deptId"
|
||||
:label="dept.deptName"
|
||||
:value="dept.deptId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="创建方式" prop="createMode">
|
||||
<el-radio-group v-model="form.createMode" :disabled="isEdit">
|
||||
<el-radio label="1">模板导入</el-radio>
|
||||
<el-radio label="2">绩效网格</el-radio>
|
||||
<el-radio label="3">地理网格</el-radio>
|
||||
<el-radio label="4">绘制网格</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 模板导入 -->
|
||||
<template v-if="form.createMode === '1'">
|
||||
<el-form-item label="客户文件" prop="file">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
:action="uploadUrl"
|
||||
:headers="uploadHeaders"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:on-change="handleFileChange"
|
||||
:auto-upload="false"
|
||||
:show-file-list="true"
|
||||
:limit="1"
|
||||
accept=".xlsx,.xls"
|
||||
>
|
||||
<el-button slot="trigger" size="small" icon="el-icon-upload">选择文件</el-button>
|
||||
<el-button size="small" type="text" @click.stop="downloadTemplate" style="margin-left: 15px">
|
||||
下载模板
|
||||
</el-button>
|
||||
<div slot="tip" class="el-upload__tip">
|
||||
仅支持Excel文件,文件大小不超过10MB
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 绩效网格 -->
|
||||
<template v-if="form.createMode === '2'">
|
||||
<el-form-item label="业务类型" prop="cmpmBizType">
|
||||
<el-select v-model="gridForm.cmpmBizType" placeholder="请选择业务类型" style="width: 100%" @change="handleCmpmBizTypeChange">
|
||||
<el-option label="零售" value="retail" />
|
||||
<el-option label="对公" value="corporate" />
|
||||
<el-option label="对公账户" value="corporate_account" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userNames">
|
||||
<el-select
|
||||
v-model="gridForm.userNames"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请选择客户经理"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in userOptions"
|
||||
:key="user.userName"
|
||||
:label="user.nickName"
|
||||
:value="user.userName"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 地理网格 -->
|
||||
<template v-if="form.createMode === '3'">
|
||||
<el-form-item label="网格级别">
|
||||
<el-radio-group v-model="regionQuery.gridLevel">
|
||||
<el-radio label="1">总行行政网格</el-radio>
|
||||
<el-radio label="2">支行行政网格</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="责任类型">
|
||||
<el-select v-model="regionQuery.gridDutyType" placeholder="请选择责任类型" style="width: 100%" clearable>
|
||||
<el-option label="责任网格" value="1" />
|
||||
<el-option label="竞争网格" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="网格名称">
|
||||
<el-input v-model="regionQuery.gridName" placeholder="请输入网格名称(可选)" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="loadRegionGridOptions" :loading="regionLoading">查询网格</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetRegionQuery">重置条件</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择网格">
|
||||
<el-select
|
||||
v-model="gridForm.regionGridIds"
|
||||
multiple
|
||||
filterable
|
||||
collapse-tags
|
||||
placeholder="请先查询网格,然后选择"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="grid in regionGridOptions"
|
||||
:key="grid.gridId"
|
||||
:label="grid.gridName"
|
||||
:value="grid.gridId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 绘制网格 -->
|
||||
<template v-if="form.createMode === '4'">
|
||||
<el-form-item label="绘制网格" prop="drawGridIds">
|
||||
<el-select
|
||||
v-model="gridForm.drawGridIds"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请选择绘制网格"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="grid in drawGridOptions"
|
||||
:key="grid.gridId"
|
||||
:label="grid.gridName"
|
||||
:value="grid.gridId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" :loading="submitting" :disabled="isProcessing" @click="handleSubmit">
|
||||
{{ submitButtonText }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createCustGroupByGrid,
|
||||
createCustGroupByTemplate,
|
||||
updateCustGroupByGrid,
|
||||
updateCustGroupByTemplate,
|
||||
downloadTemplate,
|
||||
getManagerList,
|
||||
getRegionGridListForGroup
|
||||
} from '@/api/group/custGroup'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { listUser } from '@/api/system/user'
|
||||
import { listDept } from '@/api/system/dept'
|
||||
import { getSimpleGridList } from '@/api/grid/list/gridlist'
|
||||
|
||||
export default {
|
||||
name: 'CreateDialog',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
groupData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 提交中状态
|
||||
submitting: false,
|
||||
// 上传地址
|
||||
uploadUrl: process.env.VUE_APP_BASE_API + '/group/cust/createByTemplate',
|
||||
uploadHeaders: { Authorization: 'Bearer ' + getToken() },
|
||||
// 处理中提示
|
||||
processTipVisible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 是否正在处理中(创建或更新)
|
||||
isProcessing() {
|
||||
return this.form.createStatus === '0'
|
||||
},
|
||||
// 提交按钮文本
|
||||
submitButtonText() {
|
||||
if (this.submitting) return '提交中...'
|
||||
if (this.isEdit && this.isProcessing) return '客群处理中,请稍后...'
|
||||
return '确 定'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 提交中状态
|
||||
submitting: false,
|
||||
// 上传地址
|
||||
uploadUrl: process.env.VUE_APP_BASE_API + '/group/cust/createByTemplate',
|
||||
uploadHeaders: { Authorization: 'Bearer ' + getToken() },
|
||||
// 处理中提示
|
||||
processTipVisible: false,
|
||||
// 表单数据
|
||||
form: {
|
||||
id: null,
|
||||
groupName: null,
|
||||
groupMode: '0',
|
||||
createMode: '1',
|
||||
groupStatus: '0',
|
||||
shareEnabled: 0,
|
||||
shareDeptIdList: [],
|
||||
remark: null,
|
||||
validTime: null
|
||||
},
|
||||
// 共享开关
|
||||
shareEnabled: false,
|
||||
// 网格表单数据
|
||||
gridForm: {
|
||||
gridType: '0',
|
||||
cmpmBizType: null,
|
||||
userNames: [],
|
||||
regionGridIds: [],
|
||||
drawGridIds: []
|
||||
},
|
||||
// 上传文件
|
||||
uploadFile: null,
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
groupName: [
|
||||
{ required: true, message: '请输入客群名称', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||
],
|
||||
groupMode: [{ required: true, message: '请选择客群模式', trigger: 'change' }],
|
||||
createMode: [{ required: true, message: '请选择创建方式', trigger: 'change' }]
|
||||
},
|
||||
// 部门选项
|
||||
deptOptions: [],
|
||||
// 用户选项
|
||||
userOptions: [],
|
||||
// 地理网格选项
|
||||
regionGridOptions: [],
|
||||
// 地理网格查询条件
|
||||
regionQuery: {
|
||||
gridLevel: '1',
|
||||
gridDutyType: null,
|
||||
gridName: null
|
||||
},
|
||||
// 地理网格加载状态
|
||||
regionLoading: false,
|
||||
// 绘制网格选项
|
||||
drawGridOptions: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
groupData: {
|
||||
handler(val) {
|
||||
if (val && Object.keys(val).length > 0) {
|
||||
this.form = { ...val }
|
||||
this.shareEnabled = val.shareEnabled === 1
|
||||
// 解析 shareDeptIds 字符串为 shareDeptIdList 数组
|
||||
if (val.shareDeptIds) {
|
||||
this.form.shareDeptIdList = val.shareDeptIds.split(',').filter(id => id)
|
||||
} else {
|
||||
this.form.shareDeptIdList = []
|
||||
}
|
||||
|
||||
// 反显网格数据(需要在 form.createMode watch 之后执行)
|
||||
this.$nextTick(() => {
|
||||
this.restoreGridData(val)
|
||||
})
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
'form.createMode'(val) {
|
||||
// 重置网格表单
|
||||
this.gridForm = {
|
||||
gridType: val === '2' ? '0' : val === '3' ? '1' : '2',
|
||||
regionGridIds: [],
|
||||
drawGridIds: []
|
||||
}
|
||||
// 切换到地理网格模式时,重置查询条件
|
||||
if (val === '3') {
|
||||
this.resetRegionQuery()
|
||||
}
|
||||
// 切换到绘制网格模式时,加载绘制网格列表
|
||||
if (val === '4') {
|
||||
this.loadDrawGridOptions()
|
||||
}
|
||||
},
|
||||
'gridForm.cmpmBizType'(val) {
|
||||
// 当业务类型改变时,清空已选择的客户经理
|
||||
if (val) {
|
||||
this.gridForm.userNames = []
|
||||
this.loadManagerOptions()
|
||||
}
|
||||
},
|
||||
'form.createStatus'(val) {
|
||||
// 监听创建状态变化,显示提示
|
||||
if (this.isEdit && val === '0') {
|
||||
this.processTipVisible = true
|
||||
} else if (this.processTipVisible) {
|
||||
this.processTipVisible = false
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadDeptOptions()
|
||||
},
|
||||
methods: {
|
||||
/** 初始化 */
|
||||
init() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.form && this.$refs.form.clearValidate()
|
||||
// 编辑模式下,恢复网格数据
|
||||
if (this.isEdit && this.groupData && Object.keys(this.groupData).length > 0) {
|
||||
this.restoreGridData(this.groupData)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 反显网格数据 */
|
||||
restoreGridData(data) {
|
||||
if (!this.isEdit) return
|
||||
|
||||
const createMode = String(data.createMode)
|
||||
// 根据创建方式反显网格数据
|
||||
if (createMode === '2' && data.gridType === '0') {
|
||||
// 绩效网格
|
||||
this.gridForm.gridType = '0'
|
||||
this.gridForm.cmpmBizType = data.cmpmBizType
|
||||
if (data.gridUserNames) {
|
||||
this.gridForm.userNames = data.gridUserNames.split(',').filter(n => n)
|
||||
// 加载客户经理选项
|
||||
this.loadManagerOptions()
|
||||
}
|
||||
} else if (createMode === '3' && data.gridType === '1') {
|
||||
// 地理网格 - 需要查询所有网格以便正确反显
|
||||
this.gridForm.gridType = '1'
|
||||
// 设置默认查询条件(获取所有网格)
|
||||
this.regionQuery = {
|
||||
gridLevel: '1',
|
||||
gridDutyType: null,
|
||||
gridName: null
|
||||
}
|
||||
if (data.regionGridIds) {
|
||||
this.gridForm.regionGridIds = data.regionGridIds.split(',').map(id => parseInt(id)).filter(id => id)
|
||||
// 加载地理网格选项(无查询条件,获取全部)
|
||||
this.loadRegionGridOptions()
|
||||
}
|
||||
} else if (createMode === '4' && data.gridType === '2') {
|
||||
// 绘制网格
|
||||
this.gridForm.gridType = '2'
|
||||
if (data.drawGridIds) {
|
||||
this.gridForm.drawGridIds = data.drawGridIds.split(',').map(id => parseInt(id)).filter(id => id)
|
||||
// 加载绘制网格选项
|
||||
this.loadDrawGridOptions()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** 加载部门选项 */
|
||||
loadDeptOptions() {
|
||||
listDept().then(response => {
|
||||
this.deptOptions = response.data || []
|
||||
}).catch(() => {
|
||||
this.deptOptions = []
|
||||
})
|
||||
},
|
||||
|
||||
/** 加载用户选项 */
|
||||
loadUserOptions() {
|
||||
listUser().then(response => {
|
||||
this.userOptions = response.rows || []
|
||||
}).catch(() => {
|
||||
this.userOptions = []
|
||||
})
|
||||
},
|
||||
|
||||
/** 根据业务类型加载客户经理选项 */
|
||||
loadManagerOptions() {
|
||||
if (!this.gridForm.cmpmBizType) {
|
||||
this.userOptions = []
|
||||
return
|
||||
}
|
||||
getManagerList(this.gridForm.cmpmBizType).then(response => {
|
||||
this.userOptions = response.data || []
|
||||
}).catch(() => {
|
||||
this.userOptions = []
|
||||
})
|
||||
},
|
||||
|
||||
/** 业务类型改变处理 */
|
||||
handleCmpmBizTypeChange(val) {
|
||||
// 清空已选择的客户经理
|
||||
this.gridForm.userNames = []
|
||||
},
|
||||
|
||||
/** 加载地理网格选项 */
|
||||
loadRegionGridOptions() {
|
||||
this.regionLoading = true
|
||||
const params = {
|
||||
gridLevel: this.regionQuery.gridLevel
|
||||
}
|
||||
if (this.regionQuery.gridDutyType) {
|
||||
params.gridDutyType = this.regionQuery.gridDutyType
|
||||
}
|
||||
if (this.regionQuery.gridName) {
|
||||
params.gridName = this.regionQuery.gridName
|
||||
}
|
||||
|
||||
getRegionGridListForGroup(params).then(response => {
|
||||
this.regionGridOptions = response.data || []
|
||||
}).catch(() => {
|
||||
this.regionGridOptions = []
|
||||
}).finally(() => {
|
||||
this.regionLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 重置地理网格查询条件 */
|
||||
resetRegionQuery() {
|
||||
this.regionQuery = {
|
||||
gridLevel: '1',
|
||||
gridDutyType: null,
|
||||
gridName: null
|
||||
}
|
||||
this.gridForm.regionGridIds = []
|
||||
},
|
||||
|
||||
/** 加载绘制网格选项 */
|
||||
loadDrawGridOptions() {
|
||||
getSimpleGridList().then(response => {
|
||||
this.drawGridOptions = response.data || []
|
||||
}).catch(() => {
|
||||
this.drawGridOptions = []
|
||||
})
|
||||
},
|
||||
|
||||
/** 文件改变 */
|
||||
handleFileChange(file) {
|
||||
this.uploadFile = file.raw
|
||||
},
|
||||
|
||||
/** 上传成功 */
|
||||
handleUploadSuccess(response) {
|
||||
this.$modal.msgSuccess('创建成功')
|
||||
this.submitting = false
|
||||
this.$emit('submit', { id: response.data })
|
||||
this.handleClose()
|
||||
},
|
||||
|
||||
/** 上传失败 */
|
||||
handleUploadError() {
|
||||
this.$modal.msgError('创建失败')
|
||||
this.submitting = false
|
||||
},
|
||||
|
||||
/** 下载模板 */
|
||||
downloadTemplate() {
|
||||
downloadTemplate().then(response => {
|
||||
const url = window.URL.createObjectURL(new Blob([response]))
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', '客户信息模板.xlsx')
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
})
|
||||
},
|
||||
|
||||
/** 提交表单 */
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
// 处理共享设置
|
||||
this.form.shareEnabled = this.shareEnabled ? 1 : 0
|
||||
// 转换 shareDeptIdList 数组为 shareDeptIds 逗号分隔字符串
|
||||
this.form.shareDeptIds = this.form.shareDeptIdList.join(',')
|
||||
|
||||
// 根据创建方式处理提交数据
|
||||
if (this.form.createMode === '1') {
|
||||
this.handleTemplateSubmit()
|
||||
} else {
|
||||
this.handleGridSubmit()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 模板导入提交 */
|
||||
handleTemplateSubmit() {
|
||||
if (!this.uploadFile && !this.isEdit) {
|
||||
this.$modal.msgWarning('请选择要上传的文件')
|
||||
return
|
||||
}
|
||||
|
||||
this.submitting = true
|
||||
|
||||
// 构建表单数据
|
||||
const formData = new FormData()
|
||||
formData.append('dto', JSON.stringify(this.form))
|
||||
if (this.uploadFile) {
|
||||
formData.append('file', this.uploadFile)
|
||||
}
|
||||
|
||||
if (this.isEdit) {
|
||||
// 编辑模式:重新导入模板文件
|
||||
updateCustGroupByTemplate(formData).then(response => {
|
||||
this.$modal.msgSuccess('客群更新中,请稍后刷新查看')
|
||||
this.submitting = false
|
||||
this.$emit('submit', { id: this.form.id })
|
||||
this.handleClose()
|
||||
}).catch(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
} else {
|
||||
// 新增模式
|
||||
createCustGroupByTemplate(formData).then(response => {
|
||||
this.$modal.msgSuccess('客群创建中,请稍后刷新查看')
|
||||
this.submitting = false
|
||||
this.$emit('submit', { id: response.data })
|
||||
this.handleClose()
|
||||
}).catch(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/** 网格导入提交 */
|
||||
handleGridSubmit() {
|
||||
this.submitting = true
|
||||
|
||||
// 构建提交数据
|
||||
const submitData = {
|
||||
custGroup: { ...this.form },
|
||||
gridType: this.form.createMode === '2' ? '0' : this.form.createMode === '3' ? '1' : '2',
|
||||
cmpmBizType: this.gridForm.cmpmBizType,
|
||||
userNames: this.gridForm.userNames,
|
||||
regionGridIds: this.gridForm.regionGridIds,
|
||||
drawGridIds: this.gridForm.drawGridIds
|
||||
}
|
||||
|
||||
if (this.isEdit) {
|
||||
// 编辑模式:重新导入网格
|
||||
updateCustGroupByGrid(submitData).then(response => {
|
||||
// 使用后端返回的消息
|
||||
const msg = response.msg || '客群更新成功'
|
||||
if (msg.includes('更新中')) {
|
||||
this.$modal.msgSuccess('客群更新中,请稍后刷新查看')
|
||||
} else {
|
||||
this.$modal.msgSuccess(msg)
|
||||
}
|
||||
this.submitting = false
|
||||
this.$emit('submit', { id: this.form.id })
|
||||
this.handleClose()
|
||||
}).catch(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
} else {
|
||||
// 新增模式
|
||||
createCustGroupByGrid(submitData).then(response => {
|
||||
this.$modal.msgSuccess('客群创建中,请稍后刷新查看')
|
||||
this.submitting = false
|
||||
this.$emit('submit', { id: response.data })
|
||||
this.handleClose()
|
||||
}).catch(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/** 关闭弹窗 */
|
||||
handleClose() {
|
||||
this.$emit('update:visible', false)
|
||||
this.resetForm()
|
||||
},
|
||||
|
||||
/** 重置表单 */
|
||||
resetForm() {
|
||||
this.form = {
|
||||
id: null,
|
||||
groupName: null,
|
||||
groupMode: '0',
|
||||
createMode: '1',
|
||||
groupStatus: '0',
|
||||
shareEnabled: 0,
|
||||
shareDeptIdList: [],
|
||||
remark: null,
|
||||
validTime: null
|
||||
}
|
||||
this.shareEnabled = false
|
||||
this.gridForm = {
|
||||
gridType: '0',
|
||||
cmpmBizType: null,
|
||||
userNames: [],
|
||||
regionGridIds: [],
|
||||
drawGridIds: []
|
||||
}
|
||||
this.uploadFile = null
|
||||
if (this.$refs.upload) {
|
||||
this.$refs.upload.clearFiles()
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.form && this.$refs.form.clearValidate()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 1.5;
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
187
ruoyi-ui/src/views/group/custGroup/detail.vue
Normal file
187
ruoyi-ui/src/views/group/custGroup/detail.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="customer-wrap detail-wrap">
|
||||
<!-- 页面头部 -->
|
||||
<div class="page-header">
|
||||
<el-button icon="el-icon-arrow-left" size="small" @click="goBack">返回</el-button>
|
||||
<span class="page-title">客群客户列表</span>
|
||||
</div>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<div class="search-area">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="80px">
|
||||
<el-form-item label="客户类型" prop="custType">
|
||||
<el-select v-model="queryParams.custType" placeholder="请选择" clearable style="width: 150px">
|
||||
<el-option label="个人" value="0" />
|
||||
<el-option label="商户" value="1" />
|
||||
<el-option label="企业" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户姓名" prop="custName">
|
||||
<el-input
|
||||
v-model="queryParams.custName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="main_table">
|
||||
<el-table v-loading="loading" :data="memberList">
|
||||
<el-table-column label="序号" type="index" width="60" align="center" :index="indexMethod" />
|
||||
<el-table-column label="客户类型" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.custType === '0'" size="small">个人</el-tag>
|
||||
<el-tag v-else-if="scope.row.custType === '1'" size="small" type="warning">商户</el-tag>
|
||||
<el-tag v-else-if="scope.row.custType === '2'" size="small" type="success">企业</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户号" prop="custId" width="150" />
|
||||
<el-table-column label="客户姓名" prop="custName" width="120" />
|
||||
<el-table-column label="身份证号" prop="custIdc" show-overflow-tooltip />
|
||||
<el-table-column label="统信码" prop="socialCreditCode" show-overflow-tooltip />
|
||||
<el-table-column label="添加时间" prop="createTime" width="180" />
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCustGroupMembers } from '@/api/group/custGroup'
|
||||
|
||||
export default {
|
||||
name: 'CustGroupDetail',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
groupId: null,
|
||||
memberList: [],
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
custType: null,
|
||||
custName: null
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.groupId = this.$route.query.groupId
|
||||
if (this.groupId) {
|
||||
this.getList()
|
||||
} else {
|
||||
this.$modal.msgError('缺少客群ID参数')
|
||||
this.goBack()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route.query.groupId'(newGroupId) {
|
||||
if (newGroupId && newGroupId !== this.groupId) {
|
||||
this.groupId = newGroupId
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询客户列表 */
|
||||
getList(param) {
|
||||
if (param) {
|
||||
this.queryParams.pageNum = param.page
|
||||
this.queryParams.pageSize = param.limit
|
||||
}
|
||||
this.loading = true
|
||||
listCustGroupMembers(this.groupId, this.queryParams).then(response => {
|
||||
this.memberList = response.rows
|
||||
this.total = response.total
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 搜索 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 重置 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
/** 返回 */
|
||||
goBack() {
|
||||
this.$router.push({ path: '/group/custGroup' })
|
||||
},
|
||||
|
||||
/** 序号计算方法 */
|
||||
indexMethod(index) {
|
||||
return (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.customer-wrap {
|
||||
background-color: #ffffff;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 3px 8px 0 #00000017;
|
||||
border-radius: 16px 16px 0 0;
|
||||
padding: 24px 30px;
|
||||
|
||||
&.detail-wrap {
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.page-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #222222;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-area {
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.el-form {
|
||||
margin-bottom: -8px;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main_table {
|
||||
::v-deep .el-pagination {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
288
ruoyi-ui/src/views/group/custGroup/index.vue
Normal file
288
ruoyi-ui/src/views/group/custGroup/index.vue
Normal file
@@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<div class="customer-wrap">
|
||||
<!-- 搜索区域 -->
|
||||
<div class="search-area" v-show="showSearch">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="80px">
|
||||
<el-form-item label="客群名称" prop="groupName">
|
||||
<el-input
|
||||
v-model="queryParams.groupName"
|
||||
placeholder="请输入客群名称"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客群模式" prop="groupMode">
|
||||
<el-select v-model="queryParams.groupMode" placeholder="请选择" clearable style="width: 150px">
|
||||
<el-option label="静态" value="0" />
|
||||
<el-option label="动态" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建方式" prop="createMode">
|
||||
<el-select v-model="queryParams.createMode" placeholder="请选择" clearable style="width: 150px">
|
||||
<el-option label="模板导入" value="1" />
|
||||
<el-option label="绩效网格" value="2" />
|
||||
<el-option label="地理网格" value="3" />
|
||||
<el-option label="绘制网格" value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客群状态" prop="groupStatus">
|
||||
<el-select v-model="queryParams.groupStatus" placeholder="请选择" clearable style="width: 150px">
|
||||
<el-option label="正常" value="0" />
|
||||
<el-option label="已禁用" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
<section class="operate-cnt">
|
||||
<div class="operate-left">
|
||||
<el-button type="primary" icon="el-icon-plus" size="small" @click="handleAdd">新增</el-button>
|
||||
<el-button type="danger" icon="el-icon-delete" size="small" :disabled="multiple" @click="handleDelete">删除</el-button>
|
||||
</div>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</section>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="main_table">
|
||||
<el-table v-loading="loading" :data="groupList" @selection-change="handleSelectionChange" style="width: 100%" max-height="625">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="客群名称" prop="groupName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="客群模式" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.groupMode === '0'" type="info" size="small">静态</el-tag>
|
||||
<el-tag v-else-if="scope.row.groupMode === '1'" type="success" size="small">动态</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建方式" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.createMode === '1'">模板导入</span>
|
||||
<span v-else-if="scope.row.createMode === '2'">绩效网格</span>
|
||||
<span v-else-if="scope.row.createMode === '3'">地理网格</span>
|
||||
<span v-else-if="scope.row.createMode === '4'">绘制网格</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户数量" align="center" prop="custCount" width="100" />
|
||||
<el-table-column label="客群状态" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.groupStatus === '0'" type="success" size="small">正常</el-tag>
|
||||
<el-tag v-else-if="scope.row.groupStatus === '1'" type="danger" size="small">已禁用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建者" prop="nickName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="创建时间" prop="createTime" width="180" />
|
||||
<el-table-column label="操作" align="center" width="180" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">查看</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 创建/编辑客群弹窗 -->
|
||||
<create-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
:group-data="form"
|
||||
:is-edit="isEdit"
|
||||
@submit="handleSubmit"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCustGroup, getCustGroup, deleteCustGroup } from '@/api/group/custGroup'
|
||||
import CreateDialog from './components/create-dialog'
|
||||
|
||||
export default {
|
||||
name: 'CustGroup',
|
||||
components: { CreateDialog },
|
||||
data() {
|
||||
return {
|
||||
// 加载状态
|
||||
loading: false,
|
||||
// 显示搜索
|
||||
showSearch: true,
|
||||
// 选中ID数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 客群列表
|
||||
groupList: [],
|
||||
// 弹窗显示
|
||||
dialogVisible: false,
|
||||
// 是否编辑
|
||||
isEdit: false,
|
||||
// 表单数据
|
||||
form: {},
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
groupName: null,
|
||||
groupMode: null,
|
||||
createMode: null,
|
||||
groupStatus: null
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 查询客群列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listCustGroup(this.queryParams).then(response => {
|
||||
this.groupList = response.rows
|
||||
this.total = response.total
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
/** 多选框选中数据 */
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.isEdit = false
|
||||
this.dialogVisible = true
|
||||
},
|
||||
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset()
|
||||
const id = row.id || this.ids[0]
|
||||
getCustGroup(id).then(response => {
|
||||
this.form = response.data
|
||||
this.isEdit = true
|
||||
this.dialogVisible = true
|
||||
})
|
||||
},
|
||||
|
||||
/** 查看按钮操作 */
|
||||
handleView(row) {
|
||||
this.$router.push({
|
||||
path: '/group/custGroup/detail',
|
||||
query: { groupId: row.id }
|
||||
})
|
||||
},
|
||||
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id ? [row.id] : this.ids
|
||||
this.$modal.confirm('是否确认删除选中的客群?').then(() => {
|
||||
return deleteCustGroup(ids)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
/** 提交表单 */
|
||||
handleSubmit() {
|
||||
this.dialogVisible = false
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: null,
|
||||
groupName: null,
|
||||
groupMode: '0',
|
||||
createMode: null,
|
||||
groupStatus: '0',
|
||||
shareEnabled: 0,
|
||||
shareDeptIdList: [],
|
||||
remark: null,
|
||||
validTime: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.customer-wrap {
|
||||
background-color: #ffffff;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 3px 8px 0 #00000017;
|
||||
border-radius: 16px 16px 0 0;
|
||||
padding: 24px 30px;
|
||||
|
||||
.search-area {
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.el-form {
|
||||
margin-bottom: -8px;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operate-cnt {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 24px 0 16px 0;
|
||||
|
||||
.operate-left {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.main_table {
|
||||
::v-deep .el-pagination {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -20,6 +20,7 @@
|
||||
<span style="font-size: 14px;">较上月变动</span>
|
||||
<span v-if="String(item.inc).includes('-')" style=" font-size: 14px;color: #00B453">{{ changeData(item.inc)
|
||||
}}<i class="el-icon-caret-bottom"></i></span>
|
||||
<span v-else-if="item.inc == 0 || item.inc === '0'" style=" font-size: 14px;color: #409EFF">{{ changeData(item.inc) }}</span>
|
||||
<span v-else style=" font-size: 14px;color: #EF3F35">{{ changeData(item.inc) }}<i
|
||||
class="el-icon-caret-top"></i></span>
|
||||
</div>
|
||||
@@ -43,6 +44,11 @@
|
||||
@click="goToCustManager(item.itemNm, 'fall')">
|
||||
{{ item.curAmt }}<i class="el-icon-caret-bottom"></i>
|
||||
</span>
|
||||
<span v-else-if="item.curAmt == 0 || item.curAmt === '0'"
|
||||
style="font-size: 14px; color: #409EFF; cursor: pointer;"
|
||||
@click="goToCustManager(item.itemNm, 'rise')">
|
||||
{{ item.curAmt }}
|
||||
</span>
|
||||
<span v-else
|
||||
style="font-size: 14px; color: #EF3F35; cursor: pointer;"
|
||||
@click="goToCustManager(item.itemNm, 'rise')">
|
||||
@@ -60,7 +66,7 @@
|
||||
<el-radio-button label="4">预警任务</el-radio-button>
|
||||
<el-radio-button label="5">二次走访提醒</el-radio-button>
|
||||
<el-radio-button label="6">走访资源提醒</el-radio-button>
|
||||
<el-radio-button label="7">营销任务</el-radio-button>
|
||||
<el-radio-button label="7" v-if="!shouldHideFor875">营销任务</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-table v-if="selectedTab==='3'" key="3" :data="tableData" :loading="loading" style="width: 100%;margin-top: 20px;">
|
||||
@@ -152,7 +158,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-table v-if="selectedTab==='7'" key="7" :data="tableData" :loading="loading" style="width: 100%;margin-top: 20px;">
|
||||
<el-table v-if="selectedTab==='7' && !shouldHideFor875" key="7" :data="tableData" :loading="loading" style="width: 100%;margin-top: 20px;">
|
||||
<el-table-column label="营销任务" width="200" prop="marketTaskName" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="客户姓名" prop="custName" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="客户号" width="200" prop="custId" min-width="140" show-overflow-tooltip />
|
||||
@@ -194,9 +200,9 @@
|
||||
<el-table-column label="结束时间" width="200" prop="endTime" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="备注" prop="remark" min-width="100" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<el-pagination @size-change="handleAgentSizeChange" @current-change="handleAgentCurrentChange"
|
||||
class="warnPagination" :page-sizes="[5, 10, 20, 30]" :page-size="agentPageSize"
|
||||
layout="->,total,sizes,prev,pager,next" :total="agentTotal" :current-page="agentPageNum"></el-pagination>
|
||||
<el-pagination @current-change="handleAgentCurrentChange"
|
||||
class="warnPagination" :page-size="5"
|
||||
layout="->,total,prev,pager,next" :total="agentTotal" :current-page="agentPageNum"></el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-vr">
|
||||
@@ -210,7 +216,7 @@
|
||||
</span>
|
||||
</p>
|
||||
<ul class="operate-cnt">
|
||||
<li v-for="(it, ind) in optArr" :key="ind" class="setLi" @mouseenter="onMouseOver(ind, true)"
|
||||
<li v-for="(it, ind) in filteredOptArr" :key="ind" class="setLi" @mouseenter="onMouseOver(ind, true)"
|
||||
@mouseleave="onMouseOver(ind, false)">
|
||||
<div class="noSetting" v-if="it.setShow">
|
||||
<svg-icon :icon-class="getCurrentImg(ind)" :class="isSetting ? 'svg-icon-imgSetting' : 'svg-icon-img'"
|
||||
@@ -230,7 +236,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="page-vr-middle page-common-wrap no-padding-cnt page-vr-top" id="yjxxMain">
|
||||
<div class="page-vr-middle page-common-wrap no-padding-cnt page-vr-top" id="yjxxMain" v-if="!shouldHideFor875">
|
||||
<p class="page-title page-btm">预警信息</p>
|
||||
<ul class="common-ul">
|
||||
<li v-for="(it, ind) in warnArr" :key="ind" style="cursor: pointer;" @click="handleWarn(it)" class="yjxxLi">
|
||||
@@ -857,6 +863,27 @@ export default {
|
||||
},
|
||||
showposition() {
|
||||
return this.userName.slice(0, 3) === '875'
|
||||
},
|
||||
// headId为875时隐藏预警信息和营销任务
|
||||
shouldHideFor875() {
|
||||
return this.userName && (this.userName + '').substring(0, 3) === '875'
|
||||
},
|
||||
// headId为875时,便捷操作只显示快速入门
|
||||
filteredOptArr() {
|
||||
// 当deptId以875开头时,只保留快速入门
|
||||
if (this.deptId && String(this.deptId).startsWith('875')) {
|
||||
return this.optArr.filter(item => item.name === '快速入门')
|
||||
}
|
||||
return this.optArr
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedTab(newVal, oldVal) {
|
||||
// 切换tab时重置分页为第1页
|
||||
if (newVal !== oldVal) {
|
||||
this.pageNum = 1
|
||||
this.agentPageNum = 1
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -1135,8 +1162,7 @@ export default {
|
||||
this.getData()
|
||||
},
|
||||
handleAgentCurrentChange(val) {
|
||||
// this.agentPageNum = val
|
||||
// this.initBranchList()
|
||||
this.agentPageNum = val
|
||||
this.pageNum = val
|
||||
this.getData()
|
||||
},
|
||||
@@ -1565,6 +1591,8 @@ p {
|
||||
.page-vr {
|
||||
width: 24%;
|
||||
margin-left: 1%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
@@ -1602,7 +1630,11 @@ p {
|
||||
}
|
||||
|
||||
.page-vr-middle {
|
||||
margin: 22px 0;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.page-vr-top {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.page-vl-top {
|
||||
@@ -1873,7 +1905,7 @@ p {
|
||||
.page-vr-btm {
|
||||
height: 350px;
|
||||
overflow-y: scroll;
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
::v-deep .el-badge__content {
|
||||
@@ -1943,8 +1975,10 @@ p {
|
||||
}
|
||||
|
||||
.url-box {
|
||||
max-height: 210px;
|
||||
flex: 1;
|
||||
overflow-y: scroll;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.yjxxLi {
|
||||
|
||||
@@ -6,24 +6,46 @@
|
||||
v-model="selectedTab"
|
||||
@input="handleChange"
|
||||
>
|
||||
<el-radio-button
|
||||
label="2"
|
||||
:disabled="isPrivate"
|
||||
:class="{ 'btn-disabled': isPrivate }"
|
||||
>企业</el-radio-button
|
||||
>
|
||||
<el-radio-button
|
||||
label="0"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>个人</el-radio-button
|
||||
>
|
||||
<el-radio-button
|
||||
label="1"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>商户</el-radio-button
|
||||
>
|
||||
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||
<el-radio-button
|
||||
label="0"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>个人</el-radio-button
|
||||
>
|
||||
<el-radio-button
|
||||
label="1"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>商户</el-radio-button
|
||||
>
|
||||
<el-radio-button
|
||||
label="2"
|
||||
:disabled="isPrivate"
|
||||
:class="{ 'btn-disabled': isPrivate }"
|
||||
>企业</el-radio-button
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-radio-button
|
||||
label="2"
|
||||
:disabled="isPrivate"
|
||||
:class="{ 'btn-disabled': isPrivate }"
|
||||
>企业</el-radio-button
|
||||
>
|
||||
<el-radio-button
|
||||
label="0"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>个人</el-radio-button
|
||||
>
|
||||
<el-radio-button
|
||||
label="1"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>商户</el-radio-button
|
||||
>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
<div class="searchForm">
|
||||
<el-form
|
||||
@@ -403,6 +425,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["roles", "userName"]),
|
||||
deptId() {
|
||||
return this.$store.state.user.deptId
|
||||
},
|
||||
//总行
|
||||
isHeadAdmin() {
|
||||
return this.roles.includes("headAdmin");
|
||||
@@ -441,12 +466,17 @@ export default {
|
||||
if (selectedTab) {
|
||||
this.selectedTab = selectedTab;
|
||||
} else {
|
||||
// 默认选中第一个tab
|
||||
const deptId = this.$store.state.user.deptId
|
||||
const deptIdStr = deptId ? String(deptId) : ''
|
||||
const defaultTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||
|
||||
if (this.isPublic) {
|
||||
this.selectedTab = '2'
|
||||
} else if (this.isPrivate) {
|
||||
this.selectedTab = '0'
|
||||
this.selectedTab = defaultTab
|
||||
} else {
|
||||
this.selectedTab = '2'
|
||||
this.selectedTab = defaultTab
|
||||
}
|
||||
}
|
||||
this.initVisitingTaskList();
|
||||
|
||||
@@ -64,9 +64,16 @@
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-radio-group class="header-radio" v-model="selectedTab" @input="handleChange">
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-radio-button label="2" :disabled="isPrivate" :class="{ 'btn-disabled': isPrivate }">企业</el-radio-button>
|
||||
<el-radio-button label="0" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">个人</el-radio-button>
|
||||
<el-radio-button label="1" :disabled="isPublic" :class="{ 'btn-disabled': isPublic }">商户</el-radio-button>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
<!-- <div class="taskTop">
|
||||
<div class="taskTop_left">
|
||||
@@ -292,6 +299,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['roles']),
|
||||
deptId() {
|
||||
return this.$store.state.user.deptId
|
||||
},
|
||||
//总行
|
||||
isHeadAdmin() {
|
||||
return this.roles.includes('headAdmin')
|
||||
@@ -334,12 +344,17 @@ export default {
|
||||
delete this.$route.query[key];
|
||||
}
|
||||
} else {
|
||||
// 默认选中第一个tab
|
||||
const deptId = this.$store.state.user.deptId
|
||||
const deptIdStr = deptId ? String(deptId) : ''
|
||||
const defaultTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||
|
||||
if (this.isPublic) {
|
||||
this.selectedTab = '2'
|
||||
} else if (this.isPrivate) {
|
||||
this.selectedTab = '0'
|
||||
this.selectedTab = defaultTab
|
||||
} else {
|
||||
this.selectedTab = '2'
|
||||
this.selectedTab = defaultTab
|
||||
}
|
||||
}
|
||||
if (this.isBranchAdmin) {
|
||||
|
||||
@@ -6,21 +6,40 @@
|
||||
class="header-radio"
|
||||
@input="handleChange"
|
||||
>
|
||||
<el-radio-button
|
||||
label="2"
|
||||
:disabled="isPrivate"
|
||||
:class="{ 'btn-disabled': isPrivate }"
|
||||
>企业</el-radio-button>
|
||||
<el-radio-button
|
||||
label="0"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>个人</el-radio-button>
|
||||
<el-radio-button
|
||||
label="1"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>商户</el-radio-button>
|
||||
<template v-if="String(deptId).substring(0, 3) === '875'">
|
||||
<el-radio-button
|
||||
label="0"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>个人</el-radio-button>
|
||||
<el-radio-button
|
||||
label="1"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>商户</el-radio-button>
|
||||
<el-radio-button
|
||||
label="2"
|
||||
:disabled="isPrivate"
|
||||
:class="{ 'btn-disabled': isPrivate }"
|
||||
>企业</el-radio-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-radio-button
|
||||
label="2"
|
||||
:disabled="isPrivate"
|
||||
:class="{ 'btn-disabled': isPrivate }"
|
||||
>企业</el-radio-button>
|
||||
<el-radio-button
|
||||
label="0"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>个人</el-radio-button>
|
||||
<el-radio-button
|
||||
label="1"
|
||||
:disabled="isPublic"
|
||||
:class="{ 'btn-disabled': isPublic }"
|
||||
>商户</el-radio-button>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
<div class="taskTop">
|
||||
<div class="taskTop_left">
|
||||
@@ -1180,6 +1199,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['roles']),
|
||||
deptId() {
|
||||
return this.$store.state.user.deptId
|
||||
},
|
||||
// 总行
|
||||
isHeadAdmin() {
|
||||
return this.roles.includes('headAdmin')
|
||||
@@ -1215,17 +1237,22 @@ export default {
|
||||
created() {
|
||||
// getGroupInfoByGroupId({})
|
||||
this.isUserType()
|
||||
// 根据deptId动态设置默认tab:默认选中第一个tab
|
||||
const deptId = this.$store.state.user.deptId
|
||||
const deptIdStr = deptId ? String(deptId) : ''
|
||||
const defaultTab = deptIdStr.startsWith('875') ? '0' : '2'
|
||||
|
||||
if (this.isPublic) {
|
||||
this.selectedTab = '2'
|
||||
this.custTypeList = [{ label: '企业', value: '2' }]
|
||||
} else if (this.isPrivate) {
|
||||
this.selectedTab = '0'
|
||||
this.selectedTab = defaultTab
|
||||
this.custTypeList = [
|
||||
{ label: '个人', value: '0' },
|
||||
{ label: '商户', value: '1' }
|
||||
]
|
||||
} else {
|
||||
this.selectedTab = '2'
|
||||
this.selectedTab = defaultTab
|
||||
this.custTypeList = [
|
||||
{ label: '个人', value: '0' },
|
||||
{ label: '商户', value: '1' },
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
<template>
|
||||
<div class="app-container" style="padding: 0px !important;">
|
||||
<el-date-picker
|
||||
v-model="reportTime"
|
||||
placeholder="请选择日期"
|
||||
@change="initCardList"
|
||||
class="time-picker"
|
||||
/>
|
||||
<el-row :gutter="24" style="padding: 0 30px;margin-left: -15px;">
|
||||
<el-row :gutter="24" style="padding: 0 30px;">
|
||||
<el-col :span="6">
|
||||
<el-card class="box-card">
|
||||
<div class="my-span-checklist-title">
|
||||
总预警推送次数
|
||||
<el-card class="stat-card stat-card-blue">
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-bell"></i>
|
||||
</div>
|
||||
<div class="my-span-checklist-main">
|
||||
<span>{{ cardInfo.alterCount }}</span>
|
||||
<div class="stat-content">
|
||||
<div class="stat-title">总预警推送次数</div>
|
||||
<div class="stat-value">{{ cardInfo.alterCount }}</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="box-card1">
|
||||
<div class="my-span-checklist-title">
|
||||
反馈完成率
|
||||
<el-card class="stat-card stat-card-blue2">
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-data-analysis"></i>
|
||||
</div>
|
||||
<div class="my-span-checklist-main">
|
||||
<span>{{ cardInfo.completeRate + '%' }}</span>
|
||||
<div class="stat-content">
|
||||
<div class="stat-title">反馈完成率</div>
|
||||
<div class="stat-value">{{ cardInfo.completeRate + '%' }}</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
@@ -35,6 +31,14 @@
|
||||
:inline="true"
|
||||
style="margin-top:20px;"
|
||||
>
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker
|
||||
v-model="reportTime"
|
||||
placeholder="请选择日期"
|
||||
@change="initCardList"
|
||||
style="width:100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="searchArray.status" placeholder="请选择状态" style="width: 100%" clearable>
|
||||
<el-option
|
||||
@@ -46,20 +50,26 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="预警类型" prop="alterType">
|
||||
<el-input
|
||||
<el-select
|
||||
v-model="searchArray.alterType"
|
||||
placeholder="请输入预警类型"
|
||||
placeholder="请选择预警类型"
|
||||
clearable
|
||||
style="width:100%"
|
||||
>
|
||||
</el-input>
|
||||
<el-option
|
||||
v-for="item in alterTypeOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="searchFn">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetFn">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="tableData" :height="dyHeight" :key="tableKey">
|
||||
<el-table v-loading="loading" :data="tableData">
|
||||
<template>
|
||||
<el-table-column label="序号" prop="xh" width="80">
|
||||
<template slot-scope="scope">
|
||||
@@ -91,17 +101,15 @@
|
||||
</el-table-column> -->
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="pagination_end">
|
||||
<el-pagination
|
||||
layout="total, prev, pager, next, jumper"
|
||||
:current-page="pageNum"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
@current-change="currentChangeFn"
|
||||
:total="total"
|
||||
@size-change="sizeChangeFn"
|
||||
></el-pagination>
|
||||
</div>
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageSize"
|
||||
layout="->,total,prev,pager,next,sizes"
|
||||
:total="total"
|
||||
:current-page="pageNum"
|
||||
@current-change="currentChangeFn"
|
||||
@size-change="sizeChangeFn"
|
||||
></el-pagination>
|
||||
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
@@ -199,7 +207,8 @@
|
||||
import {
|
||||
warningworkRecordList,
|
||||
warningworkRecordSubmit,
|
||||
warningCardNum
|
||||
warningCardNum,
|
||||
getAlterTypes
|
||||
} from "@/api/system/home";
|
||||
import _ from "lodash";
|
||||
import dayjs from "dayjs";
|
||||
@@ -243,6 +252,8 @@ export default {
|
||||
}
|
||||
],
|
||||
|
||||
alterTypeOptions: [],
|
||||
|
||||
searchArray: {
|
||||
status: "",
|
||||
alterType: ""
|
||||
@@ -252,8 +263,6 @@ export default {
|
||||
tableData: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
dyHeight: {},
|
||||
tableKey: false,
|
||||
dialogTitle: '',
|
||||
dialogForm: {
|
||||
custName: '',
|
||||
@@ -277,12 +286,9 @@ export default {
|
||||
};
|
||||
},
|
||||
created() {
|
||||
window.addEventListener("resize", () => {
|
||||
this.dyHeight = Number(window.innerHeight) - 330;
|
||||
});
|
||||
this.dyHeight = Number(window.innerHeight) - 330;
|
||||
this.resetFn();
|
||||
this.initCardList()
|
||||
this.initCardList();
|
||||
this.getAlterTypeList();
|
||||
},
|
||||
filters: {
|
||||
formatFilter(v, type, list) {
|
||||
@@ -293,6 +299,13 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAlterTypeList() {
|
||||
getAlterTypes().then(res => {
|
||||
if (res.code === 200) {
|
||||
this.alterTypeOptions = res.data || [];
|
||||
}
|
||||
});
|
||||
},
|
||||
initCardList() {
|
||||
warningCardNum({ reportTime: this.reportTime ? dayjs(this.reportTime).format('YYYY-MM-DD') + ' 23:59:59' : '' }).then(res => {
|
||||
this.cardInfo = res
|
||||
@@ -326,7 +339,6 @@ export default {
|
||||
} else {
|
||||
this.$message.error(response.msg || "操作失败");
|
||||
}
|
||||
this.tableKey = !this.tableKey;
|
||||
});
|
||||
},
|
||||
currentChangeFn(val) {
|
||||
@@ -369,45 +381,79 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pagination_end {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
::v-deep .el-pagination {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.time-picker {
|
||||
margin: 20px 0 0 0px;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
// 统计卡片样式
|
||||
.stat-card {
|
||||
height: 100px;
|
||||
border-radius: 8px;
|
||||
margin-top: 25px;
|
||||
margin-left: -30px;
|
||||
border: 3px solid #a8c2f5;
|
||||
}
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
.box-card1 {
|
||||
height: 100px;
|
||||
border-radius: 8px;
|
||||
margin-top: 25px;
|
||||
border: 3px solid #a8c2f5;
|
||||
}
|
||||
// 左侧装饰条
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.my-span-checklist-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.stat-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.my-span-checklist-main {
|
||||
line-height: 40px;
|
||||
font-size: 30px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
i {
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.stat-title {
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// 蓝色渐变卡片1(左上深→右下浅)
|
||||
&.stat-card-blue {
|
||||
background: linear-gradient(135deg, #1e7ee6 0%, #a0cfff 100%);
|
||||
}
|
||||
|
||||
// 蓝色渐变卡片2(左上浅→右下深)
|
||||
&.stat-card-blue2 {
|
||||
background: linear-gradient(135deg, #a0cfff 0%, #1e7ee6 100%);
|
||||
}
|
||||
}
|
||||
.header-radio {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user