@@ -0,0 +1,357 @@
package com.ruoyi.jarvis.service.impl ;
import com.ruoyi.jarvis.domain.OrderRows ;
import com.ruoyi.jarvis.domain.JDOrder ;
import com.ruoyi.jarvis.service.IInstructionService ;
import com.ruoyi.jarvis.service.IOrderRowsService ;
import com.ruoyi.jarvis.service.IJDOrderService ;
import com.ruoyi.jarvis.service.SuperAdminService ;
import org.springframework.stereotype.Service ;
import javax.annotation.Resource ;
import java.time.LocalDate ;
import java.time.LocalDateTime ;
import java.time.LocalTime ;
import java.time.ZoneId ;
import java.util.* ;
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
import java.util.stream.Collectors ;
/**
* 将 jd 项目中 JDUtil 的指令能力,抽取为无微信依赖的本地执行版。
* 仅实现最常用统计/订单查询与转链占位能力,按需扩展。
*/
@Service
public class InstructionServiceImpl implements IInstructionService {
@Resource
private IOrderRowsService orderRowsService ;
@Resource
private IJDOrderService jdOrderService ;
@Resource
private SuperAdminService superAdminService ;
@Override
public String execute ( String command ) {
if ( command = = null | | command . trim ( ) . isEmpty ( ) ) {
return " 请输入指令 " ;
}
String input = command . trim ( ) ;
// 一级命令分流:京系(统计/订单)、录单/慢单、转链/礼金…
if ( input . startsWith ( " 京 " ) | | menuKeywords ( ) . contains ( input ) ) {
return handleJingFen ( input . replaceFirst ( " ^京 " , " " ) ) ;
}
if ( input . startsWith ( " 录单 " ) | | input . startsWith ( " 慢单 " ) | | input . startsWith ( " 慢搜 " ) | | input . startsWith ( " 慢查 " ) | | input . startsWith ( " 单 " ) ) {
return handleRecordLike ( input ) ;
}
if ( input . startsWith ( " 转链 " ) | | input . startsWith ( " 文案 " ) | | input . startsWith ( " 转 " ) ) {
return handleTransferLike ( input ) ;
}
return helpText ( ) ;
}
private Set < String > menuKeywords ( ) {
return new HashSet < > ( Arrays . asList ( " 菜单 " , " 今日统计 " , " 昨日统计 " , " 三日统计 " , " 七日统计 " , " 一个月统计 " , " 两个月统计 " , " 三个月统计 " , " 这个月统计 " , " 上个月统计 " , " 今日订单 " , " 昨日订单 " , " 七日订单 " , " 总统计 " ) ) ;
}
private String handleJingFen ( String cmd ) {
String action = cmd . trim ( ) ;
if ( action . isEmpty ( ) | | action . equals ( " 菜单 " ) ) {
return jingMenu ( ) ;
}
// 取出所有订单(排除被删除/无效:这里沿用 OrderRowsService 的常规查询,必要时可增加过滤参数)
List < OrderRows > all = orderRowsService . selectOrderRowsList ( new OrderRows ( ) ) ;
if ( all = = null ) all = Collections . emptyList ( ) ;
switch ( action ) {
case " 今日统计 " : return statsText ( filterByDays ( all , 0 ) , " 今日统计 " ) ;
case " 昨日统计 " : return statsText ( filterYesterday ( all ) , " 昨日统计 " ) ;
case " 三日统计 " : return statsText ( filterByRange ( all , 3 ) , " 三日统计 " ) ;
case " 七日统计 " : return statsText ( filterByRange ( all , 7 ) , " 七日统计 " ) ;
case " 一个月统计 " : return statsText ( filterByRange ( all , 30 ) , " 一个月统计 " ) ;
case " 两个月统计 " : return statsText ( filterByRange ( all , 60 ) , " 两个月统计 " ) ;
case " 三个月统计 " : return statsText ( filterByRange ( all , 90 ) , " 三个月统计 " ) ;
case " 这个月统计 " : return statsText ( filterThisMonth ( all ) , " 这个月统计 " ) ;
case " 上个月统计 " : return statsText ( filterLastMonth ( all ) , " 上个月统计 " ) ;
case " 总统计 " : return statsText ( all , " 总统计 " ) ;
case " 今日订单 " : return listOrders ( filterByDays ( all , 0 ) , " 今日订单 " ) ;
case " 昨日订单 " : return listOrders ( filterYesterday ( all ) , " 昨日订单 " ) ;
case " 七日订单 " : return listOrders ( filterByRange ( all , 7 ) , " 七日订单 " ) ;
default :
// 高级命令: 违规N、SKU、搜索、JF… 此处按需扩展
if ( action . startsWith ( " 高级 " ) ) {
return " 高级指令暂未开放 " ;
}
return jingMenu ( ) ;
}
}
private String handleRecordLike ( String input ) {
// 仅实现“慢搜/慢查 关键词”与“录单/慢单 日期范围”的只读输出,避免侵入写库
if ( input . startsWith ( " 慢搜 " ) | | input . startsWith ( " 慢查 " ) ) {
String kw = input . replaceFirst ( " ^慢搜|^慢查 " , " " ) . trim ( ) ;
if ( kw . isEmpty ( ) ) return " 请输入搜索关键词 " ;
List < JDOrder > list = jdOrderService . selectJDOrderList ( new JDOrder ( ) ) ;
if ( list = = null ) list = Collections . emptyList ( ) ;
String low = kw . toLowerCase ( Locale . ROOT ) ;
List < JDOrder > matched = list . stream ( ) . filter ( o - >
contains ( o . getRemark ( ) , low ) | | contains ( o . getOrderId ( ) , low ) | | contains ( o . getModelNumber ( ) , low ) | | contains ( o . getAddress ( ) , low ) | | contains ( o . getBuyer ( ) , low )
) . limit ( 50 ) . collect ( Collectors . toList ( ) ) ;
if ( matched . isEmpty ( ) ) return " 未找到匹配的订单 " ;
StringBuilder sb = new StringBuilder ( ) ;
int i = 0 ;
for ( JDOrder o : matched ) {
i + + ;
sb . append ( i ) . append ( " , 单: " ) . append ( nvl ( o . getRemark ( ) ) )
. append ( " \ n分销标记: " ) . append ( nvl ( o . getDistributionMark ( ) ) )
. append ( " \ n型号: " ) . append ( nvl ( o . getModelNumber ( ) ) )
. append ( " \ n链接: " ) . append ( nvl ( o . getLink ( ) ) )
. append ( " \ n下单付款: " ) . append ( nvl ( o . getPaymentAmount ( ) ) )
. append ( " \ n后返金额: " ) . append ( nvl ( o . getRebateAmount ( ) ) )
. append ( " \ n地址: " ) . append ( nvl ( o . getAddress ( ) ) )
. append ( " \ n物流链接: " ) . append ( nvl ( o . getLogisticsLink ( ) ) )
. append ( " \ n订单号: " ) . append ( nvl ( o . getOrderId ( ) ) )
. append ( " \ n下单人: " ) . append ( nvl ( o . getBuyer ( ) ) )
. append ( " \ n下单时间: " ) . append ( nvl ( o . getOrderTime ( ) ) )
. append ( " \ n备注: " ) . append ( nvl ( o . getStatus ( ) ) )
. append ( " \ n━━━━━━━━━━━━ \ n " ) ;
}
return sb . toString ( ) ;
}
if ( input . startsWith ( " 录单 " ) | | input . startsWith ( " 慢单 " ) ) {
// 提取日期( yyyyMMdd 或 yyyyMMdd-yyyyMMdd 或 关键词:昨日/三日/七日)
List < LocalDate > range = parseDateRange ( input . replaceFirst ( " ^录单|^慢单 " , " " ) . trim ( ) ) ;
if ( range = = null ) {
LocalDate today = effectiveToday ( ) . toLocalDate ( ) ;
range = Arrays . asList ( today , today ) ;
}
Date start = Date . from ( range . get ( 0 ) . atStartOfDay ( ZoneId . systemDefault ( ) ) . toInstant ( ) ) ;
Date end = Date . from ( range . get ( 1 ) . atTime ( LocalTime . MAX ) . atZone ( ZoneId . systemDefault ( ) ) . toInstant ( ) ) ;
List < JDOrder > list = jdOrderService . selectJDOrderList ( new JDOrder ( ) ) ;
if ( list = = null ) list = Collections . emptyList ( ) ;
List < JDOrder > filtered = list . stream ( ) . filter ( o - > o . getOrderTime ( ) ! = null & & ! o . getOrderTime ( ) . before ( start ) & & ! o . getOrderTime ( ) . after ( end ) ) . collect ( Collectors . toList ( ) ) ;
if ( filtered . isEmpty ( ) ) return " 时间范围内没有订单 " ;
StringBuilder sb = new StringBuilder ( ) ;
for ( JDOrder o : filtered ) {
sb . append ( nvl ( o . getRemark ( ) ) ) . append ( '\t' )
. append ( nvl ( o . getOrderId ( ) ) ) . append ( '\t' )
. append ( fmt ( o . getOrderTime ( ) ) ) . append ( '\t' )
. append ( nvl ( o . getModelNumber ( ) ) ) . append ( '\t' )
. append ( nvl ( o . getAddress ( ) ) ) . append ( '\t' )
. append ( nvl ( o . getLogisticsLink ( ) ) ) . append ( '\t' )
. append ( '\t' ) . append ( nvl ( o . getBuyer ( ) ) ) . append ( '\t' )
. append ( nvl ( o . getPaymentAmount ( ) ) ) . append ( '\t' )
. append ( nvl ( o . getRebateAmount ( ) ) ) . append ( '\t' )
. append ( mapDistribution ( o . getDistributionMark ( ) ) ) . append ( '\t' )
. append ( nvl ( o . getStatus ( ) ) ) . append ( " \ n " ) ;
}
return sb . toString ( ) ;
}
// 单… 的解析/入库在 jd 工程内较复杂,这里仅返回模板校验结果
if ( input . startsWith ( " 单 " ) ) {
return " 录单模板校验暂未开放(仅支持 慢搜/慢查、录单/慢单 查询) " ;
}
return helpText ( ) ;
}
private String handleTransferLike ( String input ) {
// 前端仍复用已有 /jarvis/jdorder/generatePromotionContent 接口,这里仅提供一个回显占位
return " 指令入口:请在 \" 一键转链 \" 页面使用(输入文案/链接后点生成) " ;
}
// ===== 统计与列表渲染 =====
private String statsText ( List < OrderRows > rows , String title ) {
long total = rows . size ( ) ;
long paid = rows . stream ( ) . filter ( o - > Integer . valueOf ( 16 ) . equals ( o . getValidCode ( ) ) ) . count ( ) ;
long pending = rows . stream ( ) . filter ( o - > Integer . valueOf ( 15 ) . equals ( o . getValidCode ( ) ) ) . count ( ) ;
long completed = rows . stream ( ) . filter ( o - > Integer . valueOf ( 17 ) . equals ( o . getValidCode ( ) ) ) . count ( ) ;
long canceled = rows . stream ( ) . filter ( o - > o . getValidCode ( ) ! = null & & o . getValidCode ( ) ! = 16 & & o . getValidCode ( ) ! = 17 ) . count ( ) ;
long violation = rows . stream ( ) . filter ( o - > o . getValidCode ( ) ! = null & & ( o . getValidCode ( ) = = 13 | | o . getValidCode ( ) = = 25 | | o . getValidCode ( ) = = 26 | | o . getValidCode ( ) = = 27 | | o . getValidCode ( ) = = 28 | | o . getValidCode ( ) = = 29 ) ) . count ( ) ;
double paidCommission = rows . stream ( ) . filter ( o - > Integer . valueOf ( 16 ) . equals ( o . getValidCode ( ) ) & & o . getEstimateFee ( ) ! = null ) . mapToDouble ( OrderRows : : getEstimateFee ) . sum ( ) ;
double pendingCommission = rows . stream ( ) . filter ( o - > Integer . valueOf ( 15 ) . equals ( o . getValidCode ( ) ) & & o . getEstimateFee ( ) ! = null ) . mapToDouble ( OrderRows : : getEstimateFee ) . sum ( ) ;
double completedCommission = rows . stream ( ) . filter ( o - > Integer . valueOf ( 17 ) . equals ( o . getValidCode ( ) ) & & o . getEstimateFee ( ) ! = null ) . mapToDouble ( OrderRows : : getEstimateFee ) . sum ( ) ;
double violationCommission = rows . stream ( ) . filter ( o - > o . getValidCode ( ) ! = null & & ( o . getValidCode ( ) = = 13 | | o . getValidCode ( ) = = 25 | | o . getValidCode ( ) = = 26 | | o . getValidCode ( ) = = 27 | | o . getValidCode ( ) = = 28 | | o . getValidCode ( ) = = 29 ) & & o . getEstimateCosPrice ( ) ! = null & & o . getCommissionRate ( ) ! = null ) . mapToDouble ( o - > o . getEstimateCosPrice ( ) * o . getCommissionRate ( ) * 0 . 01 ) . sum ( ) ;
StringBuilder sb = new StringBuilder ( ) ;
sb . append ( " * " ) . append ( title ) . append ( " * \ n " )
. append ( " ━━━━━━━━━━━━ \ n " )
. append ( " 订单总数: " ) . append ( total ) . append ( " \ n " )
. append ( " 有效订单: " ) . append ( total - canceled ) . append ( " \ n " )
. append ( " 已取消: " ) . append ( canceled ) . append ( " \ n " )
. append ( " ──────────── \ n " )
. append ( " 已付款: " ) . append ( paid ) . append ( " \ n " )
. append ( " 已付款佣金: " ) . append ( fmt ( paidCommission ) ) . append ( " \ n " )
. append ( " ──────────── \ n " )
. append ( " 待付款: " ) . append ( pending ) . append ( " \ n " )
. append ( " 待付款佣金: " ) . append ( fmt ( pendingCommission ) ) . append ( " \ n " )
. append ( " ──────────── \ n " )
. append ( " 已完成: " ) . append ( completed ) . append ( " \ n " )
. append ( " 已完成佣金: " ) . append ( fmt ( completedCommission ) ) . append ( " \ n " )
. append ( " ──────────── \ n " )
. append ( " 违规订单: " ) . append ( violation ) . append ( " \ n " )
. append ( " 违规佣金: " ) . append ( fmt ( violationCommission ) ) . append ( " \ n " )
. append ( " ━━━━━━━━━━━━ " ) ;
return sb . toString ( ) ;
}
private String listOrders ( List < OrderRows > rows , String title ) {
if ( rows . isEmpty ( ) ) return title + " :无 " ;
StringBuilder sb = new StringBuilder ( ) ;
sb . append ( title ) . append ( " ( " ) . append ( rows . size ( ) ) . append ( " 条) \ n " ) ;
int i = 0 ;
for ( OrderRows r : rows ) {
i + + ;
sb . append ( i ) . append ( " . " ) . append ( Optional . ofNullable ( r . getSkuName ( ) ) . orElse ( " - " ) )
. append ( " | 订单号: " ) . append ( Optional . ofNullable ( r . getOrderId ( ) ) . map ( String : : valueOf ) . orElse ( " - " ) )
. append ( " | 时间: " ) . append ( fmt ( r . getOrderTime ( ) ) )
. append ( " | 有效: " ) . append ( r . getValidCode ( ) = = null ? " - " : r . getValidCode ( ) ) . append ( " \ n " ) ;
if ( i > = 100 ) break ; // 最多100条
}
return sb . toString ( ) ;
}
// ===== 过滤器 =====
private List < OrderRows > filterByDays ( List < OrderRows > list , int daysBack ) {
LocalDate now = LocalDate . now ( ) ;
return list . stream ( ) . filter ( o - > {
Date d = o . getOrderTime ( ) ;
if ( d = = null ) return false ;
LocalDate od = d . toInstant ( ) . atZone ( ZoneId . systemDefault ( ) ) . toLocalDate ( ) ;
if ( daysBack = = 0 ) return od . equals ( now ) ;
return ! od . isBefore ( now . minusDays ( daysBack ) ) & & ! od . isAfter ( now ) ;
} ) . collect ( Collectors . toList ( ) ) ;
}
private List < OrderRows > filterByRange ( List < OrderRows > list , int days ) {
return filterByDays ( list , days ) ;
}
private List < OrderRows > filterYesterday ( List < OrderRows > list ) {
LocalDate y = LocalDate . now ( ) . minusDays ( 1 ) ;
return list . stream ( ) . filter ( o - > {
Date d = o . getOrderTime ( ) ; if ( d = = null ) return false ;
LocalDate od = d . toInstant ( ) . atZone ( ZoneId . systemDefault ( ) ) . toLocalDate ( ) ;
return od . equals ( y ) ;
} ) . collect ( Collectors . toList ( ) ) ;
}
private List < OrderRows > filterThisMonth ( List < OrderRows > list ) {
LocalDate now = LocalDate . now ( ) ;
return list . stream ( ) . filter ( o - > {
Date d = o . getOrderTime ( ) ; if ( d = = null ) return false ;
LocalDate od = d . toInstant ( ) . atZone ( ZoneId . systemDefault ( ) ) . toLocalDate ( ) ;
return od . getYear ( ) = = now . getYear ( ) & & od . getMonth ( ) = = now . getMonth ( ) ;
} ) . collect ( Collectors . toList ( ) ) ;
}
private List < OrderRows > filterLastMonth ( List < OrderRows > list ) {
LocalDate now = LocalDate . now ( ) . minusMonths ( 1 ) ;
return list . stream ( ) . filter ( o - > {
Date d = o . getOrderTime ( ) ; if ( d = = null ) return false ;
LocalDate od = d . toInstant ( ) . atZone ( ZoneId . systemDefault ( ) ) . toLocalDate ( ) ;
return od . getYear ( ) = = now . getYear ( ) & & od . getMonth ( ) = = now . getMonth ( ) ;
} ) . collect ( Collectors . toList ( ) ) ;
}
// ===== 工具 =====
private String jingMenu ( ) {
StringBuilder content = new StringBuilder ( ) ;
content . append ( " 菜单:京+命令,如: 京今日统计 \ r \ n " )
. append ( " 今日统计 \ r \ n " )
. append ( " 昨日统计 \ r \ n " )
. append ( " 七日统计 \ r \ n " )
. append ( " 一个月统计 \ r \ n " )
. append ( " 两个月统计 \ r \ n " )
. append ( " 三个月统计 \ r \ n " )
. append ( " 总统计 \ r \ n " )
. append ( " 这个月统计 \ r \ n " )
. append ( " 上个月统计 \ r \ n " )
. append ( " \ r \ n " )
. append ( " 今日订单 \ r \ n " )
. append ( " 昨日订单 \ r \ n " )
. append ( " 七日订单 \ r \ n " ) ;
return content . toString ( ) ;
}
private String helpText ( ) {
return " 可用指令示例: \ n "
+ " 1) 京菜单(查看所有可用命令) \ n "
+ " 2) 京今日统计 / 京昨日统计 / 京七日统计 / 京一个月统计 等 \ n "
+ " 3) 京今日订单 / 京昨日订单 / 京七日订单 \ n "
+ " 4) 慢搜 关键词 / 慢查 关键词(从录单库模糊查询) \ n "
+ " 5) 录单20250101-20250107 或 录单昨日|三日|七日(仅查询导出) \ n "
+ " \ n提示: 转链与礼金请前往‘ 一键转链’ 页面使用。 " ;
}
private LocalDateTime effectiveToday ( ) {
LocalDateTime now = LocalDateTime . now ( ) ;
if ( now . getHour ( ) < 8 ) return now . minusDays ( 1 ) ;
return now ;
}
private List < LocalDate > parseDateRange ( String s ) {
if ( s = = null | | s . isEmpty ( ) ) return null ;
if ( " 昨日 " . equals ( s ) ) {
LocalDate y = LocalDate . now ( ) . minusDays ( 1 ) ;
return Arrays . asList ( y , y ) ;
}
if ( " 三日 " . equals ( s ) ) {
LocalDate end = LocalDate . now ( ) . minusDays ( 1 ) ;
LocalDate start = LocalDate . now ( ) . minusDays ( 3 ) ;
return Arrays . asList ( start , end ) ;
}
if ( " 七日 " . equals ( s ) ) {
LocalDate end = LocalDate . now ( ) . minusDays ( 1 ) ;
LocalDate start = LocalDate . now ( ) . minusDays ( 7 ) ;
return Arrays . asList ( start , end ) ;
}
Pattern p = Pattern . compile ( " ( \\ d{8})(?:-( \\ d{8}))? " ) ;
Matcher m = p . matcher ( s ) ;
if ( m . find ( ) ) {
LocalDate d1 = parseYmd ( m . group ( 1 ) ) ;
LocalDate d2 = m . group ( 2 ) ! = null ? parseYmd ( m . group ( 2 ) ) : d1 ;
return Arrays . asList ( d1 , d2 ) ;
}
return null ;
}
private LocalDate parseYmd ( String ymd ) {
int y = Integer . parseInt ( ymd . substring ( 0 , 4 ) ) ;
int mo = Integer . parseInt ( ymd . substring ( 4 , 6 ) ) ;
int d = Integer . parseInt ( ymd . substring ( 6 , 8 ) ) ;
return LocalDate . of ( y , mo , d ) ;
}
private String mapDistribution ( String v ) {
if ( v = = null ) return " " ;
if ( v . startsWith ( " H " ) ) return " 鸿 " ;
if ( v . startsWith ( " F " ) ) return " 凡 " ;
return " " ;
}
private boolean contains ( Object field , String kwLower ) {
if ( field = = null ) return false ;
return String . valueOf ( field ) . toLowerCase ( Locale . ROOT ) . contains ( kwLower ) ;
}
private String nvl ( Object v ) {
return v = = null ? " " : String . valueOf ( v ) ;
}
private String fmt ( Object v ) {
if ( v = = null ) return " " ;
if ( v instanceof Number ) {
return String . format ( Locale . ROOT , " %.2f " , ( ( Number ) v ) . doubleValue ( ) ) ;
}
if ( v instanceof Date ) {
java . text . SimpleDateFormat sdf = new java . text . SimpleDateFormat ( " yyyy-MM-dd HH:mm:ss " ) ;
return sdf . format ( ( Date ) v ) ;
}
return String . valueOf ( v ) ;
}
}