@@ -3,6 +3,7 @@ package com.rj.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper ;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page ;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl ;
import com.rj.dto.LbBuyerTradeStats ;
import com.rj.entity.LbDailyUserTrade ;
import com.rj.entity.LbDepartmentUser ;
import com.rj.entity.LbOrderRow ;
@@ -492,27 +493,7 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
}
}
Set < String > tradeActive UserIds = new HashSet < > ( ) ;
Map < String , LbOrderRow > latestOrderByBuyerId = new HashMap < > ( ) ;
List < LbOrderRow > orderRows = lbOrderRowMapper . selectList (
new LambdaQueryWrapper < LbOrderRow > ( )
. eq ( LbOrderRow : : getTenantId , tenantIdTrim )
. eq ( LbOrderRow : : getDataType , ORDER_DATA_TYPE_DETAIL )
. isNotNull ( LbOrderRow : : getBuyerId )
. isNotNull ( LbOrderRow : : getPayTime ) ) ;
if ( orderRows ! = null ) {
for ( LbOrderRow row : orderRows ) {
if ( ! isPaidOrderRow ( row ) ) {
continue ;
}
String buyerUserId = String . valueOf ( row . getBuyerId ( ) ) ;
tradeActiveUserIds . add ( buyerUserId ) ;
LbOrderRow prev = latestOrderByBuyerId . get ( buyerUserId ) ;
if ( prev = = null | | isOrderPayTimeNewer ( row , prev ) ) {
latestOrderByBuyerId . put ( buyerUserId , row ) ;
}
}
}
Map < String , LbBuyerTradeStats > tradeStatsBy UserId = loadBuyerTradeStatsFromOrderRow ( tenantIdTrim ) ;
List < LbDepartmentUser > toInsert = new ArrayList < > ( ) ;
List < LbDepartmentUser > toUpdate = new ArrayList < > ( ) ;
@@ -556,16 +537,17 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
entity . setOrgChain ( buildOrgChain ( lbUser . getId ( ) , userById ) ) ;
LbOrderRow latestOrder = latestOrderByBuy erId . get ( userId ) ;
if ( latestOrder ! = null ) {
entity . setLastTradeDate ( parseOrderTimeToDate ( latestOrder . getPayTime ( ) ) ) ;
entity . setLastBuyAmt ( latestOrder . getTotalMoney ( ) ) ;
LbBuyerTradeStats tradeStats = tradeStatsByUs erId . get ( userId ) ;
if ( tradeStats ! = null ) {
entity . setLastTradeDate ( parseOrderTimeToDate ( tradeStats . getPayTime ( ) ) ) ;
entity . setLastBuyAmt ( tradeStats . getTotalMoney ( ) ) ;
entity . setIsActive ( 1 ) ;
} else {
entity . setLastTradeDate ( null ) ;
entity . setLastBuyAmt ( null ) ;
entity . setIsActive ( 0 ) ;
}
entity . setUpdateTime ( now ) ;
entity . setIsActive ( tradeActiveUserIds . contains ( userId ) ? 1 : 0 ) ;
if ( isInsert ) {
toInsert . add ( entity ) ;
@@ -654,7 +636,7 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
. replace ( ORG_CHAIN_NODE_SEPARATOR , " " ) ;
}
private String resolveDisplayName ( LbUser user ) {
private static String resolveDisplayName ( LbUser user ) {
if ( user . getNickname ( ) ! = null & & ! user . getNickname ( ) . trim ( ) . isEmpty ( ) ) {
return user . getNickname ( ) . trim ( ) ;
}
@@ -675,19 +657,9 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
}
String tenantIdTrim = tenantId . trim ( ) ;
LambdaQueryWrapper < LbDepartment User > queryWrapp er = new LambdaQueryWrapper < LbDepartmentUser > ( )
. eq ( LbDepartment User : : getTenantId , tenantIdTrim ) ;
if ( isActive ! = null ) {
queryWrapper . eq ( LbDepartmentUser : : getIsActive , isActive ) ;
}
if ( joinDate ! = null ) {
queryWrapper . gt ( LbDepartmentUser : : getJoinDate , joinDate ) ;
}
if ( lastTradeDate ! = null ) {
queryWrapper . gt ( LbDepartmentUser : : getLastTradeDate , lastTradeDate ) ;
}
List < LbDepartmentUser > deptUsers = this . list ( queryWrapper ) ;
if ( deptUsers = = null | | deptUsers . isEmpty ( ) ) {
List < Lb User > lbUs ers = lbUserMapper . selectList (
new LambdaQueryWrapper < LbUser > ( ) . eq ( LbUser : : getTenantId , tenantIdTrim ) ) ;
if ( lbUsers = = null | | lbUsers . isEmpty ( ) ) {
result . put ( " success " , true ) ;
result . put ( " message " , " 查询成功 " ) ;
result . put ( " data " , new ArrayList < > ( ) ) ;
@@ -696,40 +668,67 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
return result ;
}
Map < String , LbDepartment User > userRecordMap = new LinkedHashMap < > ( ) ;
for ( LbDepartment User row : dept Users) {
String uid = trimToNull ( row . getUser Id ( ) ) ;
if ( uid = = null ) {
Map < String , LbUser > userById = new LinkedHashMap < > ( ) ;
for ( LbUser lbUser : lb Users) {
if ( lbUser . getId ( ) ! = null ) {
userById . put ( String . valueOf ( lbUser . getId ( ) ) , lbUser ) ;
}
}
Map < String , LbBuyerTradeStats > tradeStatsByUserId = loadBuyerTradeStatsFromOrderRow ( tenantIdTrim ) ;
boolean hasFilter = isActive ! = null | | joinDate ! = null | | lastTradeDate ! = null ;
Set < String > matchedUserIds = new HashSet < > ( ) ;
Map < String , LocalDate > joinDateByUserId = new HashMap < > ( ) ;
Map < String , LocalDate > lastTradeDateByUserId = new HashMap < > ( ) ;
Map < String , BigDecimal > lastBuyAmtByUserId = new HashMap < > ( ) ;
Map < String , Integer > isActiveByUserId = new HashMap < > ( ) ;
for ( LbUser lbUser : lbUsers ) {
if ( lbUser . getId ( ) = = null ) {
continue ;
}
LbDepartmentUser existing = userRecordMap . get ( uid ) ;
if ( existing = = null | | isNewerDepartmentUser ( row , existing ) ) {
userRecordMap . put ( uid , row ) ;
String userId = String . valueOf ( lbUser . getId ( ) ) ;
LocalDate userJoinDate = lbUser . getJoinTime ( ) ! = null
? lbUser . getJoinTime ( ) . toLocalDate ( ) : null ;
LbBuyerTradeStats tradeStats = tradeStatsByUserId . get ( userId ) ;
LocalDate userLastTradeDate = tradeStats = = null
? null : parseOrderTimeToDate ( tradeStats . getPayTime ( ) ) ;
BigDecimal userLastBuyAmt = tradeStats = = null ? null : tradeStats . getTotalMoney ( ) ;
int userIsActive = tradeStats = = null ? 0 : 1 ;
joinDateByUserId . put ( userId , userJoinDate ) ;
lastTradeDateByUserId . put ( userId , userLastTradeDate ) ;
lastBuyAmtByUserId . put ( userId , userLastBuyAmt ) ;
isActiveByUserId . put ( userId , userIsActive ) ;
if ( ! hasFilter | | matchesTreeFilter (
userIsActive , userJoinDate , userLastTradeDate , isActive , joinDate , lastTradeDate ) ) {
matchedUserIds . add ( userId ) ;
}
}
Map < String , String > userIdByRecord Id = new HashMap < > ( ) ;
Set < String > allUserIds = new HashSet < > ( userRecordMap . keySet ( ) ) ;
for ( LbDepartmentUser row : deptUsers ) {
String rowId = trimToNull ( row . getId ( ) ) ;
String uid = trimToNull ( row . getUserId ( ) ) ;
if ( rowId ! = null & & uid ! = null ) {
userIdByRecordId . put ( rowId , uid ) ;
}
}
Map < String , String > parentByUserId = new HashMap < > ( ) ;
for ( LbDepartmentUser row : userRecordMap . values ( ) ) {
String uid = row . getUserId ( ) . trim ( ) ;
String parentUserId = resolveParentUserId ( row , allUserIds , userIdByRecordId ) ;
if ( parentUserId ! = null ) {
parentByUserId . put ( uid , parentUserId ) ;
}
}
Set < String > visibleUser Ids = hasFilter
? includeAncestorUserIds ( matchedUserIds , userById )
: new HashSet < > ( userById . keySet ( ) ) ;
Map < String , Map < String , Object > > nodeByUserId = new LinkedHashMap < > ( ) ;
for ( LbDe partm entUser row : userRecordMap . values ( ) ) {
nodeByUserId . put ( row . getUserId ( ) . trim ( ) , toDepartmentUserTreeNode ( row ) ) ;
Map < String , String > parentBy UserId = new HashMap < > ( ) ;
for ( String userId : visibleUserIds ) {
LbUser lbUser = userById . get ( userId ) ;
if ( lbUser = = null ) {
continue ;
}
nodeByUserId . put ( userId , toLbUserTreeNode (
lbUser ,
joinDateByUserId . get ( userId ) ,
lastTradeDateByUserId . get ( userId ) ,
lastBuyAmtByUserId . get ( userId ) ,
isActiveByUserId . getOrDefault ( userId , 0 ) ) ) ;
String parentUserId = resolveLbUserParentId ( lbUser , userById ) ;
if ( parentUserId ! = null & & visibleUserIds . contains ( parentUserId ) ) {
parentByUserId . put ( userId , parentUserId ) ;
}
}
List < Map < String , Object > > treeRoots = new ArrayList < > ( ) ;
@@ -747,11 +746,12 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
children . add ( node ) ;
}
sortDepartmentUserTreeByName ( treeRoots ) ;
List < Map < String , Object > > treeData = skipFirstTreeLevel ( treeRoots ) ;
result . put ( " success " , true ) ;
result . put ( " message " , " 查询成功 " ) ;
result . put ( " data " , treeRoots ) ;
result . put ( " total " , nodeByUserId . size ( ) ) ;
result . put ( " data " , treeData ) ;
result . put ( " total " , countTreeNodes ( treeData ) ) ;
result . put ( " tenantId " , tenantIdTrim ) ;
return result ;
} catch ( Exception e ) {
@@ -762,46 +762,122 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
}
/**
* parent_id 兼容两种存法:父 user_id, 或父记录 id(UUID) 。
* 去掉第 1 级根节点,将其 children 提升为返回给前端的顶层 。
*/
private static String resolveParentUserId ( LbDepartmentUser row ,
Set < String > allUserIds ,
Map < String , String > userIdByRecordId ) {
String parentRef = trimToNull ( row . getParentId ( ) ) ;
if ( parentRef = = null ) {
return null ;
private static List < Map < String , Object > > skipFirstTreeLevel ( List < Map < String , Object > > treeRoots ) {
List < Map < String , Object > > result = new ArrayList < > ( ) ;
for ( Map < String , Object > root : treeRoots ) {
@SuppressWarnings ( " unchecked " )
List < Map < String , Object > > children = ( List < Map < String , Object > > ) root . get ( " children " ) ;
if ( children ! = null & & ! children . isEmpty ( ) ) {
result . addAll ( children ) ;
}
}
if ( allUserIds . contains ( parentRef ) ) {
return parentRef ;
}
return userIdByRecordId . get ( parentRef ) ;
sortDepartmentUserTreeByName ( result ) ;
return result ;
}
private static Map < String , Object > toDepartmentUserTreeNode ( LbDepartmentUser row ) {
Map < String , Object > node = new LinkedHashMap < > ( ) ;
node . put ( " userId " , trimToNull ( row . getUserId ( ) ) ) ;
node . put ( " name " , trimToNull ( row . getName ( ) ) ) ;
node . put ( " phone " , trimToNull ( row . getPhone ( ) ) ) ;
node . put ( " joinDate " , row . getJoinDate ( ) ) ;
node . put ( " lastTradeDate " , row . getLastTradeDate ( ) ) ;
node . put ( " lastBuyAmt " , row . getLastBuyAmt ( ) ) ;
node . put ( " isActive " , row . getIsActive ( ) ) ;
node . put ( " children " , new ArrayList < Map < String , Object > > ( ) ) ;
return node ;
private static int countTreeNodes ( List < Map < String , Object > > nodes ) {
int count = 0 ;
for ( Map < String , Object > node : nodes ) {
count + + ;
@SuppressWarnings ( " unchecked " )
List < Map < String , Object > > children = ( List < Map < String , Object > > ) node . get( " children " ) ;
if ( children ! = null & & ! children . isEmpty ( ) ) {
count + = countTreeNodes ( children ) ;
}
}
return count ;
}
private static boolean isNewerDepartmentUser ( LbDepartmentUser candidate , LbDepartmentUser existing ) {
LocalDateTime candidateTime = candidate . getUpdateTime ( ) ! = null
? candidate . getUpdateTime ( ) : candidate . getCreateTime ( ) ;
LocalDateTime existingTime = existing . getUpdateTime ( ) ! = null
? existing . getUpdateTime ( ) : existing . getCreateTime ( ) ;
if ( candidateTime = = null ) {
private static boolean matchesTreeFilter ( int userIsActive ,
LocalDate userJoinDate ,
LocalDate userLastTradeDate ,
Integer isActive ,
LocalDate joinDate ,
LocalDate lastTradeDate ) {
if ( isActive ! = null & & userIsActive ! = isActive ) {
return false ;
}
if ( existingTime = = null ) {
return tru e;
if ( joinDate ! = null & & ( userJoinDate = = null | | ! userJoinDate . isAfter ( joinDate ) ) ) {
return fals e;
}
return candidateTime . isAfter ( existingTime ) ;
return lastTradeDate = = null
| | ( userLastTradeDate ! = null & & userLastTradeDate . isAfter ( lastTradeDate ) ) ;
}
/**
* 筛选命中用户时,沿 lb_user.pid 向上补全祖先,避免子节点因上级被筛掉而挂错层级。
*/
private static Set < String > includeAncestorUserIds ( Set < String > userIds , Map < String , LbUser > userById ) {
Set < String > expanded = new HashSet < > ( userIds ) ;
for ( String userId : userIds ) {
LbUser current = userById . get ( userId ) ;
Set < Long > visited = new HashSet < > ( ) ;
while ( current ! = null ) {
Long pid = current . getPid ( ) ;
if ( pid = = null | | pid < = 0 | | ! visited . add ( pid ) ) {
break ;
}
String parentUserId = String . valueOf ( pid ) ;
if ( ! userById . containsKey ( parentUserId ) ) {
break ;
}
expanded . add ( parentUserId ) ;
current = userById . get ( parentUserId ) ;
}
}
return expanded ;
}
/**
* 从 lb_user.pid 解析上级 userId; pid 必须在同租户用户索引中存在。
*/
private static String resolveLbUserParentId ( LbUser user , Map < String , LbUser > userById ) {
if ( user = = null | | user . getPid ( ) = = null | | user . getPid ( ) < = 0 ) {
return null ;
}
String parentUserId = String . valueOf ( user . getPid ( ) ) ;
return userById . containsKey ( parentUserId ) ? parentUserId : null ;
}
/**
* 一次 SQL 从 lb_order_row 汇总每位买家的最新已支付订单统计。
*/
private Map < String , LbBuyerTradeStats > loadBuyerTradeStatsFromOrderRow ( String tenantId ) {
List < LbBuyerTradeStats > rows = lbOrderRowMapper . selectLatestPaidStatsByTenantId (
tenantId , ORDER_DATA_TYPE_DETAIL ) ;
Map < String , LbBuyerTradeStats > statsByUserId = new HashMap < > ( ) ;
if ( rows = = null | | rows . isEmpty ( ) ) {
return statsByUserId ;
}
for ( LbBuyerTradeStats row : rows ) {
if ( row . getBuyerId ( ) = = null ) {
continue ;
}
statsByUserId . put ( String . valueOf ( row . getBuyerId ( ) ) , row ) ;
}
return statsByUserId ;
}
private static Map < String , Object > toLbUserTreeNode ( LbUser user ,
LocalDate joinDate ,
LocalDate lastTradeDate ,
BigDecimal lastBuyAmt ,
int isActive ) {
Map < String , Object > node = new LinkedHashMap < > ( ) ;
node . put ( " userId " , user . getId ( ) = = null ? null : String . valueOf ( user . getId ( ) ) ) ;
node . put ( " name " , trimToNull ( resolveDisplayName ( user ) ) ) ;
node . put ( " phone " , trimToNull ( user . getMobile ( ) ) ) ;
Long pid = user . getPid ( ) ;
node . put ( " parentId " , pid = = null | | pid < = 0 ? null : String . valueOf ( pid ) ) ;
node . put ( " parentName " , trimToNull ( user . getPname ( ) ) ) ;
node . put ( " joinDate " , joinDate ) ;
node . put ( " lastTradeDate " , lastTradeDate ) ;
node . put ( " lastBuyAmt " , lastBuyAmt ) ;
node . put ( " isActive " , isActive ) ;
node . put ( " children " , new ArrayList < Map < String , Object > > ( ) ) ;
return node ;
}
private static void sortDepartmentUserTreeByName ( List < Map < String , Object > > nodes ) {