TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(
最近开发,有地方需要用到多线程,每个线程里面处理多个方法,过程中遇到了一个问题,我们使用平时的@Transactional注解,就是当前一个方法执行完成(比如插入操作),后一个方法是不会事务回滚的。当时觉得很不可思议,后来经过半天时间,终于挖出原因,并成功解决。
我这里先说明原因:多线程底层连接数据库的时候,时使用的线程变量(TheadLocal),所以,开多少线程理论上就会建立多少个连接,每个线程有自己的连接,事务肯定不是同一个了。
解决办法:我强制手动把每个线程的事务状态放到一个同步集合里面。然后如果有单个异常,循环回滚每个线程。
代码如下:
1、注入
@Autowired
private PlatformTransactionManager transactionManager;
2、多线程操作
@Override
public String importBatch(String url,String taskId) {
if (StringUtils.isEmpty(taskId)){
return "请选择外呼任务";
}
Task task = taskService.selectById(taskId);
if (task == null){
return "外呼任务不存在,Id为:"+taskId;
}
String filePath = tmpFilePath + url;
List customers = ExcelTemplateExportUtil.importExcel(filePath, 0, 1, Customer.class);
if (CollectionUtils.isEmpty(customers)){
return "Excel数据不能为空";
}
int totalCustomerCnt = customers.size();
if (totalCustomerCnt > 5000){
return "最多支持5000条,请分批导入";
}
//过滤掉空属性
List phones = new ArrayList<>();
List customersNotNull = new ArrayList<>();
String corpCode = ExecutionContext.getCorpCode();
String userId = ExecutionContext.getUserId();
for (Customer customer : customers) {
String phone = customer.getPhone();
if (StringUtils.isEmpty(customer.getName()) || StringUtils.isEmpty(phone)){
continue;
}
customersNotNull.add(customer);
phones.add(phone);
}
String result = "";
int notNullSize = customersNotNull.size();
int size = customers.size();
if (notNullSize < size){
result += "存在客户名称或手机号码为空:"+(size - notNullSize)+"条记录";
}
//过滤手机号重复的
List validCustomers = new ArrayList<>();
List repeatCustomers = this.selectList(new EntityWrapper().eq("corp_code", corpCode).in("phone", phones));
if (CollectionUtils.isNotEmpty(repeatCustomers)){
result += "
存在手机号码重复:"+(repeatCustomers.size())+"条记录";
List repeatPhones = new ArrayList<>(repeatCustomers.size());
for (Customer repeatCustomer : repeatCustomers) {
repeatPhones.add(repeatCustomer.getPhone());
}
for (Customer customer : customersNotNull) {
if (repeatPhones.contains(customer.getPhone())){
continue;
}
validCustomers.add(customer);
}
}else {
validCustomers = customersNotNull;
}
for (Customer validCustomer : validCustomers) {
validCustomer.setCustomerType(UkConstant.CUSTOMER_TYPE_INDIVIDUALITY);
validCustomer.setChannelType(UkConstant.CHANNEL_TYPE_CALL_OUT);
validCustomer.setCalloutTaskStatus(0);
validCustomer.setCalloutDistributeStatus(0);
validCustomer.setCalloutTaskId(taskId);
validCustomer.setCorpCode(corpCode);
validCustomer.setCreater(userId);
}
if (CollectionUtils.isEmpty(validCustomers)){
return "成功导入:"+ 0 +"条记录
"+result;
}
List> customersList = new ArrayList<>();
int total = validCustomers.size();
int threads = 5;
int oneSize = total/5 +1;
int start = 0;
int end = 0;
for (int i = 0; i transactionStatuses = Collections.synchronizedList(new ArrayList());
CountDownLatch latch= new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
int finalI = i;
ThreadPoolUtils.fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 事物隔离级别,开启新事务,这样会比较安全些。
TransactionStatus status = transactionManager.getTransaction(def); // 获得事务状态
transactionStatuses.add(status);
try{
//执行业务逻辑 插入或更新操作
improtInsertBath(corpCode,userId,customersList.get(finalI));
}catch(Exception e){
e.printStackTrace();
//异常 回滚所有事务
for (TransactionStatus transactionStatus:transactionStatuses) {
transactionStatus.setRollbackOnly();
}
}
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新外呼任务客户总数 customerCnt、未分配客户数 notDistributeCnt、未拨打数 not_call_cnt
task.setCustomerCnt(task.getCustomerCnt()+total);
task.setNotDistributeCnt(task.getNotDistributeCnt()+total);
task.setNotCallCnt(task.getNotCallCnt()+total);
taskService.updateById(task);
return "成功导入:"+ total +"条记录
"+result;
}
3、整体类
package com.ps.uzkefu.apps.crm.service.impl;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.ps.uzkefu.apps.callout.entity.Task;
import com.ps.uzkefu.apps.callout.service.TaskService;
import com.ps.uzkefu.apps.crm.entity.Customer;
import com.ps.uzkefu.apps.crm.entity.CustomerRecord;
import com.ps.uzkefu.apps.crm.mapper.CustomerDao;
import com.ps.uzkefu.apps.crm.service.CustomerRecordService;
import com.ps.uzkefu.apps.crm.service.CustomerService;
import com.ps.uzkefu.apps.oms.account.entity.DefinedField;
import com.ps.uzkefu.apps.oms.account.entity.DefinedFieldOption;
import com.ps.uzkefu.apps.oms.account.entity.DefinedFieldValue;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldOptionService;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldService;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldValueService;
import com.ps.uzkefu.apps.oms.account.service.UserService;
import com.ps.uzkefu.base.BaseServiceImpl;
import com.ps.uzkefu.common.ExecutionContext;
import com.ps.uzkefu.common.UkConstant;
import com.ps.uzkefu.util.DateUtil;
import com.ps.uzkefu.util.ExcelTemplateExportUtil;
import com.ps.uzkefu.util.ThreadPoolUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.BatchExecutorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.sql.BatchUpdateException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
/**
*
* 客户 服务实现类
*
*
* @author WuZhiWei
* @since 2018-07-06
*/
@Transactional(rollbackFor = {Exception.class})
@Service
public class CustomerServiceImpl extends BaseServiceImpl implements CustomerService {
@Autowired
private DefinedFieldValueService definedFieldValueService;
@Autowired
private DefinedFieldService definedFieldService;
@Autowired
private DefinedFieldOptionService definedFieldOptionService;
@Autowired
private CustomerRecordService customerRecordService;
@Autowired
private UserService userService;
@Autowired
private TaskService taskService;
@Autowired
private CustomerDao customerDao;
@Autowired
private PlatformTransactionManager transactionManager;
@Value("${tmp.file.path}")
private String tmpFilePath;
@Override
public String saveOrUpdate(Customer customer) {
String id = customer.getId();
String phone = customer.getPhone();
String corpCode = ExecutionContext.getCorpCode();
customer.setCorpCode(corpCode);
if (StringUtils.isEmpty(id)){
Customer selectOne = this.selectOne(new EntityWrapper().
eq("corp_code", corpCode).eq("phone", phone));
if (selectOne != null){
return "联系号码已存在";
}
}
Customer selectOne = this.selectOne(new EntityWrapper().
eq("corp_code", corpCode).eq("phone", phone)
.where("id !={0}",id));
if (selectOne != null){
return "联系号码已存在";
}
if (StringUtils.isEmpty(customer.getCorpName())){
customer.setCustomerType(UkConstant.CUSTOMER_TYPE_INDIVIDUALITY);
}else {
customer.setCustomerType(UkConstant.CUSTOMER_TYPE_CORP);
}
if (StringUtils.isEmpty(customer.getUserId())){
customer.setUserId(ExecutionContext.getUserId());
}
//自定义属性
List definedFieldValues = customer.getDefinedFieldValues();
//变更记录
String updateRecord = getUpdateRecord(customer, id, corpCode, definedFieldValues);
customer.setAssignDate(new Date());
this.insertOrUpdate(customer);
if (StringUtils.isEmpty(id)){
id = customer.getId();
}
if (StringUtils.isNotEmpty(updateRecord)){
CustomerRecord customerRecord = new CustomerRecord(UkConstant.RECORD_TYPE_UPDATE,updateRecord,null,null,null,id);
customerRecordService.insert(customerRecord);
}
//自定义保存或更新处理
if (CollectionUtils.isNotEmpty(definedFieldValues)){
for (DefinedFieldValue fieldValue : definedFieldValues) {
fieldValue.setResourceId(id);
}
definedFieldValueService.delete(new EntityWrapper().eq("resource_id",id));
definedFieldValueService.insertBatch(definedFieldValues);
}
return "";
}
private String getUpdateRecord(Customer customer, String id, String corpCode, List definedFieldValues) {
String recordContent = "";
if (StringUtils.isNotEmpty(id)){
Customer customerDb = this.selectById(id);
Map uidNameMap = userService.getAllUidNameMap();
recordContent = dealUpdate("客户姓名",customer.getName(),customerDb.getName(),recordContent);
recordContent = dealUpdate("公司名称",customer.getCorpName(),customerDb.getCorpName(),recordContent);
recordContent =dealUpdate("联系号码",customer.getPhone(),customerDb.getPhone(),recordContent);
recordContent =dealUpdate("联系邮箱",customer.getEmail(),customerDb.getEmail(),recordContent);
recordContent =dealUpdate("微信",customer.getWx(),customerDb.getWx(),recordContent);
recordContent =dealUpdate("QQ",customer.getQq(),customerDb.getQq(),recordContent);
recordContent =dealUpdate("所在省",customer.getProvince(),customerDb.getProvince(),recordContent);
recordContent =dealUpdate("所在市",customer.getCity(),customerDb.getCity(),recordContent);
recordContent =dealUpdate("描述",customer.getDescription(),customerDb.getDescription(),recordContent);
recordContent =dealUpdate("所属员工",uidNameMap.get(customer.getUserId()),uidNameMap.get(customerDb.getUserId()),recordContent);
if (CollectionUtils.isNotEmpty(definedFieldValues)){
Map fieldIdValueMap = new HashMap<>();
for (DefinedFieldValue fieldValue : definedFieldValues) {
fieldIdValueMap.put(fieldValue.getFieldId(),fieldValue.getFieldValue());
}
Map fieldIdValueMapDb = new HashMap<>();
List definedFieldValuesDb = definedFieldValueService.selectList(new EntityWrapper().
eq("corp_code", corpCode).eq("resource_id", id));
if (CollectionUtils.isNotEmpty(definedFieldValuesDb)){
for (DefinedFieldValue fieldValue : definedFieldValuesDb) {
fieldIdValueMapDb.put(fieldValue.getFieldId(),fieldValue.getFieldValue());
}
}
List definedFields = definedFieldService.selectList(new EntityWrapper().
eq("corp_code", corpCode).eq("biz_type", UkConstant.DEFINED_BIZ_TYPE_CRM));
List fieldOptions = definedFieldOptionService.selectList(new EntityWrapper().eq("corp_code", corpCode));
Map optionIdNameMap;
if (CollectionUtils.isEmpty(fieldOptions)){
optionIdNameMap = MapUtils.EMPTY_MAP;
}else {
optionIdNameMap = new HashMap<>(fieldOptions.size());
}
for (DefinedFieldOption option : fieldOptions) {
optionIdNameMap.put(option.getId(),option.getOptionName());
}
if (CollectionUtils.isNotEmpty(definedFields)){
for (DefinedField field : definedFields) {
String fieldName = field.getFieldName();
String fieldType = field.getFieldType();
String fieldId = field.getId();
String pre = fieldIdValueMapDb.get(fieldId);
if (pre == null){
pre = "";
}
String now = fieldIdValueMap.get(fieldId);
if (now == null){
now = "";
}
if (UkConstant.DEFINED_FIELD_TYPE_SELECT.equals(fieldType) ||
UkConstant.DEFINED_FIELD_TYPE_RADIO.equals(fieldType) ||
UkConstant.DEFINED_FIELD_TYPE_CHECKBOX.equals(fieldType)){
pre = optionIdNameMap.get(pre);
now = optionIdNameMap.get(now);
}
recordContent = dealUpdate(fieldName,now,pre,recordContent);
}
}
}
}else {
recordContent = "创建了客户";
}
return recordContent;
}
private String dealUpdate(String label,String now,String pre,String result){
String preStr = pre;
if (StringUtils.isEmpty(pre)){
pre = "";
preStr = "<空>";
}
String nowStr = now;
if (StringUtils.isEmpty(now)){
now = "";
nowStr = "<空>";
}
if (now.equals(pre)){
return result;
}
result += label + ":" + preStr + "-->" + nowStr+"";
return result;
}
@Override
public String importBatch(String url,String taskId) {
if (StringUtils.isEmpty(taskId)){
return "请选择外呼任务";
}
Task task = taskService.selectById(taskId);
if (task == null){
return "外呼任务不存在,Id为:"+taskId;
}
String filePath = tmpFilePath + url;
List customers = ExcelTemplateExportUtil.importExcel(filePath, 0, 1, Customer.class);
if (CollectionUtils.isEmpty(customers)){
return "Excel数据不能为空";
}
int totalCustomerCnt = customers.size();
if (totalCustomerCnt > 5000){
return "最多支持5000条,请分批导入";
}
//过滤掉空属性
List phones = new ArrayList<>();
List customersNotNull = new ArrayList<>();
String corpCode = ExecutionContext.getCorpCode();
String userId = ExecutionContext.getUserId();
for (Customer customer : customers) {
String phone = customer.getPhone();
if (StringUtils.isEmpty(customer.getName()) || StringUtils.isEmpty(phone)){
continue;
}
customersNotNull.add(customer);
phones.add(phone);
}
String result = "";
int notNullSize = customersNotNull.size();
int size = customers.size();
if (notNullSize < size){
result += "存在客户名称或手机号码为空:"+(size - notNullSize)+"条记录";
}
//过滤手机号重复的
List validCustomers = new ArrayList<>();
List repeatCustomers = this.selectList(new EntityWrapper().eq("corp_code", corpCode).in("phone", phones));
if (CollectionUtils.isNotEmpty(repeatCustomers)){
result += "
存在手机号码重复:"+(repeatCustomers.size())+"条记录";
List repeatPhones = new ArrayList<>(repeatCustomers.size());
for (Customer repeatCustomer : repeatCustomers) {
repeatPhones.add(repeatCustomer.getPhone());
}
for (Customer customer : customersNotNull) {
if (repeatPhones.contains(customer.getPhone())){
continue;
}
validCustomers.add(customer);
}
}else {
validCustomers = customersNotNull;
}
for (Customer validCustomer : validCustomers) {
validCustomer.setCustomerType(UkConstant.CUSTOMER_TYPE_INDIVIDUALITY);
validCustomer.setChannelType(UkConstant.CHANNEL_TYPE_CALL_OUT);
validCustomer.setCalloutTaskStatus(0);
validCustomer.setCalloutDistributeStatus(0);
validCustomer.setCalloutTaskId(taskId);
validCustomer.setCorpCode(corpCode);
validCustomer.setCreater(userId);
}
if (CollectionUtils.isEmpty(validCustomers)){
return "成功导入:"+ 0 +"条记录
"+result;
}
List> customersList = new ArrayList<>();
int total = validCustomers.size();
int threads = 5;
int oneSize = total/5 +1;
int start = 0;
int end = 0;
for (int i = 0; i transactionStatuses = Collections.synchronizedList(new ArrayList());
CountDownLatch latch= new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
int finalI = i;
ThreadPoolUtils.fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 事物隔离级别,开启新事务,这样会比较安全些。
TransactionStatus status = transactionManager.getTransaction(def); // 获得事务状态
transactionStatuses.add(status);
try{
improtInsertBath(corpCode,userId,customersList.get(finalI));
}catch(Exception e){
e.printStackTrace();
for (TransactionStatus transactionStatus:transactionStatuses) {
transactionStatus.setRollbackOnly();
}
}
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新外呼任务客户总数 customerCnt、未分配客户数 notDistributeCnt、未拨打数 not_call_cnt
task.setCustomerCnt(task.getCustomerCnt()+total);
task.setNotDistributeCnt(task.getNotDistributeCnt()+total);
task.setNotCallCnt(task.getNotCallCnt()+total);
taskService.updateById(task);
return "成功导入:"+ total +"条记录
"+result;
}
@Override
public Page callOutTask(Page page, Customer customer) {
if (StringUtils.isNotBlank(customer.getAssignTimeStart())){
String start = customer.getAssignTimeStart().substring(0,10)+" 00:00:00";
String end = customer.getAssignTimeStart().substring(13)+" 23:59:59";
customer.setAssignTimeStart(start);
customer.setAssignTimeEnd(end);
}else {
customer.setAssignTimeEnd(DateUtil.getConvertDateFormat("yyyy-MM-dd HH:mm:ss",new Date()));
}
if (StringUtils.isNotBlank(customer.getCalloutTimeStart())){
String start = customer.getCalloutTimeStart().substring(0,10)+" 00:00:00";
String end = customer.getCalloutTimeStart().substring(13)+" 23:59:59";
customer.setCalloutTimeStart(start);
customer.setCalloutTimeEnd(end);
}
List ids = new ArrayList<>();
if (StringUtils.isNotBlank(customer.getHasLabel())){
String[] ss = customer.getHasLabel().substring(1).split("\\,");
for (String id:ss) {
ids.add(id);
}
customer.setLabelIds(ids);
}
customer.setCorpCode(ExecutionContext.getCorpCode());
return page.setRecords(this.baseMapper.callOutTask(page, customer));
}
@Override
public Map getCallOutTaskStatistics(String userId,List taskIds) {
List customers = customerDao.getCallOutTaskStatistics(ExecutionContext.getCorpCode(), userId, taskIds);
if (CollectionUtils.isEmpty(customers)){
return MapUtils.EMPTY_MAP;
}
Map taskIdStatusCntMap = new HashMap<>(customers.size());
for (Customer customer : customers) {
taskIdStatusCntMap.put(customer.getCalloutTaskId()+"-"+customer.getCalloutTaskStatus(),customer.getCalloutTaskStatusCnt());
}
return taskIdStatusCntMap;
}
@Override
public Page findPageForProcess(Page page, Customer customer) {
List records = customerDao.findPageForProcess(page, customer);
return page.setRecords(records);
}
@Transactional(rollbackFor = {MybatisPlusException.class})
@Override
public void improtInsertBath(String corpCode,String userId,List list){
insertBatch(list);//TODO 检查validCustomers,一个线程1K条数据导入
//处理客户轨迹记录
int validCustomerSize = list.size();
List customerRecords = new ArrayList<>(validCustomerSize);
for (Customer validCustomer : list) {
CustomerRecord customerRecord = new CustomerRecord(UkConstant.RECORD_TYPE_UPDATE,"导入了客户",null,null,null,validCustomer.getId());
customerRecord.setCreater(userId);
customerRecord.setCorpCode(corpCode);
customerRecords.add(customerRecord);
}
boolean result = customerRecordService.insertBatch(customerRecords);
}
}
以上在测试环境,数据插入ok,上生产
数据无法插入数据库,可能原因 事务未提交 手动提交
代码如下
package com.ps.uzkefu.apps.crm.service.impl;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.ps.uzkefu.apps.callout.entity.Task;
import com.ps.uzkefu.apps.callout.service.TaskService;
import com.ps.uzkefu.apps.crm.entity.Customer;
import com.ps.uzkefu.apps.crm.entity.CustomerRecord;
import com.ps.uzkefu.apps.crm.mapper.CustomerDao;
import com.ps.uzkefu.apps.crm.service.CustomerRecordService;
import com.ps.uzkefu.apps.crm.service.CustomerService;
import com.ps.uzkefu.apps.oms.account.entity.DefinedField;
import com.ps.uzkefu.apps.oms.account.entity.DefinedFieldOption;
import com.ps.uzkefu.apps.oms.account.entity.DefinedFieldValue;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldOptionService;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldService;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldValueService;
import com.ps.uzkefu.apps.oms.account.service.UserService;
import com.ps.uzkefu.base.BaseServiceImpl;
import com.ps.uzkefu.common.ExecutionContext;
import com.ps.uzkefu.common.UkConstant;
import com.ps.uzkefu.util.DateUtil;
import com.ps.uzkefu.util.ExcelTemplateExportUtil;
import com.ps.uzkefu.util.ThreadPoolUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.BatchExecutorException;
import org.apache.ibatis.transaction.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.sql.BatchUpdateException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
/**
*
* 客户 服务实现类
*
*
* @author WuZhiWei
* @since 2018-07-06
*/
@Transactional(rollbackFor = {Exception.class})
@Service
public class CustomerServiceImpl extends BaseServiceImpl implements CustomerService {
@Autowired
private DefinedFieldValueService definedFieldValueService;
@Autowired
private DefinedFieldService definedFieldService;
@Autowired
private DefinedFieldOptionService definedFieldOptionService;
@Autowired
private CustomerRecordService customerRecordService;
@Autowired
private UserService userService;
@Autowired
private TaskService taskService;
@Autowired
private CustomerDao customerDao;
@Autowired
private PlatformTransactionManager transactionManager;
@Value("${tmp.file.path}")
private String tmpFilePath;
@Override
public String saveOrUpdate(Customer customer) {
String id = customer.getId();
String phone = customer.getPhone();
String corpCode = ExecutionContext.getCorpCode();
customer.setCorpCode(corpCode);
if (StringUtils.isEmpty(id)){
Customer selectOne = this.selectOne(new EntityWrapper().
eq("corp_code", corpCode).eq("phone", phone));
if (selectOne != null){
return "联系号码已存在";
}
}
Customer selectOne = this.selectOne(new EntityWrapper().
eq("corp_code", corpCode).eq("phone", phone)
.where("id !={0}",id));
if (selectOne != null){
return "联系号码已存在";
}
if (StringUtils.isEmpty(customer.getCorpName())){
customer.setCustomerType(UkConstant.CUSTOMER_TYPE_INDIVIDUALITY);
}else {
customer.setCustomerType(UkConstant.CUSTOMER_TYPE_CORP);
}
if (StringUtils.isEmpty(customer.getUserId())){
customer.setUserId(ExecutionContext.getUserId());
}
//自定义属性
List definedFieldValues = customer.getDefinedFieldValues();
//变更记录
String updateRecord = getUpdateRecord(customer, id, corpCode, definedFieldValues);
customer.setAssignDate(new Date());
this.insertOrUpdate(customer);
if (StringUtils.isEmpty(id)){
id = customer.getId();
}
if (StringUtils.isNotEmpty(updateRecord)){
CustomerRecord customerRecord = new CustomerRecord(UkConstant.RECORD_TYPE_UPDATE,updateRecord,null,null,null,id);
customerRecordService.insert(customerRecord);
}
//自定义保存或更新处理
if (CollectionUtils.isNotEmpty(definedFieldValues)){
for (DefinedFieldValue fieldValue : definedFieldValues) {
fieldValue.setResourceId(id);
}
definedFieldValueService.delete(new EntityWrapper().eq("resource_id",id));
definedFieldValueService.insertBatch(definedFieldValues);
}
return "";
}
private String getUpdateRecord(Customer customer, String id, String corpCode, List definedFieldValues) {
String recordContent = "";
if (StringUtils.isNotEmpty(id)){
Customer customerDb = this.selectById(id);
Map uidNameMap = userService.getAllUidNameMap();
recordContent = dealUpdate("客户姓名",customer.getName(),customerDb.getName(),recordContent);
recordContent = dealUpdate("公司名称",customer.getCorpName(),customerDb.getCorpName(),recordContent);
if (StringUtils.isNotEmpty(customer.getPhone())){
recordContent =dealUpdate("联系号码",customer.getPhone(),customerDb.getPhone(),recordContent);
}
recordContent =dealUpdate("联系邮箱",customer.getEmail(),customerDb.getEmail(),recordContent);
recordContent =dealUpdate("微信",customer.getWx(),customerDb.getWx(),recordContent);
recordContent =dealUpdate("QQ",customer.getQq(),customerDb.getQq(),recordContent);
recordContent =dealUpdate("所在省",customer.getProvince(),customerDb.getProvince(),recordContent);
recordContent =dealUpdate("所在市",customer.getCity(),customerDb.getCity(),recordContent);
recordContent =dealUpdate("描述",customer.getDescription(),customerDb.getDescription(),recordContent);
recordContent =dealUpdate("所属员工",uidNameMap.get(customer.getUserId()),uidNameMap.get(customerDb.getUserId()),recordContent);
if (CollectionUtils.isNotEmpty(definedFieldValues)){
Map fieldIdValueMap = new HashMap<>();
for (DefinedFieldValue fieldValue : definedFieldValues) {
fieldIdValueMap.put(fieldValue.getFieldId(),fieldValue.getFieldValue());
}
Map fieldIdValueMapDb = new HashMap<>();
List definedFieldValuesDb = definedFieldValueService.selectList(new EntityWrapper().
eq("corp_code", corpCode).eq("resource_id", id));
if (CollectionUtils.isNotEmpty(definedFieldValuesDb)){
for (DefinedFieldValue fieldValue : definedFieldValuesDb) {
fieldIdValueMapDb.put(fieldValue.getFieldId(),fieldValue.getFieldValue());
}
}
List definedFields = definedFieldService.selectList(new EntityWrapper().
eq("corp_code", corpCode).eq("biz_type", UkConstant.DEFINED_BIZ_TYPE_CRM));
List fieldOptions = definedFieldOptionService.selectList(new EntityWrapper().eq("corp_code", corpCode));
Map optionIdNameMap;
if (CollectionUtils.isEmpty(fieldOptions)){
optionIdNameMap = MapUtils.EMPTY_MAP;
}else {
optionIdNameMap = new HashMap<>(fieldOptions.size());
}
for (DefinedFieldOption option : fieldOptions) {
optionIdNameMap.put(option.getId(),option.getOptionName());
}
if (CollectionUtils.isNotEmpty(definedFields)){
for (DefinedField field : definedFields) {
String fieldName = field.getFieldName();
String fieldType = field.getFieldType();
String fieldId = field.getId();
String pre = fieldIdValueMapDb.get(fieldId);
if (pre == null){
pre = "";
}
String now = fieldIdValueMap.get(fieldId);
if (now == null){
now = "";
}
if (UkConstant.DEFINED_FIELD_TYPE_SELECT.equals(fieldType) ||
UkConstant.DEFINED_FIELD_TYPE_RADIO.equals(fieldType) ||
UkConstant.DEFINED_FIELD_TYPE_CHECKBOX.equals(fieldType)){
pre = optionIdNameMap.get(pre);
now = optionIdNameMap.get(now);
}
recordContent = dealUpdate(fieldName,now,pre,recordContent);
}
}
}
}else {
recordContent = "创建了客户";
}
return recordContent;
}
private String dealUpdate(String label,String now,String pre,String result){
String preStr = pre;
if (StringUtils.isEmpty(pre)){
pre = "";
preStr = "<空>";
}
String nowStr = now;
if (StringUtils.isEmpty(now)){
now = "";
nowStr = "<空>";
}
if (now.equals(pre)){
return result;
}
result += label + ":" + preStr + "-->" + nowStr+"";
return result;
}
@Override
public String importBatch(String url,String taskId) {
if (StringUtils.isEmpty(taskId)){
return "请选择外呼任务";
}
Task task = taskService.selectById(taskId);
if (task == null){
return "外呼任务不存在,Id为:"+taskId;
}
String filePath = tmpFilePath + url;
List customers = ExcelTemplateExportUtil.importExcel(filePath, 0, 1, Customer.class);
if (CollectionUtils.isEmpty(customers)){
return "Excel数据不能为空";
}
int totalCustomerCnt = customers.size();
if (totalCustomerCnt > 5000){
return "最多支持5000条,请分批导入";
}
//过滤掉空属性
List phones = new ArrayList<>();
List customersNotNull = new ArrayList<>();
String corpCode = ExecutionContext.getCorpCode();
String userId = ExecutionContext.getUserId();
for (Customer customer : customers) {
String phone = customer.getPhone();
if (StringUtils.isEmpty(customer.getName()) || StringUtils.isEmpty(phone)){
continue;
}
customersNotNull.add(customer);
phones.add(phone);
}
String result = "";
int notNullSize = customersNotNull.size();
int size = customers.size();
if (notNullSize < size){
result += "存在客户名称或手机号码为空:"+(size - notNullSize)+"条记录";
}
//过滤手机号重复的
List validCustomers = new ArrayList<>();
List repeatCustomers = this.selectList(new EntityWrapper().eq("corp_code", corpCode).in("phone", phones));
if (CollectionUtils.isNotEmpty(repeatCustomers)){
result += "
存在手机号码重复:"+(repeatCustomers.size())+"条记录";
List repeatPhones = new ArrayList<>(repeatCustomers.size());
for (Customer repeatCustomer : repeatCustomers) {
repeatPhones.add(repeatCustomer.getPhone());
}
for (Customer customer : customersNotNull) {
if (repeatPhones.contains(customer.getPhone())){
continue;
}
validCustomers.add(customer);
}
}else {
validCustomers = customersNotNull;
}
for (Customer validCustomer : validCustomers) {
validCustomer.setCustomerType(UkConstant.CUSTOMER_TYPE_INDIVIDUALITY);
validCustomer.setChannelType(UkConstant.CHANNEL_TYPE_CALL_OUT);
validCustomer.setCalloutTaskStatus(0);
validCustomer.setCalloutDistributeStatus(0);
validCustomer.setCalloutTaskId(taskId);
validCustomer.setCorpCode(corpCode);
validCustomer.setCreater(userId);
}
if (CollectionUtils.isEmpty(validCustomers)){
return "成功导入:"+ 0 +"条记录
"+result;
}
List> customersList = new ArrayList<>();
int total = validCustomers.size();
int threads = 1;
if (total >=1000){
threads = 5;
}
int oneSize = total/threads +1;
int start = 0;
int end = 0;
for (int i = 0; i transactionStatuses = Collections.synchronizedList(new ArrayList());
CountDownLatch latch= new CountDownLatch(threads);
Map totalMap = new HashMap();
for (int i = 0; i < threads; i++) {
int finalI = i;
ThreadPoolUtils.fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 事物隔离级别,开启新事务,这样会比较安全些。
TransactionStatus status = transactionManager.getTransaction(def); // 获得事务状态
transactionStatuses.add(status);
try{
improtInsertBath(corpCode,userId,customersList.get(finalI));
totalMap.put("total"+finalI,customersList.get(finalI).size());
//提交事务
transactionManager.commit(status);
}catch(Exception e){
e.printStackTrace();
//回滚 事务 这样做一个线程出问题 只能回滚出问题的线程
transactionManager.rollback(status);
}
latch.countDown();
}
});
}
try {
// for (TransactionStatus transactionStatus:transactionStatuses) {
// transactionManager.commit(transactionStatus);
// }
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
int countTotal = 0;
for (Integer count: totalMap.values()) {
countTotal = countTotal+count;
}
//更新外呼任务客户总数 customerCnt、未分配客户数 notDistributeCnt、未拨打数 not_call_cnt
task.setCustomerCnt(task.getCustomerCnt()+countTotal);
task.setNotDistributeCnt(task.getNotDistributeCnt()+countTotal);
task.setNotCallCnt(task.getNotCallCnt()+countTotal);
taskService.updateById(task);
return "成功导入:"+ countTotal +"条记录
"+result;
}
@Override
public Page callOutTask(Page page, Customer customer) {
if (StringUtils.isNotBlank(customer.getAssignTimeStart())){
String start = customer.getAssignTimeStart().substring(0,10)+" 00:00:00";
String end = customer.getAssignTimeStart().substring(13)+" 23:59:59";
customer.setAssignTimeStart(start);
customer.setAssignTimeEnd(end);
}else {
customer.setAssignTimeEnd(DateUtil.getConvertDateFormat("yyyy-MM-dd HH:mm:ss",new Date()));
}
if (StringUtils.isNotBlank(customer.getCalloutTimeStart())){
String start = customer.getCalloutTimeStart().substring(0,10)+" 00:00:00";
String end = customer.getCalloutTimeStart().substring(13)+" 23:59:59";
customer.setCalloutTimeStart(start);
customer.setCalloutTimeEnd(end);
}
List ids = new ArrayList<>();
if (StringUtils.isNotBlank(customer.getHasLabel())){
String[] ss = customer.getHasLabel().substring(1).split("\\,");
for (String id:ss) {
ids.add(id);
}
customer.setLabelIds(ids);
}
customer.setCorpCode(ExecutionContext.getCorpCode());
return page.setRecords(this.baseMapper.callOutTask(page, customer));
}
@Override
public Map getCallOutTaskStatistics(String userId,List taskIds) {
List customers = customerDao.getCallOutTaskStatistics(ExecutionContext.getCorpCode(), userId, taskIds);
if (CollectionUtils.isEmpty(customers)){
return MapUtils.EMPTY_MAP;
}
Map taskIdStatusCntMap = new HashMap<>(customers.size());
for (Customer customer : customers) {
taskIdStatusCntMap.put(customer.getCalloutTaskId()+"-"+customer.getCalloutTaskStatus(),customer.getCalloutTaskStatusCnt());
}
return taskIdStatusCntMap;
}
@Override
public Page findPageForProcess(Page page, Customer customer) {
List records = customerDao.findPageForProcess(page, customer);
return page.setRecords(records);
}
@Transactional(rollbackFor = {MybatisPlusException.class})
@Override
public void improtInsertBath(String corpCode,String userId,List list){
insertBatch(list);//TODO 检查validCustomers,一个线程1K条数据导入
//处理客户轨迹记录
int validCustomerSize = list.size();
List customerRecords = new ArrayList<>(validCustomerSize);
for (Customer validCustomer : list) {
CustomerRecord customerRecord = new CustomerRecord(UkConstant.RECORD_TYPE_UPDATE,"导入了客户",null,null,null,validCustomer.getId());
customerRecord.setCreater(userId);
customerRecord.setCorpCode(corpCode);
customerRecords.add(customerRecord);
}
boolean result = customerRecordService.insertBatch(customerRecords);
}
}